Validating multiple fields with the same name - cakephp

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');
}
}
?>

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.

Associations in CakePHP

I have 2 db tables : Users & Projects.
Fields of Users table are :
id, name, username, password, email, address, phone.
Fields of Projects table are :
id, name, description, user_id, deadline, budget.
What I need is that, I'll have 1 form, where there will be all fields from these 2 tables. And, when I submit the form, these fields will be saved in these 2 tables.
For example, my form will be like this :
<?php
echo $this->Form->create('User');
echo $this->Form->input('name',array('type'=>'text','div'=>false));
echo $this->Form->input('username',array('type'=>'text','div'=>false));
echo $this->Form->input('password',array('type'=>'password','div'=>'false));
echo $this->Form->input('email',array('type'=>'email','div'=>false));
echo $this->Form->input('address',array('type'=>'textarea','div'=>false));
echo $this->Form->input('phone',array('type'=>'tel','div'=>false));
echo $this->Form->input('name',array('type'=>'text','div'=>false));
echo $this->Form->input('description',array('type'=>'text','div'=>false));
echo $this->Form->input('deadline',array('type'=>'date','div'=>false));
echo $this->Form->input('budget',array('type'=>'num','div'=>false));
echo $this->Form->submit('Save');
echo $this->Form->end();
?>
Now, I want that when I submit the form, the fields will be saved in the corresponding tables; Users table will receive & save its fields, and Projects table will receive & save its fields.
For this, I tried hasMany association between Users & Projects tables, means, each user from Users table will have many projects from Projects table.
In User.php, I tried this :
class User extends AppModel{
public $hasMany=array(
'Project'=>array(
'className'=>'Project')
);
}
I thought it'd work, but it didn't, only Users table get its values, no value goes to Project table. What is the problem here ? What should I do ?
Thanks.
try this in your view
echo $this->Form->create('User');
echo $this->Form->input('name',array('type'=>'text','div'=>false));
echo $this->Form->input('username',array('type'=>'text','div'=>false));
echo $this->Form->input('password',array('type'=>'password','div'=>false));
echo $this->Form->input('email',array('type'=>'email','div'=>false));
echo $this->Form->input('address',array('type'=>'textarea','div'=>false));
echo $this->Form->input('phone',array('type'=>'tel','div'=>false));
echo $this->Form->input('Project.0.name',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.0.description',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.0.deadline',array('type'=>'date','div'=>false));
echo $this->Form->input('Project.0.budget',array('type'=>'num','div'=>false));
echo $this->Form->submit('Save');
echo $this->Form->end();
And in your controller you have to use
$this->User->saveAssociated($data);
see the manual about how to save associated data
At first, add the model names User and Project as prefix to your form fields to separate them:
View:
<?php
echo $this->Form->create('User');
echo $this->Form->input('User.name',array('type'=>'text','div'=>false));
echo $this->Form->input('User.username',array('type'=>'text','div'=>false));
echo $this->Form->input('User.password',array('type'=>'password','div'=>false));
echo $this->Form->input('User.email',array('type'=>'email','div'=>false));
echo $this->Form->input('User.address',array('type'=>'textarea','div'=>false));
echo $this->Form->input('User.phone',array('type'=>'tel','div'=>false));
echo $this->Form->input('Project.name',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.description',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.deadline',array('type'=>'date','div'=>false));
echo $this->Form->input('Project.budget',array('type'=>'num','div'=>false));
echo $this->Form->submit('Save');
echo $this->Form->end();
At second, use the separation to save your data into different models. Do not forget to add an associated column user_id to your table projects.
Controller
class UserController extends AppController
{
function create()
{
$this->loadModel('Project');
if($this->request->is('post', 'put'))
{
$this->User->getDataSource()->begin($this); // Starts transaction
if($this->User->save($this->request->data['User']))
{
// Success, save project now
$this->request->data['Project']['user_id'] = $this->User->getLastInsertId();
if($this->Project->save($this->request->data['Project']))
{
$this->User->getDataSource()->commit($this); // Commit
// success
} else {
$this->User->getDataSource()->rollback($this); // Rollback
}
} else {
$this->User->getDataSource()->rollback($this); // Rollback
}
}
}
}
I tested this code → works.
Your question is not very clear to me but what I see is you need to save data using associations.
But an association is a connection between two Active Record models. Associations are created to maintain Primary Key-Foreign Key information between instances of the two models.
You will need to save your USERS table data first and get the primary ID of last saved and use it as user_id for PROJECTS table and save PROJECT table information creating a PROJECT instance.
view:
<?php
echo $this->Form->create('User');
echo $this->Form->input('name',array('type'=>'text','div'=>false));
echo $this->Form->input('username',array('type'=>'text','div'=>false));
echo $this->Form->input('password',array('type'=>'password','div'=>'false));
echo $this->Form->input('email',array('type'=>'email','div'=>false));
echo $this->Form->input('address',array('type'=>'textarea','div'=>false));
echo $this->Form->input('phone',array('type'=>'tel','div'=>false));
// adding associate model name to every field..
echo $this->Form->input('Project.name',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.description',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.deadline',array('type'=>'date','div'=>false));
echo $this->Form->input('Project.budget',array('type'=>'num','div'=>false));
echo $this->Form->submit('Save');
echo $this->Form->end();
Note: You can disable all 'div' on form creation.. Look documentation for details...
User model:
class User extends AppModel{
public $hasMany=array('Project');
}
UsersController:
public function add(){
if($this->request->is('post')){
$this->User->create();
if($this->User->save($this->request->data)){// if you association is correct, all associated data will save automatically.
$this->Session->setFlash('saved');
$this->redirect('where you wants to redirect');
}else{
$this->Session->setFlash('Something went wrong. Try again');
}
}
}
Easy way with saveAll
<!-- View -->
<?php
echo $this->Form->create('User');
echo $this->Form->input('name',array('type'=>'text','div'=>false));
echo $this->Form->input('username',array('type'=>'text','div'=>false));
echo $this->Form->input('password',array('type'=>'password','div'=>false));
echo $this->Form->input('email',array('type'=>'email','div'=>false));
echo $this->Form->input('address',array('type'=>'textarea','div'=>false));
echo $this->Form->input('phone',array('type'=>'tel','div'=>false));
echo $this->Form->input('Project.0.name',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.0.description',array('type'=>'text','div'=>false));
echo $this->Form->input('Project.0.deadline',array('type'=>'date','div'=>false));
echo $this->Form->input('Project.0.budget',array('type'=>'num','div'=>false));
echo $this->Form->submit('Save');
echo $this->Form->end();
?>
<!-- Controller -->
<?php
class UserController extends AppController
{
function create()
{
if($this->request->is('post', 'put'))
{
if($this->User->saveAll($this->request->data))
{
// Success - set your flashMessage
} else {
// Error - set your flashMessage
}
}
}
}
?>

cakePHP pre-populates password field and breaks on update

I have a user profile update page, which pre-populates the text fields with the users information gathered from $this->request->data. Everything works fine, except the password field is pre-populated with what appears to be the encrypted password from the database, so on save, it fails the validation as the password exceeds the number of characters allowed, and even if it stores, it will then replace the users actual password with the encrypted same password, if that makes sense.
What would be the best work-around for this?
Controller:
public function profile() {
if($this->request->is('post') || $this->request->is('put')) {
if($this->Auth->user("id") == $this->request->data['User']['id']) {
$this->request->data['User']['user_status_id'] = $this->Auth->user("user_status_id");
$this->request->data['User']['user_type_id'] = $this->Auth->user("user_type_id");
if($this->User->save($this->request->data)) {
$this->Session->setFlash('Your profile has been updated','default',array('class'=>'success'));
} else {
$this->Session->setFlash("An error has occured updating your profile.");
}
} else {
$this->Session->setFlash("Unable to save this result as there is an ID mismatch.");
}
} else {
$this->request->data = $this->User->read(null,$this->Auth->user("id"));
}
}
View:
<h1>Profile</h1>
<h3>Update your profile</h3>
<div class="left">
<div class="formContainer">
<?php
echo $this->Form->create('User');
echo $this->Form->input('username',array("id" => "profileUsername"));
echo $this->Form->input('password',array("id" => "profilePassword"));
echo $this->Form->input('company');
echo $this->Form->input('first_name');
echo $this->Form->input('last_name');
echo $this->Form->input('telephone');
echo $this->Form->input('fax');
echo $this->Form->input('id',array('type' => 'hidden'));
echo $this->Form->end(__('Update'));
?>
</div>
</div>
The problem you have is, if you have stored your password as you should (with a one way hash), you can't reverse it to show the plain text version so you could simply set the password data to null (leaving the password field empty):
$this->request->data['User']['password'] = NULL;
You then have two options:
Require the users password to update their data (a nice little security measure)
Only update the users password when the user has entered a new password
The 2nd option may break your validation if you have set password validation rules to allowEmpty => false, in this case you could use multiple validation sets which you can do by following either of these (all work in cakephp 2.x) multivalidateable behaviour, multiple validation sets
read this and use this behavior:
http://www.dereuromark.de/2011/08/25/working-with-passwords-in-cakephp
the password is a one-way ticket - due to the technical details of hashs.
basically you never pass the password hash back into the view. always display empty fields (like you should have seen on pretty much every other website in the www).
you then ignore and posted field which is empty. only if the password field is freshly set you actually trigger the validation.
<?php echo $this->Form->input('password', array('label' => false,'type'=>'password','required'=>'false')); ?>
Output
<input name="data[User][password]" type="password" id="UserPassword">
Be careful! use this only on the edit part
$this->Form->input('password',array('value'=>null));

Edit multiple rows with one form in cake

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']));

Cakephp Validation

I am using an Account Controller which doesnt have its own table but uses User Model.
All works fine except - when I validate any form. It says validation fails (when I try to fail the validation to check) but doesnt throw the error below the field
View
<?php echo $this->Form->input('id'); ?>
<label for="UserPassword">New Password:</label>
<?php echo $this->Form->text('password', array('type' => 'password', 'value' => 'harsha')); ?><em>Password must be min 6 characters.</em> <?php echo $form->error('password'); ?>
Controller Action
if($this->User->saveField('password', $this->data['User']['password'], array('validate' => 'first'))) {
$this->Session->setFlash('Password has been changed.', 'flash-success');
} else {
$this->Session->setFlash('There was some problem with our system. Please try after some time.', 'flash-warning');
}
Try debug()ing the contents of $this->validationErrors in your view, as well as $this->data in your controller just after a form submission. This should give you a lot more information to work from.
I suspect that your problem is Cake is building form inputs based on the wrong model -- building form fields for Account.id and Account.password instead of User.id and User.password. This is because FormHelper takes its default model from the controller/view it's invoked from, which in your case appears AccountsController.
In order to generate the User.id and User.password fields your controller's submission handling expects, you'll need to prepend User. in your FormHelper calls. Thus:
$this->Form->input('User.id');
$this->Form->text('User.password');
Have you tried:
echo $session->flash();
Note that whatever the manual says, it returns, not echoes. I logged this a while back and it has been changed in the 1.3 manual, but not the 1.2.
Hi you who's asking If you want to show error-message that return from UserModel's validate So you can add line code bellow after input form password
<?php
if ($this->Form->isFieldError('password')) {
echo $this->Form->error('password', array('class' => 'error'));
?>
and if you want to show error-message that set by method setFlash
you must redirect page and then use $this->Session->flash('flash-name') in page you want to show it
<?php
//in UsersController
$this->Session->setFlash('message here', 'flash-name');
//in view
echo $this->Session->flash('flash-name');
?>
Good luck!

Resources