How to insert multiple entrys - cakephp

iam new to cake and want to set up a order process where the user could send some recommendations (friends mail adr) to get a discount. each recommendation reduces the price. so i want to have up to 5 recommendation inserts in one step.
recommendation table is like (id, order_id, email)
i extend the order model with recommendations
class Order extends AppModel {
public $hasMany = array(
'Recommendation' => array(
'className' => 'Recommendation',
)
);
in the order controller i have to use the saveall method.
now, how should the order add view look like. if i use
echo $this->Form->input('Recommendation.mail');
it will only save one recommendation, or ? But i would like to have up to 5 of them on one page...
Thank you very much,
Julius

Change your form to this
echo $this->Form->input('Recommendation.0.mail');
echo $this->Form->input('Recommendation.1.mail');
echo $this->Form->input('Recommendation.2.mail');
echo $this->Form->input('Recommendation.3.mail');
echo $this->Form->input('Recommendation.4.mail');
Then in your controller,use the saveAll method
$this->Recommendation->saveAll($this->request->data);

Related

Editing associated models data in cakephp

I'm using CakePHP 2.3.6. I have some Models in my project, which are associated and I defined the association explicitly. Now, I have an Edit form, where I retrieve all the models data, and trying to edit those data in the corresponding models. But, edition is not happening, instead, new rows are created in the associated tables.
Here is my associations among the models :
in the model User.php :
public $hasMany=array('Education'=>array(
'className'=>'Education
'foreignKey'=>'user_id'
),
'Experience'=>array(
'className'=>'Experience',
'foreignKey'=>'user_id'
),
'Employment'=>array(
'className'=>'Employment',
'foreignKey'=>'user_id'
),
'ProfessionalQualification'=>array(
'className'=>'ProfessionalQualification',
'foreignKey'=>'user_id'
)
)
in the model Education.php :
public $belongsTo=array('User'=>array(
'className'=>'User',
'foreignKey'=>'user_id'
)
)
in the model Experience.php
public $belongsTo=array('User'=>array(
'className'=>'User',
'foreignKey'=>'user_id'
)
)
in the model Employment.php :
public $belongsTo=array('User'=>array(
'className'=>'User',
'foreignKey'=>'user_id'
)
)
in the model ProfessionalQualification.php :
public $belongsTo=array('User'=>array(
'className'=>'User',
'foreignKey'=>'user_id'
)
)
Now, in the Edit form (View/Users/edit.ctp) :
echo $this->Form->create('User');
echo $this->Form->input('User.name');
echo $this->Form->input('User.username');
echo $this->Form->input('User.password');
echo $this->Form->input('User.address');
echo $this->Form->input('User.phone');
echo $this->Form->input('Education.0.degree');
echo $this->Form->input('Education.0.passing_year');
echo $this->Form->input('Experience.0.title');
echo $this->Form->input('Experience.0.description');
echo $this->Form->input('Employment.0.company');
echo $this->Form->input('Employment.0.description');
echo $this->Form->input('ProfessionalQualification.0.certificate');
echo $this->Form->input('ProfessionalQualification.0.description');
echo $this->Form->submit('Save');
echo $this->Form->end();
in the UsersController.php controller :
public function edit(){
$this->set('title_for_layout','Edit CV');
if(!AuthComponent::user('id'))
throw new NotFoundException(__('Invalid User'));
$user=$this->User->findById(AuthComponent::user('id'));
if(!$user)
throw new NotFoundException(__('Invalid User'));
if($this->request->is('post') || $this->request->is('put')){
if(!$this->User->saveAll($this->request->data)){
$this->Session->setFlash('Sorry, your info could not be saved. Please, try again.');
$this->redirect(array('action'=>'edit'));
}
}
if(!$this->request->data)
$this->request->data=$user;
}
Here, I am trying saveAll() function on User model. I Also tried using save() & saveAssociated() functions, but same result. It doesn't change the corresponding rows in the tables, instead, it creates a new row in all tables, accept the users table, this table successfully gets the new values and updates the values.
What should I do ? Please help me.
Thanks
Here is a way to make it works (in theory) but I'm not sure it's the best and, it's not "secure": Adding the id for related field in your form, with hidden field, like so:
echo $this->Form->create('User');
echo $this->Form->hidden('User.id');
echo $this->Form->input('User.name');
/* ... */
echo $this->Form->hidden('Education.0.id');
echo $this->Form->input('Education.0.degree');
/* ... */
echo $this->Form->hidden('Employment.0.id');
echo $this->Form->input('Employment.0.company');
/* ... */
echo $this->Form->submit('Save');
echo $this->Form->end();
You should check in your controller that the id of your associated models match the id of your main model, if not people will be able to edit associated model not linked with their user model.
Edit: This is an edit for further explanation about the discussion in comment.
When you have hasMany relationship, like so:
public $hasMany = array('Experience') ; // In User class
You have to specify ID when saving the relation, if not the Cake engine cannot infer that you want to update a model instead of saving one.
$data = array(
'User' => array(
'id' => 33,
'name' => 'Holt'
),
'Experience' => array(
array(
/* 'id' => 45, */
'name' => 'PHP'
)
)
) ;
$this->User->saveAssociated($data) ;
Here, you know that the User you're refering to is Holt with the id 33, and so you know that the experiences in the following array refers to this user. But, if you don't specify the id field in the Experience array, how the engine would know which experience you're refering to?
If the relation was a belongsTo, it's easy to infer that's the Experience related to user Holt (there is only one Experience per user according to relation belongsTo or hasOne), and by the way you wouldn't have a nested array.
There are different ways to tell the engine you want to update a model, the 2 I use are:
Setting the id value in the data array (like above)
Setting the $this->Model->id value in your controller: This value is implicitely set when you do a $this->Model->read or a $this->Model->find (like you do)
What I'm sure, is that the first option works for associated model (like in the above data array if you uncomment the id), I'm not sure that the second works for associated model, like doing $this->User->Experience->id = ..., you can try it, I cannot check it right now.

CakePHP Not Saving BelongsTo Key

I'm trying to save a record that belongs to another model. Yet, I get an error that the foreign key is missing. The CakePHP docs show examples of this working but I can't get it to work.
Here is the Form:
echo $this->Form->create('Message', array('action'=>'add'));
echo $this->Form->hidden('Ticket.id');
echo $this->Form->input('Message.message');
echo $this->Form->submit('Save Message');
echo $this->Form->end();
Here is what is returned to the controller upon submit:
Array(
[Ticket] => Array
(
[id] => 2
)
[Message] => Array
(
[message] => Message text
)
)
Here is my Message model:
class Message extends AppModel {
public $belongsTo = array('Ticket');
}
Here is my Ticket model:
class Ticket extends AppModel {
public $hasMany = 'Message';
}
Here is my controller logic:
$this->Message->save($this->request->data);
Here is the error message I receive:
Error: SQLSTATE[HY000]: General error: 1364 Field 'ticket_id' doesn't have a default value
The CakePHP documents are pretty clear that Cake will grab the id from the Ticket array. The only way I can get this to work is if I manually assign the ticket id to the message array.
$this->request->data['Message']['ticket_id'] = $this->request->data['Ticket']['id']
I shouldn't have to do that. Where's the magic? I've read at least 20 similar posts but none exactly like this (probably because this is so basic that nobody has this problem because it works for them).
I'm using version 2.4.2
Thanks for any help.
Don't use Model::save() in this case, the reason is that you also want to save associated data, in order for you save data for the main model along with its associated models you need to use Model::saveAssociated().
Your controller should be looking like this
$this->Message->saveAssociated($this->request->data);

How to sort data of bind table in cakephp 2.0

I need the data from various table. I bind them by using cakephp's $hasMany variable. The data is fetched successfully. But I need to sort the result coming from $hasMany table.
for eg. I have two tables
Survey
Questions
Now Survey table contains the data related to Survey like title, id, purpose and Questions table contains question for related survey. I bind questions table with survey in survey model. Now I have a field in Questions table with name ordering. I need to fetch data in that order.
How can I fetch it in that way?
Please help me.
If you want to sort the data directly when it is fetched from db
You can define default order when adding relations between tables in your models. In your Survey model:
var $hasMany = array(
'Question' => array(
'order' => 'ordering DESC'
)
);
See http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany.
You can also define custom ordering when retrieving data from your controller in your conditional array, http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find:
$conditions = array(
'order' => array('Question.ordering DESC')
);
If you are paginating your result with the paginator component, you can setup it like in following example book.cakephp.org/2.0/en/core-libraries/components/pagination.html#query-setup:
public $paginate = array(
'order' => array(
'Question.ordering' => 'desc'
)
);
If you want to sort the data presented in the view
Here you can use the pagination helper (together with the pagination component in the controller) as:
echo $this->Paginator->sort('Question.ordering');
see book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#creating-sort-links
Sorry for removing 'http://' on the two last links, but I am not allowed to post more than two links (<10 rep).
In CakePHP you can sort by associated tables. but you need to sort by column, you should use something like this in your view:
<?php echo $this->Paginator->sort('Question.ordering'); ?>

cakePHP hasOne relationship not auto completing dropdown field

I'm trying to implement a hasone relationship between 2 models, but I can't have the 'add' form autocomplete with the possible options in the second model (the one that belongsTo the first one). This is my code:
- model 1: item.php
<?php
class Item extends AppModel{
var $name = 'Item';
var $primaryKey = 'id';
var $hasOne = 'CoverImage';
}
?>
- model 2: cover_image.php
<?php
class CoverImage extends AppModel{
var $name = 'CoverImage';
var $primaryKey = 'id';
var $belongsTo = array(
'Item' => array(
'className' => 'Item',
'foreignKey' => 'item_id'
));
}
?>
- add view of model 2: add.ctp
<?php echo $this->Form->create('CoverImage',array('url' => array('controller' => 'admins', 'action' => 'add')));?>
<fieldset>
<legend><?php __('Info'); ?></legend>
<?php
echo $this->Form->input('item_id');
echo $this->Form->input('description');
?>
</fieldset>
<?php echo $this->Form->end(__('Create', true));?>
For what I see in Cake's documentation, with this relationship, in the add view I should see a dropdown list in the item_id field to be able to select to which item does this CoverImage belongs to, but the dropdown is empty (and yes, I have some items in the items table already).
Maybe I'm missing something or I've done something wrong, but I can't figure it out. Thanks so much in advance for any clues!
EDIT
One think I've just realized is that if I do this:
echo $this->Form->input('item_id', array('type'=>'text'));
instead of this:
echo $this->Form->input('item_id');
I can add/edit the *item_id* field, I can see its value in the text box. However, if I leave the other one, I just see an empty dropbox and when I try to add/edit a CoverImage, it doesn't work, it just shows an empty white page, not even with errors...
Maybe this is a lead to something...
In order for that to work you have to create a list of possible options in the controller. That does not happen automatically.
public function add() {
$items = $this->CoverImage->Item->find('list');
$this->set(compact('items'));
}
The FormHelper only automatically infers that the field item_id should be populated by the options in the variable $items (plural, no _id).
Do be careful that Items that already haveOne CoverImage should not be part of that list. find('list', array('conditions' => array('CoverItem.id' => null))) will probably* take care of that, but you'll need to recheck just before saving as well, or you need to rethink your associations.
* Not sure off the top of my head whether that'll work for 'list' searches.
EXCELLENT QUESTION. You've run afoul of a disingenuous feature of Cake's associations:
Considering you defined the relationship as hasOne? Guessing at the trace but Cake probably even correctly inferred your preference for list functionality. You got your automagic list...
...of One.
$hasOne is pretty exclusive like that. It "uses up" those "has" relationships (it's makes the relationship a de facto Singleton - so Users only have 1 Profile <-> Profile only has 1 User). Consider - Database can have many configurations, but Dbo will only ever have one Connection at a time and Connection will only have one Dbo. Thus -> hasOne is for marrying two Models til die() do they part.
-- So it doesn't get used nearly as much as hasMany and belongsTo.
For your purpose, you probably want to change to a different association.
Adding an additional $this->Item->find doesn't really fix what's wrong (and I wouldn't recommend it, unless you're mostly done with both models/controllers, or you actively want things to start getting weird fast.)
Also, changing how you call the Form Helper methods - if you return a 'list' type fetch from a find, Cake will automatically produce an option list out of it. What's actually happening is, you're sneaking around your Model on a very thin margin of View functionality. That's why specifying the input type to "break the magic" tends to be discouraged (which, you totally can if you want. Just understand what's actually happening, or: see, weird, fast.)
But you might want to rethink how you've associated your models - wouldn't it also be correct to say, each Item belongsTo a CoverImage (same as each CoverImage belongs to an Item) -- because you have a form expressly permitting a CoverImage to select an Item, any Item, to be displayed with? You'll probably get better results.
HTH. :)

CakePHP assocation question

I'm creating a small timesheet application. Timesheets have athletes, and each athlete has personal split times (like in running, or race car driving)
An Athlete hasMany Run, a Run belongsTo Athlete, An Athlete hasAndBelongsToMany Timesheets (and vice versa). A Timesheet hasMany Run, and finally a Run belongsTo Timesheet.
When I'm adding new runs in my view, I'm unable to get anything but the athlete_id in the select box. I'd really like to have their names instead. Instead of
<?php echo $run['athlete_id'];?>, I've tried <?php echo $athlete['Athlete']['last_name'] ?> but I can't seem to get it to work. Any help would be greatly appreciated. Thanks!
Without knowing exactly how you are building your forms/data it is hard to tell, but how I would do it is.
In the RunController add
$athletes = $this->Run->Athlete->find('list');
$this->set('athletes', $athletes);
and then in the View use this form helper line.
<?php echo $form->input('Run.athlete_id', array('type' => 'select', 'options' => $athletes)); ?>
This should work, there is also a way to use 'compact' to make it a little easier but the above should work fine.
---- BEGIN EDIT ----
I did a little research and found the compact method.
In your RunController use
$athletes = $this->Run->Athlete->find('list');
$this->set(compact('athletes'));
and then in your View use
<?php echo $form->input('Run.athlete_id'); ?>
and the form helper will automatically find the compacted Athlete array and build the select.
---- END EDIT ----
Hope this helps.
Cheers,
Dean
Try printing out the content of the $run: print_r($run) and see if the ['Athlete'] is there.
If not, you might have to manually contain the Athlete model when you do your run query:
$this->Run->contain('Athlete');
Don't forget to use the displayField property of the Model class i.e.
<?php
class Athlete extends AppModel {
public $name = "Athlete";
public $displayField = "name"; // the field name that has the athletes name in it
}
?>
http://book.cakephp.org/view/438/displayField

Resources