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.

app/Providers/AppServiceProvider.php
<?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.

app/Config/Providers.yaml
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í.

app/Controllers/ArticleController.php
<?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.

app/Services/ArticleRepository.php
<?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.

app/Services/ArticleRepositoryInterface.php
<?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.

app/Services/ArticleStore.php
<?php

namespace App\Services;

final class ArticleStore
{
    public function titles(): array
    {
        return ['First article', 'Second article'];
    }
}
Související dokumentaceVíce informací najdete v kapitole Dependency Injection a Service Providers v dokumentaci.

← Všechny ukázky