I have a php script that performs validation I have a code block that sets up 4 fields that need the same validation. What I want to achieve is that instead in this code block, I can setup the key name, the display name and field type and below set codes which would then automatically validate the fields according to the rules set for each field type.
Here is the code block in question:
// key name => display name
$fields = [
'firstName' => 'First Name',
'lastName' => 'Last Name',
'companyName' => 'Company Name',
'companyAddress' => 'Company Address',
];
So in this block I want to setup the key name, display name and field type. Currently I just got the these four fields. Is there a way I can achieve what I desire?
Here is my full code:
function validate($formData)
{
// Initiate Array
$validationMSG = array(); // array to hold validation errors
// what to validate (basics, i.e. required fields)
// key name => display name
$fields = [
'firstName' => 'First Name',
'lastName' => 'Last Name',
'companyName' => 'Company Name',
'companyAddress' => 'Company Address',
];
//simple loop
foreach($fields as $name => $display){
if(empty($formData[$name])){
$validationMSG[$name] = "${display} is required.";
}
}
//and NOW wee can perform some specific tests:
$pname_exp = '/^[a-zA-Z0-9\_]{2,20}/';
if(isset($formData['firstName']) && !preg_match($pname_exp, $formData['firstName'])){
$validationMSG['firstName'] = 'First Name is not valid.';
}
if(isset($formData['lastName']) && !preg_match($pname_exp, $formData['lastName'])){
$validationMSG['lastName'] = 'Last Name is required.';
}
//removed company name and company address checks, because we are done with them in the loop.
// Validate state
if (!isset($formData['state'])) {
$validationMSG['state'] = 'State is required.';
}
// Validate city
if (!isset($formData['city'])) {
$validationMSG['city'] = 'City is required.';
}
// Validate Zipcode - If Field is Empty
if (!isset($formData['zipcode'])) {
$validationMSG['zipcode'] = 'Zipcode is required.';
}
// Validate emailAddress
if (!isset($formData['emailAddress'])) {
$validationMSG['emailAddress'] = 'Email Address is required.';
}
// Check if emailAddress is a valid email address
elseif (!filter_var($formData['emailAddress'], FILTER_VALIDATE_EMAIL)) {
$validationMSG['emailAddress'] = 'Email address is not valid.';
}
//Validate phoneNumber
if (!isset($formData['phoneNumber'])) {
$validationMSG['phoneNumber'] = 'Phone Number is required.';
}
//Validate phoneNumber
elseif (preg_match('/^[0-9-\s]+$/D', $formData['phoneNumber'])) {
$validationMSG['phoneNumber'] = 'Must be a valid phone number.';
}
// Validate message
if (!isset($formData['message'])) {
$validationMSG['message'] = 'Message is required.';
}
if (!empty($validationMSG)) {
return $validationMSG;
}
else {
$captcha = checkCaptcha($formData['g-recaptcha-response']);
if(!$captcha['isSuccess']){
$validationMSG['captcha'] = 'ReCaptcha is required.';
return $validationMSG;
}
//End of Validation Function
}
}
//testing
$input = ['firstName' => 'John'];
$errors = validate($input);
var_dump($errors);
You should be using an approach like this. This is a starting point to write a better validation check in your loops:
<?php
function validate($formData)
{
// Initiate Array
$validationMSG = array(); // array to hold validation errors
// what to validate (basics, i.e. required fields)
// key name => display name
$fields = [
'firstName' => [
'label' => 'First Name',
'rules' => 'required'
],
'lastName' => [
'label' => 'Last Name',
'rules' => 'required'
],
'emailAddress' => [
'label' => 'Email',
'rules' => 'required|email'
]
];
//simple loop
foreach($fields as $fieldName => $args) {
$rules = explode('|', $args['rules']);
foreach($rules as $rule)
{
if($rule == 'required' && (!isset($formData[$fieldName]) || empty($formData[$fieldName])))
{
$validationMSG[$fieldName][] = sprintf('%s is a required field.', $args['label']);
}
if((isset($formData[$fieldName]) && $rule == 'email') && !filter_var($formData[$fieldName], FILTER_VALIDATE_EMAIL))
{
$validationMSG[$fieldName][] = sprintf('%s must be a valid email.', $args['label']);
}
}
}
return $validationMSG;
}
This can be improved but the concept will get you started.
Here is a way to do validation, while still having a format that can be promoted to a typed class (e.g., with namespace and dependency injection, etc) on a refactor. What I will show you is a way to acquire an object in such a way you can validate the message payload, based on a common naming construct.
First, create a regular php function called userActions(). This will be used to (globally or as an include where needed) call into and get these objects. This will return a closure.
function userActions(): \Closure { ... }
Next, you'll need to encapsulate your procedure. I recommend doing it like this:
$startUserRecord = static function(
string $firstName,
string $lastName,
string $companyName,
string $companyAddress
) {
return new class(...func_get_args()) {
private $payload = [];
private $firstName;
private $lastName;
private $companyName;
private $companyAddress;
public function __construct(
string $firstName,
string $lastName,
string $companyName,
string $companyAddress
) {
$this->payload = func_get_args();
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->companyName = $companyName;
$this->companyAddress = $companyAddress;
}
public function validate(): array {
// Here is where you would actually validate this one message.
// Empty array means no validation messages (errors).
return [];
}
public function payload(): array {
return $this->payload;
}
};
};
// Other ones go here, too.
$saveUserProfile = static function() { ... };
$resetUserLogin = static function() { ... };
Corral them up to together, so we can use() them in our userActivities() closure:
$activities= [
'startUserRecord' => $startUserRecord,
'saveUserProfile ' => $saveUserProfile,
'resetUserLogin ' => $resetUserLogin,
];
Now, I've called it startUserRecord because I needed to make up a command name; give it the name you wish, however I recommend verb + subject + context construct, generally.
Static checks work for the "string, integer" and other low-level type checks, or class-based constructs as you have available (say, an Email value object). The validation of the values, as you can see, occurs inside the validate() method.
Next, create the closure that you'll return that actually allows you to search for and get only the command objects you want:
return static function(/* autoset as $get */) use($activities) {
$get = func_get_args();
$find = static function($name) use(&$get) {
return in_array($name, $get);
};
$filtered = array_filter($activities, $find, ARRAY_FILTER_USE_KEY);
$found = [];
// Keep in the same order.
foreach($get as $name) {
$found[] = $filtered[$name];
}
return $found;
};
Pay attention to the use() statement on the closure(s).
This is put within the userActivities() function declaration. Now, you can call and set it up, and generally use what you have:
$fields = [
'firstName' => 'First Name',
'lastName' => 'Last Name',
'companyName' => 'Company Name',
'companyAddress' => 'Company Address',
];
$userActivities = userActions();
[$startUserRecord] = $userActivities('startUserRecord');
$activity = $startUserRecord(...array_values($fields));
if ($errors = $activity->validate()) {
throw new InvalidCommand($errors);
}
// Do something with $activity->payload().
Note:
I'm not modifying the payload. You can add a conditioner/sanitizer method that acts on the payload or return the object's properties in some form.
We're destructuring with the [$startUserRecord] line. This allows you to return multiple entries and load them into their own variable names. An example:
[$saveUserProfile,$resetUserLogin] = $userActivites('saveUserProfile','resetUserLogin');
$startUserRecord(...array_values($fields)) This is called a spread or splat operation (splat operator: ... in the invocation), and makes each array item it's own separate argument, in order. I'm getting to that with array_values() here, however it's better to pass in an actual array without the keys, to maintain order in all cases.
Here it is put together: https://3v4l.org/sn61Q
This is just a starting point. Change it up, do what you need to, this should give you some idea what you can accomplish.
For instance, you could move the initializers to a with() method and "autoload" little closure-based validators that you share within the userActivities() function, like a $validateEmail() or $validatePhone() closures you declare and then use($validateEmail, ...) and within that closure new class($validateEmail, ...) to share these within an activity's context.
Related
I had been slowly learning PHP OOP, I decided it was time to start inserting into my table, however it doesn't seem to be inserting. I compared my code with working versions and I can't see what the problem might be, I attempted a var_dump(), the query returned as I expected, I tested my database class by creating an new user, it was successfully created so I assume it isn't that, I tested the SQL query and it was able to insert, I'm at a loss for it might be now
form
<?php
session_start();
require ("classes/Review.php");
require ("classes/Database.php");
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
$reviewing = new ReviewValidation();
$review = $reviewTitle = "";
$post = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
$reviewTitle = $post['reviewTitle'];
$review = $post['review'];
$errors = array();
$fields = array(
'title' => array(
'validate' => 'title',
'message' => 'Title must be at least fife characters',
'value' => $reviewTitle,
),
'review' => array(
'validate' => 'review',
'message' => 'Your review must be at least three hundred characters',
'value' => $review,
)
);
foreach($fields as $key => $value)
{
$validation_result = $reviewing->{$value['validate']}($value['value']);
if(!$validation_result)
{
$errors[] = ['name' => $key, 'error' => $value['message']];
}
}
if(empty($errors))
{
try
{
$db = new Database;
$success = ["message" => "Review subbbmitted"];
$process = $db->prepare('INSERT INTO reviews (reviewTitle)
VALUES
(:reviewTitle');
$process->bindValue(':reviewTitle', $reviewTitle);
$process->execute();
}
catch(Exception $e)
{
$errors[] = ['response' => 'fail'];
}
}
}
header('Content-Type: application/json');
if (empty($errors))
{
echo json_encode($success);
}
else
{
echo json_encode(["errors" => $errors]);
}
class
<?php
class ReviewValidation
{
private
$db,
$review,
$reviewTitle;
private static
$reviewLength = 50,
$rewviewtitleLength = 5;
public static function title($reviewTitle)
{
return(strlen($reviewTitle) >= self::$rewviewtitleLength);
}
public static function review($review)
{
return(strlen($review) >= self::$reviewLength);
}
}
Looks like you may be missing the closing ) in the insert query:
$process = $db->prepare('INSERT INTO reviews (reviewTitle)
VALUES
(:reviewTitle)');
If you add in a closing parenthesis after :reviewTitle, before the single quote your syntax will be correct (shown above).
I also noticed that your calls to the static methods in the ReviewValidation class are using the object operator (->). To access static methods you need to utilize the scope resolution operator.
So your $validation_result line should look like:
$validation_result = ReviewValidation::{$value['validate']}($value['value']);
I think because of this, the validation may have been passing, which is why you where getting the no default value issue.
I would like to read about your opinions on what the best way to create a validation class is.
Below i pasted in my version, it's not really extensive yet, but is it the right approach so far?
I want every element which could appear in a form and which should be validated to have its own properties, in terms of string length for strings or file size for images or files in general (see below).
Also, is it better to declare these rules as nested arrays or should I put them in one big string, which would then be split up during the process?
mb_internal_encoding("UTF-8");
class Phrase {
//creates parts of sentences, not important
static function additive(array $limbs) {
return implode(' and ', array_filter([implode(', ', array_slice($limbs, 0, -1)), end($limbs)], 'strlen'));
}
}
class Text {
static function validate($item) {
$err = array();
$value = $_POST[$item] ?? $_GET[$item];
$criteria = FormProcess::$criteria[$item];
foreach($criteria as $critKey => $critVal) {
if($critKey === 'required' && empty($value)) {
$err[] = "is required";
} else if(!empty($value)) {
switch($critKey) {
case 'length':
if(is_array($critVal)) {
//min and max set
if(mb_strlen($value) < $critVal[0] || mb_strlen($value) > $critVal[1]) {
$this->err[] = "must contain between {$critVal[0]} and {$critVal[1]} characters";
}
} else {
//max set only
if(mb_strlen($value) > $critVal) {
$err[] = "must contain a maximum of $critVal characters";
}
}
break;
case 'pattern':
if(!preg_match($critVal[0], $value)) {
$err[] = "may consist of {$critVal[1]} only";
}
break;
case 'function':
$result = static::$critVal($value);
if($result) {
$err[] = $result;
}
break;
}
}
}
if(!empty($err)) {
return "{$criteria['name']} " . Phrase::additive($err) . "!";
}
return false;
}
private static function email($email) {
//checks if given string is a valid email address
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return "is invalid";
}
return false;
}
}
class File {
//checks for aspects like filesize...
static function validate() {
//...
}
}
class Image extends File {
static function validate() {
parent::validate(); //perform general file checks first
//...checks for images specifically
}
}
class Video extends File {
static function validate() {
parent::validate(); //perform general file checks first
//...checks for videos specifically
}
}
class FormProcess {
public $errors;
//declare, what kind of requirements the items must meet
static $criteria = array(
'email' => array(
'type' => 'Text',
'required' => true,
'name' => 'Email',
'length' => 48,
'function' => 'email',
),
'username' => array(
'type' => 'Text',
'required' => true,
'name' => 'Username',
'length' => [4, 24],
'pattern' => ['/^[-\w]+$/', "alphanumeric characters, underscores and hyphens"],
),
'password' => array(
'type' => 'Text',
'required' => true,
'name' => 'Password',
'length' => [6, 100],
'pattern' => ['/^[\S]+$/', "non-whitespace characters"],
),
);
//runs the validate function on each item while storing occuring errors
function __construct(array $items) {
foreach($items as $item) {
$class = self::$criteria[$item]['type'];
$result = $class::validate($item);
if($result) {
$this->errors[] = $result;
}
}
}
}
Then all you had to do, is, naming all expected items (by their html 'name' attribute in the form) in an array and pass it through the constructor, which would then run the appropriate validation function on each item.
$expected = ['username', 'password'];
$signup = new FormProcess($expected);
if($signup->errors) {
?>
There was something wrong with your request:
<ul>
<?php foreach($signup->errors as $error) { ?>
<li><?= $error ?></li>
<?php } ?>
</ul>
<?php
}
I hope to learn from mistakes and make improvements to the code from you where they are needed.
Thank you in advance!
I want to properly check the user data from a form in PHP. I'm using filter class. I need to use a callback function to check the password. To achieve this, I used this tutorial on twentieths point (callback filter):
http://www.phpro.org/tutorials/Filtering-Data-with-PHP.html#21
However I still get this error:
"Warning: filter_var(): First argument is expected to be a valid callback" error.
Here is my class :
class Callback {
private function validPasswd($sPasswd) {
$r1 = '/#[A-Z]+#/'; //at least one uppercase
$r2 = '/#[a-z]+#/'; //at least one lowercase
$r3 = '/#[!##$%^&*()\-_=+{};:,<.>]+#/'; // at least one special char
$r4 = '/#[0-9]+#/'; //at least one number
if (preg_match($r1, $sPasswd)) {
return false;
}
if (preg_match_all($r2, $sPasswd)) {
return false;
}
if (preg_match_all($r3, $sPasswd)) {
return false;
}
if (preg_match_all($r4, $sPasswd)) {
return false;
}
if (strlen($sPasswd) < 8) {
return false;
}
return $sPasswd;
}
}
and here's the part of my method:
public function createAccountAction() {
//get password from form to verification.
$sPasswd = $_POST['password'];
//prepare all values to be checked with a filter
$aOptions = array(
'first-name' => FILTER_SANITIZE_STRING,
'last-name' => FILTER_SANITIZE_STRING,
'login' => FILTER_SANITIZE_STRING,
'password' => array(
'filter' => FILTER_CALLBACK,
'options' => 'validPasswd'
),
'email' => FILTER_SANITIZE_EMAIL,
'conf-email' => FILTER_SANITIZE_EMAIL
);
var_dump(filter_var($sPasswd, FILTER_CALLBACK, array("options" => array("Callback"=>"validPasswd"))));
namespace and use are correct. I think it's the way I call the method that is not good.
You should be giving the instance that the method should be called on and the method to be called.
If the createAccountAction method is defined in Callback:
return filter_var($sPasswd, FILTER_CALLBACK, ["options" => [$this, "validPasswd"]]);
If the createAccountAction method is defined elsewhere:
// Give Callback::validPasswd public access and...
return filter_var($sPasswd, FILTER_CALLBACK, ["options" => [new Callback, "validPasswd"]]);
I want to add unique Elements in a Zend Form Collection.
I found this awesome work from Aron Kerr
I do the forms and fieldsets like in Aron Kerr´s Example and it works fine.
In my case i create a Form to insert a collection of stores from a company.
My Form
First of all i have a Application\Form\CompanyStoreForm with a StoreFieldset like this:
$this->add(array(
'name' => 'company',
'type' => 'Application\Form\Stores\CompanyStoresFieldset',
));
The Fieldsets
Application\Form\Stores\CompanyStoresFieldset has a Collection of Store Entities like this:
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'stores',
'options' => array(
'target_element' => array(
'type' => 'Application\Form\Fieldset\StoreEntityFieldset',
),
),
));
Application\Form\Fieldset\StoreEntityFieldset
$this->add(array(
'name' => 'storeName',
'attributes' => ...,
'options' => ...,
));
//AddressFieldset
$this->add(array(
'name' => 'address',
'type' => 'Application\Form\Fieldset\AddressFieldset',
));
The difference to Arron Kerrs CategoryFieldset is I adding one more fieldset: Application\Form\Fieldset\AddressFieldset
Application\Form\Fieldset\AddressFieldset has a text-element streetName.
The Inputfilters
The CompanyStoresFieldsetInputFilter has no elements to validate.
The StoreEntityFieldsetInputFilter has validators for storeName and the Application\Form\Fieldset\AddressFieldset like this
public function __construct() {
$factory = new InputFactory();
$this->add($factory->createInput([
'name' => 'storeName',
'required' => true,
'filters' => array( ....
),
'validators' => array(...
),
]));
$this->add(new AddressFieldsetInputFilter(), 'address');
}
The AddressFieldset has another Inputfilter AddressFieldsetInputFilter.
In AddressFieldsetInputFilter I adding a InputFilter for streetName.
FormFactory
Adding all Inputfilters to the Form like this
public function createService(ServiceLocatorInterface $serviceLocator) {
$form = $serviceLocator->get('FormElementManager')->get('Application\Form\CompanyStoreForm');
//Create a Form Inputfilter
$formFilter = new InputFilter();
//Create Inputfilter for CompanyStoresFieldsetInputFilter()
$formFilter->add(new CompanyStoresFieldsetInputFilter(), 'company');
//Create Inputfilter for StoreEntityFieldsetInputFilter()
$storeInputFilter = new CollectionInputFilter();
$storeInputFilter->setInputFilter(new StoreEntityFieldsetInputFilter());
$storeInputFilter->setUniqueFields(array('storeName'));
$storeInputFilter->setMessage('Just insert one entry with this store name.');
$formFilter->get('company')->add($storeInputFilter, 'stores');
$form->setInputFilter($formFilter);
return $form;
}
I use Aron Kerrs CollectionInputFilter.
The storeName should be unique in the whole collection.
All works fine, so far!
But now my problem!
The streetName should be unique in the whole collection.
But the Element is in the AddressFieldset.
I can´t do something like this:
$storeInputFilter->setUniqueFields(array('storeName', 'address' => array('streetName')));
I thought I should extend Aron Kerrs isValid() from CollectionInputFilter
Aron Kerrs Original isValid()
public function isValid()
{
$valid = parent::isValid();
// Check that any fields set to unique are unique
if($this->uniqueFields)
{
// for each of the unique fields specified spin through the collection rows and grab the values of the elements specified as unique.
foreach($this->uniqueFields as $k => $elementName)
{
$validationValues = array();
foreach($this->collectionValues as $rowKey => $rowValue)
{
// Check if the row has a deleted element and if it is set to 1. If it is don't validate this row.
if(array_key_exists('deleted', $rowValue) && $rowValue['deleted'] == 1) continue;
$validationValues[] = $rowValue[$elementName];
}
// Get only the unique values and then check if the count of unique values differs from the total count
$uniqueValues = array_unique($validationValues);
if(count($uniqueValues) < count($validationValues))
{
// The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
$duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
$valid = false;
$message = ($this->getMessage()) ? $this->getMessage() : $this::UNIQUE_MESSAGE;
foreach($duplicates as $duplicate)
{
$this->invalidInputs[$duplicate][$elementName] = array('unique' => $message);
}
}
}
return $valid;
}
}
First of all I try (just for testing) to add a error message to streetName in the first entry of the collection.
$this->invalidInputs[0]['address']['streetName'] = array('unique' => $message);
But it doens´t work.
Adding it to storeName it works
$this->invalidInputs[0]['storeName'] = array('unique' => $message);
I think the reason is the Fieldset has an own InputFilter()?
When i do a var_dump($this->collectionValues()) i received a multidimensional array of all values (also of the addressFieldset).
That´s fine! But i can´t add error messages to the element in the fieldset.
How can i do this?
I don´t want to insert all Elements of the AddressFieldset in the StoreEntityFieldset. (I use the AddressFieldset also in other Forms)
I figured it out. You simply can add values with
$this->invalidInputs[<entry-key>]['address']['streetName'] = array('unique' => $message);
I don´t know how it does not work yesterday. It was another bug.
I wrote a solution for my problem. Maybe it´s not the best, but it works for me.
CollectionInputFilter
class CollectionInputFilter extends ZendCollectionInputFilter
{
protected $uniqueFields;
protected $validationValues = array();
protected $message = array();
const UNIQUE_MESSAGE = 'Each item must be unique within the collection';
/**
* #return the $message
*/
public function getMessageByElement($elementName, $fieldset = null)
{
if($fieldset != null){
return $this->message[$fieldset][$elementName];
}
return $this->message[$elementName];
}
/**
* #param field_type $message
*/
public function setMessage($message)
{
$this->message = $message;
}
/**
* #return the $uniqueFields
*/
public function getUniqueFields()
{
return $this->uniqueFields;
}
/**
* #param multitype:string $uniqueFields
*/
public function setUniqueFields($uniqueFields)
{
$this->uniqueFields = $uniqueFields;
}
public function isValid()
{
$valid = parent::isValid();
// Check that any fields set to unique are unique
if($this->uniqueFields)
{
foreach($this->uniqueFields as $key => $elementOrFieldset){
// if the $elementOrFieldset is a fieldset, $key is our fieldset name, $elementOrFieldset is our collection of elements we have to check
if(is_array($elementOrFieldset) && !is_numeric($key)){
// We need to validate every element in the fieldset that should be unique
foreach($elementOrFieldset as $elementKey => $elementName){
// $key is our fieldset key, $elementName is the name of our element that should be unique
$validationValues = $this->getValidCollectionValues($elementName, $key);
// get just unique values
$uniqueValues = array_unique($validationValues);
//If we have a difference, not all are unique
if(count($uniqueValues) < count($validationValues))
{
// The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
$duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
$valid = false;
$message = ($this->getMessageByElement($elementName, $key)) ? $this->getMessageByElement($elementName, $key) : $this::UNIQUE_MESSAGE;
// set error messages
foreach($duplicates as $duplicate)
{
//$duplicate = our collection entry key, $key is our fieldsetname
$this->invalidInputs[$duplicate][$key][$elementName] = array('unique' => $message);
}
}
}
}
//its just a element in our collection, $elementOrFieldset is a simple element
else {
// in this case $key is our element key , we don´t need the second param because we haven´t a fieldset
$validationValues = $this->getValidCollectionValues($elementOrFieldset);
$uniqueValues = array_unique($validationValues);
if(count($uniqueValues) < count($validationValues))
{
// The counts didn't match so now grab the row keys where the duplicate values were and set the element message to the element on that row
$duplicates = array_keys(array_diff_key($validationValues, $uniqueValues));
$valid = false;
$message = ($this->getMessageByElement($elementOrFieldset)) ? $this->getMessageByElement($elementOrFieldset) : $this::UNIQUE_MESSAGE;
foreach($duplicates as $duplicate)
{
$this->invalidInputs[$duplicate][$elementOrFieldset] = array('unique' => $message);
}
}
}
}
}
return $valid;
}
/**
*
* #param type $elementName
* #param type $fieldset
* #return type
*/
public function getValidCollectionValues($elementName, $fieldset = null){
$validationValues = array();
foreach($this->collectionValues as $rowKey => $collection){
// If our values are in a fieldset
if($fieldset != null && is_array($collection[$fieldset])){
$rowValue = $collection[$fieldset][$elementName];
}
else{
//collection is one element like $key => $value
$rowValue = $collection[$elementName];
}
// Check if the row has a deleted element and if it is set to 1. If it is don't validate this row.
if($rowValue == 1 && $rowKey == 'deleted') continue;
$validationValues[$rowKey] = $rowValue;
}
return $validationValues;
}
public function getMessages()
{
$messages = array();
if (is_array($this->getInvalidInput()) || $this->getInvalidInput() instanceof Traversable) {
foreach ($this->getInvalidInput() as $key => $inputs) {
foreach ($inputs as $name => $input) {
if(!is_string($input) && !is_array($input))
{
$messages[$key][$name] = $input->getMessages();
continue;
}
$messages[$key][$name] = $input;
}
}
}
return $messages;
}
}
Define a CollectionInputFilter (in a factory)
$storeInputFilter = new CollectionInputFilter();
$storeInputFilter->setInputFilter(new StoreEntityFieldsetInputFilter());
$storeInputFilter->setUniqueFields(array('storeName', 'address' => array('streetName')));
$storeInputFilter->setMessage(array('storeName' => 'Just insert one entry with this store name.', 'address' => array('streetName' => 'You already insert a store with this street name')));
$formFilter->get('company')->add($storeInputFilter, 'stores');
So let me explain:
Now, we can add elements as unique in fieldsets in our collection.
We can NOT add collection fieldsets in our collection and not another fieldsets in our fieldsets.
In my opinion if anyone want to do this cases, they better should refactor the form :-)
setUniqueFields
Add a simple element as unique
array('your-unique-element','another-element');
If you want to add a element as unique in a fieldset
array('your-unique-element', 'fieldsetname' => array('your-unique-element-in-fieldset'))
We can add special messages for every element with setMessage
Add Message for a Element in the collection
array('storeName' => 'Just insert one entry...')
Add message for a Element in a fieldset
array('fieldset-name' => array('your-unique-element-in-fieldset' => 'You already insert ..'))
I read the official page about validation in laravel http://laravel.com/docs/validation#working-with-error-messages and I followed them. So my code is
$input = Input::all();
$validation = Validator::make($input, Restaurant::$rules);
if ($validation->passes())
{
Restaurant::create($input);
return Redirect::route('users.index');
}
else{
$messages = $validation->messages();
foreach ($messages->all() as $message)
{
echo $message + "</br>";
}
}
I can see the error message but it is just 00. Is there a better way to know in which form's field the error is and what is the error description?
I already have rules and I now the input is breaking the rules but I need to read the error message
$messages = $validation->messages();
foreach ($messages->all('<li>:message</li>') as $message)
{
echo $message;
}
Official Documentation
After calling the messages method on a Validator instance, you will receive a MessageBag instance, which has a variety of convenient methods for working with error messages.
According to the MessageBag documentation the function all Get all of the messages for every key in the bag.
You can access errors through the errors() object, and loop through the all rules' keys.
Something like this:
Route::get('error', function(){
$inputs = array(
'id' => 5,
'parentID' => 'string',
'title' => 'abc',
);
$rules = array(
'id' => 'required|integer',
'parentID' => 'required|integer|min:1',
'title' => 'required|min:5',
);
$validation = Validator::make($inputs, $rules);
if($validation->passes()) {
return 'passed!';
} else {
$errors = $validation->errors(); //here's the magic
$out = '';
foreach($rules as $key => $value) {
if($errors->has($key)) { //checks whether that input has an error.
$out.= '<p>'$key.' field has this error:'.$errors->first($key).'</p>'; //echo out the first error of that input
}
}
return $out;
}
});