Laravel 5.1. Indexed array's validation - php

I have input form, where user choose the quantity of item's details for his preorder. Ex: Julia's preorder : drink's - water 1 bottle, milk 1 glass, bulletproof coffee 1 cup.
#foreach ($item->detail()->orderBy('sequence')->get() as $detail)
<td><input name="estimate_quantity[]"></td>
#endforeach
I want to validate my quantity, only integers greater than zero or equal to it.
So I made rule
public function rules()
{
$rules = [
'estimate_quantity' => 'required|array',
];
$estimate_quantity = $this->request->get('estimate_quantity');
foreach ($estimate_quantity as $index => $value){
$rules["estimate_quantity.$index"] = 'integer|min:0';
}
return $rules;
}
It doesn't work. If I entered alpha character, it will be an error
ErrorException in helpers.php line 468:
htmlentities() expects parameter 1 to be string, array given
1.What is the right way to do this validation? Making it in controller doesn't look's nice.
2.If i will make my custom rule in separate file, where better to store it? Create app/Validations folder?
3.What magic happening after rules execution and before controller method's execution?
I'm pretty new in Laravel and programming as well, sorry for this easy questions.

i think you can give the array element instead of . notation use array index notation below should work
public function rules()
{
$rules = [
'estimate_quantity' => 'required|array',
];
$estimate_quantity = $this->input('estimate_quantity');
foreach ($estimate_quantity as $index => $value){
$rules["estimate_quantity[".$index."]"] = 'integer|min:0';
}
return $rules;
}

Related

Merge Laravel unique validations with array generic validations

I found a code snippet that solved the problem I had with uniqueness validation, when dealing with arrays:
$rules = [ // this ones are ok for all
'staff.*.name' => 'required|max:128',
'staff.*.description' => 'max:512',
'staff.*.anothervalue' => 'string',
];
// here loop through the staff array to add the ignore
foreach($request->staff as $key => $staff) {
if ( array_key_exists('id', $staff) && $staff['id'] ) { // if have an id, means an update, so add the id to ignore
$rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users,id,'.$staff['id']]);
} else { // just check if the email it's not unique
$rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users']);
}
}
Now I need to go a step further and avoid duplication of email values inside the same array (not in database), so I found the "distinct" rule in Laravel documentation. Also, there is the need for making the email values required only if some another value is present in the same array entry. I see that there is a "required_with" rule that comes handy in those situations. However, I can't find the way to combine the rules generated with the foreach loop with those that use the "wildcard" array notation.
Your suggestions will be appreciated.
~~You can mix and match.~~
Edit: Turns out you cannot.
So you would need to overwrite the rule each time, or set all the rules as per staff.*.email when keyed individually; or make some custom validation logic that throws a BadRequestHttpException/ValidationException or similar.
e.g.:
foreach ...
$rules['staff.'.$key.'.email' => 'required_with:staff.*.id|email|distinct|unique:users']);

Laravel: Using validator's sometimes method when input is an array

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

Elegently handle property of non-object errors that surface from different locations in code

My app reads from a DB that get's written by another API, now in some outlandish cases (that actually happened today) it wrote a customer id of 0, which ofcourse, does not exist.
I am looking for an elegant 'from-the-top' model or even presenter solution for handling erroneous ID's that do not exist.
So instead of finding every $whatever->customer->id in my app and then writing in an isset()/empty() ternary function, I am looking to pacify this error in a more elegant way where any customer instantiation/eloquent object would send the string "NA" to a non existent object, so even if an email/phone/etc or any other column of customer model, it would return a simple "NA" string.
I am struggling to find an eloquent solution that would provide 1 point of change.
you can use withDefault() modifier on your relationship.
example:
use Illuminate\Database\Eloquent\Model;
class Whatever extends Model {
public function customer() {
return $this->belongsTo(Customer::class, 'customer_id', 'id')
->withDefault([
'id' => 'NA',
'name' => 'Unknown'
// etc
]);
}
}
I would suggest you take a look at a Laravel class that most people don't know about. That is Fluent.
It allows you to do stuff like this:
$fluent = new Fluent([
'one' => 1,
'two => 2,
]);
echo $fluent->get('one'); // returns 1
echo $fluent->get('three'); // returns null
echo $fluent->get('three', 3); // returns 3
As you can imagine, it's perfect to use with third-party APIs and data that sometimes provide unexpected results. You can also do a lot more with Fluent.
Alternatively, you could look into Laravel helpers such as array_get(). From the documentation:
The array_get function retrieves a value from a deeply nested array using "dot" notation:
$array = ['products' => ['desk' => ['price' => 100]]];
$price = array_get($array, 'products.desk.price');
// 100
The array_get function also accepts a default value, which will be returned if the specific key is not found:
$discount = array_get($array, 'products.desk.discount', 0);
// 0

validate fields where field name suffixed with number in laravel

I have dynamically created element in JQuery where i have assigned controls name as location_1,location_2,location_3, floor_1, floor_2,floor_3 and so on. It was not possible to use html-control array because these all fields are interdependent. So for me the ultimate solution was suffixing number.
Now i want to validate these fields where rules should looks something like
['location_*' => 'required|exists:locations,id'],
['Floor_*' => 'required|exists:floor,id']
how can i do this so that i could get an error back if it failed?
The easiest way is probably to loop over the request data and create rules based on the locations and floors you find. For instance for floors, given your example:
$rules = [...] // initialise your rules array here
$locationRule = 'required|exists:locations,id';
foreach ($request->all() as $input) {
if (preg_match('#^location_#', $input) {
$rules[$input] = $locationRule;
}
}

CakePHP - a code sample seems strange to me, what am i missing?

Attached code taken from cakephp bakery, where someone uploaded a sample about custom validation rules.
class Contact extends AppModel
{
var $name = 'Contact';
var $validate = array(
'email' => array(
'identicalFieldValues' => array(
'rule' => array('identicalFieldValues', 'confirm_email' ),
'message' => 'Please re-enter your password twice so that the values match'
)
)
);
function identicalFieldValues( $field=array(), $compare_field=null )
{
foreach( $field as $key => $value ){
$v1 = $value;
$v2 = $this->data[$this->name][ $compare_field ];
if($v1 !== $v2) {
return FALSE;
} else {
continue;
}
}
return TRUE;
}
}
In the code, the guy used a foreach to access an array member which he had its name already!
As far as I understand - it's a waste of resources and a bad(even strange) practice.
One more thing about the code:
I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over.
Please enlighten me.
In the code, the guy used a foreach to access an array member which he had its name already! As far as I understand - it's a waste of resources and a bad(even strange) practice.
The first parameter is always an array on one key and its value, the second parameter comes from the call to that function, in a block named as the key... So, all you need is to send the key and no need to iterate
The code uses foreach to iterate through $field, which is an array of one key value pair. It all starts when the validation routine invokes identicalFieldValues, passing it two values - $field, which would be an array that looks like:
array (
[email] => 'user entered value 1'
)
The second parameter $compare_field would be set to the string confirm_email.
In this particular case, it doesn't look like it makes a lot of sense to use foreach since your array only has one key-value pair. But you must write code this way because CakePHP will pass an array to the method.
I believe the reason why CakePHP does this is because an array is the only way to pass both the field name and its value. While in this case the field name (email) is irrelevant, you might need in other cases.
What you are seeing here is one of the caveats of using frameworks. Most of the time, they simplify your code. But sometimes you have to write code that you wouldn't write normally just so the framework is happy.
One more thing about the code: I don't understand the usage of the continue there. it's a single field array, isn't it? the comparison should happen once and the loop will be over. Please enlighten me.
Indeed. And since there are no statements in the foreach loop following continue, the whole else block could also be omitted.
A simplified version of this would be:
function identicalFieldValues($field=array(), $compare_field=null)
{
foreach ($field as $field) {
$compare = $this->data[$this->name][$compare_field];
if ($field !== $compare) {
return FALSE;
}
}
return TRUE;
}
And I agree with you, the loop only goes through one iteration when validating the email field. regardless of the field. You still need the foreach because you are getting an array though.

Categories