CakePHP model relations, how to avoid unnecessary data? - cakephp

I'm learning CakePHP, thus I'm making a simple blog in order to learn basic functionality, this blog will have tables holding posts, users, users activation codes and taxonomy along with taxonomy relationships between posts and tags.
Ok, the thing now is, I've managed to set everything up properly and every time I fetch a post, it returns a lot of data:
Array
(
[Post] => Array
(
[post_id] => 1
[post_title] => Test 1
[post_nice_name] => test-1
[post_author] => 1
[post_content] => I'm testing this piece of crap.
[post_creation_time] => 2011-11-13 22:50:05
[post_last_modification] => 2011-11-13 22:50:05
[post_allow_comments] => 1
[post_allow_trackback] => 1
[post_display] => 1
)
[User] => Array
(
[user_id] => 1
[user_email] => XX#XXXXXXXXXXx.XX.XX
[user_password] => XXXXXXXXXXXXXXXXXX
[user_creation_time] => 2011-11-13 10:48:10
[user_last_login] => 2011-11-13 22:49:21
[user_birthday] => 1993-08-24 03:00:00
)
[TaxonomyTags] => Array
(
[0] => Array
(
[tag_id] => 1
[tag_name] => test1
[tag_description] => This tag is a test
[PostsTaxonomyTag] => Array
(
[relation_id] => 1
[post_id] => 1
[taxonomy_tag_id] => 1
)
)
[1] => Array
(
[tag_id] => 2
[tag_name] => test2
[tag_description] => This tag is just another test.
[PostsTaxonomyTag] => Array
(
[relation_id] => 2
[post_id] => 1
[taxonomy_tag_id] => 2
)
)
)
)
This much data is really unnecessary: I don't need the PostsTaxonomyTag array for every tag, nor do I need that much of the user's information, I don't even need some of the post's information!. So I wanted to know if there's any way to filter this information before it is passed on to the view.

You can unbind models befor a find function to prevent unneeded data
$this->Model->unbind(array('hasMany' => array('assocModel')));
I prefer to use the containable behavior for most models which forces you to state the relationships you need at find though. Mcheck the docs about that behavior.
$this->Model->find(
'all',
array(
'conditions' => array(
//set conditions
),
'contain' => array(
'Model2',
'model3'
)
)
);
You can set conditions, order, etc. in the containable set making it powerful for getting exactly the data you need.

... or you can simple use 'recursive' => -1 in options for find.

Related

Cakephp Internationalization adds '_locale' property in contain associations

Cakephp seems to be adding the property "_locale" on "contain" associations, its ok if this property sits inside the Entity object, but not in the array that holds the associations as it will break a lot of stuff, this only happens when the language is not the default and the Model has the translate behavior, any ideas on how to fix / remove it?
[product_variations] => Array(
[0] => App\Model\Entity\ProductsVariation Object(
[id] => 15
[product_id] => 12
[name] => Adults
[description] => <p>Adult Ticket</p>
[price_mode] => final
[price] => 15
[stock] => -1
[weight] => 0
[sku] => OITGXNRMARTPPDV
[data] =>
[active] => 1
[enquire] =>
[position] => 1
[products_prices] => Array
(
)
[_locale] => pt
[[new]] =>
[[accessible]] => Array
(
[*] => 1
)
[[dirty]] => Array
(
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => ProductVariations
),
[_locale] => pt
)
Update: Seems to be due to the use of the "matching" method "Model->find(...)->matching('ProductVariations')...
The manual explains it:
So, after you save your first article, you can now save a translation for it, there are a couple ways to do it. The first one is setting the language directly into the entity:
$article->_locale = 'es';
$article->title = 'Mi primer Artículo';
$this->Articles->save($article);
So be aware of this when you modify the data. I don't know what happens if you remove it and then save the record or try do some something else i18n related with it. :)
The code related to this property is here in the TranslateTrait.
You can try to add a result formatter by calling formatResults() on the query and unset this property from there.

phpactiverecord retrieve records

Let's make it short, I pass an user id into a function and try to get all attributes for this user:
$user = \Sitename\UsrManager::getUserById($user_id);
I then print_r($user) and get a bunch of data:
Sitename\User Object ( [model:protected] => User Object ( [errors] => [attributes:ActiveRecord\Model:private] => Array ( [user_id] => 18 [company_name] => ABC Ltd [company_logo] => logo.png [brief] => Sample text [profile] => Some Profile ) [__dirty:ActiveRecord\Model:private] => Array ( ) [__readonly:ActiveRecord\Model:private] => [__relationships:ActiveRecord\Model:private] => Array ( ) [__new_record:ActiveRecord\Model:private] => ) [_password_updated:Sitename\User:private] => [template:protected] =>
If I want to get specific attribute such as Company Name, I try echo the company name as $user->company_name, but there is nothing to show, what is the proper way to call data(object?) in my example? it's like similar to the old way to call the necessary data like $row['company_name'].

CakePHP Custom route calling wrong action

I have the following code in routes.php:
Router::connect('/c/details/:id/:slug',
array('controller' => 'cars'),
array('pass' => array('id', 'slug'))
);
If I try to access http://domain.com/c/details/123/abc, it works. However, if I remove abc (ie the slug), CakePHP tries to access the action 123 (which is the id, not the action).
Error: The action 123 is not defined in controller CarsController
If I use /c/details/:id/:slug/:action/*, which was what I had before upgrading from 1.2 (yeah, pretty old) to 2.2.1 and it was working fine, CakePHP also tries to access action 123 whether I have a slug or not.
URLs without slugs always worked before upgrading CakePHP and with the code I have in the controller, if there was no slug in the URL, it would redirect to the right URL.
Edit: I just checked and it seems that when I don't provide with a slug, the whole thing is shifted. c is ignored, details becomes the controller and 123becomes the action.
[request] => CakeRequest Object
(
[params] => Array
(
[plugin] =>
[controller] => details
[action] => 123
[named] => Array()
[pass] => Array()
[isAjax] =>
)
When the correct would be, and which is what I get if I provide with a slug:
[request] => CakeRequest Object
(
[params] => Array
(
[plugin] =>
[controller] => cars
[action] => index
[named] => Array()
[pass] => Array
(
[0] => 123
[1] => abc
)
[id] => 123
[slug] => abc
[isAjax] =>
)
Any idea what may be causing this issue now?
You may need 2 routes if you want the slug to be optional (not sure). In any case, add the action key to each route as well.
Router::connect('/c/details/:id',
array('controller' => 'cars', 'action' => 'details'),
array('pass' => array('id'))
);
Router::connect('/c/details/:id/:slug',
array('controller' => 'cars', 'action' => 'details'),
array('pass' => array('id', 'slug'))
);

Reorder an array based on values

I need to reorder an array so that all slugs get loaded before parents, but only if a parent has a value that matches a slug value exactly.
For example, take this array:
Array (
[0] => Array (
[parent] => information_desk_3
[slug] => about_dream_portal_1
)
[1] => Array (
[parent] => forum
[slug] => information_desk_3
)
[2] => Array (
[parent] => about_dream_portal_1
[slug] => testing_2
)
[3] => Array (
[parent] => testing_2
[slug] => information_desk_4
)
)
I need it so that it gets ordered as follows:
Array (
[0] => Array (
[parent] => forum
[slug] => information_desk_3
)
[1] => Array (
[parent] => information_desk_3
[slug] => about_dream_portal_1
)
[2] => Array (
[parent] => about_dream_portal_1
[slug] => testing_2
)
[3] => Array (
[parent] => testing_2
[slug] => information_desk_4
)
)
But the catch is, it needs to search through all slugs and reorder them so that the slugs come first in the array, before the parents that match the value of that slug. Please someone help me, I've been at it for hours now to no avail.
Sounds like what you really have is a graph, and you want a partial ordering of nodes. That means you can solve the problem using standard graph algorithms.
If you build a tree of items where each parent knows about all its children then you can do a breadth-first traversal of the tree to build your partially ordered output.
In your example it's a very simple graph:
forum -> information_desk_3 -> about_dream_portal_1 -> testing_2 -> information_desk_4
If you start at forum and walk down the tree adding nodes to the output list as you go then you'll wind up with the correct order.
If you have multiple disjoint trees then it gets a little trickier, but still shouldn't be too hard. You just need to figure out which nodes are root nodes and then walk each tree in turn.

Cakephp "joins" destroys conditions?

A bug has a state and hasandbelongs to many usergroups among other things.
Now, when listing the bugs I used the pagination helper and also give the user the ability to filter it by various setting. Works great so far. You can filter it by project, you can filter it by state (via the state_id property of the bug) and by several other items. Now I want it to be filtered by the groups that are responsible for the bug.
Since this is a HABTM connection I used "joins" to connect up the tables.
This is what my $this->paginate looks like:
[limit] => 10
[contain] => Array
(
[0] => Project
[1] => User
[2] => Priority
[3] => State
[Comment] => Array
(
[0] => User
)
[4] => Screenshot
[5] => Group
)
[conditions] => Array
(
[Bug.project_id] => 26
[Bug.state_id] => 1
)
[Bug] => Array
(
[joins] => Array
(
[0] => Array
(
[table] => bugs_groups
[alias] => BugsGroups
[type] => inner
[conditions] => Array
(
[0] => BugsGroups.bug_id = Bug.id
)
)
[1] => Array
(
[table] => groups
[alias] => Group
[type] => inner
[conditions] => Array
(
[0] => Group.id = BugsGroups.group_id
[Group.id] => 9
)
)
)
)
The strange thing is - as soon as I look for a Group by using the
"join" the previous conditions (project_id, state_id) are completely ignored?! Is this expected behavior in cake and how would I circumvent it?
Shouldn't these:
[0] => BugsGroups.bug_id = Bug.id
[0] => Group.id = BugsGroups.group_id
Look like:
[BugsGroups.bug_id] => Bug.id
[Group.id] => BugsGroups.group_id
?
Colleague found the solution - putting the joins inside the "Bug" key was the problem. Moving it one step up ($this->paginate['joins'] instead of $this->paginate['Bug']['joins']) makes it work beautifully...

Resources