Open marvoh opened 2 years ago
Hi, thanks for reporting the issue. I haven't seen the code in the link on SO. Could you re-try with docs in here?
If that fails, could you send failing test case in PR to reproduce this error? I'll check what I can do to fix it then.
This is how my entities look like: Language.php
<?php
namespace App\Entity;
use App\Repository\LanguageRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
use App\Behaviour\TranslatableOverride;
use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
/**
* @ORM\Entity(repositoryClass=LanguageRepository::class)
* @ORM\HasLifecycleCallbacks()
* @ApiResource(
* normalizationContext={"groups" = {"read"}},
* denormalizationContext={"groups" = {"write"}}
* )
*/
class Language implements TranslatableInterface
{
use TranslatableTrait;
use TranslatableOverride;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"read"})
*/
private ?int $id = null;
/**
* @ORM\Column(type="datetime_immutable")
* @Groups({"read"})
*/
private $createdAt;
/**
* @ORM\Column(type="datetime_immutable", nullable=true)
* @Groups({"read"})
*/
private $updatedAt;
/**
* @ORM\Column(type="boolean")
* @Groups({"read", "write"})
*/
private $isEnabled;
/**
* @ORM\Column(type="string", length=5)
* @Groups({"read", "write"})
*/
private $code;
private $timezone = 'Africa/Nairobi';
/**
* @var Collection
* @Groups({"read"})
*/
protected $translations;
/**
* @var Collection
* @Groups({"write"})
*/
protected $newTranslations;
public function getId(): ?int
{
return $this->id;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeImmutable $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getIsEnabled(): ?bool
{
return $this->isEnabled;
}
public function setIsEnabled(bool $isEnabled): self
{
$this->isEnabled = $isEnabled;
return $this;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
/**
* @throws \Exception
* @ORM\PrePersist()
*/
public function beforeUpdate(){
$this->SetUpdatedAt(new \DateTimeImmutable('now', new \DateTimeZone($this->timezone)));
}
/**
* @throws \Exception
* @ORM\PrePersist()
*/
public function beforeSave(){
$this->setCreatedAt(new \DateTimeImmutable('now', new \DateTimeZone($this->timezone)));
}
}
LanguageTranslation.php
<?php
namespace App\Entity;
use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslationTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity
* @ApiResource()
*/
class LanguageTranslation implements TranslationInterface
{
use TranslationTrait;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
protected $name;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
}
I would then expect to add/edit some translations : We need to fill the newTranslations attribute inside the Language when we are sending to the api:
"newTranslations": {
"en": {
"description": "Firstname"
},
"fr": {
"description": "Prénom"
}
}
I created a new trait that I called TranslatableOverride. I imported it on directly on my Entity next to ORMBehaviors\Translatable\Translation:
<?php
declare(strict_types=1);
namespace App\Behaviour;
use Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
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();
}
}
}
Thanks for sharing. We'll need the test case in our /tests
and see the Github Actions failing.
Could you send it in pull-request?
I think this is an issue with api platform not knowing how to normalize the translated class.
I would try to insert this inside your translated class first. Maybe the API-Platform Objectnormalizer will be able to work with it.
public function __call($method, $arguments)
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}
If not - than you can create a custom Normalizer with php bin/console make:serializer:normalizer
and put this in:
public function supportsNormalization($data, $format = null): bool
{
return $data instanceof \Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
}
Now you fixed your error for sure.
I'm attempting to combine KNP translatable entities as per documentation with the API platform with some hints from this Stackoverflow post.
Whenever I make a request with a payload that looks like this for example:
{"isEnabled": true, "code": "en", "newTranslations": [ {"en": {"name":"English"}},{"de": {"name":"Englisch"}} ]}
, I get the error message below:"Could not denormalize object of type \"Knp\\DoctrineBehaviors\\Contract\\Entity\\TranslationInterface[]\", no supporting normalizer found."
I'm I supposed to implement my own normalizer?