¿Cómo reutilizar el código con los paquetes de Symfony 5? Parte 4. Extensión del paquete de host

Hablemos sobre cómo detener el copiar y pegar entre proyectos y transferir el código a un paquete de plug-in reutilizable Symfony 5. Una serie de artículos que resumen mi experiencia con los paquetes se llevarán a cabo en la práctica desde la creación de un paquete mínimo y la refactorización de una aplicación de demostración para las pruebas y el ciclo de lanzamiento del paquete.


Al diseñar un paquete, debe pensar qué debe encapsularse dentro de él y qué debe ser accesible para el usuario. ¿Debería un paquete tener una funcionalidad fija o debería ser flexible y permitirse expandirse? Si se requiere flexibilidad, debe proporcionar algunos puntos de integración para expandir el paquete, su interfaz.


Intentemos proporcionar dichos puntos en nuestra aplicación de demostración. En este articulo:


  • Conexión de lógica personalizada a un paquete
  • Etiquetado
  • Pase del compilador
  • Servicios de configuración automática

Contenido de la serie

Si no completa constantemente el tutorial, descargue la aplicación desde el repositorio y cambie a la rama de integración 3 .


Instrucciones para instalar e iniciar el proyecto en un archivo README.md.
Encontrará la versión final del código para este artículo en la rama de 4 extensiones .


Tarea


El calendario tiene la función de exportar eventos a GoogleCalendar o iCalendar.
Nuestra tarea es hacer que nuestro paquete sea más flexible y agregar la capacidad para que los usuarios del paquete lo expandan con sus propios formatos de exportación en sus aplicaciones.


Por ejemplo, agregue la exportación a un archivo JSON. Empecemos.



¿Cómo se organiza la exportación de eventos?


Para comprender cómo agregar un nuevo formato, veamos cómo funciona EventExporter .


La lógica de exportación se implementa en el componente EventExporterque se encuentra en services/EventExporter. Ya lo hemos movido al paquete y corregido los nombres del espacio de nombres. Los archivos componentes principales son:


  • ExporterInterface simulando formato de exportación de eventos y
  • ExporterManager,

ExporterInterface.


— , ( Google Calendar), ( iCalendar). inline. EventController::export(), .


,


  • inline

2 AbtractInlineExporter AbstractFileExporter. ( ), , (ExportedFile).


2 — GoogleCalendarExporter ICalendarExporter.


, inline , , .



- JSON-.


:


git checkout 4-extend -- src/Service/EventExporter/JsonExporter.php

src/Service/EventExporter/JsonExporter.php:


<?php
declare(strict_types=1);

namespace App\Service\EventExporter;

use bravik\CalendarBundle\Entity\Event;
use bravik\CalendarBundle\Service\EventExporter\AbstractFileExporter;
use bravik\CalendarBundle\Service\EventExporter\ExportedFile;

/**
 * Generates a JSON file
 */
class JsonExporter extends AbstractFileExporter
{
    private const DATE_FORMAT = 'Y-m-d H:i:s';

    public function getName(): string
    {
        return ' JSON';
    }

    public function getType(): string
    {
        return 'json-file';
    }

    public function export(Event $event): ExportedFile
    {
        $data = [
            'id'            => $event->getId(),
            'title'         => $event->getTitle(),
            'description'   => $event->getDescription(),
            'venueName'     => $event->getVenueName(),
            'venueAddress'  => $event->getVenueAddress(),
            'startsAt'      => $event->getStartsAt()->format(self::DATE_FORMAT),
            'endsAt'        => $event->getEndsAt() ? $event->getEndsAt()->format(self::DATE_FORMAT) : null,
        ];

        return new ExportedFile('event.json', 'application/json', json_encode($data));
    }
}

?


DI- config/services.yaml . ExporterManager .


Symfony - . , , ExporterManager services.yaml . :


bravik\CalendarBundle\Service\EventExporter\ExporterManager:
    arguments:
        $exporters:
            - '@bravik\CalendarBundle\Service\EventExporter\Exporters\GoogleCalendarExporter'
            - '@bravik\CalendarBundle\Service\EventExporter\Exporters\ICalendarExporter'
            - 'App\Service\EventExporter\Exporters\JsonExporter'

: , , , .


, - , , ExporterManager - ? .


, . .

ExporterManager.


, , ExporterManager::registerExporter(). . , - registerExporter. DI-.



Symfony . — , DI- - . , ExporterManager.


JsonExporter services.yaml -:


App\Service\EventExporter\JsonExporter:
    tags: ['bravik.calendar.exporter']

services.yaml :


bravik\CalendarBundle\Service\EventExporter\Exporters\GoogleCalendarExporter:
    tags: ['bravik.calendar.exporter']
bravik\CalendarBundle\Service\EventExporter\Exporters\ICalendarExporter:
    tags: ['bravik.calendar.exporter']

, , , vendor.package.name.



Symfony 3.4 .


bravik.calendar.exporter ExporterManager. services.yaml:


bravik\CalendarBundle\Service\EventExporter\ExporterManager:
    arguments:
        $exporters: !tagged bravik.calendar.exporter

!tagged <tag-name> iterable , . typehint ExporterManager:


    public function __construct(iterable $exporters) {
    //...
    }

, .


, « » « JSON», JSON-.


( - , 4-extend .)


!tagged <tag-name>, , — , . .


Compiler Pass


services.yaml DI- . . Symfony , , PHP-. Symfony .

Compiler Pass. Symfony Compiler Pass.


, , ExporterManager.


bundles/CalendarBundle/src/DependencyInjection/Compiler/ExporterRegistrationPass.php, CompilerPassInterface:


namespace bravik\CalendarBundle\DependencyInjection\Compiler;

use bravik\CalendarBundle\Service\EventExporter\ExporterManager;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ExporterRegistrationPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->has(ExporterManager::class)) {
            return;
        }

        $exporterManagerDefinition = $container->findDefinition(ExporterManager::class);

        $taggedServices = $container->findTaggedServiceIds('bravik.calendar.exporter');

        $exporterReferences = [];
        foreach ($taggedServices as $id => $tags) {
            $exporterReferences[] = new Reference($id);
        }

        $exporterManagerDefinition->setArguments(['$exporters' => $exporterReferences]);
    }
}

Compiler Pass src/CalendarBundle. Bundle Bundle::build() :


    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new ExporterRegistrationPass());
    }

, pass , die("pass") process . pass — .


?


ExporterManager , DI-.
ExporterManager :


$exporterManagerDefinition = $container->findDefinition(ExporterManager::class);

Definition?


, - (Definition).


DI- , , «». Definition «»: , .

, - . , , , DI- .


, -. bravik.calendar.exporter.


$taggedServices = $container->findTaggedServiceIds('bravik.calendar.exporter');

, Reference , , ExporterManager, $exporters .


$exporterReferences = [];
foreach ($taggedServices as $id => $tags) {
    $exporterReferences[] = new Reference($id);
}

$exporterManagerDefinition->setArguments(['$exporters' => $exporterReferences]);

services.yaml ExporterManager:


bravik\CalendarBundle\Service\EventExporter\ExporterManager: ~

, .


(autoconfiguration)


services.yaml.


, 2 :


bravik\CalendarBundle\Service\EventExporter\Exporters\GoogleCalendarExporter:
    tags: ['bravik.calendar.exporter']
bravik\CalendarBundle\Service\EventExporter\Exporters\ICalendarExporter:
    tags: ['bravik.calendar.exporter']

:


App\Service\EventExporter\JsonExporter:
    tags: ['bravik.calendar.exporter']

, 3, 30 . .


, .

ExporterInterface.


№1. _instanceof


services.yaml :


_instanceof:
    # Apply tag to all ExporterInterface implementations
    bravik\CalendarBundle\Service\EventExporter\ExporterInterface:
        tags: ['bravik.calendar.exporter']

— !


App\Service\EventExporter\JsonExporter, .
, , . , .


№2. Symfony


, DependencyInjection/CalendarExtension load():


 public function load(array $configs, ContainerBuilder $container)
{
    $container->registerForAutoconfiguration(ExporterInterface::class)
        ->addTag('bravik.calendar.exporter');

   //...
}

Symfony , , .


, ExporterManager. :


# Register all EventExporter classes as injectable services
bravik\CalendarBundle\Service\EventExporter\:
    resource: '../src/Service/EventExporter/*'

.



Symfony, , . Symfony: , , Compiler Pass .


4-extend.


, , Symfony .


Puede leer algunos ejemplos aún más avanzados de redefinición de servicios y uso de alias de servicio para crear puntos de extensión:
( https://symfonycasts.com/screencast/symfony-bundle/override-service#play )


Otros artículos de la serie:


Parte 1. El paquete mínimo
Parte 2. Sacamos el código y las plantillas en el paquete
Parte 3. Integración del paquete con el host: plantillas, estilos, JS
Parte 4. Interfaz para expandir el paquete
Parte 5. Parámetros y configuración
Parte 6. Pruebas, microaplicación dentro del paquete
Parte 7 Ciclo de lanzamiento, instalación y actualización.


All Articles