I would like to use custom validation for textarea because there will be a Slovak accents but when I will enter the new line throws me an error.
Please advise how to validate enter button? thank you
var $validate = array('text'=>array('custom'=>array(
'rule' => array('custom', '/^[a-zA-Z0-9cšltžýáíéúóäônd".:,´()CŠLTŽÝÁÍÉÚÓND ]*$/i'),
'message' => 'Zadávajte prosím len čísla alebo písmená')
)
);
You need a custom validation rule and I would suggest externalizing that to a model function.
Also your inline validation seems a bit broken to me.
So what you can do:
var $validate = array(
'text' => array(
'rule' => 'myNewCustomRule',
'message' => 'Zadávajte prosím len čísla alebo písmená'
));
And the function in the model:
function myNewCustomRule($custom) {
//You may need to add here because the $data array is passed using the form field name as the key, you will have to extract the value to make the function generic
//array_shift($custom);
return preg_match('/^[a-zA-Z0-9cšltžýáíéúóäônd".:,´()CŠLTŽÝÁÍÉÚÓND ]*$/i', $custom);
}
Related
In Symfony admin, I have a form, where the second field type depends on the ChoiceField value selected. The second field can be of Symfony Url field type or sonata provided sonata_type_model_list field type.
I have created an ajax request to My Bundle Controller to return the form, which contains the needed field.
> /src/MyBundle/Controller/MyController.php
namespace MyBundle\Controller
use Sonata\AdminBundle\Controller\CRUDController;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Sonata\AdminBundle\Form\FormMapper;
class MyController extends CRUDController
{
public function getFieldAction()
{
//getting the value of choice field
$type = $this->get('request')->get('type');
//sonata.admin.reference is a service name of ReferenceBundle admin class
$fieldDescription = $this->admin->getModelManager()
->getNewFieldDescriptionInstance($this->admin->getClass(), 'reference');
$fieldDescription->setAssociationAdmin($this->container->get('sonata.admin.reference'));
$fieldDescription->setAdmin($this->admin);
$fieldDescription->setAssociationMapping(array(
'fieldName' => 'reference',
'type' => ClassMetadataInfo::ONE_TO_MANY,
));
// Getting form mapper in controller:
$contractor = $this->container->get('sonata.admin.builder.orm_form');
$mapper = new FormMapper($contractor, $this->admin->getFormBuilder(), $this->admin);
$form_mapper = $mapper->add('reference', 'sonata_type_model_list', array(
'translation_domain' => 'ReferenceBundle',
'sonata_field_description' => $fieldDescription,
'class' => $this->container->get('sonata.admin.reference')->getClass(),
'model_manager' => $this->container->get('sonata.admin.reference')->getModelManager(),
'label' => 'Reference',
'required' => false,
));
//#ToDo build $form from $form_mapper
return $this->render('MyBundle:Form:field.view.html.twig', array(
'form' => $form->createView(),
));
}
}
I cannot find any method in Sonata\AdminBundle\Form\FormMapper class to build a form (it seems to be possible with create() method, but it only works with common Symfony field types, not Sonata form field types, which are commonly generated in Block or Admin classes).
Is it possible to use Sonata\AdminBundle\Form\FormMapper in the Controller to build a form?
Or is there another way I can build a form with Sonata form field types in the Controller?
You should not use a controller, rather a Sonata Admin Service.
Sonata provides you with the 'sonata_type_choice_field_mask' type which allows you to change the fields displayed on the form dynamically depending on the value of this 'sonata_type_choice_field_mask' input.
Here is the doc where you can find everything about sonata types and the choice field mask.
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('reference', 'sonata_type_choice_field_mask', array(
'choices' => array(
//The list of available 'Reference' here
'choice1',
'choice2'
),
'map' => array(
//What you want to display depending of the selected option
'choice1' => array(
// List of the fields displayed if choice 1 is selected
'field1', 'field3'
),
'choice2' => array(
// List of the fields displayed if choice 2 is selected
'field2', 'field3'
)
),
'placeholder' => 'Choose an option',
'required' => false
))
->add('field1', 'sonata_type_model_list', array(/* Options goes here */))
->add('field2', 'url', array(/* Options goes here */))
->add('field3')
;
}
It looks like you have access to the admin which directly handles building the form with Sonata\AdminBundle\Admin\AbstractAdmin::getForm.
$form = $this->admin->getForm();
I'm new to CakePhp, I'm using CakePhp 2.x.
I am probably going about solving the problem below the wrong way. And I just know I'm overlooked something real simple but,.....
I'm validating login details based on 'Between 5 to 15 characters' they are retuning errors as expected.
[The MODEL]
public $validate = array(
'username' => array(
'between' => array(
'rule' => array('between', 5, 15),
'message' => 'Between 5 to 15 characters'
)
),
'password' => array(
'rule' => array('minLength', '8'),
'message' => 'Minimum 8 characters long'
)
);
[The CONTROLLER]
public function login() {
if ($this->request->data) {
$this->User->set($this->request->data);
if ($this->User->validates() && $this->Auth->login()) {
if ($user = $this->Auth->user()) {
$this->render($this->Auth->redirect());
}else{
//??
}
}else{
$this->User->create();
pr($this->User->invalidFields());
$errors = $this->User->validationErrors;
$data = compact('errors');
$this->set('errors', $data);
$this->set('_serialize', array('errors'));
$this->Session->setFlash('Your username/password combination was incorrect');
}
}
}
So, the problem is, if the fields follow the rules in the model above even if the login details (the user) doesn't exist, no errors will be returned (no good). Would it be correct to add an other validation for this, adding another rule to check if that user actually exists? If so how!?
Or, do I work this into the controllers login function checking if the user exists? I'm a little confused now. Maybe I've been looking at the screen for too long.
Thanks.
Would it be correct to add an other validation for this, adding
another rule to check if that user actually exists? If so how!?
You can add as many rules as you want. In this case you want the rule "unique". Read this section of the book about data validation.
Or, do I work this into the controllers login function checking if the
user exists?
All data manipulation and validation should happen in the model layer of the MVC stack. So put everything into a model method and pass the post data to it and validate it there. You can put all logic into the controller to but that's stupid in terms of not following the MVC pattern. Models can be shared between shells and controllers for example, a controller not. Again you could instantiate a controller in a shell but doing all of this negates any benefit and idea the MVC pattern has. Also a model is competitively easy to test. And yes, you should unit test your code. Check how our users plugin is doing it for example.
You can specify multiple rules per field...
Follow this link to learn more about it...
http://book.cakephp.org/2.0/en/models/data-validation.html#multiple-rules-per-field
a sample code is given below
<?php
[IN The MODEL]
//the following code checks if the username is notempty, is a valid email and is it already taken or not...
public $validate = array(
'username' => array(
'notempty' => array(
'rule' => array('notempty'),
'message' => 'Please enter a valid email.',
),
'email' => array(
'rule' => array('email'),
'message' => 'Please enter a valid email.',
),
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'This username has already been taken.'
)
)
);
?>
I am sure I am not the first who has composite unique keys in tables and who wants to validate them. I do not want to invent the bicycle so I ask here first. I have several tables that have 'id' columns as primary keys and two other columns as unique composite keys. It would be nice to have a validation rule to check that the submitted entry is unique and display a validation error if it is not. In Cakephp it could be done by a custom validation rule. I am pretty sure somebody has created such method already.
Ideally it would be a method in app_model.php that could be used by different models.
I am using that function:
function checkUnique($data, $fields) {
if (!is_array($fields)) {
$fields = array($fields);
}
foreach($fields as $key) {
$tmp[$key] = $this->data[$this->name][$key];
}
if (isset($this->data[$this->name][$this->primaryKey]) && $this->data[$this->name][$this->primaryKey] > 0) {
$tmp[$this->primaryKey." !="] = $this->data[$this->name][$this->primaryKey];
}
//return false;
return $this->isUnique($tmp, false);
}
basically the usage is:
'field1' => array(
'checkUnique' => array(
'rule' => array('checkUnique', array('field1', 'field2')),
'message' => 'This field need to be non-empty and the row need to be unique'
),
),
'field2' => array(
'checkUnique' => array(
'rule' => array('checkUnique', array('field1', 'field2')),
'message' => 'This field need to be non-empty and the row need to be unique'
),
),
So basically this will show the warning under each of the fields saying that it's not unique.
I am using this a lot and it's working properly.
In the CakePHP/2.x versions released in the last few years, the isUnique rule optionally accepts several columns:
You can validate that a set of fields are unique by providing multiple
fields and set $or to false:
public $validate = array(
'email' => array(
'rule' => array('isUnique', array('email', 'username'), false),
'message' => 'This username & email combination has already been used.'
)
);
I'm not sure of the exact version when the feature was available but there's a bug fixed in core as late as October 2014 filed against 2.3 and 2.4 branches.
You could put it in app model, but my suggestion would just be to add it to the model directly by placing the rule with it's $validate property.
Check out the built in isUnique rule.
im doing a custom validation but it does not display error message when invalidated.
do you know where is the problem? I think the problem might be in the invalidate function. do you know how to set it up for the nested validation like this one?
var $validate = array(
'receiver' => array(
'maxMsg' => array(
'rule' => array('maxMsgSend'),
//'message' => ''
),
'notEmpty' => array(
'rule' => array('notEmpty'),
'message' => 'field must not be left empty'
))......
custom validation method in the model:
function maxMsgSend ( $data )
{
$id = User::$auth['User']['id'];
$count_contacts = (int)$this->Contact->find( 'count', array( 'conditions' =>array( 'and' =>array( 'Contact.contact_status_id' => '2',
'Contact.user_id' => $id))));
$current_credit = (int)$this->field( '3_credit_counter', array( 'id' => $id));
$max_allowed_messages = ($count_contacts >= $current_credit)? $current_credit: $count_contacts ;
if ($data>$max_allowed_messages)
{
$this->invalidate('maxMsg', "you can send maximum of {$max_allowed_messages} text messages.");
}
}
UPDATE: how is solved it.
i moved the the guts of the function to beforeValidate() in the model.
function beforeValidate($data) {
if (isset($this->data['User']['receiver']))
{
$id = User::$auth['User']['id'];
$count_contacts = (int)$this->Contact->find( 'count', array( 'conditions' =>array( 'and' =>array( 'Contact.contact_status_id' => '2',
'Contact.user_id' => $id))));
$current_credit = (int)$this->field( '3_credit_counter', array( 'id' => $id));
$max_allowed_messages = ($count_contacts >= $current_credit)? $current_credit: $count_contacts ;
if ($data>$max_allowed_messages)
{
$this->invalidate('receiver', "you can send maximum of {$max_allowed_messages} text messages.");
return false;
}
}
return true;
}
I think your maxMsgSend function still needs to return false if validation fails.
I think the problem is in your Model::maxMsgSend function. As written in the bakery, (http://bakery.cakephp.org/articles/view/using-equalto-validation-to-compare-two-form-fields), to build a custom validation rule (they want to compare two fields, but the concepts are the same), they write:
I return a false if the values don't match, and a true if they do.
Check out their code for the Model class, about half way down. In short, you don't need to call invalidate from within the custom validation method; you simply return true if it passes validation, and false if it doesn't pass validation.
I want to use CakePHP's core validation for lists in my model:
var $validate = array(
'selectBox' => array(
'allowedChoice' => array(
'rule' => array('inList', $listToCheck),
'message' => 'Enter something in listToCheck.'
)
)
);
However, the $listToCheck array is the same array that's used in the view, to populate a selectbox. Where do I put this function?
public function getList() {
return array('hi'=>'Hello','bi'=>'Goodbye','si'=>'Salutations');
}
Already in my controller, in one of the actions I'm setting it for the view, like:
public function actionForForm() {
$options = $this->getList();
$this->set('options', $options);
}
So, I don't want to have to copy the getList() function...where can I put it so the Model can call it to populate its $listToCheck array?
Thanks for your help.
Considering that it's data, you should store the list of valid choices in the model.
class MyModel extends AppModel {
var $fieldAbcChoices = array('a' => 'The A', 'b' => 'The B', 'c' => 'The C');
}
You can get that variable in the Controller simply like this:
$this->set('fieldAbcs', $this->MyModel->fieldAbcChoices);
Unfortunately you can't simply use that variable in the rule declaration for the inList rule, since rules are declared as instance variables and those can only be initialized statically (no variables allowed). The best way around that is to set the variable in the Constructor:
var $validate = array(
'fieldAbc' => array(
'allowedChoice' => array(
'rule' => array('inList', array()),
'message' => 'Enter something in listToCheck.'
)
)
);
function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$this->validate['fieldAbc']['allowedChoice']['rule'][1] = array_keys($this->fieldAbcChoices);
}
If you're not comfortable overriding the Constructor, you could also do this in a beforeValidate() callback.
Also note that you shouldn't name your field 'selectBox'. :)