doctrine-extensions / DoctrineExtensions

Doctrine2 behavioral extensions, Translatable, Sluggable, Tree-NestedSet, Timestampable, Loggable, Sortable
MIT License
4.04k stars 1.27k forks source link

doctrine extensions translatable not working in symfony 6.4 with orm 3.1 #2867

Closed fecou closed 3 weeks ago

fecou commented 2 months ago

Environment

Symfony 6.4 Doctrine ORM 3.1

Package

"gedmo/doctrine-extensions": "^3.16",

$ composer show --latest gedmo/doctrine-extensions
name     : gedmo/doctrine-extensions
descrip. : Doctrine behavioral extensions
keywords : Blameable, behaviors, doctrine, extensions, gedmo, loggable, nestedset, odm, orm, sluggable, sortable, timestampable, translatable, tree, uploadable
versions : * v3.16.1
latest   : v3.16.1
type     : library
license  : MIT License (MIT) (OSI approved) https://spdx.org/licenses/MIT.html#licenseText
homepage : http://gediminasm.org/
source   : [git] https://github.com/doctrine-extensions/DoctrineExtensions.git e85560ed96f977b8c29428a99222cb2ef2f0e80d
dist     : [zip] https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/e85560ed96f977b8c29428a99222cb2ef2f0e80d e85560ed96f977b8c29428a99222cb2ef2f0e80d
path     : /media/donnees/web/kreatys/prev-and-co/vendor/gedmo/doctrine-extensions
names    : gedmo/doctrine-extensions

support
email : gediminas.morkevicius@gmail.com
issues : https://github.com/doctrine-extensions/DoctrineExtensions/issues
source : https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.16.1
wiki : https://github.com/Atlantic18/DoctrineExtensions/tree/main/doc

autoload
psr-4
Gedmo\ => src/

requires
behat/transliterator ^1.2
doctrine/collections ^1.2 || ^2.0
doctrine/common ^2.13 || ^3.0
doctrine/deprecations ^1.0
doctrine/event-manager ^1.2 || ^2.0
doctrine/persistence ^2.2 || ^3.0
php ^7.4 || ^8.0
psr/cache ^1 || ^2 || ^3
psr/clock ^1
symfony/cache ^5.4 || ^6.0 || ^7.0

requires (dev)
doctrine/annotations ^1.13 || ^2.0
doctrine/cache ^1.11 || ^2.0
doctrine/dbal ^3.2
doctrine/doctrine-bundle ^2.3
doctrine/mongodb-odm ^2.3
doctrine/orm ^2.14.0 || ^3.0
friendsofphp/php-cs-fixer ^3.14.0
nesbot/carbon ^2.71 || ^3.0
phpstan/phpstan ^1.11
phpstan/phpstan-doctrine ^1.4
phpstan/phpstan-phpunit ^1.4
phpunit/phpunit ^9.6
rector/rector ^1.1
symfony/console ^5.4 || ^6.0 || ^7.0
symfony/doctrine-bridge ^5.4 || ^6.0 || ^7.0
symfony/phpunit-bridge ^6.0 || ^7.0
symfony/uid ^5.4 || ^6.0 || ^7.0
symfony/yaml ^5.4 || ^6.0 || ^7.0

suggests
doctrine/mongodb-odm to use the extensions with the MongoDB ODM
doctrine/orm to use the extensions with the ORM

conflicts
doctrine/annotations <1.13 || >=3.0
doctrine/dbal <3.2 || >=4.0
doctrine/mongodb-odm <2.3 || >=3.0
doctrine/orm <2.14.0 || 2.16.0 || 2.16.1 || >=4.0

Doctrine packages

"doctrine/annotations": "^2.0", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.12", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.1",

$ composer show --latest 'doctrine/*'
Direct dependencies required in composer.json:
doctrine/annotations                2.0.2  2.0.2  Docblock Annotations Parser
doctrine/dbal                       3.8.4  4.1.1  Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.
doctrine/doctrine-bundle            2.12.0 2.13.0 Symfony DoctrineBundle
doctrine/doctrine-fixtures-bundle   3.6.1  3.6.1  Symfony DoctrineFixturesBundle
doctrine/doctrine-migrations-bundle 3.3.0  3.3.1  Symfony DoctrineMigrationsBundle
doctrine/orm                        3.1.2  3.2.2  Object-Relational-Mapper for PHP

Transitive dependencies not required in composer.json:
doctrine/cache                      2.2.0  2.2.0  PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memca...
doctrine/collections                2.2.2  2.2.2  PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.
doctrine/common                     3.4.4  3.4.4  PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on...
doctrine/data-fixtures              1.7.0  1.7.0  Data Fixtures for all Doctrine Object Managers
doctrine/deprecations               1.1.3  1.1.3  A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or ...
doctrine/event-manager              2.0.0  2.0.1  The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.
doctrine/inflector                  2.0.10 2.0.10 PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and sin...
doctrine/instantiator               2.0.0  2.0.0  A small, lightweight utility to instantiate objects in PHP without invoking their constructors
doctrine/lexer                      3.0.1  3.0.1  PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.
doctrine/migrations                 3.7.4  3.8.1  PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for ver...
doctrine/persistence                3.3.2  3.3.3  The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object ma...
doctrine/sql-formatter              1.2.0  1.4.1  a PHP SQL highlighting library

PHP version

$ php -v
PHP 8.2.17 (cli) (built: Mar 22 2024 11:33:03) (NTS)

Subject

I installed doctrine extensions and doctrine annotations. in config/doctrine.yaml : mappings : translatable: type: attribute is_bundle: false prefix: Gedmo\Translatable\Entity dir: '%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity' alias: GedmoTranslatable

I have a country entity : ` ... use Gedmo\Mapping\Annotation as Gedmo; use Gedmo\Translatable\Translatable;

[ORM\Entity(repositoryClass: CountryRepository::class)]

class Country implements Translatable {

#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;

#[Gedmo\Translatable]
#[ORM\Column(length: 255)]
private ?string $nom = null;

/**
 * @Gedmo\Locale
 * Used locale to override Translation listener`s locale
 * this is not a mapped field of entity metadata, just a simple property
 */
#[Gedmo\Locale]
private $locale;

... public function setTranslatableLocale($locale) { $this->locale = $locale; } ... `

I have also a fixture ... $repository = $em->getRepository('Gedmo\\Translatable\\Entity\\Translation'); ... $repository->translate($country, 'nom', 'fr', 'Allemagne') But if I do that, I get error : The translation listener could not be found

Also if I open a form switching locale in french. I write a name of the country in french. I save. After I switch to english. I write a name of the country in english. I save.

I expect that when something to be written in the table ext_translations, but nothing is written. The attribute #[Gedmo\Translatable] is ignored. My fields with this attribute aren't translatable.

ofourny commented 1 month ago

So had the same problem : The translation listener could not be found. I found a fix, sorry if it's not the right explanation but it works for me for now.

I have "php": ">=8.3", "doctrine/doctrine-bundle": "^2.13", "doctrine/orm": "^3.2.2" it was working in 6.4, but at equals other packages not working in 7.0 and after.

So this is my original configuration,

    Gedmo\Translatable\TranslatableListener:
        calls:
            - [setTranslatableLocale, ['%locale%']]     # Set locale, typically the current request's locale
            - [setDefaultLocale, ['%locale%']]          # Set default locale, fallback to the default app locale
        tags:
            - { name: doctrine.event_subscriber, connection: default }

I tried different things as it is deprecated to declared events like that but from this doc it should still works. https://symfony.com/doc/7.0/doctrine/events.html

So I tried with the new way using AsDoctrineListener, and made a listener like this ;

<?php

namespace App\Listeners;

use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Events;
use Gedmo\Translatable\TranslatableListener;

#[AsDoctrineListener(event: Events::postLoad, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::postPersist, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::preFlush, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::onFlush, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::loadClassMetadata, priority: 0, connection: 'default')]

class WatbookTranslatableListener extends TranslatableListener
{

}
mbabker commented 1 month ago

You don't need to subclass the listener, just update your config to match the updated documentation.

ofourny commented 1 month ago

Thanks I was looking for something like that, looked in the wrong place.

fecou commented 1 month ago

Thank you for your answers. It works much better know. For example I can know make fixtures with translations. But it still doesnt work in my edit form with a translatable field. When I look in the database, I have a new ext_translations, so it seems ok. But the language is "en_US" instead of "en". When I assign the language, I assign "en_US" but in the route I only have "en". Maybe it explains why if in my controller I put dd($request->getLocale()); it prints "en" instead of "en_US". There is an inconsistency there that doesnt allow me to use the doctrine extension with translatable. Any idea how to solve that ?

fecou commented 1 month ago

In services.yaml I have :

parameters: locale: 'fr_FR' # gedmo translatable

and

gedmo.mapping.driver.attribute:
    class: Gedmo\Mapping\Driver\AttributeReader
# Gedmo Translatable Extension Listener (see https://github.com/doctrine-extensions/DoctrineExtensions/blob/main/doc/frameworks/symfony.md#registering-extension-listeners)
gedmo.listener.translatable:
    class: Gedmo\Translatable\TranslatableListener
    tags:
        - { name: doctrine.event_listener, event: 'postLoad' }
        - { name: doctrine.event_listener, event: 'postPersist' }
        - { name: doctrine.event_listener, event: 'preFlush' }
        - { name: doctrine.event_listener, event: 'onFlush' }
        - { name: doctrine.event_listener, event: 'loadClassMetadata' }
    calls:
        # Uncomment the below call if using attributes, and comment the call for the annotation reader
        - [ setAnnotationReader, [ '@gedmo.mapping.driver.attribute' ] ]
        # The `annotation_reader` service was deprecated in Symfony 6.4 and removed in Symfony 7.0
        # - [ setAnnotationReader, [ '@annotation_reader' ] ]
        # The Kernel's `locale` parameter is used to configure the default locale for the extension
        - [ setDefaultLocale, [ '%locale%' ] ]

In doctrine.yaml, I have :

doctrine:orm:mappings: translatable: type: attribute alias: GedmoTranslatable prefix: Gedmo\Translatable\Entity dir: '%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Translatable/Entity'

And in translations.yaml, I have :

framework: default_locale: fr_FR enabled_locales: ['fr_FR', 'en_US'] translator: default_path: '%kernel.project_dir%/translations' fallbacks:

fecou commented 1 month ago

What is really strange is that in my entry in ext_translations table I change "fr_FR" to "fr", matching in fact with the $request->getLocale(), twig stills serving the field in english. So it's as if symfony wouldnt translate and ignores ext_translations entries.

fecou commented 3 weeks ago

Reading the documentation again, at https://github.com/doctrine-extensions/DoctrineExtensions/blob/main/doc/frameworks/symfony.md#configuring-extensions-via-event-subscribers, I realize I didn't add the event listener. So I have : `<?php

namespace App\EventListener;

// see https://github.com/doctrine-extensions/DoctrineExtensions/blob/main/doc/frameworks/symfony.md#configuring-extensions-via-event-subscribers

use Gedmo\Translatable\TranslatableListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

final class GedmoExtensionsEventSubscriber implements EventSubscriberInterface { public function __construct( private TranslatableListener $translatableListener, private ?AuthorizationCheckerInterface $authorizationChecker = null, private ?TokenStorageInterface $tokenStorage = null, ) {}

public static function getSubscribedEvents(): array
{
    return [
        KernelEvents::REQUEST => [
            ['configureTranslatableListener'], // Must run after the locale is configured
        ],
    ];
}

/**
 * Configures the translatable listener using the request locale
 */
public function configureTranslatableListener(RequestEvent $event): void
{
    $this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale());
}

}` But adding this listener gives me this error : Cannot autowire service "App\EventListener\GedmoExtensionsEventSubscriber": argument "$translatableListener" of method "__construct()" references class "Gedmo\Translatable\TranslatableListener" but no such service exists.
What is the problem there ? Thank you !

fecou commented 3 weeks ago

In services.yam, I added : `App\EventListener\GedmoExtensionsEventSubscriber: arguments:

It works know !