1

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 :

  • no "local" field
  • no "empty" field

In ArticleTranslation table :

  • no "translatable_id" field
  • no "currentLocal"
  • no "defaultLocal"

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

skytorner
  • 405
  • 2
  • 8
  • 19
  • 1
    Were you able to find the solution? Im facing the same problem. Thanks for any help – Tahir Jun 22 '18 at 08:27
  • @Tahir still not found a solution.. – skytorner Jun 25 '18 at 08:26
  • In my case, subscriber was not registered in the bundle list. We need to register subscriber bundle as mentioned in the [documentation](https://github.com/KnpLabs/DoctrineBehaviors#subscribers). After fixing this, I got all columns in db. Although im stuck at how can I send/get translations using default endpoints. – Tahir Jun 25 '18 at 08:39

2 Answers2

2

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.

doydoy44
  • 5,720
  • 4
  • 29
  • 45
Maël
  • 259
  • 1
  • 11
  • Thanks @Maël for taking the time to answer this post, I'll try your solution when i can ! – skytorner Sep 04 '18 at 17:14
  • Hi, keep getting `"Could not denormalize object of type \"Knp\\DoctrineBehaviors\\Contract\\Entity\\TranslationInterface[]\", no supporting normalizer found."` when I try to persist newTranslations according to your answer. Would you have an idea what's wrong? – Masinde Muliro Sep 30 '21 at 11:47
0

I think you did a mistake. You have an entity Article which should be the translatable and you want to translate some fields which will be the translations?

So you should make the opposite, put the use ORMBehaviors\Translatable\Translatableon the Article and the use ORMBehaviors\Translatable\Translation on the ArticleTranslation

The fix for your Article entity :

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\Translatable,
        ORMBehaviors\Timestampable\Timestampable
    ;

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

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

and the fix for your ArticleTranslation entity :

    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\Translation;

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

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

Let me know if everything is alright now.

Maël
  • 259
  • 1
  • 11
  • thanks for the reply! im in an another project now but i will take a look asap ! By the way, have u been able to use [knplabsBehaviour](https://github.com/KnpLabs/DoctrineBehaviors#translatable) with Api Platform ? – skytorner Jul 06 '18 at 11:32
  • Yes it works. I've just a trouble on `POST` and `PUT`. I think the `newTranslations` field isn't process with api-platform. But I'm gonna find a solution/trick. – Maël Jul 06 '18 at 11:39
  • Have you found a solution with POST/PUT? Is there any other way to integrate Entity translation in ApiPlatform? I'm looking for solution where entity (e.g. Article) on GET will give either content with possibility to select translation via locale key or by passing locale and in response receive content in language specified by locale. Haven't found yet best solution :( – xoxn-- 1'w3k4n Aug 22 '18 at 06:25
  • I made a new complete answer. – Maël Aug 29 '18 at 06:35