CakePHP 2.0 - containable behavior not containing model - cakephp

*EDIT/UPDATE*
I found a solution but don't really know why this has to be. If anyone could answer why this is that would be great.
I found that the MediaFloorplan would show up in my data when I added "floorplan_id" to my field list for MoveInHome being that MoveInHome hasMany Floorplan. Strange because "id" is found within the fields for the contained "Floorplan"
*END EDIT/UPDATE*
I'm working on a project for a home builder and am having a problem getting some data via containable.
App model has this:
public $actsAs = array("Containable");
I'm making a call from FloorplansController to MoveInHome model
$this->loadModel("MoveInHome");
$moveInReadyData = $this->MoveInHome->getByCommunity($communityid,4);
MoveInHome belongsTo Floorplan
Floorplan hasMany MediaFloorplan
Here's the function in the model:
public function getByCommunity($id,$limit=4) {
$params = array(
"fields" => array(
"address",
"city",
"state",
),
"conditions" => array(
"MoveInHome.community_id" => $id,
"MoveInHome.active" => 1,
"MoveInHome.sold" => 0
),
"limit" => $limit,
"order" => "RAND()",
"contain" => array(
"MediaMoveInHome" => array(
"order" => array(
"featured" => "DESC"
),
"fields" => array(
"media_move_in_home_file_path",
),
"limit" => 1,
),
"Floorplan" => array(
"fields" => array(
"id",
"name",
"style",
"bedrooms",
"bathrooms",
"sf_bsmt",
"sf_1flr",
"sf_2flr"
),
"MediaFloorplan" => array(
"conditions" => array(
"MediaFloorplan.type" => "main",
),
"fields" => array(
"id",
"media_floorplan_file_path"
)
)
)
)
);
return $this->find("all", $params);
}
All of the data comes back fine except for that MediaFloorplan doesn't come back at all. Note that I've made a similar contain request in other areas of the site. Would the problem stem from making the request from the FloorplansController to MoveInReady and trying to contain Floorplan, etc?
Thanks for the help.

As you found yourself, and DavidYell clarified in his comment on the question, you have to include the foreign keys in the fields option.
From the actual CakePHP documentation: here
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.

Related

Getting data by search filter from associated models

i have an question regarding search filter in cakephp. Without complicating my question, below are the structure of what i want....
1) I have a projects table.
2) another one is project_funder_names table which is associated with projects table. project_id is in project_funder_names table. i have made project_funder_names table because i need multiple funder names for a single project, thats why i have made this table.
3) now the main point is i want if i search multiple funders in search filter which is coming in dropdown with checkbox, i will get project details according to these values. so how it would happen.
here is my cakephp find all query....
$project_info = $this->Project->find('all', array(
'conditions' =>
array(
'Project.status' => 1,
'OR' => array($search)),
'fields' => array('id', 'title', 'short_description', 'budget_allocation', 'currency', 'total_comments', 'published_date'),
'contain' => array(
'ProjectFunderName' => array(
'conditions' => array($search_funder)),
'Currency' => array('currency_symbol'),
'ProjectBookmark' => array('project_id', 'user_id')
)
)
);
problem is in $search_funder.
please help me for this.. thanks.
Looks like you need to search results based on associated models. One drawback of using containable behavior is if you're trying to assign a condition to an associated model, the main model will be retrieved no matter what.
In situations where you'd want to retrieve the main as well as the associated records based on a condition for the associated model, I'd suggest you to use join.
$joins = array(
array('table' => 'project_funder_names',
'alias' => 'ProjectFunderName',
'type' => 'LEFT',
'conditions' => array('ProjectFunderName.project_id = Project.id')
),
array('table' => 'currencies',
'alias' => 'Currency',
'type' => 'LEFT',
// It's unclear how currencies is associated with the other tables. Use appropriate table join condition here
),
array('table' => 'project_bookmarks',
'alias' => 'ProjectBookmark',
'type' => 'LEFT',
'conditions' => array('ProjectBookmark.project_id = Project.id')
)
)
);
$project_info = $this->Project->find('all',
array(
"joins" => $joins,
"fields" => array(.........) // Specify all your desired fields here
"conditions" => array(....) // Specify all conditions for Project, ProjectFunderName models.
)
);
Hope this helps.
Peace! xD

Using limit() on contained model

The Code
Say I have two models, named Product and Image, which are linked by Product hasMany Image and Image belongsTo Product.
Now, say I want to fetch all products with the first image each. I would use this code:
$this->Products->find('all')
->contain([
'Images' => function($q) {
return $q
->order('created ASC')
->limit(1);
}
]);
Looks about right, right? Except now only one of the products contains an image, although actually each product contains at least one image (if queried without the limit).
The resulting Queries
The problem seems to be with the limit, since this produces the following two queries (for example):
SELECT
Products.id AS `Products__id`,
FROM
products Products
and
SELECT
Images.id AS `Images__id`,
Images.product_id AS `Images__product_id`,
Images.created AS `Images__created`
FROM
images Images
WHERE
Images.product_id in (1,2,3,4,5)
ORDER BY
created ASC
LIMIT 1
Looking at the second query, it is quite obvious how this will always result in only one image.
The Problem
However, I would have expected the Cake ORM to limit the images to 1 per product when I called limit(1).
My question: Is this an error in how I use the ORM? If so, how should I limit the number of images to one per image?
The cleanest way you can do this is by creating another association:
$this->hasOne('FirstImage', [
'className' => 'Images',
'foreignKey' => 'image_id',
'strategy' => 'select',
'sort' => ['FirstImage.created' => 'DESC'],
'conditions' => function ($e, $query) {
$query->limit(1);
return [];
}
])
Check it ,this one is my code
$this->Orders->hasOne('Collections', [
'className' => 'Collections',
'foreignKey' => 'order_id',
'strategy' => 'select',
'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) {
$query->order(['Collections.id' => 'ASC']);
return [];
}
]);
$Lists = $this->Orders->find('all')->where($condition)->contain(['Collections'])->order(['Orders.due_date DESC']);
$this->set(compact('Lists'));
If the reason you are limiting it to one image is that you want to have a defaulted image. You might consider adding a default field in the image table and doing a Alias like this:
var $hasOne = array(
'CoverImage' => array(
'className' => 'Image',
'conditions' => array('CoverImage.default' => true),
),
It looks like you are using Cake v3 so you can just add the equivalent association as the above is a 2.x sample.
In the Model definition we would do the following:
public $hasMany = [
'ModelName' => [
'limit' => 1,
'order' => 'ModelName.field DESC'
],
];
as described in the docs: https://book.cakephp.org/2/en/models/associations-linking-models-together.html#hasmany

CakePHP save failing with validation errors set but empty

A model that I am using with both TranslateBehavior and TreeBehavior is failing to save. I suspect it is something to do with one of these behaviors. Here's the data going in:
array(
'Category' => array(
'id' => '3252',
'parent_id' => null,
'sort_order' => '0',
'name' => array(
'eng' => 'english name',
'fra' => 'french name',
'deu' => 'german name',
'ita' => 'italian name',
'spa' => 'spanish name'
),
)
)
And $this->Category->validationErrors is:
array(
'parent_id' => array(),
'sort_order' => array()
)
There are no validation rules defined in the model. There is no beforeSave() or beforeValidate() method defined.
I am not getting any validation errors displayed on screen, although all fields have the red "error" outline.
Edit - the save operation is not getting as far as Category::beforeSave(). I created that function and it does not get run. It gets as far as Category::beforeValidate().
$validates = $this->Category->validates($this->request->data);
debug($validates);
$saved = $this->Category->saveAll($this->request->data);
debug($saved);
In the above code, $validates is true, $saved is false. Category beforeSave is not called at any point in the above process. The validation seems to fail on the call to saveAll(). I need to use saveAll rather than save to save all translations at once (I am doing this elsewhere with another model with no problems).
So, after a while debugging I have found the problem:
public $hasMany = array(
'Category' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
I have no idea why I wrote this - I should have been aware that it was going to cause problems, I think I meant to write...
public $hasMany = array(
'Children' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
Anyway, changed it to the latter and these errors have gone.
Maybe it doesn't like the null and zero value of parent_id and sort_order? Also in the database what are their field types set as? Do they allow null values? etc. I'm guessing that as there are no validation rules in the model or parent/App model, then it must be some default validation with cake's lib linking to the database/mysql table itself. So I would check the Categories table structure for the parent_id and sort_order fields.

Containable with Condition

I am using containable with CakePHP. My tried code is ...
public function detail($slug = "") {
$this->Poet->contain('User.id', 'User.full_name', 'Song.id', 'Song.name', 'Song.name_hindi', 'Song.slug');
$result = $this->Poet->findBySlug($slug);
if (!$result) {
throw new NotFoundException(__('Invalid Poet - ' . $slug));
}
pr($result);
die();
$this->Poet->id = $result['Poet']['id'];
$this->set('result', $result);
}
Like this. Now I have Song.status as my association with Song table. I want to fetch only those records that has status = 1. Is it possible? Can I select only active records with my piece of code.
Use a normal find
While the magic findBy* methods are handy from time to time, it's a good idea to only use them for trivial queries - your query is nolonger trivial. Instead use a normal find call e.g.:
$result = $this->Poet->find('first', array(
'contain' => array(
'User' => array(
'id',
'full_name'
),
'Song' => array(
'id',
'name',
'name_hindi',
'slug',
)
),
'conditions' => array(
'slug' => $slug,
'Song.status' => 1 // <-
)
));
Does a Poet hasMany songs?
You don't mention your associations in the question, which is rather fundamental to providing an accurate answer, however it seems likely that a poet has many songs. With that in mind the first example will generate an sql error, as there will be no join between Poet and Song.
Containable does permit filtering associated data e.g.:
$result = $this->Poet->find('first', array(
'contain' => array(
'User' => array(
'id',
'full_name'
),
'Song' => array(
'id',
'name',
'name_hindi',
'slug',
'Song.status = 1' // <-
)
),
'conditions' => array(
'slug' => $slug
)
));
This will return the poet (whether they have relevant songs or not), and only the songs with a status of "1". You can achieve exactly the same thing by defining the condition in the association definition (either directly in the model or by using bindModel).

CakePHP Model Relationship with Multiple Foreign Keys

In my CakePHP app I have models for Matches and Teams. Each Match has a home_team_id and an away_team_id, both of which reference a different Team.
In my team.php file, I am able to form the relationship for a Team's home matches:
var $hasMany = array(
'HomeMatch' => array('className' => 'Match', 'foreignKey' => 'home_team_id'),
'AwayMatch' => array('className' => 'Match', 'foreignKey' => 'away_team_id')
);
My problem is that I cannot automatically retrieve a Team's home and away Matches in a single array. That is, the retrieved Matches are returned in separate HomeMatch and AwayMatch arrays, which causes sorting difficulties.
I have tried the following:
var $hasMany = array(
'Match' => array('foreignKey' => array('home_team_id', 'away_team_id'))
);
...with no luck.
Any ideas on how to combine these two foreign keys into a single relationship?
Thanks, Ben
A custom finderQuery should do the trick:
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'finderQuery' => 'SELECT *
FROM `matches` as `Match`
WHERE `Match`.`home_team_id` = {$__cakeID__$}
OR `Match`.`away_team_id` = {$__cakeID__$}'
)
);
I was having a similar issue and instead of creating a finderQuery I used the conditions operator and it worked great!
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'conditions' => array(
'OR' => array(
array('Match.home_team_id' => '{$__cakeID__$}'),
array('Match.away_team_id' => '{$__cakeID__$}')
)
),
)
);
They are returned in seperate array's because the sort of represent different models (in this particular case the model is the same).
You should probably build a helper method to go over the retrieved data (in the model object or in a separate helper class) and "flatten" it. then you'd be able to sort it.
Ken.

Resources