CakePHP - Model validation does not work - forms

again alot of similar questions out there but none of them really help me.
HTML5 form validation seems to be triggering with messages "Please fill in this field" instead of the model validation messages which should be "Please enter the model"
I have a form to add Computers to the database.
Here is my form:
echo $this->Form->create('Computer');
echo $this->Form->input('Computer.model', array('label' => 'Model'));
echo $this->Form->input('Computer.memory', array('label' => 'memory'));
echo $this->Form->input('Computer.hdd', array('label' => 'hdd'));
echo $this->Form->input('Computer.price', array('label' => 'price'));
echo $this->Form->end('Save Computer');
Here is the full controller code with index and add actions
<?php
class ComputersController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('add');
}
public function index() {
$this->set('computers', $this->Computer->find('all'));
}
public function add() {
if ($this->request->is('post')) {
if (!empty($this->request->data)) {
$this->Computer->save($this->request->data);
$this->Session->setFlash(__('Your Computer has been saved, or so it seems.....'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('Not sure why we got here 1.'));
} else {
$this->Session->setFlash(__('By right, this should be the index page'));
}
}
}
?>
Here's the model
<?php
class Computer extends AppModel {
public $validate = array(
'model' => array(
'Please enter model name'=> array(
'rule'=>'notEmpty',
'message'=>'Please enter model'
)
)
);
}
?>
I read from other forms that triggering the model save function, which I do, will automatically trigger the model validation. How can i get the model validation to work?
Thanks
Kevin

As you were saying, if you have the notEmpty validation in the model, CakePHP adds required="required" on the input attributes. This is handled by the browser, so you see the default Please enter this field message when you try to submit an empty value. An advantage is that if you are using the browser in a different language, the message will be displayed in that language.
If you want to change that message, you can try a solution like the ones from this question. (this is probably not what you want)
If you want to remove that client-side message, you can disable it using novalidate
echo $this->Form->create('Computer', array('novalidate' => 'novalidate'));
This way, the HTML5 required property will be ignored, and you will get the message from the model.
I am not sure if there is a way to tell Cake to use the server-side value on the client.

$this->{Model}->save() returns false if the validation fails, but in your case you're redirecting with a flash message after save function. so first check the form is saving perfectly or not, if perfectly saving then redirect to listing page other wise render your view file with a flash message where you can view the validation messages.
if ($this->Computer->save($this->request->data)) {
$this->Session->setFlash(__('Your Computer has been saved, or so it seems.....'));
return $this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('Unable to save form'));
}
Note: To disable html validation just do
$this->Form->inputDefaults(array(
'required' => false
));
in your view file
Hope this helps you.

Set 'novalidate' => true in options for FormHelper::create()
echo $this->Form->create('Computer', array('novalidate' => true));
For more information, go to http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html

Related

CSRF field is missing when I embed my form with a requestAction in CakePHP 3

I want to embed a contact form in multiple places on my website.
I developed a contact form in a contact() function within my MessagesController.php:
// MessagesController.php
public function contact()
{
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
... // shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
I loaded the CSRF component in the initialize() function of the AppController.php:
// AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Csrf');
... // shortened for brevity
}
The form is rendered with a contact.ctp and it works fine.
I followed CakePHP's cookbook which suggests using requestAction() within an element, then echoing the element where I want it:
// contact_form.ctp
<?php
echo $this->requestAction(
['controller' => 'Messages', 'action' => 'contact']
);
?>
And:
// home.ctp
<?= $this->element('contact_form'); ?>
The problem is that the form is rendered fine, but the CSRF hidden field is missing. It should be automatically added to the form since the CSRF component is called in the AppController.php.
I guess either using an element with a requestAction() isn't the solution for this particular case, or I am doing something wrong.
Any ideas? Thanks in advance for the input!
Request parameters need to be passed manually
requestAction() uses a new \Cake\Network\Request instance, and it doesn't pass the _Token and _csrf parameters to it, so that's why things break.
While you could pass them yourself via the $extra argument, like
$this->requestAction(
['controller' => 'Messages', 'action' => 'contact'],
[
'_Token' => $this->request->param('_Token'),
'_csrf' => $this->request->param('_csrf')
]
);
Use a cell instead
I would suggest using a cell instead, which is way more lightweight than requesting an action, also it operates in the current request and thus will work with the CSRF component out of the box.
You'd pretty much just need to copy your controller action code (as far as the code is concerned that you are showing), and add a loadModel() call to load the Messages table, something like
src/View/Cell/ContactFormCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class ContactFormCell extends Cell
{
public function display()
{
$this->loadModel('Messages');
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
// ... shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
}
Create the form in the corresponding cell template
src/Template/Cell/ContactForm/display.ctp
<?php
echo $this->Form->create(
/* ... */,
// The URL needs to be set explicitly, as the form is being
// created in the context of the current request
['url' => ['controller' => 'Messages', 'action' => 'contact']]
);
// ...
And then wherever you want to place the form, just use <?= $this->cell('ContactForm') ?>.
See also
API > \Cake\Routing\RequestActionTrait::requestAction()
Cookbook > Views > Cells

How to disable filter of a field in Zend Framework 2 in the controller?

Form to add/edit user I get from service manager with already installed filter, which is the test password. But this password is not needed when the user is edited. Can I somehow disabled password field validation in the controller?
In the getServiceConfig function of the module:
// ....
'UserCRUDFilter' => function($sm)
{
return new \Users\Form\UserCRUDFilter();
},
'UserCRUDForm' => function($sm, $param, $param1)
{
$form = new \Users\Form\UserCRUDForm();
$form->setInputFilter($sm->get('UserCRUDFilter'));
return $form;
},
// ....
In the controller I first of all getting a form object from service manager:
$form = $this->getServiceLocator()->get('UserCRUDForm');
Then disable user password validation and requirements, when user is edited and password not specified:
if ($user_id > 0 && $this->request->getPost('password') == '') {
$form->.... // Someway gained access to the filter class and change the password field validation
}
And after this i make a validation:
$form->isValid();
I found it!
// If user is editted - clear password requirement
if ($user_id > 0) {
$form->getInputFilter()->get('password')->setRequired(false);
$form->getInputFilter()->get('confirm_password')->setRequired(false);
}
This lines is disables requirement of input form fields :)
if you like to set all validators by yourself, call inside your form class
$this->setUseInputFilterDefaults(false);
to disable auto element validations/filter added from zend.
if you like to remove filter from elements call in your controller after your form object this
$form->getInputFilter()->remove('InputFilterName');
$form->get('password')->removeValidator('VALIDATOR_NAME'); should do the trick.
Note that you may have to iterate trough the Validatorchain when using Fieldsets.
$inputFilter->add(array(
'name' => 'password',
'required' => true,
'allow_empty' => true,
));
And on ModeleTable: saveModule:
public function saveAlbum(Album $album)
{
$data = array(
'name' => $album->name,
);
if (isset($album->password)){
$data['password'] = $album->password;
}

Zend: Redirect from form without validation

I have a form for the creation of new "groups". I now added a small "go back" image with which the user should be able to go back one step. I don't know why, but when I click this new image, the controller and action used for the form which I want to leave (/admin/creategroup) is called again with HTTP POST set. Therefore, the form validation is done, and I'm stuck at this form with the validation errors displayed.
This is a snippet of the code from my form with both image-buttons. I wan't the "go back"-image to redirect me to the specified controller without validating the form:
$this->addElement('image', 'btnBack', array (
'name' => 'btnBack',
'id' => 'btnBack',
'label' => '',
'title' => 'Go back',
'alt' => 'Go back',
'src' => '/img/undo.png',
'onClick' => "window.location='/admin/groupoverview'"
));
$this->addElement('image', 'btnSave', array (
'name' => 'btnSave',
'id' => 'btnSave',
'label' => '',
'title' => 'Save this new group',
'alt' => 'Save this new group',
'src' => '/img/save.png',
'onClick' => "document.forms[0].submit();"
));
Edit:
I already thought of the possibility to check in /admin/creategroup whether it was called from the 'btnBack'-image or the 'btnSave'-image and skip form validation and redirect correctly if the source was the 'btnBack'-image.
I just think that there should be a nicer solution to directly redirect from the form and circumvent calling /admin/creategroup again.
Edit2:
My view script:
<div id="createGroupMask">
<br/>
Use the form below to create a new group
<?php
$this->form->setAction($this->url());
echo $this->form;
?>
</div>
My action in the controller:
public function creategroupAction()
{
$form = new Application_Form_CreateGroup();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
// Data for new group is valid
...
} else {
// Form data was invalid
// => This is where I land when pressing the 'back' image
// No further code here
}
}
$this->view->form = $form;
}
Now there is something to work with:
The isValid() loop is incorrect, your form will never evaluate as inValid with respect to the elements you've presented, you will never get to the else.
public function creategroupAction()
{
$form = new Application_Form_CreateGroup();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
// Data for new group is valid
...
} else {
/* This is incorrect */
// Form data was invalid
// => This is where I land when pressing the 'back' image
// No further code here
}
}
$this->view->form = $form;
}
My problem is that I'm not sure what is going to be submitted from your form, I'm not really familiar with how your using "onClick" and what I presume is javascript. It looks like element btnBack should redirect on click and element btnSave should POST. However this does not seem to be happening.
I have done this type of thing in PHP and ZF with submit buttons, perhaps the flow of what I did will help:
NOTE: for this type of flow to work you must give the button element a label. The label is used as the submit value.
//psuedoCode
public function creategroupAction()
{
$form = new Application_Form_CreateGroup();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
//I would probably opt to perform this task with a switch loop
if ($form->getValue('btnBack') === some true value) {
$this->_redirect('new url');
}
if ($form->getValue('btnSave') === some true value) {
//Process and save data
}
} else {
//Display form errors
}
$this->view->form = $form;
}
I think when all is said and done the crux of your problem is that you did not give your button elements a label.
I tried adding labels to my images, but this didn't work.
I also tried to use the isChecked() method on my btnBack-image like this:
if ($form->btnBack->isChecked()) {
// 'Go back' image was clicked so this is no real error, just redirect
}
This didn't work either.
I finally was able to check which image was clicked via the following method as answered in Zend form: image as submit button:
public function creategroupAction()
{
$form = new Application_Form_CreateGroup();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
// Data for new group is valid
...
} else {
// Form data was invalid
if (isset($this->_request->btnBack_x)) {
// 'Go back' image was pressed, so this is no error
// -> redirect to group overview page
$this->_redirect('/admin/groupoverview');
}
}
}
$this->view->form = $form;
}
I guess this doesn't thoroughly answer the original question as the validation is still done and I'm only checking for this 'special case' where the 'Go back' image was clicked, but I'll mark it as answered anyways.
Tim Fountain suggested an even cleaner approach in my somewhat related question:
Zend forms: How to surround an image-element with a hyperlink?

Simple form without model in CakePHP

I'm trying to add form for request of additional information in product page. It's simple form with name, country, email and question fields.
I founded this tutorial , but i don't like idea that just for small form I need make new model and controller.
So i add form in view.ctp
print $this->Form->create('', array('action' => 'sendemail'));
print $this->Form->input('name',array('label' => __('Name',true).':'));
print $this->Form->input('country',array('label' => __('Country',true).':'));
print $this->Form->input('email',array('label' => __('E-mail',true).':'));
print $this->Form->input('question',array('type' => 'textarea', 'label' => __('Your question',true).':'));
print $this->Form->end(__('Send', true));
and add function sendemail in products_controller.php
function sendemail(){
if(!empty($this->data)){
if($this->data['Product']['name'] && $this->data['Product']['country'] && $this->data['Product']['email'] && $this->data['Product']['question']){
// sending email
$this->Session->setFlash(__('Thanks, your qustion was send.', true), 'default', array('class' => 'message status'));
$this->redirect($this->referer());
} else {
// validation
$this->Session->setFlash(__('Fill all fiels', true), 'default', array('class' => 'message error'));
$this->redirect($this->referer());
}
} else {
$this->Session->setFlash(__('Error occurred', true), 'default', array('class' => 'message error'));
$this->redirect($this->referer());
}
}
So is there any way how to validate the form properly and if some field isn't fill after redirection this field will be set as error form input? Also how can I set all already filled fields back to form?
Thanks for any help!
You don't need a database table, but CakePHPs validation support is inherently tied to models. Your code is going to be a lot cleaner than any alternative by creating a dummy model and storing your validation rules in it.
To get around using a table, add var $useTable = false; to your model definition.
To validate without saving, call $this->MyModel->set($this->data); and then a call to $this->MyModel->validates() will perform the check and populate $this->MyModel->validationErrors for automagic population in the form.

Zend form in a popup (fancybox, lightbox....)

I am developping a web application using Zend and I ran out of ideas for a problem I am having. In just a few words, I am trying to have a contact form in a popup (Fancybox, lightbox, colorbox or whatever...). The whole thing works fine, in the sense that it shows up the contact form in the popup and allows to send emails. However, whenever there are errors (unfilled input or filled wrong), I couldn't get those errors to be displayed on the popup (it actually redirects me back to the form in a normal display (view+layout), to show the errors.
It is perhaps possible but I now thought that perhaps I could more easily bring my error message to a new popup (the contact page, filled unproperly, would lead to a error popup page...). I think this alternative could look cool but am having real trouble doing it. Now my real question is : Can we really make a form on a popup, using Facybox (Lighbox or any other actually ... just want my popup) and Zend? Any Guru outhere??
Thanks a lot
here is the code:
the link for instance:
<a class="popLink" href=" <?php echo $this->url(array('module'=>'default', 'controller'=>'contact', 'action'=>'sendmail')).'?ProID='.$this->proProfil->getProID(); ?>">Contact</a>
the action:
public function sendmailAction()
{
$this->_helper->layout()->setLayout('blank');
$request = $this->getRequest();
$proID = $this->_getParam("ProID");
$professionalsList = new Model_DirPro();
$proName = $professionalsList->getProInfo($proID);
$translate = Zend_Registry::get('translate');
Zend_Validate_Abstract::setDefaultTranslator($translate);
Zend_Form::setDefaultTranslator($translate);
$contactform = new Form_ContactForm();
$contactform->setTranslator($translate);
$contactform->setAttrib('id', 'contact');
$this->view->contactform = $contactform;
$this->view->proName = $proName;
if ($request->isPost()){
if ($contactform->isValid($this->_getAllParams())){
$mailSubject = $contactform->getValue('mailsubject');
if ($contactform->mailattcht->isUploaded()) {
$contactform->mailattcht->receive();
//etc....
the form:
class Form_ContactForm extends Zend_Form
{
public function init ()
{
$this->setName("email");
$this->setMethod('post');
$this->addElement('text', 'mailsubject',
array('filters' => array('StringTrim'),
'validators' => array(), 'required' => true, 'label' => 'Subject:'));
$mailattcht = new Zend_Form_Element_File('mailattcht');
$mailattcht->setLabel('Attach File:')->setDestination(APPLICATION_PATH.'/../public/mails');
$mailattcht->addValidator('Count', false, 1);
$mailattcht->addValidator('Size', false, 8000000);
$mailattcht->addValidator('Extension', false,
'jpg,png,gif,ppt,pptx,doc,docx,xls,xslx,pdf');
$this->addElement($mailattcht, 'mailattcht');
$this->addElement('textarea', 'mailbody',
array('filters' => array('StringTrim'),
'validators' => array(), 'required' => true, 'label' => 'Body:'));
$this->addElement('submit', 'send',
array('required' => false, 'ignore' => true, 'label' => 'Send'));
$this->addElement('hidden', 'return', array(
'value' => Zend_Controller_Front::getInstance()->getRequest()->getRequestUri(),
));
$this->setAttrib('enctype', 'multipart/form-data');
}
}
I would suggest implementing AJAX validation. This would allow for the form to be verified before it is submitted. ZendCasts has a good tutorial on how to accomplish this: http://www.zendcasts.com/ajaxify-your-zend_form-validation-with-jquery/2010/04/
Ajax requests are handled via the contextSwitch action helper. You can to specify the various contexts an action needs to handle (xml or json) in the init method of the controller as follows:
public function init()
{
$this->_helper->contextSwitch()
->addActionContext('send-mail', 'json')
->initContext()
;
}
The request url should contain a "format=json" appended to the query string. This will execute the action and send the response in json format. The default behaviour of JSON context is to extract all the public properties of the view and encode them as JSON. Further details can be found here http://framework.zend.com/manual/en/zend.controller.actionhelpers.html
I found a "probably not the prettiest" working solution, it is to indeed use ajax as mentioned in the previous zendcast for validation to stop the real validation (preventdefault), process the data return the result and if everything's ok restart it.