doctrine-extensionsapi-platform.com

API Platform - Entity Translation, Doctrine Translatable


I'm trying to add KnpLabs Doctrine Behaviors - and precisely, the Translatable Behavior - on one of the entity in my API Platform project.

Here's what I've done so far :

    namespace App\Entity;

    use Doctrine\ORM\Mapping as ORM;
    use Knp\DoctrineBehaviors\Model as ORMBehaviors;
    use ApiPlatform\Core\Annotation\ApiResource;

    /**
    * @ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
    * @ApiResource
    */
    class Article
    {
        use ORMBehaviors\Translatable\Translation,
            ORMBehaviors\Timestampable\Timestampable
        ;

        /**
        * @ORM\Id
        * @ORM\GeneratedValue
        * @ORM\Column(type="integer")
        */
        protected $id;

        /**
         * Get id
         *
         * @return int
         */
         public function getId()
         {
             return $this->id;
         }
    }

And here's the Entity translation :

    namespace App\Entity;

    use Doctrine\ORM\Mapping as ORM;
    use Knp\DoctrineBehaviors\Model as ORMBehaviors;
    use ApiPlatform\Core\Annotation\ApiResource;
    use App\Traits as CustomTraits;

    /**
     * @ORM\Entity(repositoryClass="App\Repository\ArticleTranslationRepository")
     * @ApiResource
     */
     class ArticleTranslation
     {
          use ORMBehaviors\Translatable\Translatable;

          /**
           * @var string
           * 
           * @ORM\Column(name="someFieldToTranslate", type="string", length=255, nullable=true)
           */
           private $someFieldToTranslate;

           public function getSomeFieldToTranslate(){...}
           public function setSomeFieldToTranslate($someFieldToTranslate){...}
     }

Here's the basic "configuration" for getting Translatable Behavior working according to the doc.

Issues start when I try to update the DB schema : I got this error:

No identifier/primary key specified for Entity "App\Entity\ArticleTranslation". Every Entity must have an identifier/primary key in . (which is being imported from "/Sites/bookshop-api/config/routes/api_platform.yaml"). Make sure there is a
loader supporting the "api_platform" type.

However in Translatable Traits, there's already an ID and documentation precise that Translation Entity should only have fields we want to translate...

Anyway, I've put an ID to this ArtcleTranslation Entity to get rid of the error :

    namespace App\Entity;

    use Doctrine\ORM\Mapping as ORM;
    use Knp\DoctrineBehaviors\Model as ORMBehaviors;
    use ApiPlatform\Core\Annotation\ApiResource;
    use App\Traits as CustomTraits;

    /**
     * @ORM\Entity(repositoryClass="App\Repository\ArticleTranslationRepository")
     * @ApiResource
     */
     class ArticleTranslation
     {
          use ORMBehaviors\Translatable\Translatable;

          /**
           * @ORM\Id
           * @ORM\GeneratedValue
           * @ORM\Column(type="integer")
           */
           protected $id;


          /**
           * @var string
           * 
           * @ORM\Column(name="content", type="string", length=255, nullable=true)
           */
           private $content;

           public function getContent(){...}
           public function setContent($someContent){...}
     }

From here, no error when I update the DB schema. Perfect ! Now I can take a look at the Swagger Documentation :

enter image description here

Everything looks fine ! But when I do take a look at the DB :

In Article table :

In ArticleTranslation table :

I guess it must be linked but in the swagger POST Tab, the model is different too.

enter image description here article_translation model

I only tried /GET and /POST method on both entities, they're working (I can see it in DB) but no relation between the 2 of them.

I hope my post is not too long but I tried to be the more specific !

Thanks in advance


Solution

  • I did another answer as the first question was to resolve a mistake and not to explain how I integrated Knp Labs Doctrine Translations with Api Platform.

    Summary :

    We have an entity Article which has some fields translated inside an ArticleTranslation. We want to retrieve the entity through Api platform with its translations and we want to add or update translations through the api.

    What I did :

    1 - Article entity has a use ORMBehaviors\Translatable\Translatable. If we look inside of this trait, it has 2 attributes : $translations and $newTranslations. We need to expose these attributes inside the Article entity :

    class Article {
        use ORMBehaviors\Translatable\Translatable;
    
        protected $translations;
        protected $newTranslations;
    }
    

    Now we have a new attribute translations which is automatically filled from ArticleTranslation when we are getting some Article from the api

    2 - Now we want to add/edit some translations : We need to fill the newTranslations attribute inside the Article when we are sending to the api:

    "newTranslations": {
        "en": {
          "description": "Firstname"
        },
        "fr": {
          "description": "Prénom"
        }
    }
    

    Now we are receiving the new translations into the api but it's not persisted because we have to call the function mergeNewTranslations(). This function just take all translations inside the attribute $newTranslations and merge it with the $translations attribute in order to persist it.

    3 - I created a new trait that I called TranslatableOverride. I imported it on directly on my Entity next to ORMBehaviors\Translatable\Translation:

    trait TranslatableOverride
    {
    
        /**
         * Set collection of new translations.
         *
         * @return ArrayCollection
         */
        public function setNewTranslations($newTranslations)
        {
            if ($newTranslations) {
                foreach ($newTranslations as $locale => $translations) {
                    foreach ($translations as $key => $value) {
                        $tr = $this->translate($locale);
                        $setter = 'set' . ucfirst($key);
                        if (method_exists($tr, $setter)) {
                            $tr->{$setter}($value);
                        }
                    }
                }
    
                $this->mergeNewTranslations();
            }
        }
    }
    

    I'm not sure if it's pretty but it works like a charm with api-platform.

    I didn't think about getting only one translation at a time. For the moment, I retrieve my entities with the whole bunch of translations which is definitely not efficient. I will add an override for the getTranslations in my override trait I guess.