Locastic / ApiPlatformTranslationBundle

Translation bundle for ApiPlatform based on Sylius translation
MIT License
85 stars 28 forks source link

Uncaught Error: Return value of Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable::getTranslations() must implement interface Doctrine\Common\Collections\Collection, null returned #37

Closed danplaton closed 3 years ago

danplaton commented 3 years ago

hello. I have a fresh installation of the api platform with locastic translation bundle. After setting up the Entity and Translation entity when I do a POST request to create a new book I get this error: Uncaught Error: Return value of Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable::getTranslations() must implement interface Doctrine\Common\Collections\Collection, null returned

My Book.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\Repository\BookRepository;
use Doctrine\Common\Collections\Collection;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;

/**
 * @ApiResource(
 *  attributes={"filters"={"translation.groups"}},
 *  normalizationContext={"groups"={"book:read"}, "swagger_definition_name"="Read"},
 *  denormalizationContext={"groups"={"book:write"}, "swagger_definition_name"="Write"},
 * itemOperations={
 *    "get",
 *    "put"={
 *      "normalization_context"={"groups"={"translations"}}
 *    }
 *  },
 *  collectionOperations={
 *    "get",
 *    "post"={
 *      "normalization_context"={"groups"={"translations"}}
 *    },
 *  }
 * )
 * @ORM\Entity(repositoryClass=BookRepository::class)
 */
class Book extends AbstractTranslatable
{
  /**
   * @ORM\Id
   * @ORM\GeneratedValue
   * @ORM\Column(type="integer")
   * 
   * @Groups({"book:read"})
   */
  private $id;

  /**
   * @ORM\Column(type="string", length=255)
   * 
   * @Groups({"book:read"})
   */
  private $title;

  /**
   * @ORM\Column(type="text")
   * 
   * @Groups({"book:read"})
   */
  private $description;

  /**
   * @ORM\OneToMany(targetEntity="BookTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"PERSIST"}, orphanRemoval=true)
   *
   * @Groups({"book:write"})
   * @Groups({"translations"})
   */
  protected $translations;

  public function __construct()
  {
    $this->translations= new ArrayCollection();
  }

  public function getId(): ?int
  {
    return $this->id;
  }

  public function setTitle(string $title)
  {
    $this->getTranslation()->setTitle($title);
  }

  public function getTitle(): ?string
  {
    return $this->getTranslation()->getTitle();
  }

  public function setDescription(string $description)
  {
    $this->getTranslation()->setDescription($description);
  }

  public function getDescription(): ?string
  {
    return $this->getTranslation()->getDescription();
  }

  protected function createTranslation(): TranslationInterface
  {
    return new BookTranslation();
  }
}

My BookTranslation.php

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;

/**
 * @ORM\Entity()
 */
class BookTranslation extends AbstractTranslation
{
  /**
   * @ORM\Id
   * @ORM\GeneratedValue
   * @ORM\Column(type="integer")
   */
  private $id;

  /**
   * @ORM\ManyToOne(targetEntity="Book", inversedBy="translations")
   */
  protected $translatable;

  /**
   * @ORM\Column(type="string")
   * 
   * @Groups({"book:read", "book:write", "translations"})
   */
  private $title;

  /**
   * @ORM\Column(type="text")
   * 
   * @Groups({"book:read", "book:write", "translations"})
   */
  private $description;

  /**
   * @ORM\Column(type="string")
   *
   * @Groups({"book:write", "translations"})
   */
  protected $locale;

  public function setTitle(string $title): void
  {
    $this->title = $title;
  }

  public function getTitle(): ?string
  {
    return $this->title;
  }

  public function setDescription(string $description): void
  {
    $this->description = $description;
  }

  public function getDescription(): ?string
  {
    return $this->description;
  }
}

PHP version: PHP 7.4.9 (cli) (built: Aug 4 2020 11:52:41) ( ZTS Visual C++ 2017 x64 ) MySQL version: MySQL 8.0.21

Why does this is caused?

paullla commented 3 years ago

Hi @danplaton !

You need to call parent constructor in Book entity, because that is where translation collection is initialised.

And something not related to this issue I noticed: there is no need to map title and description in Book to the database. Those fields will be saved in BookTranslation entity with locale it belongs to. Book only has this two fields to show them translated in response.

I hope this helps. Good luck!