6.2 Containable
The original text for this section has changed since it was translated. Please help resolve this difference. You can:
Novinkou v jádru CakePHP je ContainableBehavior. Toto chování modelu Vám umožní filtrovat a limitovat vyhledávací operace. Použití Containable Vám pomůže zmenšit počet dotazů do databáze, zvýší rychlost a celkovou výkonnost aplikace. Pomůže Vám také najít a filtrovat data pro klienty v čisté logické struktuře.
K použití nového chování (behavior) musíte přidat vlastnost $actsAs do modelu:
class Post extends AppModel {
var $actsAs = array('Containable');
}
class Post extends AppModel {var $actsAs = array('Containable');}
Můžete také připojit chování dynamicky (za běhu):
$this->Post->Behaviors->attach('Containable');
$this->Post->Behaviors->attach('Containable');
K předvedení jak Containable pracuje se pojďme podívat na několik příkladů. Začneme voláním find() na modelu Post. Post je spojeno s komentářem (Post hasMany Comment), a pomocí HABTM propojené s tagama (Post hasAndBelongsToMany Tag). Množství dat načtených v normálním volání je celkem značné:
debug($this->Post->find('all'));
debug($this->Post->find('all'));
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[id] => 1
[post_id] => 1
[author] => Daniel
[email] => dan@example.com
[website] => http://example.com
[comment] => First comment
[created] => 2008-05-18 00:00:00
)
[1] => Array
(
[id] => 2
[post_id] => 1
[author] => Sam
[email] => sam@example.net
[website] => http://example.net
[comment] => Second comment
[created] => 2008-05-18 00:00:00
)
)
[Tag] => Array
(
[0] => Array
(
[id] => 1
[name] => A
)
[1] => Array
(
[id] => 2
[name] => B
)
)
)
[1] => Array
(
[Post] => Array
(...
Někdy nepotřebujete tolik informací z Post modelu. Jednou věcí, kterou ContainableBehavior dělá je omezení toho co find() vrací.
Například pro získání informací pouze z modelu Post uděláme následující:
$this->Post->contain();
$this->Post->find('all');
$this->Post->contain();$this->Post->find('all');
$this->Post->find('all', array('contain' => false));
$this->Post->find('all', array('contain' => false));
Máme hotovo, skončili jsme s něčím mnohem více stručným:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
)
[1] => Array
(
[Post] => Array
(
[id] => 2
[title] => Second article
[content] => bbb
[created] => 2008-05-19 00:00:00
)
)
toto ale není novinka - můžete to samé udělat bez ContainableBehavior následovně:
$this->Post->recursive = -1;
$this->Post->find('all');
$this->Post->recursive = -1;$this->Post->find('all');
$this->Post->contain('Tag');
$this->Post->find('all');
$this->Post->contain('Tag');$this->Post->find('all');
$this->Post->find('all', array('contain' => 'Tag'));
$this->Post->find('all', array('contain' => 'Tag'));
Bez Containable bychom museli složitě použít metodu modelu unbindModel(). Containable řeší tento problém čistě.
Containable jde ještě o krok dál: můžete filtrovat data z asociovaných modelů. Pokud se podíváte na výsledek originálního volání find(), všimnete si pole author v Comment modelu. Pokud Vás zajímají pouze údaje z Post a jen autoři komentářů, můžete provést následující:
$this->Post->contain('Comment.author');
$this->Post->find('all');
//or..
$this->Post->find('all', array('contain' => 'Comment.author'));
$this->Post->contain('Comment.author');$this->Post->find('all');//or..$this->Post->find('all', array('contain' => 'Comment.author'));
Containable jsme řekli, aby nám vrátil pouze informace o příspěvku a autory komentářů. Výstup bude vypadat takto:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[author] => Daniel
[post_id] => 1
)
[1] => Array
(
[author] => Sam
[post_id] => 1
)
)
)
[1] => Array
(...
Jak vidite, pole Comment obsahuje pouze pole autor (plus post_id které CakePHP potřebuje k mapování výsledků).
Můžete také filtrovat asociované komentáře (Comment) specifikováním podmínky
$this->Post->contain('Comment.author = "Daniel"');
$this->Post->find('all');
//or...
$this->Post->find('all', array('contain' => 'Comment.author = "Daniel"'));
$this->Post->contain('Comment.author = "Daniel"');$this->Post->find('all');//or...$this->Post->find('all', array('contain' => 'Comment.author = "Daniel"'));
Toto nám vrací příspěvky s komentářem, jejichž autorem je Daniel:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[id] => 1
[post_id] => 1
[author] => Daniel
[email] => dan@example.com
[website] => http://example.com
[comment] => First comment
[created] => 2008-05-18 00:00:00
)
)
)
Dodatečné filtrování může být doplněno standardními volbami Model->find() :
$this->Post->find('all', array('contain' => array(
'Comment' => array(
'conditions' => array('Comment.author =' => "Daniel"),
'order' => 'Comment.created DESC'
)
)));
$this->Post->find('all', array('contain' => array('Comment' => array('conditions' => array('Comment.author =' => "Daniel"),'order' => 'Comment.created DESC'))));
Zde je příklad využívající Containable chování na hlubším a komplexním relačním modelu.
Uvažujme následující vazby:
User->Profile User->Account->AccountSummary User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes User->Post->Tag
Tady je ukázka jak získáme vazby zobrazené výše s Containable:
$this->User->find('all', array(
'contain'=>array(
'Profile',
'Account'=>array('AccountSummary'),
'Post'=>array(
'PostAttachment'=>array(
'fields'=>array('id','name'),
'PostAttachmentHistory'=>array(
'HistoryNotes'=>array(
'fields'=>array('id', 'note')
)
)
),
'Tag'=>array('conditions'=>array('Tag.name LIKE'=>'%happy%'))
)
)
));
$this->User->find('all', array('contain'=>array('Profile','Account'=>array('AccountSummary'),'Post'=>array('PostAttachment'=>array('fields'=>array('id','name'),'PostAttachmentHistory'=>array('HistoryNotes'=>array('fields'=>array('id', 'note')))),'Tag'=>array('conditions'=>array('Tag.name LIKE'=>'%happy%'))))));
Mějte na mysli, že 'contain' klíč je použit pouze jedenkrát v main modelu, nepoužívejte jej znovu v dalších relacích modelů.
# Using Containable
There is no translation yet for this section. Please help out and translate this.. More information about translations
To see how Containable works, let's look at a few examples. First, we'll start off with a find() call on a model named Post. Let's say that Post hasMany Comment, and Post hasAndBelongsToMany Tag. The amount of data fetched in a normal find() call is rather extensive:
debug($this->Post->find('all'));
debug($this->Post->find('all'));
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[id] => 1
[post_id] => 1
[author] => Daniel
[email] => dan@example.com
[website] => http://example.com
[comment] => First comment
[created] => 2008-05-18 00:00:00
)
[1] => Array
(
[id] => 2
[post_id] => 1
[author] => Sam
[email] => sam@example.net
[website] => http://example.net
[comment] => Second comment
[created] => 2008-05-18 00:00:00
)
)
[Tag] => Array
(
[0] => Array
(
[id] => 1
[name] => Awesome
)
[1] => Array
(
[id] => 2
[name] => Baking
)
)
)
[1] => Array
(
[Post] => Array
(...
For some interfaces in your application, you may not need that much information from the Post model. One thing the ContainableBehavior does is help you cut down on what find() returns.
For example, to get only the post-related information, you can do the following:
$this->Post->contain();
$this->Post->find('all');
$this->Post->contain();$this->Post->find('all');
You can also invoke Containable's magic from inside the find() call:
$this->Post->find('all', array('contain' => false));
$this->Post->find('all', array('contain' => false));
Having done that, you end up with something a lot more concise:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
)
[1] => Array
(
[Post] => Array
(
[id] => 2
[title] => Second article
[content] => bbb
[created] => 2008-05-19 00:00:00
)
)
This sort of help isn't new: in fact, you can do that without the ContainableBehavior doing something like this:
$this->Post->recursive = -1;
$this->Post->find('all');
$this->Post->recursive = -1;$this->Post->find('all');
Containable really shines when you have complex associations, and you want to pare down things that sit at the same level. The model's $recursive property is helpful if you want to hack off an entire level of recursion, but not when you want to pick and choose what to keep at each level. Let's see how it works by using the contain() method.
The contain method's first argument accepts the name, or an array of names, of the models to keep in the find operation. If we wanted to fetch all posts and their related tags (without any comment information), we'd try something like this:
$this->Post->contain('Tag');
$this->Post->find('all');
$this->Post->contain('Tag');$this->Post->find('all');
Again, we can use the contain key inside a find() call:
$this->Post->find('all', array('contain' => 'Tag'));
$this->Post->find('all', array('contain' => 'Tag'));
Without Containable, you'd end up needing to use the unbindModel() method of the model, multiple times if you're paring off multiple models. Containable creates a cleaner way to accomplish this same task.
# Containing deeper associations
There is no translation yet for this section. Please help out and translate this.. More information about translations
Containable also goes a step deeper: you can filter the data of the associated models. If you look at the results of the original find() call, notice the author field in the Comment model. If you are interested in the posts and the names of the comment authors — and nothing else — you could do something like the following:
$this->Post->contain('Comment.author');
$this->Post->find('all');
//or..
$this->Post->find('all', array('contain' => 'Comment.author'));
$this->Post->contain('Comment.author');$this->Post->find('all');//or..$this->Post->find('all', array('contain' => 'Comment.author'));
Here, we've told Containable to give us our post information, and just the author field of the associated Comment model. The output of the find call might look something like this:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[author] => Daniel
[post_id] => 1
)
[1] => Array
(
[author] => Sam
[post_id] => 1
)
)
)
[1] => Array
(...
As you can see, the Comment arrays only contain the author field (plus the post_id which is needed by CakePHP to map the results).
You can also filter the associated Comment data by specifying a condition:
$this->Post->contain('Comment.author = "Daniel"');
$this->Post->find('all');
//or...
$this->Post->find('all', array('contain' => 'Comment.author = "Daniel"'));
$this->Post->contain('Comment.author = "Daniel"');$this->Post->find('all');//or...$this->Post->find('all', array('contain' => 'Comment.author = "Daniel"'));
This gives us a result that gives us posts with comments authored by Daniel:
[0] => Array
(
[Post] => Array
(
[id] => 1
[title] => First article
[content] => aaa
[created] => 2008-05-18 00:00:00
)
[Comment] => Array
(
[0] => Array
(
[id] => 1
[post_id] => 1
[author] => Daniel
[email] => dan@example.com
[website] => http://example.com
[comment] => First comment
[created] => 2008-05-18 00:00:00
)
)
)
Additional filtering can be performed by supplying the standard Model->find() options:
$this->Post->find('all', array('contain' => array(
'Comment' => array(
'conditions' => array('Comment.author =' => "Daniel"),
'order' => 'Comment.created DESC'
)
)));
$this->Post->find('all', array('contain' => array('Comment' => array('conditions' => array('Comment.author =' => "Daniel"),'order' => 'Comment.created DESC'))));
Here's an example of using the ContainableBehavior when you've got deep and complex model relationships.
Let's consider the following model associations:
User->Profile User->Account->AccountSummary User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes User->Post->Tag
This is how we retrieve the above associations with Containable:
$this->User->find('all', array(
'contain'=>array(
'Profile',
'Account' => array(
'AccountSummary'
),
'Post' => array(
'PostAttachment' => array(
'fields' => array('id', 'name'),
'PostAttachmentHistory' => array(
'HistoryNotes' => array(
'fields' => array('id', 'note')
)
)
),
'Tag' => array(
'conditions' => array('Tag.name LIKE' => '%happy%')
)
)
)
));
$this->User->find('all', array('contain'=>array('Profile','Account' => array('AccountSummary'),'Post' => array('PostAttachment' => array('fields' => array('id', 'name'),'PostAttachmentHistory' => array('HistoryNotes' => array('fields' => array('id', 'note')))),'Tag' => array('conditions' => array('Tag.name LIKE' => '%happy%'))))));
Keep in mind that contain key is only used once in the main model, you don't need to use 'contain' again for related models
When using 'fields' and 'contain' options - be careful to include all foreign keys that your query directly or indirectly requires. Please also note that because Containable must to be attached to all models used in containment, you may consider attaching it to your AppModel.
# ContainableBehavior options
There is no translation yet for this section. Please help out and translate this.. More information about translations
The ContainableBehavior has a number of options that can be set when the Behavior is attached to a model. The settings allow you to fine tune the behavior of Containable and work with other behaviors more easily.
- recursive (boolean, optional) set to true to allow containable to automatically determine the recursiveness level needed to fetch specified models, and set the model recursiveness to this level. setting it to false disables this feature. The default value is
true. - notices (boolean, optional) issues E_NOTICES for bindings referenced in a containable call that are not valid. The default value is
true. - autoFields: (boolean, optional) auto-add needed fields to fetch requested bindings. The default value is
true.
You can change ContainableBehavior settings at run time by reattaching the behavior as seen in Using behaviors
ContainableBehavior can sometimes cause issues with other behaviors or queries that use aggregate functions and/or GROUP BY statements. If you get invalid SQL errors due to mixing of aggregate and non-aggregate fields, try disabling the autoFields setting.
$this->Post->Behaviors->attach('Containable', array('autoFields' => false));
$this->Post->Behaviors->attach('Containable', array('autoFields' => false));


























