multiple HABTM relationships for same tables - Cake 3 - cakephp

I have 2 tables in my cakephp 3 app - Items and Colors. An Item can have multiple Primary Colors and Secondary Colors as well.
So, I have created 2 junction tables - items_primary_colors and items_secondary_colors.
Both have the same schema - item_id and color_id (Joining the Items and Colors tables)
I am not sure how to specify these relationships in TableModel and how to format the form data to save both types of colors.
My ItemTable.php code has -
$this->belongsToMany('Colors', [
'foreignKey' => 'item_id',
'targetForeignKey' => 'color_id',
'joinTable' => 'items_primary_colors'
]);
$this->belongsToMany('Colors', [
'foreignKey' => 'item_id',
'targetForeignKey' => 'color_id',
'joinTable' => 'items_secondary_colors'
]);
And, I am formatting the form data this way -
[primary_colors] => Array
(
[_ids] => Array
(
[0] => 2
)
)
[secondary_colors] => Array
(
[_ids] => Array
(
[0] => 3
)
)
Its not working. How should I deal with this?

You need to give the belongsToMany relationships different names. Try
$this->belongsToMany('PrimaryColors', [
'className' => 'Colors',
'foreignKey' => 'item_id',
'targetForeignKey' => 'color_id',
'joinTable' => 'items_primary_colors'
]);
$this->belongsToMany('SecondaryColors', [
'className' => 'Colors',
'foreignKey' => 'item_id',
'targetForeignKey' => 'color_id',
'joinTable' => 'items_secondary_colors'
]);

Related

cakephp model chaining and retrieving results from multiple models

I am in process of chaining multiple models. The goal is to retrieve the category for the keyword when I pass a keyword.
Models
Keyword
columns:
id
keyword
KeywordCategory
columns:
keyword_id
category_id
Category
columns:
id
category
The following are the association definitions that I made.
Keyword
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'keyword_id',
'dependent' => true
)
);
KeywordCategory
var $belongsTo = array(
'Keyword' => array(
'className' => 'Keyword',
'foreignKey' => 'keyword_id',
'dependent' => true
),
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'dependent' => true
)
);
Category:
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'category_id',
'dependent' => true
)
);
My question is when I currently retrieve the Keyword category currently it will return something like following:
Array
(
[Keyword] => Array
(
[id] => 2
[keyword] => rc
[modified] =>
[created] =>
)
[KeywordCategory] => Array
(
[0] => Array
(
[keyword_id] => 2
[category_id] => 2
)
[1] => Array
(
[keyword_id] => 2
[category_id] => 1
)
)
)
I want to also return the Categories signified by category_id in the result. Can anyone suggest the change that I need to make?
Thank you

cakephp hasmany form data

I want to save multiple categories for a product.
I have the models:
Category.php
public $hasAndBelongsToMany = array(
'Product' => array(
'className' => 'Product',
'joinTable' => 'product_categories',
'foreignKey' => 'category_id',
'associationForeignKey' => 'product_id',
'unique' => 'keepExisting',
)
);
Product.php
public $hasMany = array(
'ProductCategory' => array(
'className' => 'ProductCategory',
'foreignKey' => 'product_id',
'dependent' => false,
),
ProductCategory.php
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
),
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
)
);
So in Product/add view I add a set of checkboxes of the Categories by:
echo $this->Form->input('ProductCategory.category_id',array(
'label' => __('Category',true),
'type' => 'select',
'multiple' => 'checkbox',
'options' => $categories
));
But this produces a set of inputs with the names of: name="data[ProductCategory][category_id][]" rather than name="data[ProductCategory][0][category_id]" the zero being incremented.
If they are in the format with the key between the model and field then I can use saveAll()?
As it is in the format I am getting I would have to manipulate the request->data to get it into the form I want to be able to save()
Am I going about this the correct way? Or maybe my models are set up incorrectly?
Also, what happens with editing hasMany data? What if for instance I uncheck an option and add another? Does cake automatically delete all associated records before adding new ones?
EDIT.
Essentially what I am asking is is there a better or quicker way of doing this, which works right now:
if ($this->Product->save($this->request->data)) {
$this->Product->ProductCategory->deleteAll(array('ProductCategory.product_id' => $this->Product->id));
foreach ($this->request->data['ProductCategory']['category_id'] as $cat_id) {
$this->Product->ProductCategory->create();
$this->Product->ProductCategory->set(array(
'product_id' => $this->Product->id,
'category_id' => $cat_id
));
$this->Product->ProductCategory->save();
}
}
in your form iterate however many you wish to display along the lines of
for/while/do()
{
$counter++
$this->Form->text('Product.'.$counter.'.price');
$this->Form->text('Product.'.$counter.'.description');
}
I would use saveAll() for this as it's designed to save a record and all it's related records for you automatically, assuming that your id's are in the data and it's formatted correctly.
http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array

cakephp habtm nested data

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;

CakePHP saveAll saves one HABTM, but not another

I have a Territory that habtm both a User and a School:
public $hasAndBelongsToMany = array(
'School' => array(
'className' => 'School',
'joinTable' => 'schools_territories',
'foreignKey' => 'territory_id',
'associationForeignKey' => 'school_id',
'order' => 'School.name',
),
'User' => array(
'className' => 'User',
'joinTable' => 'territories_users',
'foreignKey' => 'territory_id',
'associationForeignKey' => 'user_id',
'order' => array( 'User.last_name', 'User.first_name' ),
),
);
When I add a Territory, the form collects School data and sets the User data based on the currently authenticated user. A call to $this->Territory->saveAll( $this->data ), though, saves the Territory and School data, but nothing for the User association.
This is one of those moments where I know I've done something really, really stupid, but just can't see it. Here's what the data looks like immediately before saving:
Array
(
[Territory] => Array
(
[id] =>
[advisor_first_name] => Oompa
[advisor_last_name] => Loompa
[usrp_number] => 3908
[school_search] => sylv
)
[School] => Array
(
[School] => Array
(
[0] => 4ee0bbde-3714-419d-88a5-6d78147402e8
[1] => 4ee0bbde-33d8-49ce-8e71-6d78147402e8
[2] => 4ee0bbde-e190-4894-a522-6d78147402e8
[3] => 4ee0bbde-7734-47ef-a2c0-6d78147402e8
[4] => 4ee0bbde-be54-4087-8437-6d78147402e8
[5] => 4ee0bbde-de00-473e-b6ae-6d78147402e8
[6] => 4ee0bbde-7fc0-4683-a957-6d78147402e8
[7] => 4ee0bbde-0434-4b78-9295-6d78147402e8
[8] => 4ee0bbde-defc-43cb-8c8e-6d78147402e8
[9] => 4ee0bbde-de18-45da-8e23-6d78147402e8
[10] => 4ee0bbde-fff8-4fc0-bcd7-6d78147402e8
[11] => 4ee0bbde-e1c8-42f5-9663-6d78147402e8
)
)
[User] => Array
(
[User] => Array
(
[0] => e71fcad2-2a71-11e1-837a-66730eed7998
)
)
)
One interesting note, perhaps, is that when I dump $this->Territory from the TerritoriesController, I see a School property (containing the School model definition, of course), but no User property. On the other hand, both models are correctly represented in the hasAndBelongsToMany property array.
If anyone can see what I'm missing, I'd be eternally grateful. Seriously. My eyes are bleeding from the combination of looking and weeping.
Thanks.

Getting all the associated tags for every item after a filterby(tag)

I have the typical association HABTM Item <-> Tag
If i get all the Items with tag= "test" doing this:
$items = $this->Item->Tag->find('all', array('conditions'=>array('Tag.name'=>$tag)));
The $items array shows correctly the [Items] array containing the Items associated with the tag, but
How can i get also all the Tags for every item, so I can show after the item and all his tags ?
Thank you.
Edit: Seems like Cake didn't like the idea. You can hack it by calling the association a little differently:
$this->Item->bindModel(array('hasAndBelongsToMany' => array(
'BlahTag'=>array(
'className' => 'Tag',
'joinTable' => 'items_tags',
'foreignKey' => 'item_id',
'associationForeignKey' => 'tag_id',
'unique' => true,
)
)),false);
$this->Item->BlahTag->bindModel(array('hasAndBelongsToMany' => array(
'Item'=>array(
'className' => 'Item',
'joinTable' => 'items_tags',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'item_id',
'unique' => true,
)
)),false);
$items = $this->Item->BlahTag->find('all', array(
'contain'=>array('Item'=>array('BlahTag')),
'conditions'=>array('BlahTag.name'=>$tag)
));

Resources