First of all I love the way that validation is going through, can now easily use
public function authorize(Authenticator $auth)
{
return $auth->user()->hasRole('administrator');
}
hat's not the problem, I bump always into another problem... that is when you update an record, how to do things with the rules? If I need to update an email, I need the following string: 'email' => 'unique:users,email_address,10'. In this case it should look like:
public function rules()
{
return [
'email' => 'required|unique:users,id,?????',
'tags' => 'required'
];
}
It's more simple.
The Laravel documentation says "If your table uses a primary key column name other than id, you may specify it as the fourth parameter":
'email' => 'unique:users,email_address,'.$user->id.',user_id'
If for example, you want to verify if a username exists, but excluding current user ID:
// UpdateUserRequest.php
public function rules() {
//
return [
'username' => 'required|unique:users,username,' . $this->id . ',id',
];
}
Related
I have not so much practical experience with Laravel yet and I wondered what is the best way to deal with similar validation logic and where to put it.
Let's say I have an API resource Controller for Products with a store and an update method like so:
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:100',
'description' => 'nullable|string|max:1000',
'price' =>'required|decimal:0,2|lt:1000'
]);
return Product::create($request->all());
}
public function update(Request $request, Product $product)
{
$request->validate([
'name' => 'string|max:100',
'description' => 'nullable|string|max:1000',
'price' =>'decimal:0,2|lt:1000'
]);
return Product::update($request->all());
}
The only difference between the validation in store and update is that store adds the 'required' rule for 'name' and 'price'. My question is, if I can encapsulate both validations in one Form Request, or how can I avoid code duplication without adding unnecessary code?
With my understanding of Form Requests I would probably create two Form Request classes, StoreProductRequest and UpdateProductRequest, and maybe another helper class that defines the core validation rules. Then each Form request could call for example ProductHelper::getBaseValidationRules() and merge that with their extra requirements. Somehow I find that a bit overkill.
you can create a request for your validations and use them in your controllers
for example
php artisan make:request YOUR_REQUEST_NAME
then inside your request you can add your validations like this
public function rules()
{
return [
'name' => 'required|string|max:100',
'description' => 'nullable|string|max:1000',
'price' => 'required|decimal:0,2|lt:1000'
];
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
and in your method you can call it like this
public function update(YOUR_REQUEST_NAME $request, Product $product)
{
return Product::update($request->all());
}
for more information you can read this
https://laravel.com/docs/5.0/validation#form-request-validation
in case you want condition in the rules please check this video
https://www.youtube.com/watch?v=epMaClBOlw0&ab_channel=CodeWithDary
Okay based on the suggestions, I came up with the following solution:
I created a Form Request named ProductRequest and implemented the rules method as follows:
public function rules()
{
$rules = [
'name' => ['string', 'max:100'],
'description' => ['nullable', 'string', 'max:1000'],
'price' => ['decimal:0,2', 'lt:1000'],
];
// If the user wants to create a new Instance some fields are mandatory.
if ($this->method() === 'POST') {
$rules['name'][] = 'required';
$rules['price'][] = 'required';
}
return $rules;
}
This is fine for me. Although in a bigger project I probably would create two Form Requests, StoreProductRequest and UpdateProductRequest. They would share and update a base set of rules as I described in the question.
I want to pass $params['user_id'] to $fieldValidations and check if the hour is unique for specific user_id not for all hours hour in the database table
I created a model post
class Post extends Model
{
protected $fillable = ['user_id', 'hour'];
public static $fieldValidations = [
'user_id' => 'required',
'hour' => 'required|date_format:Y-m-d H:i:s|unique:post,hour,NULL,user_id,'
];
}
and a controller post
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$params = $request->all();
$params['user_id'] = 12;
$validator = Validator::make($params, Post::$fieldValidations);
if ($validator->fails()) {
return Response::json($validator->errors()->all(), 422);
}
}
}
I don't think you can do this using the unique validation rule. From Laravel 5.7 documentation:
The field under validation must be unique in a given database table.
Note it says table and not column.
You may have to just query the database and return a JSON response error if it fails. Also, in your current code inside the validation rules, you are specifying that user_id is the primary id key column in the post table. I think that is likely an error and should be removed, even though it's irrelevant given that you can't accomplish what you want using the unique rule. Also, you ended the rule with a comma.
if (Post::where(['user_id' => $params['user_id'], 'hour' => $params['hour']])->exists()) {
return response()->json(['status' => 'error', 'msg' => 'Error', 'errors' => ['hour_error' => ['That hour already exists on the user!']]], 422);
}
Lastly, instead of using $params = $request->all(), I prefer to use the request() helper function and just inline it into the rest of the code. But, that's up to you.
I'm trying to make simple unique slugs. The slugs are saved correctly in database, so the function is working. I have problems with making them unique.
I have this rule in TagCreateRequest.php
public function rules()
{
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:tag,tag_slug,'
];
$rule = 'unique:tag';
$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
$rule .= ',tag_slug,' . $id;
}
$rules['tag_slug'][] = $rule;
return $rules;
}
and this in my store function in the controller
public function store(TagCreateRequest $request)
{
$tag = new Tag();
foreach (array_keys($this->fields) as $field) {
$tag->$field = $request->get($field);
}
$tag->save();
return redirect()->route('tags');
}
The error is about trying to add duplicate value
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'unique slug' for key 'tags_tag_unique'
Can someone help me to fix this issue?
You can access the id field magically. ID must be the same route parameter used in your route.
If you use id parameter like as Route::put('post/{id}/update') then you can magically access the id parameter inside your form request. Otherwise, if you call the parameter of {post} Route::put('post/{post}/update'), in your form request you must be call $this->post instead of $this->id, ok?
Please try it:
public function rules()
{
$rules = [
'tag' => 'required|min:3'
];
$slugRule = 'required|alpha_dash|unique:tag_slug';
if (! empty($this->id)) {
$slugRule = 'required|alpha_dash|unique:tag_slug,'.$this->id;
}
$rules['tag_slug'] = $slugRule;
return $rules;
}
This FormRequest will work fine on the store() and update() methods if you inject him in both methods.
See it:
// Your store route
Route::post('/post/store', ['as' => 'post.store', 'uses' => 'YourController#store']);
// YourController store method
public function store(NameSpaced\FormRequest $request)
{
// ...
}
// Your update route
Route::post('/post/{id}/update', ['as' => 'post.update', 'uses' => 'YourController#store']);
// YourController update method
public function update(NameSpaced\FormRequest $request)
{
// ...
}
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:[table name],[column name]'
];
Try this the first is table name and the second is column name that you wanted to unique, write without adding square braces. or you just pass table name like this,
$rules = [
'tag' => 'required|min:3',
'tag_slug' => 'required|alpha_dash|unique:[table name]'
];
laravel auto checks for the column.
I hope it helps.
I would suggest that you automatically generate a new slug whenever you are creating a tag. I got myself in same issues that you have listed here, so i decided on automatically generating whenever i am creating a new item. I used laravel-sluggable. It automatically generates unique slugs.
As per your question, i have defined a unique slug rule in one of my demo apps like this:
public function rules()
{
return [
'name' => 'required|string|max:255',
'slug' => 'required|string|max:255|unique:categories,slug,'.$this->segment(3),
];
}
Please note that $this->segment(3) refers to the id of the model being updated in the backend pages, it can be different in your application.
I am trying to validate an update user profile form, whereby the validation should check that the email doesn't exist already, but disregard if the users existing email remains.
However, this continues to return validation error message 'This email has already been taken'.
I'm really unsure where I'm going wrong. Otherwise, the update form works and updates perfectly.
HTML
{{ Form::text('email', Input::old('email', $user->email), array('id' => 'email', 'placeholder' => 'email', 'class' => 'form-control')) }}
Route
Route::post('users/edit/{user}', array('before' => 'admin', 'uses' => 'UserController#update'));
User Model
'email' => 'unique:users,email,{{{ $id }}}'
Your rule is written correctly in order to ignore a specific id, however, you'll need to update the value of {{{ $id }}} in your unique rule before attempting the validation.
I'm not necessarily a big fan of this method, but assuming your rules are a static attribute on the User object, you can create a static method that will hydrate and return the rules with the correct values.
class User extends Eloquent {
public static $rules = array(
'email' => 'unique:users,email,%1$s'
);
public static function getRules($id = 'NULL') {
$rules = self::$rules;
$rules['email'] = sprintf($rules['email'], $id);
return $rules;
}
}
You can accomplish this with the sometimes function of the validator
Something like:
$validator->sometimes('email', 'unique:users,email', function ($input) {
return $input->email == Input::get('email');
});
See http://laravel.com/docs/4.2/validation#conditionally-adding-rules for more info
I'm using a validation service to validate user submitted form input (something along the lines of: http://laravel.io/bin/vrk).
Using this approach (validation service classes) to validate user submitted form data against a set of rules, how can I validate user submitted data when rules have a unique rule. For example, if a user has the username of John then when I try to update the model validation fails (because John exists as a username, even though it belongs to the current model).
To solve this in Laravel I can do something like 'username' => 'required|alpha_dash|unique:users,username'.$id. How should I modify my current code, in the link, to best accommodate this? Should I have separate validator classes depending on the scenario (for example, UserCreateValidator, UserUpdateValidator, etc). Or should I do something like create separate validation rules in UserValidator class and pass which rule I want as an argument to either the constructor or the passes() method when calling UserValidator?
I think you could do something like this
First update UserValidator rules like this.
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'default' => [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
],
'update' => [
'username' => null,
]
];
}
Then modify Validator's passes method like this
public function passes($rule = null) {
$rules = $this->rules['default'];
if ($rule && isset($this->rules[$rule])) {
$rules = array_merge($rules, $this->rules[$rule]);
}
$validator = \Validator::make($input, $rules);
if ($validator->fails()) {
$this->validator = $validator;
return false;
}
return true;
}
Then in your controller's PUT method, this will merge update rules to default rules
$rule = 'update';
// user has changed his username
if ($input['username'] !== $old_username) {
$rule = 'create'; // validate uniqueness
}
else {
unset($input['username']); // remove it, we don't validate it anymore since it's the same
}
$validator->passes($rule); // override 'default' rules with 'update' rules
You don't have to change your controller's POST method, it'll stay the same
$validator->passes(); // use 'default' rules
If I'm understanding right, you have issues updateng data because of primary key constraints on your model. What you need to do is to create 2 sets of rules, one for insert, and one for update.
Asuming you have a set of rules like this:
protected $rules = [
'id' => 'required|unique:users'
]
You should implement something like this:
protected $rules = [
'id' => 'required|unique|unique:users,id,' . $this->id
];
This should tell laravel to ignore the duplicate id in the table users for the specified id, in this case, the id for the current object.
You can read more about this on laravel's documentation at http://laravel.com/docs/validation
unique:table,column,except,idColumn
The field under validation must be unique on a given database table.
If the column option is not specified, the field name will be used.
Well, what are you doing on post?
Because this is what you should be doing:
$user = User::find($userId);
$user->username = $input['username'];
$user->email = $input['email'];
$user->save();
To update a record.
Or
$input = array('username' => 'w0rldart', 'email' => 'hahafu#dumbledore.com');
// Retrieve the user by the attributes, or create it if it doesn't exist,
// based on the data above, which can come from an Input::all();
$user = User::firstOrCreate($input);
... many possibilities. But you could also do:
$input = array_forget($input, 'username');
To comply with your case, by removing the username index from the input array.
This is all I call tell you, based on the information you gave us. If you want more, post the controller's put method.
Update:
Here's my version of your PUT method: http://laravel.io/bin/OaX
I really think that try catch syntax is useless, since it's obvious that a User model will always be there. But I still don't know what you're trying to update. Even though I can't test it right now, I don't think that updating should be giving that problem, and if it does, retrieve user by username/id then unset the username index in your input array, and update it according to your specifications.
A little modification in UserValidator class
class UserValidator extends Validator {
// Override parent class $rules
protected $rules = [
'username' => 'required|alpha_dash|unique:users',
'password' => 'required|between:6,16|confirmed',
'password_confirmation' => 'required|between:6,16'
];
// ADD THIS
public function __construct(Array $rules = array())
{
parent::__construct();
if(count($rules)){
foreach($rules as $k => $v) $this->rules[$k] = $v;
}
}
}
In your controller putUpdate method
$user = User::whereUsername($username)->firstOrFail();
$rules = ['username' => 'required|alpha_dash|unique:users,username,'. $user->id];
// Pass the rule to update the rule for username in this method
$validator = \Services\Validators\UserValidator(Input::all(), $rules);
Check the manual here.