Edit multiple rows with one form in cake - cakephp

In my cake app I have a model called faqs, controller called faqs_controller & view called faqsindex.php.
I'm making a CMS so users can change the FAQs. The db table 'faqs' has 5 columns id, category, question, answer and number. "Number" is the order in which the FAQ's will appear.
The loop that lists all of the FAQs looks more or less like this:
<?php
foreach ($faqs as $faq):
<tr>
<td><?php echo $faq['Faq']['category']; ?></td>
<td><?php echo $faq['Faq']['number']; ?></td>
<td><?php echo $faq['Faq']['question']; ?></td>
<td><?php echo $faq['Faq']['answer']; ?></td>
</tr>
<?php endforeach; ?>
I want to make it so that the user can change the "number" cell from this screen, instead of going into a separate edit screen for each row and changing the number there.
You know, like how netflix's queue works, where the user can reorder it from the list, you don't have to click on the movie you want to see to change its order in your queue.
EDIT I set up edit in faqs_controller.php like this:
function edit() {
if(!empty($this->data)) {
$this->Faq->saveAll($this->data['Faq']);
}
else {
$this->data['Faq'] = Set::combine($this->Faq->find('all'), '{n}.Faq.id', '{n}.Faq');
}
}
and in the index view I made a foreach that looks like this:
echo $form->create('Faq', array('action'=>'edit'));
foreach($this->viewVars['faqs'] as $key => $value) {
echo 'id:'.$value['Faq']['id'];
echo '<br/>question:'.$value['Faq']['question'];
echo $form->input('Faq.'.$key.'.number');
}
In this case the foreach goes round 8 times because there are 8 rows. If I submit them, I create 8 new rows & can't update existing rows.
-EDIT-
I changed the form echo here:
echo $form->input('Faq.'.$key.'.question',array('value'=>$value['Faq']['question']));
to prepopulate the form. What I can't figure out is how to update the proper row. If I submit the form I get 8 mysql queries like this:
INSERT INTO faqs (question) VALUES ('THE NEW QUESTION I JUST ADDED?') when I don't want an insert but an update.

Put each faq id inside your form?
echo $form->create('Faq', array('action'=>'edit'));
foreach($this->viewVars['faqs'] as $key => $value) {
echo 'id:'.$value['Faq']['id'];
echo '<br/>question:'.$value['Faq']['question'];
echo $form->hidden('Faq.'.$key.'.id', array('value' => $value['Faq']['id']));
echo $form->input('Faq.'.$key.'.number');
}

if you are using jquery: http://jqueryui.com/demos/sortable/ All the ordering is done on client side, you put the order value into hidden input and use javascript to change them according to user interaction. There's nothing to change on the server side script.
Edit:
echo $form->create('Faq', array('action'=>'edit'));
foreach($this->data['Faq'] as $key => $value) {
echo 'id:'.$value['Faq']['id'];
echo '<br/>question:'.$value['Faq']['question'];
echo $form->input('Faq.'.$key.'.number');
echo $form->input('Faq.'.$key.'.id');
}
echo $form->end('Save');
and the controller:
function edit() {
if(!empty($this->data)) {
$this->Faq->saveAll($this->data['Faq']);
}
$this->data['Faq'] = Set::combine($this->Faq->find('all'), '{n}.Faq.id', '{n}.Faq');
}
unless you redirect them somewhere else after saving.

If you're wanting to update a specific row with Cake it is as simple as setting the appropriate row ID in your saveAll() query.
function edit() {
if (!empty($this->data)) {
$this->Faq->saveAll($this->data['Faq'], array('conditions' => array('Faq.id' => $yourId)));
}
...
}
This is telling Cake's ORM to save the information into the row only where Faq.id is equal to $yourId. If $yourId matches an existing ID in your table then that row should be updated.
Edit
You can put the id field into a hidden form element using CakePHP's FormHelper.
$this->Form->hidden('id', array('value' => $value['Faq']['id']));

Related

Trying to add data to database but getting "Error: Call to a member function save() on boolean"

I'm new to cakephp and I'm trying to add data to my coupon table with the following code.
In my UsersController I have:
public function addCoupon() {
if ($this->request->is('post')) {
if ($this->Coupon->save($this->request->data)) {
$this->Session->setFlash(__('The data has been saved'));
} else {
$this->Session->setFlash(__('The data could not be saved. Please, try again.'));
}
}
}
In my add_coupon.cpt I have:
<?php
echo $this->Form->create('Coupon',['url' => ['action' => 'addCoupon']]);
echo $this->Form->input('coupon_code');
echo $this->Form->input('expiration_date');
echo $this->Form->input('discount_amount');
echo $this->Form->input('usage_limit');
echo $this->Form->input('domain_limit');
echo $this->Form->input('description');
echo $this->Form->input('type');
echo $this->Form->button('Submit');
echo $this->Form->end();
?>
What is wrong with my code?
If your model is, in fact, named Coupon (singular) rather than Coupons (plural), try adding the following line of code anywhere before $this->Coupon->save(this->request->data):
$this->loadModel('Coupon');
But if it's actually named Coupons as convention would prescribe, change $this->Coupon->save(this->request->data) to $this->Coupons->save(this->request->data)
and add this line instead:
$this->loadModel('Coupons');
By default, the Users controller will only load the Users model. If you use any other models, you need to load them manually. Alternatively, you could move all of this to the Coupons controller, where it probably belongs anyways.

Changing table field using 2 different models

I have an IT query system were by users can add their queries, when they add their query the IT department then replies/comments on the query.
How is should work is when the user adds a query it must change the query_type field in the it_queries table to OPEN and when the IT department views the query they reply/comment on it and must change query_type field from OPEN to PENDING then finally when the user has been helped/assisted they should be able to mark the query as closed using a checkbox, that will then change the status from PENDING TO CLOSE.
Not sure if they is a way of setting the constants in the add views and insert in the table.
Obviously i am learning and can someone please guide me on the steps i must take.
Here is my code for the add it_query for the users
<?php echo $this->Form->create('ItQuery'); ?>
<fieldset>
<legend><?php echo __('Add It Query'); ?></legend>
<?php
echo $this->Form->hidden('hr_employee_id',array('value'=>$loggedInId));
echo $this->Form->input('it_query_type_id');
echo $this->Form->input('comment');
echo $this->Form->hidden('status_type', array('value'=>OPEN));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
Here is the code for my comments for IT Department
<?php echo $this->Form->create('ItQueryComment'); ?>
<?php echo __('Add It Query Comment'); ?>
<?php
echo $this->Form->input('it_query_id');
echo $this->Form->input('comment');
echo $this->Form->input('close_query');
?>
<?php echo $this->Form->end(__('Submit')); ?>
Here is the code for my ItQueryComments
public function add() {
if ($this->request->is('post')) {
$this->ItQueryComment->create();
if ($this->ItQueryComment->save($this->request->data)) {
$this->Session->setFlash(__('The it query comment has been saved'));
$this->redirect(array('controller' => 'it_queries','action' => 'view', $this->request->data['ItQueryComment']['it_query_id']));
} else {
$this->Session->setFlash(__('The it query comment could not be saved. Please, try again.'));
}
}
$itQueries = $this->ItQueryComment->ItQuery->find('list');
$hrEmployees = $this->ItQueryComment->HrEmployee->find('list');
$this->set(compact('itQueries', 'hrEmployees'));
}
While saving a comment for a particular Itquery , in your add function of Comments Controller after adding comment to database get its id by using $this->getLastInsertID();
and call function updateAll for Itquery Model and change status of that particular Query to "Pending".

Validating multiple fields with the same name

I have a view file that looks like this:
<?php
echo $this->Form->create('ModelName');
echo $this->Form->input('ModelName.0.firstField');
echo $this->Form->input('ModelName.0.secondField');
echo $this->Form->input('ModelName.1.firstField');
echo $this->Form->input('ModelName.1.secondField');
echo $this->Form->end();
?>
My question is, how do I validate this data? I'm not saving, so it seems pointless to me to call the save or saveAll methods. I just want to validate the data before I process it and display the results to the user.
What happens currently using:
<?php
if ($this->request->is('post')) {
$this->ModelName->set($this->request->data);
if ($this->ModelName->validates()) {
echo $this->Session->setFlash('Success');
} else {
echo $this->Session->setFlash('Failure');
}
}
?>
Is it succeeds all the time even when I'm putting in data that should definitely fail.
I have also tried:
<?php
if ($this->request->is('post')) {
if ($this->ModelName->validateMany($this->request->data)) {
echo $this->Session->setFlash('Success');
} else {
echo $this->Session->setFlash('Failure');
}
}
?>
And that return success all the time as well, but that may be due to the fact that I don't know how to properly use validateMany.
Model::set is used for assigning a single set/record of data to the current instance of the Model. So it may be possible that you are only validating the first set of data in your POST data. You would have to iterate through each record in the POST data, Model::set it to the Model data, and then call Model::validates.
Instead of the above method or Model::validateMany, try using Model::saveAll without actually saving.
http://api20.cakephp.org/class/model#method-ModelsaveAll
validate: Set to false to disable validation, true to validate each record before saving, 'first' to validate all records before any are saved (default), or 'only' to only validate the records, but not save them.
<?php
if ($this->request->is('post')) {
if ($this->ModelName->saveAll($this->request->data, array('validate' => 'only'))) {
echo $this->Session->setFlash('Success');
} else {
echo $this->Session->setFlash('Failure');
}
}
?>

CakePHP How to echo just one record

I want to echo a full name from my MySQL database in my header. When that name is clicked in a list it filters all the records and displays all the records related to that name only. I managed to get the filter working, but not able to display the name in header.
<? $this->read('$jobs as $row'); ?>
<h1><?=$row['Employee']['first_name']?> <?=$row['Employee']['last_name']?>'s Jobs</h1>
<? $this->end(); ?>
If I'm not wrong, you are trying to retrieve this array, I'm assuing $jobs contains single row.
try this
<?php
if (isset($jobs)) {
foreach($jobs as $row){
if (isset($row['Employee']['last_name']))
$last = $row['Employee']['last_name'];
$first = 'N/A';
if (isset($row['Employee']['first_name']))
$first = $row['Employee']['first_name'];
?>
<h1><?php echo $first.' '. $last?>'s Jobs</h1>
<?php } }?>
OR
<h1><?php isset($jobs[0]['Employee']['first_name']) ? $jobs[0]['Employee']['first_name'] : 'N/A' .' '. isset($jobs[0]['Employee']['last_name']) ? $jobs[0]['Employee']['last_name'] : 'N/A'?>'s Jobs</h1>
This can be much more easily achieved through the use of virtual fields. The example in the Cake book is practically identical to your needs.
Just add this to your Employee model:
public $virtualFields = array(
'full_name' => 'CONCAT(Employee.first_name, " ", Employee.last_name)'
);
Now [Employee]['full_name'] can be used without having to use any logic.
Here's the link to the Cake book page covering virtual fields: http://book.cakephp.org/2.0/en/models/virtual-fields.html

Edit and save multiple records in cakephp

In my cakephp app I have an Option model.
In my option/index view I display 2 options with inputs and radio button fields.
I want to update both of them, but I get a weird behaviour.
The option I alter doesn't get saved and instead a new option is inserted with the new value.
Here is my view
<h2 class='page-title' id='manage-options'>Opzioni</h2>
<?php echo $form->create(null, array('action'=>'index')); ?>
<table>
<tr>
<td><?= $options[0]['Option']['name']?></td>
<td><?= $form->radio(
$options[0]['Option']['id'],
array(
'1' => 'Sì',
'0' => 'No'),
array('default'=> $options[0]['Option']['value'], 'legend'=>false)
);?>
</td>
</tr>
<tr>
<td><?= $options[1]['Option']['name']?></td>
<td><?= $form->input($options[1]['Option']['id'],array('label'=>false,'value' => $options[1]['Option']['value'] ))?></td>
</tr>
</table>
<?php echo $form->submit('Salva'); ?>
<?php echo $form->end(); ?>
And my controller:
function index() {
if (!empty($this->data)) {
foreach($this->data['Option'] as $id => $value) :
$this->Option->id = $id;
$feedback = $this->Option->read();
$this->Option->saveField('value', $value);
endforeach;
$this->Session->setFlash('Opzioni aggiornate');
}
$this->Option->recursive = 0;
$this->set('options', $this->paginate());
}
Before posting here I spent two hours googling for answers and experimenting. I know about saveAll() and i have tried these solutions:
http://planetcakephp.org/aggregator/items/2172-cakephp-multi-record-forms
http://teknoid.wordpress.com/2008/10/27/editing-multiple-records-with-saveall/
I have been tweaking my code to fit these patterns, but I got no results (oscillating between 'not working' and 'not working and I get an extra record'), so I decided to post my original code.
Can you help, indicating the most proper way to do this?
Cheeers,
Davide
The problem was with the data in the DB. The kind folks on the cakephp IRC channel called my attention to the fact that in most databases ID = 0 equals 'new record'. For some reason I had an option with ID 0, so when updating the underlaying mysql query actually created a new record.
Changed the IDs, problem fixed.
The main problem with your code that I see is that your fields, both the radio and the input, are being built with only an ID value as the first parameter. The correct "cake way" of building a field is having the first parameter be Model.fieldname, in your case I believe it would be $form->input('Option.id', array())?>
If you inspect the html generated by your code you will see the field name is data[id], and it should be data[Option][id] if you want to loop through $this->data['Option'] in your controller.
Try changing your code to include the Model.fieldname as the first parameter and then it should have the data submitted correctly to your controller.

Resources