Im creating some small API with which i can create blog posts through postman. Im using laravel 5 and i encoutered a problem.
In the API call, i can specify a user id, so the post is written by someone else than whoever makes the API call right now. My currently logged in user is specified with a token in postman.
So my problem is now, when i create the API call and specify my user_id as empty string
"userID": ""
it will throw an error, because i specified the id to be an int like so
'userID' => ['integer']
The error is
"Type error: Argument passed to construct() must be of the type integer or null, string given",
Why does it accept an empty string? How can i validate that correctly?
Note that the userID doesn't have to be specified in the post request. If not specified, it will just take the one from the user you are currently logged in with. (Specified in the token)
Using two validation rules together will fix the issue.
required and integer
and as you said, you dont want to make it mandatory:
use these validation rules combination:
nullable|integer
I had this problem. So, I found this description in the laravel docs:
"By default, when an attribute being validated is not present or contains an empty string, normal validation rules, including custom extensions, are not run."
So... when you have an attribute with empty string data, the normal validation rules are not executed.
So... I had an idea to solve this problem which was override the function setAttribute in my models:
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (is_string($value))
{
$this->attributes[$key] = empty(trim($value)) ? null : $value;
}
}
So... All times that will have to save de model in your database, the empty values will be converted in a null values.
Therefore, if the data is null or integer, use the "nullable | integer" rule to use the validation rule. If the data is an empty string, the validation rule will not be considered, but the data will be converted to an empty string before being saved to the database.
I created a my BaseModel(abstract class) with the method setAttribute and all my models inherit this class.
abstract class BaseModel extends Model
{
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (is_string($value))
{
$this->attributes[$key] = empty(trim($value)) ? null : $value;
}
} ...
You need to make userId field required as this:
'userId' => 'required|integer'
Related
In my model I have the following:
protected $dates = ['start_date'];
I am using an input field of type 'date' to select the date. If the user removes the date, its value becomes a null string "".
when updating my model, I get the following error:
exception: "InvalidArgumentException"
file: "C:\www\projects\crm\vendor\nesbot\carbon\src\Carbon\Carbon.php"
line: 582
message: "Data missing"
I can avoid this error by using a mutator like this:
public function setStartDateAttribute($value)
{
if ($value) {
$this->attributes['start_date'] = $value;
} else {
$this->attributes['start_date'] = null;
}
}
Question:
Is there a faster/better way than using a mutator to deal with storing an empty string as a date?
Looking into this a bit deeper:
Middleware updated in 5.4
Laravel 5.4 included two new middleware in the default middleware stack: TrimStrings and ConvertEmptyStringsToNull.
These middleware will automatically trim request input values and
convert any empty strings to null. This helps you normalize the
input for every request entering into your application and not have to
worry about continually calling the trim function in every route and
controller.
From: https://laravel.com/docs/5.4/releases#laravel-5.4
So, when I am grabbing the request object, an empty date field is converted to null. The database allows null on these fields and everything works correctly.
So, through the front end, date fields can be entered and removed without error. When updating manually to an empty string as per your request
\App\Yourmodel::find(7)->update(["your_date_field" => ""]);
I had the same data missing error.
Question is, do you specifically need to pass an empty string or is making the field nullable a better option for you?
\App\Yourmodel::find(7)->update(["your_date_field" => null]);
I need to check if the key is not set in the array using Laravel validator.
That would be the complete opposite of the "required" validation rule.
Basically the array will be passed to update method if it passes the validation and I want to make sure one column will not be updated.
Is there a way to check if the value "is not present"?
Thank you
EDIT:
I'm currently using Laravel 5
EDIT:
I managed to write my own validation rule by calling Validator::extendImplicit. However I get $value as null to my validation function both when I set it to null or when I don't set it at all. Is there a way to check if the value is set?
I believe I found a solution:
$validator->extendImplicit('not_present', function($attribute, $value, $parameters)
{
return !array_key_exists($attribute, $this->data);
});
I'm not calling extendImplicit statically because the Validator class object is injected to the controller of my class.
I need to access $this->data ($this referring to the Validator object) to make sure the key doesn't exist in the array being validated.
Based on the #MaGnetas answer I came up with this 2 rules that can be applied on any model.
I'm using Laravel 5.4 so putting this lines on your AppServiceProvider.php should work.
The first approach (extendImplicit and array_key_exists)
Validator::extendImplicit('not_present', function($attribute, $value, $parameters, $validator)
{
return !array_key_exists($attribute, $validator->getData());
});
Ussing $validator->getData() we could use the Validator statically.
The second approach (extend and false)
Validator::extend('not_present', function($attribute, $value, $parameters, $validator)
{
return false;
});
You could use extend because we don't need the rule to be executed if the data has not the property (because that's exactly what we want right?)
On the docs:
By default, when an attribute being validated is not present or contains an empty value as defined by the required rule, normal validation rules, including custom extensions, are not run. more info
Important: The only difference is that using extend, empty strings will not run the validation. But if you have setting TrimStrings and ConvertEmptyStringsToNull on your middleware (which AFAIK is the default option) there will be no problem
No there is no build in validtion rule for this, but you can create your own validation rule.
The simplest way to do this:
Validator::extend('foo', function($attribute, $value, $parameters)
{
// Do some stuff
});
And check if key exists.
More information:
http://laravel.com/docs/4.2/validation#custom-validation-rules
For people looking for the not_present logic in 7.x apps (applicable for all versions), remember that you can simply use the validated data array for the same results.
$validatedKeys = $request->validate([
'sort' => 'integer',
'status' => 'in:active,inactive,archived',
]);
// Only update with keys that has been validated.
$model->update(collect($request->all())->only($validatedKeys)->all());
my model has more attributes but only these two should be updatable, therefore I too were looking for an not_present rule but ending up doing this as the results and conceptual logic is the very same. Just from another perspective.
I know this question is really old but you can also use
'email' => 'sometimes|required|not_regex:/^/i',
If the email is present in the request, the regex will match any characters in the request and if the email is an empty string but is present in request the sometimes|required will catch that.
I'm currently building a form manager in PHP to validate large forms.
I'm wondering what is the best structure for that, because the number of fields will be different each time.
I'm already filtering the field to validate by using a prefix (ex : 'user_name', will be validated, but 'name' no).
My real problem is for the validation : I must check the type of the field (mail, zipcode, phone...)
AND check that the value for this type of field is valid.
I thought that I could use HTML5 Custom data" (ex: data-fieldtype="zipcode"), but i didn't know that the server can't get this attribute...
What's the way to go ?
I could use 2 inputs per field, one for the value and one for the type, but it looks really stupid !
Thanks if you can help.
EDIT :
Your answers are all interesting, i don't know which is best.
I will probably make a mix between your solutions, depending of the kind of form.
Thanks a lot.
Methinks, this shouldn't be played via the Browser without further thought: A malicious user would be able to manipulate a "INT ONLY" field into being freetext, and your application would suddenly have to deal with freetext in a field, that is thought to be validated as an integer (und thus e.g. safe for SQL).
You have two approaches:
Have your form validation structure stored in the DB, and submit a single hidden field, that carries the ID of the validation structure. On receiving the request, your script would request the structure from the DB, unserialize it, and work on it.
If you really need to go through the browser, serialize your validation structure, base64-encode it and use it as a single hidden field. For the reasons stated above, it is mandatory to authenticate this value, either hashing (concatenate with another string only known to the server, hash it, send the hash as a second hidden field, on next request verify the hash is correct) or by encryption (encrypt the serialized data before the browser roundtrip, decrypt afterwards, key known only to the server)
Both would make your prefix-trick unnecessary, increasing readability and maintainability.
If no framework is used, you can use an array of field => options.
$rules = [
'user_name' => 'required',
'user_email' => 'email required',
// ...
];
And then feed them to some validator class, where rules are methods and they're being called inside validate method dynamically:
class Validator {
public function __construct($data) { $this->data = $data; }
private function required($field) {}
private function email($email) {}
// etc
/** #method bool validate(array $rules) */
public function validate($rules) {}
}
Never ever validate on client-side only. Always validate serverside.
Usually you will have a base class for controller, and every other controller extends it.
Aa good approach is to have en every view controller (or the bootsrtap) a method check_params().
It should 1) get a copy or $_REQUEST, check every parameter needed, 2) delete $_REQUEST, 3) write back the checked and validated params.
abstract class controller_base {
public function __construct() { ...; $this->check_param();...}
protected final function check_param() {
foreach ($this->param_list() AS $name => $type) {...}
}
abstract public function param_list();
}
class controller_login extends controller_base {
public function param_list() {
return array('name' => 'string', 'password' => 'string');
}
}
The idea is that this way you
only use params that has been sanitized
you autmaticly delete every param not needed
you have a list in every controller that states the used params.
If we create a simply route with Laravel 4 we can use where to set a regular expression for each param. passed in the URI. For example:
Route::get('users/{id}',function($id){
return $id;
})->where('id','\d+');
For every get request second param. must be digits. Problem comes when we create resources, if we use ->where() it throws an error about this is not an object.
I've tried to place where into a group, as an array as third param. in the resource but has not worked.How could we use the power of the regular expressions with the power of Laravel 4 resources?
He Joss,
I was going mad with the same question, so I did some research. All I could come to is the following conclusion: you do not need this method. In each of your controller methods you can specify your default values after each argument:
public function getPage(id = '0', name = 'John Doe'){
Next to this you can do a regular expression check, and a lot of other checks with the laravel validator like this:
//put the passed arguments in an array
$data['id'] = $id;
$data['name'] = $name;
//set your validation rules
$rules = array(
"id" => "integer"
"name" => "alpha"
);
// makes a new validator instance with the data to be checked with the given
// rules
$validator = Validator::make($data, $rules);
// use the boolean method passes() to check if the data passes the validator
if($validator->passes()){
return "stuff";
}
// set a error response that shows the user what's going on or in case of
// debugging, a response that confirms your above averages awesomeness
return "failure the give a response Sire";
}
Since most of the validation are already in the options list of laravel you might not need the regular expression check. Yet it is an option (regex:pattern).
My theory behind the none existence of the where() method in the controllers is that the method offers you the opportunity to do validation within you Route file. Since you already have this possibility in your controller there is just no need for it.
I have an application that allows users to create forms and assign validation to each field (think http://www.phpform.org/ or http://wufoo.com/). I can easily get each field’s name and label from the database, as well as the array of associated validation rules.
Say, for example, I wanted to apply a blanket rule of “required” for all of the user defined forms, I would do something like this:
foreach($fields as $field)
{
$this->form_validation->set_rules($field[‘name’], $field[‘label’], ‘required’);
}
Now, the problem is that I need to replace “required” with a callback. In order for the callback to work, I’m going to need the field’s id (so the callback can use this id to lookup the field's associated validation rules). How do I get this id value to the callback function?
foreach($fields as $field)
{
$this->form_validation->set_rules($field[‘name’], $field[‘label’], "callback__example[$field[‘id‘]]");
}
// your callback... see ~line 589 of Form_validation library
public function _example($str,$id)
{
// do something to $str using $id, return bool
}