I am working on my first form validation. The issue that I am having is error reporting to the use.
I used an array to set up my criteria/rules for the fields:
$validate = new Validation;
$validation = $validate->check($_POST, array(
'FirstName' => array(
'name' => 'First Name',
'required' => 'TRUE'),
'LastName' => array(
'name' => 'Last Name',
'required' => TRUE));
I would like to have an error show that tells the user he/she is missing a required field without show the field name, for example: FirstName is required. I would like to see: First Name is required.
I looped through each array:
public function check($source, $items = array())
foreach($items as $item => $rules){
foreach($rules as $rule => $rule_value){
echo "{$item} {$rule} must be {$rule_value}<br>";
}
}
}
When I echo out the loops I my criteria, however, When I would try to echo out $rule_value [0], I would only get the first letter of that array.
Any suggestions?
I think that you may get rid of the second foreach loop and do something like this:
public function check($source, $items = array())
{
// In your example you have two arrays in $items so the following
// foreach will iterate two times
foreach($items as $item => $values) {
// $item is going to be 'FirstName' on the first iteration
// and 'LastName' on the second iteration
//
// $values are going to be the array that's associated
// with 'FirstName' => array(...) and 'LastName' => array(...)
// i.e. $values = array('name' => '...', 'required' = TRUE/FALSE)
// Therefore, you can easily check if a given $item is required
// and if the $source contains that $item or not:
if ($values['required'] && empty($source[$item])) {
echo "{$values['name']} is required!";
}
// EDIT for your comment - validating the string length
if ( $values['minLength']
&& strlen($source[$item]) < $values['minLength'])
{
echo "{$values['name']} must be at least {$values['minLength']} characters.";
}
}
}
What the code above does is that it goes through all elements of $items and if it finds a required field, it then checks whether the $source has the appropriate value or not (or if it's empty) and echoes an error (you may want to do different validations, this is just to illustrate an example).
Footnote: By the way, I'm not sure how you separate the view and the model but I find it useful not to echo right from the logic functions. So in your check function I'd build up an array/object with all that validation errors and I'd return it to the view - which would take care of displaying the errors. This way, you could control where the errors appear visually as I'm thinking it is perhaps useful to highlight each form field with its error.
Related
I'm trying to make an application that saves grocery lists and retrieves them from a database. In the request, the values get passed along in JSON format:
"item:47" => "{"id":47,"name":"Beer","brand":"Jupiler","weight":null,"note":"Six pack;_bottles","order_id":15}"
"item:88" => "{"id":88,"name":"Tomatoes","brand":null,"weight":null,"note":null,"order_id":15}"
"item:110" => "{"id":110,"name":"Gura_Bread","brand":null,"weight":0.3,"note":null,"order_id":15}"
"item:-1" => "{"id":-1,"name":"Beef_Jerky","brand":"Canadon","weight":0.5,"notes":"Spicy_Ones"}"
New items are marked with a descending negative id, while existing items retain their id from the DB
When this arrives in the back-end of the laravel application, I would like to validate the JSON string with Laravel's validate(). The only problem is that the amount of items that can be passed varies in amount. Sometimes it can be one item, while other times it could be 10 items instead.
Is there a way to add this JSON rule that could only trigger when it notices that there's a certain string in one or multiple attributes of an incoming request? In this case, it should trigger when it sees that the string item:.
For context, here are the parameters of an example request.
"picking_method" => "Cheapest"
"item:47" => "{"id":47,"name":"Beer","brand":"Jupiler","weight":null,"note":"Six pack;_bottles","order_id":15}"
"item:88" => "{"id":88,"name":"Tomatoes","brand":null,"weight":null,"note":null,"order_id":15}"
"item:110" => "{"id":110,"name":"Gura_Bread","brand":null,"weight":0.3,"note":null,"order_id":15}"
"item:-1" => "{"id":-1,"name":"Beef_Jerky","brand":"Canadon","weight":0.5,"notes":"Spicy_Ones"}"
"store_street" => "Haag Pines"
"store_number" => "1855"
"store_postal_code" => "82792-01"
"store_city" => "Port Muhammadhaven"
"store_country" => "Guernsey"
"delivery_street" => "Rosenbaum Island"
"delivery_number" => "4974"
"delivery_postal_code" => "61093"
"delivery_city" => "East Carlee"
"delivery_country" => "Slovenia"
"delivery_notes" => null
"medical_notes" => null
After experimenting some more, I came up with this solution.
In order for this method to work, you'll need to have a substring that is the same across all the attributes that you want to check.
Before performing any validation at all, I decided to collect all the attributes that I want to check into an array with a foreach loop. This is where the substring part is important because it will be used to decide which attributes will be collected:
$item_attributes = [];
foreach ($request->post() as $key => $value) {
if (str_contains($key, 'item:')) {
array_push($item_attributes, $key);
}
}
After that, I looped over the $item_attributes array and used it to make a rules array, where every value in the $item_attributes is used as a key. As value, I added the json rule.
$rules = [];
foreach ($item_attributes as $attribute) {
$rules[$attribute] = "json";
}
After that, I validate the data and returned it, so it can be used in the main function of my code:
return $request->validate($rules);
When combined, this will result into the following method:
function validateItems(Request $request)
{
$item_attributes = [];
foreach ($request->post() as $key => $value) {
if (str_contains($key, 'item:')) {
array_push($item_attributes, $key);
}
}
$rules = [];
foreach ($item_attributes as $attribute) {
$rules[$attribute] = "json";
}
return $request->validate($rules);
}
I have a form that posts a structure field as an array. The structure array contains definitions of database table columns.
$validator = Validator::make($request->all(), [
'structure' => 'required|array|min:1',
'structure.*.name' => 'required|regex:/^[a-z]+[a-z0-9_]+$/',
'structure.*.type' => 'required|in:integer,decimal,string,text,date,datetime',
'structure.*.length' => 'nullable|numeric|required_if:structure.*.type,decimal',
'structure.*.default' => '',
'structure.*.index' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_nullable' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_primary' => 'required_if:is_auto_increment,false|boolean',
'structure.*.is_auto_increment' => 'required_if:structure.type,integer|boolean',
'structure.*.is_unique' => 'required_if:is_auto_increment,false|boolean',
'structure.*.decimal' => 'nullable|numeric|required_if:structure.*.type,decimal|lt:structure.*.length',
]);
Without going into explanation of all the rules, one thing should be made sure that the length field is always null when the type is not string or decimal as you cannot assign a length to columns other than these types. So, I am trying to use the sometimes method on the $validator instance.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
// how to access the structure type here?
});
My question is inside the closure, how do I make sure that the length is null only for the array element that has the type set to other than string or decimal.
I have tried the dd function and it seems the whole input array is passed to the closure.
$validator->sometimes('structure.*.length', 'in:null', function ($input) {
dd($input);
});
Here is the output of the dd method.
I can use a foreach construct but wouldn't that be inefficient? Checking all the elements for a single element?
How do I check the type only for the array element under consideration?
Is there a Laravel way to do this?
How about thinking opposite? if the Type is String or Decimal, the Length field will become Required.
$validator->sometimes('structure.*.length', 'required', function ($input) {
return $input->type == 'string' or $input->type == 'decimal';
});
This is a great question. I took a look at the API for sometimes(). It seems, what you want to do, is currently not possible with it.
A possible alternative could be to use an After Validation Hook. For example:
$validator->after(function ($validator) {
$attributes = $validator->getData()['structure'];
foreach($attributes as $key => $value) {
if(! in_array($value['type'], ['string', 'decimal']) && ! is_null($value['length'])) {
$validator->errors()->add("structure.{$key}.length", 'Should be null');
}
}
});
I made module with form, that use autocomplete field like that:
$form['field_taxonomy_tags'] = [
'#type' => 'entity_autocomplete',
'#target_type' => 'taxonomy_term',
'#selection_settings' => [
'target_bundles' => array('tags'),
],
'#autocreate' => array(
'target_bundles' => array('tags'),
'bundle' => ('tags'),
),
'#title' => ('tags'),
'#tags' => TRUE,
];
Autocomplete works fine, and i can add taxonomy terms from tags vocabulary easily. But there is some problem with #autocreate option i think. Have searched all documentation, and code inside drupal core. Entity is never created ;/
When i try to get value from this field, my browser is dead... there is some entity type variable, but huge.
After some debugging i found way to get it work, but im not happy about it :) Very strange, maybe some of you guys can help me to find better way?
public function submitForm(array &$form, FormStateInterface $form_state) {
$tags = $form_state->getValue('field_taxonomy_tags');
foreach ($tags as $tag)
{
if(is_object($tag['entity']))
{
$tag['entity']->save();
}
}
}
As you can see, I need to save those tags manually, dont know why ;/ Without it, there is no term created.
It is better way. I dont need to save every tag, its enough if we attach them to a node. Its entity object, that can be passed as node value, and after that, all tags will be created:
$node = Node::create(array(
'type' => 'YOUR_content_type',
'title' => $form_state->getValue('title')
));
$fieldNames = array_keys($node->getFieldDefinitions());
$values = $form_state->getValues();
// be aware with that, i use this loop for testing because i have same names
// you can use $node->set('content type field name', $value); directly without any field definitions
foreach ($values as $key=>$value)
{
if(in_array($key, $fieldNames))
{
$node->set($key, $value);
}
}
// here we save all data, taxonomy entities too
$node->save();
I want to create a rule which should validate from an input array to have atelast one required and that should be numeric. The input array is goal_target[#] where # is handled by back end with unique values.
I create this rule in Model. At-least one required am handling in controller and set the values in data set. Here is controller action:
//Removing initial zeros "0"
$this->request->data['goal_target'] = array_map(function($v) { return ltrim($v, '0'); }, $this->request->data['goal_target']);
//Removing empty values from input array
$this->request->data['goal_target'] = array_filter($this->request->data['goal_target']);
//Creating a data set for passing in model
$this->request->data['Goal']['targets'] = $this->request->data['goal_target'];
$this->Goal->set($this->request->data);
if(!$json_response && $this->Goal->AddValidate())
{
//Adding goal
}
else
{
$json_response['validations'] = $this->Goal->validationErrors;
}
echo json_encode($json_response);
In my modal:
public function AddValidate() {
$validateAdd = array(
'targets'=>array(
'numeric' => array(
'rule' => 'numeric',
'allowEmpty' => true,
'message' => 'Numbers only'
)
)
);
$this->validate = $validateAdd;
return $this->validates();
}
Its not working properly even on numeric values. It always return message Numbers only. What wrong am doing please let me know. Also please tell if i can get the one required also in above rule.
Is it possible to validate multirow forms in laravel. I would like to validate a form which looks like this:
<input name="address[]" class="addr"/>
<input name="address[]" class="addr"/>
I've tried both methods but it doesn't seem to work:
$rule = array('address[]' => 'required'); //still returning error even if all required fields are filled up
$rule = array('address' => 'required'); //nothing happens
For the first one I made sure that there is no hidden address field:
$('.addr').length
The length returned is equal to the number of address fields I have filled out
Yes, use either of these:
$rule = array('address' => 'array');
$rule = array('address' => 'countmin:0');
I just extended the validator class and created a simple rule that checks if there's an empty value in the array:
public function validate_arrayfull($attribute, $value, $parameters){
$is_full = (in_array('', $value)) ? false : true;
return $is_full;
}
And in Validation.php a default error message:
"arrayfull" => "The :attribute contains empty values"
Usage:
$rule = array('address' => 'arrayfull');