0

I am using Request validation to validate the user's input.

This is UpdateUser:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate;

class UpdateUser extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return Gate::allows('update-user');
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {

        $user_id = Arr::get($this->request->get('user'), 'id');

        return [
            'user.firstname'      => 'required|string|max:255',
            'user.lastname'       => 'required|string|max:255',
            'user.email'          => "required|string|email|max:255|unique:users,email,{$user_id}",
            'user.password'       => 'sometimes|nullable|string|min:4|confirmed',
        ];
    }
}

As you can see, there is some update-specific stuff happening:

The authorize() method checks whether the user is allowed to update-user and inside the rules I am excluding the row of the current user from being unique:

'user.email'          => "required|string|email|max:255|unique:users,email,{$user_id}",

As I would like to merge UpdateUser and StoreUser, what would be the most efficient and readable way to determine, whether I am updating or saving?

This is my current approach:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate;

class UpdateUser extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        if($this->isUpdating())
        {
            return Gate::allows('update-user');
        }

        return Gate::allows('create-user');
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        if($this->isUpdating()){
            $user_id = Arr::get($this->request->get('user'), 'id');

            return [
               ...
            ];
        }

        return [];
    }
    
    /**
     * @return bool
     */
    protected function isUpdating(){
        return $this->isMethod('put') || $this->isMethod('patch');
    }
}

I am wondering if I may extend the FormRequest class and provide isUpdating() by default.

SPQRInc
  • 162
  • 4
  • 23
  • 64

3 Answers3

2

Your update and store method are not the same request type, you have PUT and PATCH method on your request instance, so you can check the request type as like :

switch ($request->method()) {
   case 'PATCH':
        // do anything in 'patch request';
        break;

   case 'PUT':
        // do anything in 'put request';
        break;

   default:
       // invalid request
       break;
}
STA
  • 30,729
  • 8
  • 45
  • 59
1

I learnt about a new approach to validation some time ago using separate validator class and I kinda like it a lot. Let me show you

Create a directory Validators and a class inside UserValidator

class UserValidator
{
    public function rules(User $user)
    {
        return [
            'user.firstname' => [
                'required',
                'string',
                'max:255',
            ],

            'user.lastname' => [
                'required',
                'string',
                'max:255',
            ],

            'user.email' => [
                $user->exists ? 'sometimes' : null,
                'required',
                'string',
                'email',
                'max:255',
                Rule::unique('users', 'email')->ignore($user->exists ? $user->id : null)
            ],

            'user.password' => [
                $user->exists ? 'sometimes' : null,
                'required',
                'string',
                'min:8'
            ],
        ];
    }

    public function validate(array $data, User $user)
    {
        return validator($data, $this->rules($user))            
            //->after(function ($validator) use ($data, $user) {
            //    Custom validation here if need be
            //})
            ->validate();
    }
}

Then authorization can be done in Controller

class UserController
{
    use AuthorizesRequests;

    /**
     * @param Request $request
     */
    public function store(Request $request)
    {
        $this->authorize('create_user', User::class);

        $data = (new UserValidator())->validate(
            $request->all(),
            $user = new User()
        );

        $user->fill($data)->save();
    }

    /**
     * @param Request $request
     * @param \App\user $user
     */
    public function update(Request $request, User $user)
    {
        $this->authorize('update_user', $user);

        $data = (new UserValidator())->validate(
            $request->all(),
            $user
        );

        $user->fill($data)->save();
    }
}

This was proposed and explained by (twitter handle) @themsaid

Donkarnash
  • 12,433
  • 5
  • 26
  • 37
0

because i'm using file upload in update i cant use put so both route are POST that why i use route name

$id= $this->route('product'); 
$rules= [
    'name' => 'required',
    'sku' =>'required|unique:products,sku,' .$id,
    'image' => 'nullable|image|max:1024', // Max 1MB
];
if(Route::currentRouteName() == "products.store"){
    $rules['sku'] = 'required|unique:products';
};

return $rules;

and the samething goes for authorization

aitbella
  • 958
  • 9
  • 19