This course is still being released! Check back later for more chapters.
Refactorización de `ObjectTranslator`
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeNuestro ObjectTranslator está creciendo un poco, y nuestro método translationsForse ha hinchado. Para hacer frente a esto, crearemos un nuevo servicio que se encargará de todas las tareas relacionadas con Doctrine. Esto implica ocuparse de la clase Translation y de todas las demás operaciones Doctrine que estamos utilizando.
En primer lugar, crea la nueva clase en el directorio src de nuestro bundle, llámala TranslatableMappingManager. Hay que reconocer que el término "gestor" es un poco rebuscado cuando no estoy seguro de cómo llamar a algo, pero por ahora servirá. Marcaremos esta clase como final y @internal.
| // ... lines 1 - 8 | |
| /** | |
| * @internal | |
| */ | |
| final class TranslatableMappingManager | |
| { | |
| // ... lines 14 - 69 | |
| } |
De este modo, si más adelante se nos ocurre un nombre mejor, podremos renombrarla fácilmente.
translatableTypeFor Método
En primer lugar, crea un método llamado public function translatableTypeFor. Este método tomará un objeto como parámetro, object $object y devolverá una cadena.
En ObjectTranslator, busca translationsFor y copia la lógica relacionada con el tipo. Pégala en nuestro nuevo método translatableTypeFor, e importa la clase necesaria. En la parte inferior, return $type:
| // ... lines 1 - 11 | |
| final class TranslatableMappingManager | |
| { | |
| // ... lines 14 - 19 | |
| public function translatableTypeFor(object $object): string | |
| { | |
| $class = new \ReflectionClass($object); | |
| $type = $class->getAttributes(Translatable::class)[0]?->newInstance()->name ?? null; | |
| if (!$type) { | |
| throw new \LogicException(sprintf('Class "%s" is not translatable.', $object::class)); | |
| } | |
| return $type; | |
| } | |
| // ... lines 31 - 69 | |
| } |
El Constructor
A continuación, crea un constructor para esta clase... y ábrelo para dejarnos espacio. Ahora copia las propiedades relacionadas con la Doctrine del constructor de ObjectTranslator($translationClass y $doctrine) y pégalas en nuestro nuevo constructor:
| // ... lines 1 - 11 | |
| final class TranslatableMappingManager | |
| { | |
| public function __construct( | |
| private string $translationClass, | |
| private ManagerRegistry $doctrine, | |
| ) { | |
| } | |
| // ... lines 19 - 69 | |
| } |
idFor Método
Esto allana el camino para nuestro siguiente método, public function idFor(), que una vez más tomará un objeto y devolverá una cadena. Para ello, volveremos atranslationsFor en ObjectTranslator, copiaremos la lógica para obtener el ID y la pegaremos en nuestro nuevo método. Al final... return $id. PhpStorm me dice que podemos inlinear el retorno. Así que, return reset($id) y elimina el retorno a continuación:
| // ... lines 1 - 11 | |
| final class TranslatableMappingManager | |
| { | |
| // ... lines 14 - 31 | |
| public function idFor(object $object): string | |
| { | |
| $om = $this->doctrine->getManagerForClass($object::class); | |
| if (!$om) { | |
| throw new \LogicException(sprintf('No object manager found for class "%s".', $object::class)); | |
| } | |
| $id = $om->getClassMetadata($object::class) | |
| ->getIdentifierValues($object) | |
| ; | |
| if (1 !== count($id)) { | |
| throw new \LogicException(sprintf('Class "%s" must have a single identifier to be translatable.', $object::class)); | |
| } | |
| $id = reset($id); | |
| return $id; | |
| } | |
| // ... lines 52 - 69 | |
| } |
translationsFor Método
Por último, crea otro método: public function translationsFor(). Este aceptará tres parámetros: string $locale, string $type, string $idy devolverá un array. Dentro de este método, coge la lógica que obtiene y normaliza las traducciones en ObjectTranslator::translationsFor(), y pégala aquí:
| // ... lines 1 - 11 | |
| final class TranslatableMappingManager | |
| { | |
| // ... lines 14 - 52 | |
| public function translationsFor(string $locale, string $type, string $id): array | |
| { | |
| /** @var Translation[] $translations */ | |
| $translations = $this->doctrine->getRepository($this->translationClass)->findBy([ | |
| 'locale' => $locale, | |
| 'objectType' => $type, | |
| 'objectId' => $id, | |
| ]); | |
| $translationValues = []; | |
| foreach ($translations as $translation) { | |
| $translationValues[$translation->field] = $translation->value; | |
| } | |
| return $translationValues; | |
| } | |
| } |
Usando TranslatableMappingManager en ObjectTranslator
Ahora que tenemos nuestra nueva clase, inyéctala en ObjectTranslator. En el constructor, sustituye las dos propiedades Doctrine porprivate TranslatableMappingManager $mappingManager:
| // ... lines 1 - 10 | |
| final class ObjectTranslator | |
| { | |
| // ... lines 13 - 15 | |
| public function __construct( | |
| private LocaleAwareInterface $localeAware, | |
| private string $defaultLocale, | |
| private TranslatableMappingManager $mappingManager, | |
| ?CacheInterface $cache = null, | |
| private ?int $cacheTtl = null, | |
| ) { | |
| // ... lines 23 - 24 | |
| } | |
| // ... lines 26 - 64 | |
| } |
En primer lugar, sustituye la lógica de obtención de tipos por$type = $this->mappingManager->translatableTypeFor($object):
| // ... lines 1 - 10 | |
| final class ObjectTranslator | |
| { | |
| // ... lines 13 - 44 | |
| private function translationsFor(object $object, string $locale): array | |
| { | |
| $type = $this->mappingManager->translatableTypeFor($object); | |
| // ... lines 48 - 63 | |
| } | |
| } |
A continuación, sustituye el código Doctrine para obtener el id por$id = $this->mappingManager->idFor($object):
| // ... lines 1 - 10 | |
| final class ObjectTranslator | |
| { | |
| // ... lines 13 - 44 | |
| private function translationsFor(object $object, string $locale): array | |
| { | |
| // ... line 47 | |
| $id = $this->mappingManager->idFor($object); | |
| // ... lines 49 - 63 | |
| } | |
| } |
Por último, en la llamada a la caché, sustituye la lógica de Doctrine para obtener y normalizar las traducciones porreturn $this->mappingManager->translationsFor($locale, $type, $id):
| // ... lines 1 - 10 | |
| final class ObjectTranslator | |
| { | |
| // ... lines 13 - 44 | |
| private function translationsFor(object $object, string $locale): array | |
| { | |
| // ... lines 47 - 49 | |
| return $this->cache->get( | |
| // ... line 51 | |
| function(ItemInterface $item) use ($locale, $type, $id) { | |
| // ... lines 53 - 60 | |
| return $this->mappingManager->translationsFor($locale, $type, $id); | |
| } | |
| ); | |
| } | |
| } |
¡Muy bien!
Actualización de las definiciones de servicio
Hemos refactorizado el código, pero necesitamos actualizar nuestras definiciones de servicio.
En services.php, añade nuestro nuevo servicio con->set('., para convertirlo en un servicio oculto,symfonycasts.object_translator.mapping_manager. Clase: TranslatableMappingManager. Para los args, utiliza->args([]) y expande. En la definición ObjectTranslator anterior, corta los argumentos relacionados con Doctrine y pégalos como argumentos de nuestro nuevo servicio:
| // ... lines 1 - 8 | |
| return static function (ContainerConfigurator $container): void { | |
| $container->services() | |
| // ... lines 11 - 20 | |
| ->set('.symfonycasts.object_translator.mapping_manager', TranslatableMappingManager::class) | |
| ->args([ | |
| abstract_arg('translation class'), | |
| service('doctrine'), | |
| ]) | |
| // ... lines 26 - 28 | |
| ; | |
| }; |
En la definición ObjectTranslator, añade un nuevo argumento:service('.symfonycasts.object_translator.mapping_manager'):
| // ... lines 1 - 8 | |
| return static function (ContainerConfigurator $container): void { | |
| $container->services() | |
| ->set('symfonycasts.object_translator', ObjectTranslator::class) | |
| ->args([ | |
| // ... lines 13 - 14 | |
| service('.symfonycasts.object_translator.mapping_manager'), | |
| ]) | |
| // ... lines 17 - 28 | |
| ; | |
| }; |
¡Me encanta el autocompletado!
Ajustar el proceso de configuración
Por último, ya que hemos ajustado los argumentos, tenemos que actualizar el procesamiento de la configuración. Éste es el último paso, ¡lo juro!
Abre ObjectTranslationBundle y busca nuestro método loadExtension().
En nuestra configuración de caché, estos índices de argumentos se han desplazado. Echa un vistazo al constructor ObjectTranslator para averiguar los nuevos índices. 0, 1, 2, 3, 4. En loadExtension() actualiza estas dos llamadas a setArgument()para que utilicen 3, 4 en lugar de 5, 6:
| // ... lines 1 - 12 | |
| final class ObjectTranslationBundle extends AbstractBundle | |
| { | |
| // ... lines 15 - 54 | |
| public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void | |
| { | |
| // ... lines 57 - 60 | |
| if ($config['cache']['enabled']) { | |
| $objectTranslatorDef->setArgument(3, new Reference($config['cache']['pool'])); | |
| $objectTranslatorDef->setArgument(4, $config['cache']['ttl']); | |
| } | |
| // ... lines 65 - 67 | |
| } | |
| } |
Hay que mover el translation_class a nuestro nuevo servicio. Por tanto, escribe$builder->getDefinition('.symfonycasts.object_translator.mapping_manager'). Copia la llamada setArgument anterior y pégala aquí. Para el índice, comprueba el constructor deTranslatableMappingManager. Es 0, así que vuelve aloadExtension(), cambia el índice a 0 y elimina esta variable pícara de arriba:
| // ... lines 1 - 12 | |
| final class ObjectTranslationBundle extends AbstractBundle | |
| { | |
| // ... lines 15 - 54 | |
| public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void | |
| { | |
| // ... lines 57 - 65 | |
| $builder->getDefinition('.symfonycasts.object_translator.mapping_manager') | |
| ->setArgument(0, $config['translation_class']); | |
| } | |
| } |
¡Uf! Eso es todo en cuanto a la refactorización. Vamos a probarlo.
En el navegador, actualiza la página de inicio en francés... y... ¡Genial! ¡Las traducciones siguen funcionando!
A continuación, nos sumergiremos en los comandos de la consola de bundle, empezando por el comando de calentar la caché de traducción.