Locastic / ApiPlatformTranslationBundle

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

No locale has been set and current locale is undefined. #28

Closed scargoll closed 3 years ago

scargoll commented 3 years ago

Hello,

All is working good for view and edit operations but i can not create, i have this error : No locale has been set and current locale is undefined.

My entity

<?php

declare(strict_types=1);

namespace App\Entity;

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

/**
 * @ApiResource(
 *      attributes={
 *          "security"="is_granted('ROLE_USER')",
 *          "security_message"="You must be connected !",
 *          "filters"={"translation.groups"},
 *      },
 *       itemOperations={
 *          "get"={
 *              "method"="GET",
 *              "normalization_context"={"groups":{"application:read", "translations"}}
 *          },
 *          "put"={
 *              "method"="PUT",
 *              "normalization_context"={"groups":{"translations"}}
 *          }
 *      },
 *      collectionOperations={
 *          "applications_by_username"={
 *              "method"="GET",
 *              "name"="applications_by_username",
 *              "controller"=GetApplicationsByUsernameController::class
 *          },
 *          "post"={
 *              "method"="POST",
 *              "normalization_context"={"groups":{"translations"}}
 *          }
 *      })
 * @ORM\Entity(repositoryClass="App\Repository\ApplicationRepository")
 * @ORM\HasLifecycleCallbacks
 *
 */
class Application extends AbstractTranslatable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     *
     *  @var int
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"application:read","translations"})
     *
     *  @var string
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\ApplicationTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"persist"}, orphanRemoval=true)
     * @Groups({"translations"})
     *
     * @var Collection
     */
    protected $translations;

    public function __construct()
    {
        parent::__construct();
    }

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

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

    public function setName(string $name): void
    {
        $this->getTranslation()->setName($name);
    } 

    public function createTranslation(): TranslationInterface
    {
        return new ApplicationTranslation();
    }

    public function __toString()
    {
        return $this->getName();
    }
}

My translation entity

<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
use Locastic\ApiPlatformTranslationBundle\Model\TranslatableInterface;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass="App\Repository\ApplicationTranslationRepository")
 */
class ApplicationTranslation extends AbstractTranslation
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     *
     * @var int
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"application:read","translations"})
     *
     * @var string
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"application:read","translations"})
     *
     * @var null|string
     */
    protected $locale;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Application", inversedBy="translations")
     *
     * @var TranslatableInterface
     */
    protected $translatable;

    public function __construct()
    {
        parent::__construct();
    }

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function __toString()
    {
        return $this->getName();
}

translation.yaml

framework:
    default_locale: fr
    translator:
        default_path: '%kernel.project_dir%/translations'
        fallbacks:
            - fr

Stacktrace RuntimeException: No locale has been set and current locale is undefined.

at vendor/locastic/api-platform-translation-bundle/src/Model/TranslatableTrait.php:65 at Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable->getTranslation() (src/Entity/Application.php:192) at App\Entity\Application->getName() (vendor/symfony/property-access/PropertyAccessor.php:405) at Symfony\Component\PropertyAccess\PropertyAccessor->readProperty(array(object(Application)), 'name', false) (vendor/symfony/property-access/PropertyAccessor.php:329) at Symfony\Component\PropertyAccess\PropertyAccessor->readPropertiesUntil(array(object(Application)), object(PropertyPath), 1, true) (vendor/symfony/property-access/PropertyAccessor.php:231) at Symfony\Component\PropertyAccess\PropertyAccessor->isReadable(object(Application), object(PropertyPath)) (vendor/easycorp/easyadmin-bundle/src/Field/Configurator/CommonPreConfigurator.php:89) at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\CommonPreConfigurator->buildValueOption(object(FieldDto), object(EntityDto)) (vendor/easycorp/easyadmin-bundle/src/Field/Configurator/CommonPreConfigurator.php:41) at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\CommonPreConfigurator->configure(object(FieldDto), object(EntityDto), object(AdminContext)) (vendor/easycorp/easyadmin-bundle/src/Factory/FieldFactory.php:87) at EasyCorp\Bundle\EasyAdminBundle\Factory\FieldFactory->processFields(object(EntityDto), object(FieldCollection)) (vendor/easycorp/easyadmin-bundle/src/Factory/EntityFactory.php:43) at EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory->processFields(object(EntityDto), object(FieldCollection)) (vendor/easycorp/easyadmin-bundle/src/Controller/AbstractCrudController.php:276) at EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController->new(object(AdminContext)) (vendor/symfony/http-kernel/HttpKernel.php:157) at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1) (vendor/symfony/http-kernel/HttpKernel.php:79) at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true) (vendor/symfony/http-kernel/Kernel.php:196) at Symfony\Component\HttpKernel\Kernel->handle(object(Request)) (public/index.php:25)

Any idea please ?

paullla commented 3 years ago

Hi @scargoll,

I found a few things that need to be fixed:

  1. You added __construct that only calls parent constructor in both Application and ApplicationTranslation. This is what made the locale error appear. You can remove them.
  2. You should add denormalizationContext group for POST operation.
  3. The name field in Application is only virtual field and should not be mapped in the database.
  4. ApplicationTranslation doesn't need to be a resource, so you can remove @ApiResource from it.
  5. Make sure you are using latest version of bundle

Here are fixed entities:

<?php

declare(strict_types=1);

namespace App\Entity;

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

/**
 * @ApiResource(
 *      attributes={
 *          "security"="is_granted('ROLE_USER')",
 *          "security_message"="You must be connected !",
 *          "filters"={"translation.groups"},
 *      },
 *       itemOperations={
 *          "get"={
 *              "method"="GET",
 *              "normalization_context"={"groups":{"application:read", "translations"}}
 *          },
 *          "put"={
 *              "method"="PUT",
 *              "normalization_context"={"groups":{"translations"}}
 *          }
 *      },
 *      collectionOperations={
 *          "applications_by_username"={
 *              "method"="GET",
 *              "name"="applications_by_username",
 *              "controller"=GetApplicationsByUsernameController::class
 *          },
 *          "post"={
 *              "method"="POST",
 *              "normalization_context"={"groups":{"translations"}},
 *              "denormalization_context"={"groups":{"application:write"}}
 *          }
 *      })
 * @ORM\Entity(repositoryClass="App\Repository\ApplicationRepository")
 * @ORM\HasLifecycleCallbacks
 *
 */
class Application extends AbstractTranslatable
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     *
     *  @var int
     */
    private $id;

    /**
     * @Groups({"application:read","translations"})
     *
     *  @var string
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\ApplicationTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"persist"}, orphanRemoval=true)
     * @Groups({"translations", "application:write"})
     *
     * @var Collection
     */
    protected $translations;

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

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

    public function setName(string $name): void
    {
        $this->getTranslation()->setName($name);
    }

    public function createTranslation(): TranslationInterface
    {
        return new ApplicationTranslation();
    }

    public function __toString()
    {
        return $this->getName();
    }
}
<?php

declare(strict_types=1);

namespace App\Entity;

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

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

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"application:read","translations", "application:write"})
     *
     * @var string
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"application:read","translations", "application:write"})
     *
     * @var null|string
     */
    protected $locale;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Application", inversedBy="translations")
     *
     * @var TranslatableInterface
     */
    protected $translatable;

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

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function __toString()
    {
        return $this->getName();
    }
}

Let me know if this helps :)