Dans cet article, je vais vous montrer comment raccourcir les contrôleurs en utilisant des classes de service et différentes manières d’initialiser ou d’injecter ce service.
Tout d’abord, la situation “avant” – vous avez un contrôleur avec deux méthodes : store()
et update()
:
class UserController extends Controller
{
public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());
$user->roles()->sync($request->input('roles', []));
// More actions with that user: let's say, 5+ more lines of code
// - Upload avatar
// - Email to the user
// - Notify admins about new user
// - Create some data for that user
// - and more...
return redirect()->route('users.index');
}
public function update(UpdateUserRequest $request, User $user)
{
$user->update($request->validated());
$user->roles()->sync($request->input('roles', []));
// Also, more actions with that user
return redirect()->route('users.index');
}
}
Ce contrôleur est trop long – la logique devrait être ailleurs.
Refactoring – Étape 1 : Classe de service
L’une des façons de le refactoriser est de créer un Service classe pour tout ce qui concerne l’utilisateur, avec des méthodes comme store()
et update()
.
Notez que Laravel n’a pas php artisan make:service
commande, vous devez créer cette classe manuellement, comme une classe PHP normale.
Et puis, nous déplaçons ce code du contrôleur vers un service :
app/Services/UserService.php:
namespace App\Services;
class UserService {
public function store(array $userData): User
{
$user = User::create($userData);
$user->roles()->sync($userData['roles']);
// More actions with that user: let's say, 5+ more lines of code
// - Upload avatar
// - Email to the user
// - Notify admins about new user
// - Create some data for that user
// - and more...
return $user;
}
public function update(array $userData, User $user): User
{
$user->update($userData);
$user->roles()->sync($userData['roles']);
// Also, more actions with that user
}
}
Ensuite, notre contrôleur devient beaucoup plus court – nous appelons simplement les méthodes de service.
Il y a quelques façons de le faire. La plus simple consiste à créer une instance de classe Service chaque fois que vous en avez besoin, comme ceci :
use App\Services\UserService;
class UserController extends Controller
{
public function store(StoreUserRequest $request)
{
(new UserService())->store($request->validated());
return redirect()->route('users.index');
}
public function update(UpdateUserRequest $request, User $user)
{
(new UserService())->update($request->validated(), $user);
return redirect()->route('users.index');
}
}
Refactoring – Étape 2 : Injecter le service
Au lieu de faire new UserService()
chaque fois que nous en avons besoin, nous pouvons simplement l’insérer en tant que dépendance dans les méthodes où nous en avons besoin.
Laravel l’initialisera automatiquement, si nous fournissons l’indice de type dans les méthodes du contrôleur :
use App\Services\UserService;
class UserController extends Controller
{
public function store(StoreUserRequest $request, UserService $userService)
{
$userService->store($request->validated());
return redirect()->route('users.index');
}
public function update(UpdateUserRequest $request, User $user, UserService $userService)
{
$userService->update($request->validated(), $user);
return redirect()->route('users.index');
}
}
On peut aller encore plus loin et injecter la classe Service dans un constructeur du Contrôleur. Ensuite, nous avons accès au service dans toutes les méthodes de contrôleur dont nous avons besoin.
use App\Services\UserService;
class UserController extends Controller
{
private UserService $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function store(StoreUserRequest $request)
{
$this->userService->store($request->validated());
return redirect()->route('users.index');
}
public function update(UpdateUserRequest $request, User $user)
{
$this->userService->update($request->validated(), $user);
return redirect()->route('users.index');
}
}
Enfin, nous pouvons utiliser la syntaxe relativement nouvelle de PHP 8 appelée promotion immobilière constructeurdonc nous n’avons même pas besoin de déclarer une variable privée, ou d’assigner quelque chose dans le constructeur :
use App\Services\UserService;
class UserController extends Controller
{
public function __construct(private UserService $userService)
{
}
// We can still use $this->userService anywhere in the Controller
}
J’ai une vidéo séparée, spécifiquement sur cette fonctionnalité PHP 8 :
Voilà pour cet article. Bien sûr, il existe d’autres façons de séparer le code du contrôleur – classes d’action, référentiels et autres modèles – alors choisissez celui que vous voulez, la logique d’injection ou d’initialisation de cette classe serait la même.