'Best way to shorten store method in controller

I'm trying to shorten my controller code, and I want to know the conventions to use with Laravel while validating and storing.

Controller

public function store(Request $request)
{
    // Validation
    $user_id = Auth::user()->id;

    $request->validate([
        'lname' => 'required|max:255',
        'fname' => 'required|max:255',
        'ar_lname' => 'required|max:255',
        'ar_fname' => 'required|max:255',
        'tel' => 'required|digits:10|unique:infos',
        'level' =>'required|max:50',
        'goal' =>'required',
        'img' => 'required|image|mimes:jpeg,bmp,png',
        'cin' => 'required|image|mimes:jpeg,bmp,png',
    ]);

    // Store
    info::create([
        'user_id' => $user_id,
        'lname' => $request->lname,
        'fname' => $request->fname,
        'ar_fname' => $request->ar_fname,
        'ar_lname' => $request->ar_lname,
        'bday' => $request->bday,
        'tel' => $request->tel,
        'level' => $request->level,
        'goal' => $request->goal,
        'img' => $request->file('img')->store('images', 'public'),
        'cin' => $request->file('cin')->store('cins/' .  $request->lname . ' '. $request->fname  ),
        'registered' => true,
    ]);

    // Redirect
    return redirect()->route('user.index');
}


Solution 1:[1]

First of all you can isolate the validation in a dedicated class following the Laravel way by creating a custom Request with your rules.

php .\artisan make:request StoreInfoRequest

StoreInfoRequest

class StoreInfoRequest extends FormRequest

    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'lname' => 'required|max:255',
            'fname' => 'required|max:255',
            'ar_lname' => 'required|max:255',
            'ar_fname' => 'required|max:255',
            'bday' => 'date',
            'tel' => 'required|digits:10|unique:infos',
            'level' => 'required|max:50',
            'goal' => 'required',
            'img' => 'required|image|mimes:jpeg,bmp,png',
            'cin' => 'required|image|mimes:jpeg,bmp,png',
        ];
    }
}

Then this can't be called a Laravel way but it will be very short and clean:

    public function store(StoreInfoRequest $request)
    {

        info::create(
            array_merge(
                ['user_id' => Auth::user()->id],
                $request->safe()->except(['img', 'cin']),
                ['img' => $request->file('img')->store('images', 'public')],
                ['cin' => $request->file('cin')->store('cins/' .  $request->lname . ' ' . $request->fname)],
                ['registered' => true],
            )
        );

        return redirect()->route('user.index');
    }

If you are using Laravel 9 you can do return to_route('user.index');

Solution 2:[2]

It is a good practice to create separate classes for each concerns, like for your controller it should only handle receiving and returning the output of your http request.

So you should create classes for the FF:

  1. Class that will handle the validation
  2. Class that you will handle the business logic

As what Medilies answered you have to create a separate file for validating all incoming data. You need to create a Request file that will handle it.

php artisan make:request StoreInfoRequest

StoreInfoRequest

class StoreInfoRequest extends FormRequest

public function authorize(): bool
{
    return true;
}

public function rules(): array
{
    // declare here everything even it is not required
    return [
        'lname' => 'required|max:255',
        'fname' => 'required|max:255',
        'bday' => 'date',

    ];
}

this will return an array of the validated columns you entered in your StoreInfoRequest $validated.

  1. Then create a service file that will handle your business logic say InfoService. Within this file you can do the eloquent saving. By then you can have clean and thin controller like this.

    public function store(StoreInfoRequest $request)
    {
        $this->InfoService->store($request->$validated);
        return redirect()->route('user.index');
    }
    

Don't forget to instantiate the service file in your controller's __constructor method.

   public function __constructor(StoreInfoRequest $storeInfoRequest)
   {
       $this->storeInfoRequest = $storeInfoRequest;
   }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2