Removing a belongsTo from a model shows warning Model 'x' is not associated with model 'y' - cakephp

I am new to CakePHP and I am having to maintain/develop a CakePHP application and struggling with removing a BelongsTo in one of the models.
The code has been copied from another project that used a lot more related tables. In the new project I do not need the relationships because it is creating LEFT JOINS in the queries that I really want to get rid of in order to speed things up. So I went in to the model and removed the $belongsTo property..
When I go to the home page I now get a warniing:-
Warning (512): Model "Product" is not associated with model "ParentProduct"
[CORE/Cake/Model/Behavior/ContainableBehavior.php, line 343]
If I turn off debugging it is not shown but I would like to know why this message is being generated. I am unable to find any information on how I should go about removing the belongsTo relationships.
Any help appreciated.

So I went in to the model and removed the $belongsTo property
Because you're asking for it
This kind of error is displayed when using the containable behavior incorrectly.
E.g. Consider User hasMany Article, and the following code:
$User->find('all', array(
'contain' => array(
'Article'
)
));
This would return all users and their articles (only), irrespective of any other associations that exist.
If an association is requested that isn't defined:
$User->find('all', array(
'contain' => array(
'Article',
'Comment'
)
));
Containable will generate a warning so that you, the developer, can know that there will be no Comments in your results which either indicates a typo or another kind of development error (Ah... it's User<-Article<-Comment).
This is in-keeping with what you describe since the model associations have been modified but (evidently) not the application code.

The message is been generated (most probably) because in your controller (check the ParentProductsController first) is something like
$this->ParentProductsController->Product->find();
That association of models no longer exists because you wanted to get rid of it, so there's no way for ParentProducts to call Product. You need to change calls like that to
ClassRegistry::init('the_name_of_the_model');
(in this case ClassRegistry::init('Product');) or load the model with
$this->loadModel('Product');
Now, this only is necessary when you call models not related to the current controller. If you are calling Product->find from the ProductsController, there will be no error.
Ok, that's the cause of the error.
But now: I don't recommend you deleting the association. There's really no need and your removing a logical association in the code even though it's still there in the database. There are other ways to avoid left joins and useless queries to the database without cutting someone a limb.
If you want to speed things up, read about Containable Behavior, and set all models to $recursive = -1 (recursive doc). That way you won't get LEFT JOINS unless you explicitly say so.

Related

How do I return models related to a related model?

I have 3 models.
Webcast and Tag are associated witha HABTMA association.
Webcast and Host are associated with a hasMany relationship (Webcast has many Host).
When I do a Tag->find I get Tag and Webcast models, however I want to get all 3. How can I go about that?
If your query is using $this->webcast->find then you'll get everything your looking for, except you can't search 'TAG' without joining the tables before the query. If you want to search 'TAG' which I recommend in this situation then your need to go into your Tag model and create relationships there too.
Tag HABTM Webcast
should do it. If you're not getting host then try 'recursive' => 2 in your query.
$this->Tag->find('all');
OR
$this->Tag->find('all', array('recursive' => 2));
From docs
The recursive property defines how deep CakePHP should go to fetch associated model data via find(), and read() methods.
http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive

Retrieving a belongsTo relationship data in a hasOne relationship (deep associations)

I'm building this CakePHP application with a Teaser model, each Teaser has one Location.
Each Location belongs to a Country and a State.
On the index side of Teaser (and view) I want to display the Country.abbr and State.title of the Location associated with this Teaser
Associations
Teaser
public $hasOne = array('Location');
Location
public $belongsTo = array('Teaser','Country','State');
Country
public $hasMany = array('Location');
State
public $hasMany = array('Location');
What have I tried
Default
If I do the following
$teasers = $this->Teaser->find('all');
in my TeasersController then it won't fetch the correct Country and State. I have seen it working and I had no problem to fetch a country title in my view through the Teaser model as follows:
$teaser['Location']['Country']['title'];
$teaser['Location']['State']['title'];
So first this worked, after working on some other stuff I noticed this stopped working (not sure if other stuff had influenced this, but I didn't touched the model. As a good programmer I started looking here.
Containable behaviour
I've read about the Containable behaviour being amazing so I looked up on how to use it and came up with the following.
$this->Teaser->Behaviors->attach('Containable');
$teasers = $this->Teaser->find('all', array(
'contain' => array(
'Location' => array(
'Country',
'State'
),
'User',
),
));
$this->set('teasers', $teasers);
This does not work, it gives the following error:
Model "Location" is not associated with model "Country"
Recursive parameter
I've tried setting recursive to 2 (or -1 in association with the Containable Behaviour) as it was a solution before for others, but I've seen comments on how you better stick with the Containable Behaviour.
Actual question
So, Does anyone has an idea what I'm doing wrong here? Is the syntax wrong or am I getting these associations all mixed up?
Related question
Furthermore a related question on the side I've left out in the main part, but is an association between Country and State a wise idea? Our would this just complicate things even more?
Rather than adding the containable behavior on the fly, I would add the following properties to your AppModel, since using containable should be the default state, rather than the exception.
public $actsAs = array('Containable');
public $recursive = -1;
In case you weren't already aware, the reason people say use containable instead of recursive is that recursive will get everything all the time, even when you don't need it. So recursive is easy to use, but it encourages bad habits.
As for your query, I don't see anything that jumps out at me as being incorrect. I would try, as a test only, doing
$this->Teaser->recursive = 2;
$teasers = $this->Teaser->find('all');
Then see what it comes back with. If it doesn't contain your associated data, you'll know there's something wrong with the associations themselves. If it does get the associated data, then it's something with the query.
As for your related question, that's more of design question which doesn't necessarily have a right answer.

setSource not taken into account by Cakephp tree behavior

Tree behavior and associations is working well in my application.
I'm trying to switch tree table on runtime, which works well with following code in beforeFind method:
$this->setSource($table);
However, when I'm reordoning the tree using the recover method, the table name is not taken into account for all queries: I'm getting SHOW COLUMN queries on the new table, and SELECT queries on the default one. I tried to disable and clear cache of my application without success.
I'm also changing the table of associated Models, but problem arrises without it too.
Any piece of advice will be appreciated.
Edit:
Here is the model used:
https://github.com/croogo/croogo/blob/1.3/models/taxonomy.php
I disabled (permanently, not at runtime) the Cache behavior.
I traced the problem into recover method, the bindModel does not take into account the useTable.
$Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
'className' => $Model->name,
'foreignKey' => $parent,
'fields' => array($Model->primaryKey, $left, $right, $parent),
))));
I tried without success:
$Model->VerifyParent->useTable = $Model->useTable;
I opened a lighthouse ticket:
https://cakephp.lighthouseapp.com/projects/42648/tickets/3820-cannot-change-table-name-using-treebehaviorrecover-in-shell
belongsTo associations cannot define custom tables for the linked model. You may need to seed ClassRegistry with a properly configured model with the VerifyParent key.
Problem can be fixed by modifying the Tree behavior:
$Model->VerifyParent->setSource($Model->useTable)
This a bug accepted and referenced here:
https://cakephp.lighthouseapp.com/projects/42648/tickets/3820-cannot-change-table-name-using-treebehaviorrecover-in-shell
However, not sure it will be fixed in cakephp source as it is very specific.

Filtering Containable Model Results

I am trying with the following line of code to filter all the Posts that have certain (irrelevant) conditions, by the tag "who"
$com= $this->Post->find('all', array('conditions' => $conditions, 'contain' => 'Tag.tag="who"'));
However, instead of the results being only the posts with the tags, I get every single post with empty tag arrays for the ones that don't have the tag "who".
I know my question has come up before, but the solutions posted have not worked with my code.
I have tried adapting the code here to my own:
http://web-development-blog.co.uk/2010/09/14/cakephp-habtm-find-with-conditions-and-containable-behavior/, but I get an SQL error stating "Tag.post_id" not found in on clause.
Please help.
Error message when trying to implement code from selected link:
SELECT `Post`.`id`, `Post`.`title`, `Post`.`body`, `Post`.`created`, `Post`.`modified`, `Tag`.`id`, `Tag`.`tag`, `Tag`.`created`, `Tag`.`modified` FROM `posts` AS `Post` LEFT JOIN `tags` AS `Tag` ON (`Tag`.`post_id` = `Post`.`id`) WHERE `Tag`.`tag` = 'who' 1054: Unknown column 'Tag.post_id' in 'on clause'
Which is caused by using this:
$this->Post->bindModel(array('hasOne' => array('Tag')));
$this->Post->contain(array(
'Tag'
));
$com=$this->Post->find('all', array(
'conditions'=>array('Tag.tag'=>'who')
));
Your error is that you are using bindModel wihtout saying the foreignkey or anything, you SHOULD establish the associations in the model and just use this on-the-fly method when is strictly neccesary.
you need to tell cake which foreignkey of not cake will use the default, in your case it will try Tag.post_id.
Also you should use a has many association or at least i think so. I said this beacause i suppose a post may have multiple tags.
once you do the asociation in the model and delete this line
$this->Post->bindModel(array('hasOne' => array('Tag')));
it should work perfectly
OR you can do the association with the correct parameters on the fly read the cookbook for this, the example that shows how to put the classname is the one you seek, just add the other attributes
EDIT:
Sorry, read something wrong... the has many association doesn't do a join so you have to use contain to specify the condition or add a condition to the association on the fly, here is how your code should look after adding the conditions with contain
$contain = array(
'Tag' => array(
'conditions'=> array('Tag.tag'=>'who')
)
);
$com=$this->Post->find('all', array(
'contain'=>$contain
));
REMEMBER contain conditions override the conditions in the associations!! if you want to have extra conditions you must add them on the fly something like:
$this->Post->hasMany['Tag']['conditions'] += array('Tag.tag'=>'who');
Remember that associations on the fly will work for that instance ONLY.
Also you may do a hasmany alias for different types (remember to define always the classname and foreignkey).
Hope it works this time, if not just comment to see it fix ;)

cakePHP, Model Recursion

I have a few models, all with appropriately named model files.
$this->Property->PropertyImage->Image->read();
All linked accordingly. Problem is, the recursive model is not able to attach to all the relationships and for some reason is returning AppModel data type when i do a var_dump on $this->PropertyImage. When i do a var_dump($this->Property); i get data type: Property.
What is going on here, what would cause this to happen? Also how can I fix this problem?
Do you hava a PropertyImage model in your application or is it a HABTM association? If you're having a Property which hasAndBelongsToMany Image, you need a pivot table (properties_images) in the database, but to access Image model from the PropertiesController, you'd do $this->Property->Image without anything in between.
Building on what Marko said, if you have a HABTM relationship your best bet is to use a join table, properties_images.
Then rather than doing a Property->PropertyImage->Image, you would just do a Property->Image->read/find().
What I failed to understand about the HABTM relationship is how to filter based on criteria in the related model. E.g., you cannot do this:
$this->Property->Image->find( 'all', array( 'conditions' => array( 'Image.id' => 7 ) ) );
Instead, you have to add the Containable behavior to the Property model, as described in the manual at http://book.cakephp.org/view/474/Containable.

Resources