简体   繁体   中英

Skinny Controllers in Laravel

I am trying to improve my Laravel code design by moving code away from the controllers as much as possible.

In my AuthController I have a store function for logging in the user, where can I move the logic in this function?

So far I have implemented a FormRequest for validation and UserResource for the response fields. I was thinking of creating a file in /Services called AuthService and moving authentication logic there, unless there is a better design pattern? Or maybe logic for authentication can be moved to the User 's model?

public function store(StoreAuthRequest $request)
{
    $user = User::where('email', $request->input('email'))->first();

    if(!$user || !Hash::check($request->input('password'), $user->password)) {
        return response([
            'message' => 'Bad credentials'
        ], 401);
    }

    $token = $user->createToken('AppToken')->plainTextToken;

    return response([
        'user' => new UserResource($user),
        'token' => $token
    ], 201);
}

I would recommend to never mixup what a "service" is/means. In Laravel a Service (not a ServiceProvider , but a literal folder called Services in the app folder) is a class that allows the developer to communicate with external services, it is not a "logic" class that drives something, like logic for logging in.

What I have done, after working so many years with Laravel, is to "mix" a little bit of DDD (Domain Driven Development), what I have done is just add a Domain folder inside app , and you put ALL your logic in there.

For example, imagine we have a Doctors app, this would be the heriarchy:

  • app
    • Console
    • Domain
    • Exceptions
    • Http
    • Models
    • Providers
  • bootstrap
  • config
  • database
  • resources
  • etc.

So, intead of having stuff inside a app/Services , just put code inside Domain , like this (following our example):

  • Domain
    • Common ( App\Domain\Common )
      • Enums
      • Events
      • Listeners
      • Jobs
      • Servicies
    • Doctor ( App\Domain\Doctor )
    • Facility ( App\Domain\Facility )
    • Patient ( App\Domain\Patient )

So, the idea is to put true logic inside this Domain folder (and further organize them), so you can then NOT mix logic or stuff togheter.

To wrap it up, you would then have controllers like this:

class PatientController extends Controller
{
    public function store(PatientRequest $request)
    {
        return \App\Domain\Patient\Entity::store(
            $request->input('name'),
            $request->input('email'),
            $request->input('age')
        );
    }
}

All the logic for "registering" (manipulating something from the Patient) model, is on the Entity class.


I have also taken a different approach by following something similar to having "modules", so everything is in a "modules" folder and that logic is just in there. I got that idea from Ryuta Hamasaki: Modularising the Monolith , but this may be way more advance.

You can write the validator directly in the controller. Because this line of code has no possibility of being reused.

Example:

public function store(Request $request)
{
    $request->validate([
        'email' => ['required', 'email'],
        'password' => ['required'],
    ]);
    $user = User::query()
        ->where('email', $request->email)
        ->first();
    // PHP 8.x
    if (! Hash::check($request->password, $user?->password)) {
        // Throw an exception
    }
    
    return response()->json([
        'token' => $user->createToken(...)->plainTextToken,
        'user' => new UserResource($user), // or UserResource::make($user)
    ])
}

End.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM