cakephp hasmany form data - cakephp

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

Related

How do I Associate a HABTM relationship when not having the foreignKey?

Given the following:
Content Model
public $hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag',
'joinTable' => 'contents_tags',
'foreignKey' => 'content_id',
'associationForeignKey' => 'tag_id',
'unique' => 'keepExisting',
)
);
Tag Model
public $hasAndBelongsToMany = array(
'Content' => array(
'className' => 'Content',
'joinTable' => 'contents_tags',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'content_id',
'unique' => 'keepExisting',
)
);
Book Model
public $belongsTo = array(
'Content' => array(
'className' => 'Content',
'foreignKey' => 'content_id',
),
);
The Book model has a content_id. How can I ( if at all possible ) relate the Book Model to the Tag Model?
If you add a HasOne or HasMany relationship from the Content to the Book Model then Tag and Book are indirectly related via Content. If you want to use a find-call on one of them and get the other as associated data then all you have to do is set the recursive variable high. Does that make sense? Also then you can do stuff like $this->Book->Content->Tag or $this->Tag->Content->Book in their respective controllers.

CakePHP .find to return multiple HABTM relationships

I have a Model call Playlist.
Playlist has a HABTM with Track
public $hasAndBelongsToMany = array(
'Track' => array(
'className' => 'Track',
'joinTable' => 'tracklists_tracks',
'foreignKey' => 'tracklist_id',
'associationForeignKey' => 'track_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => array('id','title','version'),
'order' => 'TracklistsTrack.timing'
)
);
Track has an HABTM with AppUser
public $hasAndBelongsToMany = array(
'Artist' => array(
'className' => 'AppUser',
'joinTable' => 'artists_tracks',
'foreignKey' => 'track_id',
'associationForeignKey' => 'artist_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => array('id','username')
)
);
Now, i would like, when i write this
$tracklist = $this->Tracklist->findById($id);
to get for each tracklist all the tracks (and this does work as supposed to), and for every tracks all the AppUsers (cascading).
Mind that if i write $this->Track->findById($id);
i can actually get the AppUsers related to the track
Any idea to have this out of the box thanks to my diligent naming convention ?
Thank you
If you have define the correct relationship in your model, I would suggest using CakePHP containable behavior.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
All you need to do is to include the behavior in your model and put the following in your controller
$this->TrackList->contain(array('AppUser','Artist'));

Cakephp HABTM issue retrieving multilevel data

I am creating an assessment database and using cakephp for the first time. I have gotten all the data entry and editing sorted but am having trouble retrieving some crucial data from a deep association:
The essence is
Course HABTM Units (table courses_units)
Unit hasMany Artefacts
Artefact hasOne AdminArtefact
Artefact belongsTo an Unit
The AdminArtefact model is used for each years dynamic data for an artefact (due_date, late_date and so on).
My problem is when I want to get all the due dates of artefacts for a particular course (which we need to check for hand in congestion)
If I do a findAll in the Artefacts controller with recursion set to '2' it gets all the data I need...but it overloads the page due to the amount of data returned. So I want to set the conditions for the findAll for the specific fields I need. However I cannot set the course in the query - From my understanding the course data is pulled from a 'separate' query as it appears in the results like this:
[Course] => Array
(
[0] => Array
(
[id] => 2
[course_code] => C0767S
[course_name] => BSc (hons) Entertainment Technology
[course_local_code] => ET
[course_leader] => 6
[colour] =>
[CoursesUnit] => Array
so as part of the query, I cannot set the course equal to a specific one, which I need to do so I can also specify the other data elements I want to pull and not the wealth of other data.
Ive tried loading the Unit model and changing the bind to a belongsTo instead of HABTM to the courses_units table but got the same dataset back again.
Ive tried going 'higher up the chain' to the CourseUnits controller and doing a '$uses' and specified all the relevant tables I want to pull data from but that didn't get anything useful.
Reading up on HABTM it doesn't automatically pull the data so I looked at joining the tables, but couldn't really see which model to make the join to get it to work.
I need to put this to bed now so hence I am asking the question: What is the most elegant way to get the due dates for all the artefacts for a specific course? (I need the course, the unit, the artefact and the due date...so something from all the tables)
QUERY String I want to use in Artefacts controller:
$data = $this->Artefact->find('all', array(
'fields' => array('Artefact.reference', 'AdminArtefact.due_date', 'ArtefactUnit.short_code', 'Course.course_code' )));
This gives the error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Course.course_code' in 'field list'
Thanks in advance!
Kevin
All relevant models:
class Course extends AppModel {
public $displayField = 'course_name';
public $hasAndBelongsToMany = array(
'Unit' =>
array(
'className' => 'Unit',
'joinTable' => 'courses_units',
'foreignKey' => 'course_id',
'associationForeignKey' => 'unit_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'with' => 'CoursesUnit'
)
);
public $belongsTo = array(
'CourseLeader' => array(
'className' => 'User',
'foreignKey' => 'course_leader'
)
);
class Unit extends AppModel {
public $displayField = 'short_code';
public $hasMany = array(
'UnitArtefacts' => array(
'className' => 'Artefacts',
'foreignKey' => 'unit_id'
));
public $hasAndBelongsToMany = array(
'Course' =>
array(
'className' => 'Course',
'joinTable' => 'courses_units',
'foreignKey' => 'unit_id',
'associationForeignKey' => 'course_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'with' => 'CoursesUnit'
)
);
public $belongsTo = array(
'External' => array(
'className' => 'User',
'foreignKey' => 'external_examiner'
),
'Coordinator' => array(
'className' => 'User',
'foreignKey' => 'coordinator'
),
'UnitLevel' => array(
'className' => 'Level',
'foreignKey' => 'level_id'
)
);
class Artefact extends AppModel {
public $displayField = 'reference';
public $hasOne = array(
'AdminArtefact' => array(
'className' => 'AdminArtefact',
'foreignKey' => 'artefact_id'
)
);
public $belongsTo = array(
'ArtefactUnit' => array(
'className' => 'Unit',
'foreignKey' => 'unit_id'
)
);
actually setting
'recursive' => 3,
should work.

Cakephp 2.x find first entry of releated model

I know that i can easily find related model-data through model associations, if I select a single entry. But what I want to do is to get the first image as a thumb for the galery, when I use find('all') for the index-page.
My relations (Galleries are named Albums) are:
//Album-Model
public $hasMany = array(
'Picture' => array(
'className' => 'Picture',
'foreignKey' => 'album_id',
'dependent' => true,
'conditions' => ''
)
);
and:
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Album' => array(
'className' => 'Album',
'foreignKey' => 'album_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
EDIT :
This should worked I tested it on my own!
Before your query add the following lane to specify the recursivity.
$this->Albums->recursive = 1;
$this->set('Albums', $this->Album->find('all'));
This way, it will also send the Pictures data. Then in your model associations, you can specify to only return the first image with the Album if that's what you want!
Just add 'limit' => '1', in your hasMany => Comments array, in Album Model.
Then you can iterate in your view doing
foreach($Albums as $Album){
$Album['Album']; // ALBUMS info do whatever
$Album['Picture']; // The thumbnail
}
EDIT2: I've just see you can also do
$this->Album->find('all', array('recursive' => 1));

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