Aller au contenu principal
Version: Canary đźš§

Services dans Bow Framework

Introduction​

Ă€ propos des services

Les services sont des classes qui encapsulent la logique métier de votre application. Ils permettent de découpler le code des contrôleurs et de rendre votre application plus testable et maintenable.

Les services sont automatiquement injectés par le conteneur d'injection de dépendances de Bow Framework.

Création d'un service​

Pour créer un service, utilisez la commande add:service :

php bow add:service UserService

Le service sera créé dans le dossier app/Services :

app/Services/UserService.php
namespace App\Services;

class UserService
{
//
}

Exemple pratique​

Créons un service complet pour gérer les utilisateurs :

app/Services/UserService.php
namespace App\Services;

use App\Models\User;
use Bow\Support\Collection;

class UserService
{
/**
* Récupère tous les utilisateurs
*/
public function getAllUsers(): Collection
{
return User::all();
}

/**
* Récupère un utilisateur par son ID
*/
public function findById(int $id): ?User
{
return User::find($id);
}

/**
* Crée un nouvel utilisateur
*/
public function create(array $data): User
{
return User::create([
"name" => $data["name"],
"email" => $data["email"],
"password" => app_hash($data["password"]),
]);
}

/**
* Met Ă  jour un utilisateur
*/
public function update(int $id, array $data): bool
{
$user = User::find($id);

if (!$user) {
return false;
}

return $user->update($data);
}

/**
* Supprime un utilisateur
*/
public function delete(int $id): bool
{
return User::destroy($id) > 0;
}
}

Injection dans un contrôleur​

Le service peut être injecté via le constructeur ou directement dans les méthodes :

Via le constructeur​

app/Controllers/UserController.php
namespace App\Controllers;

use App\Controllers\Controller;
use App\Services\UserService;
use Bow\Http\Request;

class UserController extends Controller
{
public function __construct(
private UserService $userService
) {}

public function index()
{
$users = $this->userService->getAllUsers();

return view("users.index", ["users" => $users]);
}

public function show(int $id)
{
$user = $this->userService->findById($id);

if (!$user) {
return app_abort(404, "Utilisateur non trouvé");
}

return view("users.show", ["user" => $user]);
}

public function store(Request $request)
{
$user = $this->userService->create($request->all());

return redirect("/users/" . $user->id);
}
}

Via les paramètres de méthode​

public function index(UserService $userService)
{
$users = $userService->getAllUsers();

return view("users.index", ["users" => $users]);
}

Injection de dépendances dans un service​

Les services peuvent eux-mêmes avoir des dépendances injectées :

app/Services/NotificationService.php
namespace App\Services;

class NotificationService
{
public function __construct(
private UserService $userService
) {}

public function notifyAllUsers(string $message): void
{
$users = $this->userService->getAllUsers();

foreach ($users as $user) {
email("emails.notification", ["message" => $message], function ($mail) use ($user) {
$mail->to($user->email)->subject("Notification");
});
}
}
}

Services avec interface (Repository Pattern)​

Pour une architecture plus flexible, utilisez des interfaces :

app/Contracts/UserRepositoryInterface.php
namespace App\Contracts;

use App\Models\User;
use Bow\Support\Collection;

interface UserRepositoryInterface
{
public function all(): Collection;
public function find(int $id): ?User;
public function create(array $data): User;
public function update(int $id, array $data): bool;
public function delete(int $id): bool;
}
app/Services/UserRepository.php
namespace App\Services;

use App\Contracts\UserRepositoryInterface;
use App\Models\User;
use Bow\Support\Collection;

class UserRepository implements UserRepositoryInterface
{
public function all(): Collection
{
return User::all();
}

public function find(int $id): ?User
{
return User::find($id);
}

public function create(array $data): User
{
return User::create($data);
}

public function update(int $id, array $data): bool
{
return User::find($id)?->update($data) ?? false;
}

public function delete(int $id): bool
{
return User::destroy($id) > 0;
}
}

Enregistrer le binding dans Kernel​

app/Kernel.php
public function boot(): void
{
app()->bind(
\App\Contracts\UserRepositoryInterface::class,
\App\Services\UserRepository::class
);
}

Utilisation​

class UserController extends Controller
{
public function __construct(
private UserRepositoryInterface $users
) {}

public function index()
{
return view("users.index", ["users" => $this->users->all()]);
}
}

Tester un service​

Les services sont faciles Ă  tester unitairement :

tests/Services/UserServiceTest.php
namespace Tests\Services;

use App\Services\UserService;
use App\Models\User;
use PHPUnit\Framework\TestCase;

class UserServiceTest extends TestCase
{
private UserService $service;

protected function setUp(): void
{
parent::setUp();
$this->service = new UserService();
}

public function test_create_user(): void
{
$data = [
"name" => "John Doe",
"email" => "john@example.com",
"password" => "secret123"
];

$user = $this->service->create($data);

$this->assertInstanceOf(User::class, $user);
$this->assertEquals("John Doe", $user->name);
}

public function test_find_user_by_id(): void
{
$user = $this->service->findById(1);

$this->assertInstanceOf(User::class, $user);
}
}

Bonnes pratiques​

Recommandations
  • Un service = une responsabilitĂ© : Chaque service doit avoir un seul domaine de responsabilitĂ©.
  • Injection de dĂ©pendances : PrĂ©fĂ©rez l'injection via le constructeur pour les dĂ©pendances obligatoires.
  • Interfaces : Utilisez des interfaces pour les services complexes afin de faciliter les tests et les changements.
  • Nommage : Utilisez des noms explicites comme UserService, PaymentService, NotificationService.
  • Tests : Les services permettent d'isoler la logique mĂ©tier et de la tester indĂ©pendamment.

Il manque quelque chose ?

Si vous rencontrez des problèmes avec la documentation ou si vous avez des suggestions pour améliorer la documentation ou le projet en général, veuillez déposer une issue pour nous, ou envoyer un tweet mentionnant le compte Twitter @bowframework ou sur directement sur le github.