11
The minimal data set that must be defined is the database name, table name, fields, and primary key:
13
class ConcreteCoughObject extends CoughObject {
14
protected $dbName = 'db_name';
15
protected $tableName = 'table_name';
16
protected $fields = array(
17
'field1' => 'field1_default_value',
18
'field2' => 'field2_default_value',
19
'field3' => 'field3_default_value',
21
'fieldn' => 'fieldn_default_value'
23
protected $pkFieldNames = array('field1'); // multi-key PK example: array('field1','field2');
26
Optional definitions include object and collection definitions:
28
class ConcreteCoughObject extends CoughObject {
29
protected $objectDefinitions = array(); // hash of [object_name] => [object definition]
30
protected $collectionDefinitions = array(); // hash of [object_name] => [object definition]
33
CoughObject calls a `initDefinitions()` function in the constructor which calls the following methods that may be used to override any of the above definitions:
35
protected function initDefinitions() {
36
$this->defineObjects();
37
$this->defineCollections();
40
If there is a need to populate object definitions by appending to the definitions that are already present, it is preferred to do it in `defineObjects` or `defineCollections` like so:
42
protected function defineObjects() {
43
parent::defineObjects();
44
$this->objectDefinitions['object_name'] = array(); // fill in the array with the definitions
46
protected function defineCollections() {
47
parent::defineCollections();
48
$this->collectionDefinitions['collection_name'] = array(); // fill in the array with the definitions
51
If needing to change the database name, table name or fields / default field values, just set them at the member variable level. If needing to do it at run-time, do it in the `initDefinitions()` method like so:
53
protected function initDefinitions() {
54
parent::initDefinitions();
55
$possibleDbs = array('database1','database2');
56
$dbIndex = array_rand($possibleDatabases);
57
$this->dbName = $possibleDatabases[$dbIndex];
63
Construction of a CoughObject can be done in a few ways:
65
Using a single value that is the primary key id of the object:
67
$object = new Object($id);
68
$object = Object::construct($id); // TODO: if ID can not be found, should NULL be returned or an empty object?
70
The above methods will initialize the key ID and attempt to look up their related data in the database. If that data is already available, the following methods will work.
72
Using pre-existing data in array form (format of [field_name] => [field_value]):
74
$object = new Object($hash);
75
$object = Object::construct($hash);
77
The `construct()` static method will call one of two other static methods:
79
public static function constructByPk($idOrIdArray) {
80
if (is_array($idOrIdArray)) {
81
$object = new Object($idOrIdArray);
84
$object = new Object($idOrIdArray);
86
return $object; // TODO if load gets no data, should we return NULL?
89
If you have a multi-key primary key, then you will have to use the `constructByPk()` method.
91
public static function constructByFields($hash) {
92
return new Order($hash);
96
### Example of overriding the static method (TODO: Moved to advanced section?) ###
98
In this static method example, either an Order or a Quote object is returned.
100
$ticket = Ticket::construct($hash);
102
The above example might work with an overridden `constructByFields` method (which `construct` will call), defined as follows:
104
class Ticket extends CoughObject {
105
const TYPE_QUOTE = 1;
106
const TYPE_ORDER = 2;
107
public static function constructByFields($fields) {
108
switch ($fields['ticket_type_id']) {
109
case Ticket::TYPE_QUOTE:
110
return new Quote($fields);
112
case Ticket::TYPE_ORDER:
113
return new Order($fields);
122
The hash data might look like:
126
'ticket_type_id' => Ticket::TYPE_QUOTE,
127
'customer_id' => 312,
128
'order_placed_datetime' => '2007-01-01 00:00:00'
133
$order = new Order($fields); // initializes the object with pre-existing data. `isInflated` will return true, as it is assumed the pre-existing data was pulled from the source.
135
$order = new Order($id); // loads from database. Find out if load succeed via `$order->isInflated()` <- TODO: Rename that function.
138
$order->load(); // loads from the database using the current key id. This method is useful when trying to construct a multi-PK object
140
$order = new Order($multiKeyHash);
143
$ticket = Ticket::construct($hash); // will switch through the hash to figure out with type of object to return, an Order or a Quote.
146
Accessing Join attributes (this is collection-related topic)
147
-------------------------
149
Need to standardize the way of accessing join fields.
152
Static Construction Implementation
153
----------------------------------
155
After given this some thought, why don't we try this:
157
protected static $dbName = 'asdlfjk';
159
the problem is that the member definition can only appear once, otherwise functions in parent classes where it is defined will not see the change. We can not overcome this by adding a `public static defineDbCOnfig()` method because the same problem will occur: parent classes will call the wrong one.
161
This might be why Propel uses a "Peer" class which contains static methods?
163
But, maybe one appearance is enough? If we are using the generator,then it can generate all these static methods and attributes for us. The question is how valuable is being able to override dbName, tableName, and all definitions (for fields, objects, and collections)? One would think the information is not dynamic at run time so there is no value in being able to override it -- you'd just change the values that are there. But, maybe someone wants to reuse some logic and they do it by extending an existing class and customizing some logic? Sounds like in that case you should be using a different design pattern, perhaps the Strategy Pattern.
165
What if we generate, in the starter classes, the static methods and member variables that are needed? This allows Cough to call the methods, allows the end user to customize them, and the only drawback I can think of is that hand-writing a Cough class might be harder (because of the static methods, the member variables are easy and required already).
167
Perhaps we should take a look at how much work there is in the static methods, and see if we can't have some of the static methods in the core Cough class... (e.g. can you do self::$dbName in a parent class when there isn't one defined until a sub class?)
169
Even if we don't want to require static methods, we could also provide an option for it. The problem is that we are trying to allow you to have a factory method that returns an object of the right type, but core Cough needs to know what that method is otherwise it will be stuck constructing only one type of object and not use your factory method (e.g. CoughCollection code that creates your elements... Now, we currently have an option to pass in the element name (if wanting to override it, but maybe we could set a variable for the factory method, if any, or even provide code that can be evaled))
172
Loading / Setting objects.
173
--------------------------
175
Listen closely, because this might solve the confusion about what setCollectionName_Collection() does (set the reference to the collection or call set on the collection which will "set" the state of the collection to what you give it, i.e. perform any needed adds and removes).
179
What if the load methods for objets and collections support parameters? For example, if you have preloaded a related object you need a way to set it so that an extra lookup isn't done. We were considering:
183
$manuf = new Manufacturer(1);
184
$product = new Product(1);
185
$product->setManufacturer_Object($manuf);
187
// And then if you call get it doesn't load because the object is already available.
188
$product->getManufacturer();
194
$manuf = new Manufacturer(1);
195
$product = new Product(1);
196
$product->loadManufacturer_Object($manuf);
199
What we are saying here is that the load method will check arguments and only perform the load if nothing was passed in. If something was passed in, it will still be setting the object, it just won't do a database lookup.
201
NOTE: object loading should setObject (i.e. we are abstracting away the object data structure this time around, so use setObject/getObject)
203
Example object load method:
206
public function loadManufacturer_Object($hashOrObject = null) {
207
if (is_null($hashOrObject)) {
208
// Do db lookup to get hash.
209
$sql = Manufacturer::getLoadSqlWithoutWhere();
210
$sql . = ' WHERE ' . $this->getDb()->generateWhere($this->getPk())
212
else if (is_array($hashOrObject)) {
215
else if (is_object($hashOrObject)) {
216
// We got the object, just set it:
217
$this->setObject('manufacturer', $hashOrObject);
222
Static methods will need a static db object too... we should provide a getter for non static methods and static methods alike:
224
self::getDb()->generateWhere($this->getPk())
226
The method might look like:
228
public static function getDb() {
229
if (is_null(self::$db)) {
230
self::$db = DatabaseFactory::getDatabase(self::$dbName);
235
If we go that route we might need to require that all generated classes implement a CoughStaticInterface or something that says the following methods must be defined:
237
public static function getDb();
238
public static function constructByKey($pk);
239
public static function constructByFields($hash);
240
public static function construct(); // ?