Refill relationships in form in cakephp in edit mode - cakephp

I got a controller named Posts, a model called Content which is properly linked with other models such as Category and Location.
In my view for add 'Content' i successfully populate the multi select lists with categories and locations to pick to relate to the post. Saving it all works perfectly.
Now in edit/update mode, I can once again fill the multi selects with categories and locations, but it will not select the ones related to the current post. When looking in the database, there are categories and locations successfully realted to the current post.
This is what I got in my controller:
$this->data = $this->Content->read();
$this->set('locations',$this->Content->Location->find('list',array('fields' => array('id','location'))));
$this->set('categories',$this->Content->Category->find('list',array('fields' => array('id','category'))));
And this is what I got in my view:
echo $this->Form->input('Location', array('type' => 'select','multiple' => 'true','options' => $locations));
echo $this->Form->input('Category', array('type' => 'select','multiple' => 'true','options' => $categories));
What am i missing here? How do i get the already related locations and categories, select in the multi select lists?
(filling of non relationship data, will repopulate textfields etc just perfectly)
Grateful for any help!
Jason

instead of
$this->data = $this->Content->read()
try
$params['conditions'] = array(
'Content.id' => $id
);
$params['contain'] = array(
'Category',
'Location'
);
$this->data = $this->Content->find('first', $params);
You will need the Containable Behaviour for that

Use this:
echo $this->Form->input('Location', array(
'label' => 'Location',
'type' => 'select',
'options' => $LocationArray,
'selected'=> 12(Selected Value)
);

Related

HABTM association not saved - cakephp

I've been searching for all types of solutions and it's not working. I have two models, Bredbook and Genre, in bredbook.php and genre.php, associated with HABTM. No way to save the association.
Here is the code of one model:
public $hasAndBelongsToMany = array('Bredbook' => array('className' => 'Bredbook',
'joinTable' => 'bredbooks_genres',
'foreignKey' => 'genre_id',
'associationForeignKey' => 'bredbook_id'));
Here is the form:
echo $this->Form->create('Bredbook', array('action' => 'firstConnection'));
echo $this->Form->input('Genre', array('options' => $genres, 'empty' => false));
echo $this->Form->end('Validate');
And in BredbooksController.php:
if ($this->request->data)
{
if($this->Bredbook->saveAssociated($this->request->data))
return $this->redirect('/bredbooks/index');
}
The creation of the bredbook is ok, but no association is created in the table bredbooks_genres. I've tried everything... any help will be welcome.
You need to send Bredbook.id in your data array:
<?php
echo $this->Form->create('Bredbook');
//It can be any other attribute, but need some Breedbook data
echo $this->Form->input('Bredbook.id', array('value' => $id));
echo $this->Form->input(
'Genre',
array(
'options' =>$genres,
'empty' => false
)
);
echo $this->Form->end('Validate');
Or make sure your $this->request->data looks similar to this before saving:
Array
(
[Bredbook] => Array
(
//It can be any other attribute, but need some Breedbook data
[id] => 1
)
[Genre] => Array
(
[Genre] => Array
(
[0] => 1
)
)
)
This Solution will work also work when you are creating a new Bredbook as long as you send some data of that Model, it doesn't need to be the id, you can also send name or other attributes from your Bredbook Model. If you send Bredbook.id it will not create a new record and only make the associations, but if you don't send the id and send other attributes (like name for example) it will create a new record and will associate it with the Genre data your are sending.
I finally did it: I don't know if it's a necessary thing, but I just used cake bake to generate everything: http://book.cakephp.org/2.0/en/console-and-shells/code-generation-with-bake.html
Now I need some work to rearrange all the views, but my saving is working, with the exact same code I posted first, and I think I saved some time too with all that generated behaviour I needed !

find() based on conditions in multiple HABTM relations

I'm working with CakePHP 2.1
Let's state I have the following models and relations:
Posts belongsTo Edition
Posts HABTM Editors
Posts HABTM Tags
I'm reasoning from the PostsController and I would like to find all Posts belonging to a certain Edition, and that Editor.id=55 is related and Tag.id=33 is related.
I have found many examples where finding Posts based on a condition over a HABTM relation is done by 'reversing' the find direction and reason from the Editor.
$this->Editor->find('all',
array(
'conditions'=>array('Editor.id'=>55),
'contain'=>array('Post')
)
);
Unfortunately this doesn't work here, because there are multiple HABTM relations that I would like to put a condition on.
Also using contain does not work since it only cuts off a branch (editors) but does not help filtering it's parent (posts).
How do I solve this?
Do I maybe need to resort to ad hoc joins? And if so, could someone explain me roughly what approach to take?
Edit:
Some pseudo code in order to illustrate what I want to achieve:
$this->Post->find('all',
array('conditions'=>array(
'Edition.id'=>4,
// start pseudo code
'Post.Editor' => 'has at least one related editor with id=55',
'Post.Tag' => 'has at least one related tag with id=33',
)
)
);
kind regards,
Bart
Edit Solution:
Following #RichardAtHome I created the solution below. Not (yet) employing multiple join conditions, but that appeared not to be necessary:
// Paginate over Cards that belong to current Subscription (belongsTo) AND the User is related to as an Editor (HABTM)
$this->paginate = array(
'fields' => array('id','title','workingtitle','attachment_count','subscription_id'),
'joins' => array(
// create Editor-join
array(
'table' => 'cards_users',
'alias' => 'Editor',
'type' => 'left',
'conditions' => array(
'Editor.card_id = Card.id'
)
),
array(
'table' => 'users',
'alias' => 'User',
'type' => 'left',
'conditions'=> array(
'User.id = Editor.user_id'
)
)
),
'conditions' => array(
'OR' => array(
// is Card in current subscription? (straightforward condition)
array('Card.subscription_id',$subscription_id),
// is current User assigned as Editor? (condition works with join above)
array('User.id' => $user['id'])
)
),
'limit' => 10
);
For this type of query, I usually find it easiest to construct the joins myself as cake won't join, it will perform multiple queries instead (select matching rows from table a, then fetch matching rows from table b).
Your query needs to look something like this:
$this->Post->find('all',
array(
'conditions'=>array('Post.edition_id'=>4),
'joins'=>array(
array(
'type'=>'inner',
'table'=>'posts_editors',
'alias'=>'PostsEditor',
'conditions'=>array(
'PostsEditor.post_id = Post.id',
'PostsEditor.editor_id'=>55 // your criteia for editor id=55
)
),
array(
'type'=>'inner',
'table'='posts_tags',
'alias'=>'PostsTag',
'conditions'=>array(
'PostsTag.post_id = Post.id',
'PostsTag.tag_id'=>33 // your criteria for tag id = 33
)
)
)
)
);
Check the SQL that's output to see what this query is actually doing behind the scenes.
Try:
$this->Post->find('all',
array('conditions'=>array(
'Edition.id'=>4,
'Editor.id' => '55',
'Tag.id' => '33',
)
)
);

CakePHP HABTM Form Submission

I have two tables, questions and tags, that have a HABTM relationship. When adding a question, I want to be able to specify a tag for the question (this would just be the first tag, with ability to add more tags later). The tags are pulled from their table. How can I configure my application so that when a question is added and a tag is specified, the join is reflected in the join table (questions_tags)?
Here is my question add action code :
function add() {
$tags = $this->Question->Tag->find('all');
$this->set('tags',$tags);
if (!empty($this->data)) {
$this->Question->create();
if ($this->Question->save($this->data)) {
$this->Session->setFlash(__('The question has been saved', true));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The question could not be saved. Please, try again.', true));
}
}
$users = $this->Question->User->find('list');
$tags = $this->Question->Tag->find('list');
$this->set(compact('users', 'tags'));
}
and here is my question add view code:
<?php
echo $this->Form->create('Question');
echo $this->Form->input('user_id',array('type' => 'hidden', 'value' => $this->Session->read('Auth.User.id')));
echo $this->Form->input('title');
echo $this->Form->input('details',array('type' => 'textarea'));
echo $this->Form->input('tag_id');
echo $this->Form->end(__('Submit', true));
?>
First make sure that your models are set up the right way. The fact that a user initially just adds one tag to your question does not change the fact that you should have a HABTM relation between the Question model and the Tag model (because you want the possibility add more tags later).
If your $this->data array is build according to the following schema:
$this->data = array(
'Question' => array(
'name' => 'Trick question'
),
'Tag' => array(
'Tag' => array(1,2,3)
)
);
Then a $this->Question->save() will save the Question data as well as the related Tag data (in this case Question 'Trick Question' with Tags with the id 1, 2 and 3).
Maybe take one step back and bake your Models, Views and Controllers for these two models (again) and see what Cake makes out of it. If I'm correct you'll just need a $this->Form->input('Tag') somewhere in your form (and if that not fills in the right data automatically you want to fill the options parameter with the result of $this->Question->Tag->find('list')).
if you have a single tag for a question, it's not HABTM. it has to be one to one or one to many relation.
in your questions model you can define property belongsTo:
class Question extends AppModel {
var $name = 'Question';
var $belongsTo = array(
'Tag' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id'
)
);
}
Something like this.
here is a link describing how to set HABTM
HABTM

CakePHP: How can I customize the labels for multiple checkboxes

I've done a lot of searching but havent been able to solve this yet. I have a user registration form where the user can select a number of store branches as favourites upon registration. I have this relationship set up as a HABTM.
I am able to display the store branches as multiple checkboxes, but I need to display the store name (the store branches belongs to store names) along with the branch name in the label for each checkbox. Something like:
Levi's - Canal Walk
where Canal Walk is the branch name, and Levi's is the store name (coming from a different table)
Can anyone please share some insight on how to do this?
I haven't tested this, but it might work (or help you along the right path):
$branches = $this->Branch->find('list', array(
'fields' => array('Branch.id', 'CONCAT(Store.name, " - ", Branch.name)'),
'joins' => array(
array(
'table' => 'stores',
'alias' => 'Store',
'type' => 'inner',
'foreignKey' => false,
'conditions' => array('Branch.store_id' => 'Store.id')
)
)
));
Set this in your controller, or keep things DRY by placing it in your model in a function such as findFullList()
Hope that helps!
I would do all of this in the view. If your HABTM relationship is setup correctly, a query of the Store model should work something like this:
$stores = $this->Store->findAll();
//Returns
Array([0] => Array(
'Store' => array(),
'Branch' => array(
[0] => array(),
[1] => array()...));
Then pass the $stores variable into the view, and iterate through it with a double nested loop.
foreach($stores as $store){
foreach($store['Branch'] as $branch){
//Code to output checkbox by using $branch['name']
}
}

how to create a chain select form in cakephp

My business directory application calls for 3 chained select boxes, and I'm using cakephp to build this application.
The hierarchy and order of choices for the sections is this:
1 - business group
2 - business type
3 - city (included in table customer)
The relationships are:
customer HABTM business types
business groups have many business types
business types have one business group, HABTM customers
I have searched for jquery plugins that help with this, and found one by Remy Sharp, but it doesn't have the more complex relationships I have.
http://remysharp.com/2007/09/18/auto-populate-multiple-select-boxes/
What I imagine happening is the first selection box (business groups) is pre-populated and once a selection is made, an event listener send a message that filters the second selection box, and the same for the third.
What I don't know is how to structure the search action based on the event listener.
Any advice or am I way off base?
As always, I come to the well for help.
Much appreciated.
Paul
Thanks very much Nick, I've read many of your posts I really appreciate your response.
I've followed your instructions but have run into problems. I've tried my best to resolve them but haven't figured it out.
This is what I've done so far:
1) created 'chained' actions in both the business_type and business_directory (renamed customer to business directory, which is more appropriate.)
business type chained action:
function chained($business_group_id) {
$business_types = $this->BusinessType->find('list', array(
'conditions' => array( 'BusinessType.business_group_id' => $business_group_id)
));
$this->set('business_types', $business_types);
}
business directory chained action:
function chained($business_type_id) {
$business_directories = $this->BusinessDirectory->bindModel(array( 'hasOne' => array('business_directories_business_types' )));
$business_directories = $this->BusinessDirectory->find('all', array(
'fields' => array( ' BusinessDirectory.city'),
'conditions' => array( 'business_directories_business_types.business_type_id' => $business_type_id)
));
$this->set('business_directories', $business_directories);
}
I did find that with a HABTM relationship, using find 'list' didn't create the join query, whereas find 'all' did.
2) I then created a search action in the business directory and corresponding view.
For the business groups I created a getList action to populate the option list in the search form:
function getList() {
return $this->BusinessGroup->find('list');
}
In the search view, I've added the javascript for the chain select:
<script type="text/javascript">
<!--
$(function () {
var group = $('#businessGoup');
var type = $('#businessType');
var city = $('#businessDirectoryCity');
type.selectChain({
target: city,
url: '../business_directories/chained/'+$(this).val(),
data: { ajax: true, anotherval: "anotherAction" }
});
group.selectChain({
target: type,
url: '../business_types/chained/'+$(this).val()
}).trigger('change');
});
//-->
</script>
And the form:
create('business_directories', array('action'=>'/search_results')); ?>
input('business_group_id',
array( 'type' => 'select',
'id' => 'businessGoup',
'empty' => '-- Select Business Group --',
'multiple' => true,
'options' => $this->requestAction('/business_groups/getList' ),
'label' => 'Business Group'));
?>
input('business_type.id',
array( 'type' => 'select',
'id' => 'businessType',
'empty' => '-- Select Business Type --',
'multiple' => true,
'options' => 'none selected',
'label' => 'Business Type'));
?>
input('business_directories.id',
array( 'type' => 'select',
'id' => 'businessDirectoryCity',
'empty' => '-- Select City --',
'multiple' => true,
'options' => 'options',
'label' => 'City'));
?>
end('Search'); ?>
When I test the business type chain function, /business_types/chained/1, everything works.
But when I test the search view, I get a javascript alert error. Then when I check firebug, I get the following two errors:
Warning (2): Missing argument 1 for BusinessTypesController::chained() [APP\controllers\business_types_controller.php, line 71]
Notice (8): Undefined variable: business_group_id [APP\controllers\business_types_controller.php, line 73]
Any additional help with this is very much appreciated.
Thanks, Paul
What you need is to have 2 actions in the controllers (business_type and customer).
each action should look like this. In that case for the business type
function chained($parent_id){
$business_types = $this->BusinessType->find('list', array('conditions'=>'BusinessType.business_group_id'=>$parent_id));
$this->set('business_types', $business_types);
}
of course you need also view for that action which will format the values in the proper format for the chained select.
For Business group you need to show all values directly so no ajax is needed.
The Customer controller's action is similar, but you need to select cities of all related customers.
Then with the chained select you need to set the proper elements and set the proper actions which need to be called.
i.e.:
$('#id-of-the-business-group').selectChain({
target: $('#id-of-the-business-type-field'),
url: '/business_types/chained/'+$(this).val()
});

Resources