====== Has many ====== The information below is copied from the API documentation on //has_many// associations\\ http://api.akelos.org/ActiveRecord/Associations/AkHasMany.html **$has_many** adds the following methods for retrieval and query of collections of associated objects. 'collection' is replaced with the singular form of current association, so **var $has_many = 'clients'** would hold an array of objects on **$this->clients** and a collection handling interface instance on **$this->client** (singular form) * collection->**load**($force_reload = false) - returns an array of all the associated objects. An empty array is returned if none are found. * collection->**add**($object, ?) - adds one or more objects to the collection by setting their foreign keys to the collection's primary key. (collection->push and $collection->concat are aliases to this method). * collection->**delete**($object, ?) - removes one or more objects from the collection by setting their foreign keys to NULL. This will also destroy the objects if they’re declared as [[belongs_to|belongs_to]] and dependent on this model. * collection->**set**($objects) - replaces the collections content by deleting and adding objects as appropriate. * collection->**setByIds**($ids) - replace the collection by the objects identified by the primary keys in ids * collection->**clear**() - removes every object from the collection. This destroys the associated objects if they are 'dependent', deletes them directly from the database if they are 'dependent' => 'delete_all', and sets their foreign keys to NULL otherwise. * collection->**isEmpty**() - returns true if there are no associated objects. * collection->**getSize**() - returns the number of associated objects. * collection->**find**() - finds an associated object according to the same rules as ActiveRecord->find. * collection->**count**() - returns the number of elements associated. (collection->size() is an alias to this method) * collection->**build**($attributes = array()) - returns a new object of the collection type that has been instantiated with attributes and linked to this object through a foreign key but has not yet been saved. *Note:* This only works if an associated object already exists, not if it?s null * collection->**create**($attributes = array()) - returns a new object of the collection type that has been instantiated with attributes and linked to this object through a foreign key and that has already been saved (if it passed the validation). *Note:* This only works if an associated object already exists, not if it?s null ==== Example ==== A Firm class declares has_many clients, which will add: $Firm->client->load() (similar to $Clients->find('all', array('conditions' => 'firm_id = '.$id)) ) $Firm->client->add() $Firm->client->delete() $Firm->client->assign() $Firm->client->assignByIds() $Firm->client->clear() $Firm->client->isEmpty() (similar to count($Firm->clients) == 0) $Firm->client->getSize() (similar to Client.count "firm_id = #{id}") $Firm->client->find() (similar to $Client->find($id, array('conditions' => 'firm_id = '.$id)) ) $Firm->client->build() (similar to new Client(array('firm_id' => $id)) ) $Firm->client->create() (similar to $c = new Client(array('firm_id' => $id)); $c->save(); return $c ) The declaration can also include an options array to specialize the behavior of the association. ==== Options ==== * '**class_name**' - specify the class name of the association. Use it only if that name can't be inferred from the association name. So "$has_many = 'products'" will by default be linked to the Product class, but if the real class name is SpecialProduct, you?ll have to specify it with this option. * '**conditions**' - specify the conditions that the associated objects must meet in order to be included as a "WHERE" sql fragment, such as "price > 5 AND name LIKE ?B%?". * '**order**' - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment, such as "last_name, first_name DESC" * '**group**' - specify the attribute by which the associated objects are returned as a "GROUP BY" sql fragment, such as "category" * '**foreign_key**' - specify the foreign key used for the association. By default this is guessed to be the name of this class in lower-case and "_id" suffixed. So a Person class that makes a has_many association will use "person_id" as the default foreign_key. * '**dependent**' - if set to 'destroy' all the associated objects are destroyed alongside this object by calling their destroy method. If set to 'delete_all' all associated objects are deleted without calling their destroy method. If set to 'nullify' all associated objects? foreign keys are set to NULL without calling their save callbacks. * '**finder_sql**' - specify a complete SQL statement to fetch the association. This is a good way to go for complex associations that depend on multiple tables. Note: When this option is used, findInCollection is not added. * '**counter_sql**' - specify a complete SQL statement to fetch the size of the association. If +'finder_sql'+ is specified but +'counter_sql'+, +'counter_sql'+ will be generated by replacing SELECT ? FROM with SELECT COUNT(*) FROM. * '**include**' - specify second-order associations that should be eager loaded when the collection is loaded. * '**group**' An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. * '**limit**' An integer determining the limit on the number of rows that should be returned. * '**offset**' An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. * '**select**' By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not include the joined columns. === Option examples === $has_many = array( 'comments' => array('order' => 'posted_on', 'include' => 'author', 'dependent' => 'nullify'), 'people' => array('conditions' => 'deleted = 0', 'order' => 'name'), 'tracks' => array('order' => 'position', 'dependent' => 'destroy'), 'members' => array('class_name' => 'Person', 'conditions' => 'role = "member"'));