Service providery
Provider registruje služby před uzavřením definic v containeru. U každé může určit transient, singleton nebo scoped životnost.
Registrace a vazby
AppServiceProvider implementuje ServiceProviderInterface. V register() deklaruje ArticleStore a ArticleRepository jako scoped služby; alias ArticleRepositoryInterface odkazuje na stejnou definici repository. Controller tak může požadovat rozhraní bez vlastní znalosti implementace.
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Services\ArticleRepository;
use App\Services\ArticleStore;
use App\Services\ArticleRepositoryInterface;
use Lemonade\Framework\Container\ContainerBuilderInterface;
use Lemonade\Framework\Core\ServiceProviderInterface;
final class AppServiceProvider implements ServiceProviderInterface
{
public function register(ContainerBuilderInterface $builder): void
{
$builder->scoped(ArticleStore::class, ArticleStore::class);
$builder->scoped(ArticleRepository::class, ArticleRepository::class);
$builder->alias(ArticleRepositoryInterface::class, ArticleRepository::class);
}
}Zapojení provideru
Providers.yaml zařadí AppServiceProvider do bootstrapu a manifest Config.yaml musí načítat modul Providers. Samotná existence třídy služby nezaregistruje. Metoda register() běží před zpracováním požadavku, proto v ní nepracujte s aktuálním requestem.
module: providers
config:
providers:
- App\Providers\AppServiceProviderŽivotnost a použití
Controller požaduje ArticleRepositoryInterface. Container použije alias z provideru a sestaví řetězec controller → repository → store; show() vrátí tituly přes Responses::json(). Zde je scoped životnost vhodná pro služby vyřešené v rámci requestu; singleton() sdílí instanci v root containeru a transient() vytváří novou při každém vyřešení.
<?php
namespace App\Controllers;
use App\Services\ArticleRepositoryInterface;
use Lemonade\Framework\Http\Response\Responses;
use Psr\Http\Message\ResponseInterface;
final readonly class ArticleController
{
public function __construct(
private ArticleRepositoryInterface $articles,
private Responses $responses,
) {}
public function show(): ResponseInterface
{
return $this->responses->json(['titles' => $this->articles->titles()]);
}
}ArticleRepository přijímá ArticleStore v konstruktoru a deleguje na něj čtení titulů. Proto jsou v provideru explicitně registrované obě konkrétní třídy; repository implementuje rozhraní z dalšího souboru.
<?php
namespace App\Services;
final class ArticleRepository implements ArticleRepositoryInterface
{
public function __construct(private readonly ArticleStore $store) {}
public function titles(): array
{
return $this->store->titles();
}
}Rozhraní určuje metodu titles(), kterou volá controller. Jeho alias v provideru musí ukazovat na implementaci ArticleRepository; bez této vazby nelze konstruktor controlleru sestavit.
<?php
namespace App\Services;
interface ArticleRepositoryInterface
{
public function titles(): array;
}ArticleStore zde vrací dvě hodnoty z paměti, aby šlo sledovat řetězec controller → repository → store bez databáze. Vlastní aplikace může úložiště nahradit, aniž by měnila rozhraní controlleru.
<?php
namespace App\Services;
final class ArticleStore
{
public function titles(): array
{
return ['First article', 'Second article'];
}
}