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
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 EventExporter
que 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 yExporterManager
,
ExporterInterface
.
— , ( Google Calendar), ( iCalendar). inline. EventController::export()
, .
,
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;
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.