cakephp habtm nested data - cakephp

I have a HABTM relationship and I'd like to get nested data for a categories table from a product table something like this product array:
array(
(int) 0 => array(
'id' => (int) 1,
'name' => 'a',
'categories' => array(
(int) 0 => array(
'id' => (int) 1
),
(int) 1 => array(
'id' => (int) 2
)
)
),
(int) 1 => array(
'id' => (int) 2,
'name' => 'b',
'categories' => array(
(int) 0 => array(
'id' => (int) 1
)
)
)
)
Is this possible with cake?
EDIT: I will try to explain further what I want to do.
Please bear with me, I am new to cake and am having trouble getting to grips it.
I have a 'product' table:
public $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category',
'joinTable' => 'categories_sculptures',
'foreignKey' => 'sculpture_id',
'associationForeignKey' => 'category_id',
'unique' => 'keepExisting'
)
)
and a category table:
public $hasAndBelongsToMany = array(
'Sculpture' => array(
'className' => 'Sculpture',
'joinTable' => 'categories_sculptures',
'foreignKey' => 'category_id',
'associationForeignKey' => 'sculpture_id',
'unique' => 'keepExisting'
)
);
and a category products table:
public $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Sculpture' => array(
'className' => 'Sculpture',
'foreignKey' => 'sculpture_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
I would like to build a view table of the sculptures and also show in each row which categories each sculpture is in (which could be more than one). As I am new to cake I don't know how I would go about this. If I was just querying mysql I could get this information with a group_concat or a nested select, or inefficiently by looping through first array and querying the category_sculpture table by sculpture key and adding the results to the first array. But I would like to know the best way to get this result the cake way.

I have found the answer to my needs:
$this->Sculpture->recursive = 1;

Related

How to get associated data of a model using custom join cakephp2

I am using cakephp2 and trying to join two tables like this :
$employee=$this -> Employee ->find ('all',array(
'joins'=>array(
array('table' => 'employee_histories', 'alias' => 'eh', 'type' => 'LEFT', 'foreignKey' => false,
'conditions' => array('eh.emp_id= Employee.emp_id'))
),
'fields'=>'Employee.*,eh.*',
));
In EmployeeHistory model there is an association with table employee_designations like this :
public $belongsTo = array(
'EmployeeDesignation' => array(
'className' => 'EmployeeDesignation',
'foreignKey' => '',
'conditions' => 'EmployeeDesignation.designation_id=EmployeeHistory.designation_id',
'fields' => '',
'order' => ''
),
);
Now is it possible to get the EmployeeDesignation result in $employee array.
You can join the employee_designations table by adding another array to your joins array. Something like this:
$employee= $this->Employee->find('all', array(
'joins'=>array(
array(
'table' => 'employee_histories',
'alias' => 'eh',
'type' => 'LEFT',
'foreignKey' => false,
'conditions' => array(
'eh.emp_id= Employee.emp_id'
)
),
// array joining the employee designations table
array(
'table' => 'employee_designations',
'alias' => 'ed',
'type' => 'LEFT',
'conditions' => array(
'eh.designation_id = ed.designation_id'
)
)
),
'fields'=>'Employee.*, eh.*, ed.*', // include ed fields
));

saveAll not working with hasMany through (Join Model)

I'm trying to do this with last 2.x CakePHP version
http://i.stack.imgur.com/IylLu.png
Without education_id in projects_students table works perfectly. But doesn't work with education_id.
this is my implementation with education_id:
ProjectsStudent.php
class ProjectsStudent extends AppModel {
public $belongsTo = array(
'Project' => array(
'className' => 'Project',
'foreignKey' => 'project_id'
),
'Student' => array(
'className' => 'Student',
'foreignKey' => 'student_id'
)
);
Student.php
public $hasMany = array('ProjectsStudent');
public $hasAndBelongsToMany = array(
'Education' => array(
'className' => 'Student',
'joinTable' => 'projects_students',
'foreignKey' => 'student_id',
'associationForeignKey' => 'project_id',
),
);
Project.php
public $belongsTo = 'Status';
public $hasMany = array(
'Doc' => array(
'className' => 'Doc',
'joinTable' => 'docs',
'foreignKey' => 'project_id',
),
'History' => array(
'className' => 'History',
'joinTable' => 'history',
'foreignKey' => 'project_id',
),
'ProjectsStudent'
);
public $hasAndBelongsToMany = array(
'Student' => array(
'className' => 'Student',
'joinTable' => 'projects_students',
'foreignKey' => 'project_id',
'associationForeignKey' => 'student_id',
),
'Professor' => array(
'className' => 'Professor',
'joinTable' => 'projects_professors',
'foreignKey' => 'project_id',
'associationForeignKey' => 'professor_id',
),
'Education' => array(
'className' => 'Education',
'joinTable' => 'projects_educations',
'foreignKey' => 'project_id',
'associationForeignKey' => 'education_id',
),
);
ProjectsController.php
$data = array(
'Project' => array(
'id' => $id,
'status_id' => 5,
'Student' => array($this->Auth->user('id'))),
'History' => array(
array(
'action' => 'update -> status 5',
'student_id' => $this->Auth->user('id'),
)
),
'ProjectsStudent' => array(array(
'education_id' => "5"))
);
$this->Project->saveAll($data, array('deep' => true))
Output error:
Error: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '109' for key 'PRIMARY'
SQL Query: INSERT INTO tfg.projects_students (project_id, education_id) VALUES (109, 5)
I think CakePHP is trying 2 inserts, with (project_id, student_id) and with (project_id, education_id)
I want just one input like INSERT INTO tfg.projects_students (project_id,student_id,education_id) VALUES (109, 1, 5)
Thanks in advance, sorry for my english.
---SOLUTION---
$data = array(
'Project' => array(
'id' => $id,
'status_id' => 5,
'Student' => array($this->Auth->user('id'))),
'History' => array(
array(
'action' => 'update -> status 5',
'student_id' => $this->Auth->user('id'),
)
),
);
$this->Project->saveAll($data, array('deep' => true));
$this->Project->ProjectsStudent->updateAll(
array('education_id' => $this->Auth->user('education')),
array('project_id' => $this->Project->id));
You appear to have setup the projects_students table to use project_id as the primary key. The primary key needs to be unique whereas the project_id foreign key is likely to be duplicated (as appears to be happening for you). The join table should be at least:-
id (int)
project_id (int)
student_id (int)
Where id is the primary key (with auto increment enabled).

Cakephp Find conditions on related table with hasAndBelongsToMany

I have the following code that gives an error from the moment I do a find condition on the associated Model (HABTM):
class Users extends Model{
public $useTable = 'users';
public $hasAndBelongsToMany = array(
'UserCategories' => array(
'className' => 'UserCategories',
'joinTable' => 'user_categories_link',
'foreignKey' => 'user_id',
'associationForeignKey' => 'category_id',
'unique' => false,
)
);
public function getData($page = 0, $category = '', $subcategory = ''){
return $this->find('all', array(
'limit' => 6,
'page' => $page,
'conditions'=> array(
'active'=> 1,
'UserCategories.category_name' => $category, // THIS GIVES ERROR
'UserCategories.category_subcategory' => $subcategory, // THIS GIVES ERROR
)
));
}
In my Controller:
$this->Users->getData(0, 'somemaincategory', 'somesubcategory');
I can't seem to do conditions on the related HABTM-Model (UserCategories in this case). I also tried to use 'contain' (with $actsAs), but then he stills gives me all the User data even if there is no Category linked with it. The Category array is in that case just blank.
Hope someone can help me.
Thanks in advance,
AƤron
Do a manual join. You can use this to do an actual inner join (contain will act as a left join). http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
$this->find('all',
array(
'conditions' => array(
'active' => 1,
),
'joins' => array(
array(
'table' => 'user_categories_link',
'alias' => 'UserCategoriesLink',
'type' => 'inner',
'conditions' => array(
'UserCategoriesLink.user_id = User.id'
),
),
array(
'table' => 'user_categories',
'alias' => 'UserCategories',
'type' => 'inner',
'conditions' => array(
'UserCategories.id = UserCategoriesLink.category_id',
'UserCategories.category_name' => $category,
'UserCategories.category_subcategory' => $subcategory,
),
)
),
)
);

CakePHP saveAll not saving key of 2nd level dependent

I am using cake PHP 2.x. My Database is set up like this.
Rating Has One Review
Review Has Many Photo
data => array(
'Rating' => array(
'rating' => '1',
'user_id' => '1',
'listing_id' => '55'
),
'Review' => array(
'title' => 'Good Service',
'date_visited' => array(
'month' => '05',
'day' => '28',
'year' => '2013',
),
'service_used' => 'Easy Checkout',
'description' => 'After a fairly quick check-in, the check out service was also breeze ',
'Photo' => array(
(int) 1 => array(
'title' => 'Test',
'photo' => array(
'name' => '2.JPG',
),
'listing_id' => '55',
'user_id' => '1'
)
)
)
)
Review.php
public $hasMany = array(
'Photo' => array(
'className' => 'Photo',
'conditions' =>'',
'order' => ''
)
);
Photo.php
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Review' => array(
'className' => 'Review',
'foreignKey' => 'review_uuid',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Listing' => array(
'className' => 'Listing',
'foreignKey' => 'listing_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
And Finally RatingsController.php
$this->Rating->create();
if ($this->Rating->saveAll($this->request->data, array('deep' => true))) {
$this->Session->setFlash(__('The rating has been saved'));
$this->redirect(array('action' => 'index'));
}
The issue is all the data is getting saved, except review_uuid (which is also being created simultaneously) )in Photos model.
mysql> select id,user_id,listing_id,review_uuid,title,photo,photo_dir from photos where ID=26;
+----+---------+------------+-------------+-------+--------------------------------------+----------- +
| id | user_id | listing_id | review_uuid | title | photo | photo_dir |
+----+---------+------------+-------------+-------+--------------------------------------+----------- +
| 26 | 1 | 55 | NULL | Test | 1a107372ef53ba26d7748a50c25e6b27.jpg | 01/77/74 |
+----+---------+------------+-------------+-------+--------------------------------------+-----------+
It doesn't look like your defining the foreign key in the hasMany relationship in Review found in Photo, your not completing the relation.
Your hasMany array in Review.php should look like:
public $hasMany = array(
'Photo' => array(
'className' => 'Photo',
'foreignKey' => 'review_uuid',
'conditions' =>'',
'order' => ''
)
);
Note the foreignKey parameter. You are telling Cake that Review has many photos and each photo record has a foreign key named review_uuid used to identify it's relationship to a review. You were defining the foreign key in your belongsTo array in the Photo model but not in your hasMany array in the Review model, so the relation was never completed.
You should also define a belongsTo relation between Review and Rating
In Review.php:
public $hasMany = array(
'Photo' => array(
'className' => 'Photo',
'foreignKey' => 'review_uuid',
'conditions' =>'',
'order' => ''
)
);
public $belongsTo = array(
'Rating' => array(
'className' => 'Rating',
'foreignKey' => 'rating_id',
'conditions' => '',
'order' => ''
)
);

Find conditions with hasMany model

I have 4 model:
Item------hasMany---->Detail
Item------hasMany---->Favourite
Item------hasMany---->Category
How can I find all Item that has Favourite.member_id = '8' and Category.item_category_id = '1'
Here is my Item model relation:
public $hasMany = array(
'Detail' => array(
'className' => 'Detail',
'foreignKey' => 'item_id',
'dependent' => true,
'conditions' => '',
'order' => 'created DESC',
),
'Category' => array(
'className' => 'Category',
'foreignKey' => 'item_id',
'dependent' => true,
'conditions' => '',
'order' => 'created DESC',
),
'Favorite' => array(
'className' => 'Favorite',
'foreignKey' => 'item_id',
'dependent' => true,
'conditions' => '',
'order' => 'created DESC',
)
);
I used this query() and it's ok:
$this->set('test',$this->Item->query("SELECT items.*
FROM items, categories, favourites
WHERE items.id = items_item_categories.item_id
AND items.id = favorites.item_id
AND categories.item_category_id = 1 AND favourites.member_id = 8"));
But I don't like using query(), and I change it into find(), and it seems not good:
$this->set('test',$this->Item->find('all', array(
'contain'=>array(
'ItemDetail',
'Category'=>array('conditions'=>array('Category.item_category_id'=>1)),
'Favorite'=>array('conditions'=>array('Favorite.member_id'=>8)));
You need call the containableBehaivor on your model
<?php
class Item extends AppModel {
public $actsAs = array('Containable');
}
in your controller you can make the query
$items = $this->Item->find('all', array(
'contain'=>array(
'ItemDetail',
'Category'=>array(
'conditions'=>array('Category.item_category_id'=>1)
),
'Favorite'=>array(
'conditions'=>array('Favorite.member_id'=>8)
)
)
);
$this->set('test', $items);
try using Joins
$items = $this->Item->find('all', array(
'joins' => array(
array(
'table' => 'categories',
'alias' => 'Category',
'type' => 'inner',
'conditions'=> array('Category.item_category_id' => 1)
),
array(
'table' => 'favorites',
'alias' => 'Favorite',
'type' => 'inner',
'conditions'=> array('Favorite.member_id' => 8, 'Item.id = Favorite.item_id')
)
),
'contain'=>array('ItemDetail','Category','Favorite')
);
$this->set('test', $items);
You could also try something like this:
$this->Item->hasMany['Favorite']['conditions']['member_id'] = 8;
Which has the same effect as rebinding the model with the condition.
Just a possible issue. The above will add the condition for the rest of the request, if you want to rollback to the previous behavior, you need to unset the condition:
unset($this->Item->hasMany['Favorite']['conditions']['member_id']);
Remember to set the $this->Item->recursive property to the desired value (recursive attribute). The default it's 1, which it's the minimum you need for this to work.

Resources