locastic / apiplatformtranslationbundle Goto Github PK
View Code? Open in Web Editor NEWTranslation bundle for ApiPlatform based on Sylius translation
License: MIT License
Translation bundle for ApiPlatform based on Sylius translation
License: MIT License
Hey there.
I am trying to setup translations with nested relationships, when posting a new entity I get the following error: Could not denormalize object of type \"Locastic\\ApiPlatformTranslationBundle\\Model\\TranslationInterface[]\", no supporting normalizer found
Do you have a solution for this?
Hello ,
my problem it's with the validation of the fields of my entity.
Example:
in my class
/**
* @ORM \ Entity ()
* @UniqueEntity (fields = {"locale", "title"})
**/
class CategoryTranslation extends AbstractTranslation
i used the UniqueEntity annotation to prevent using the same word more two times with same local value, with my POST requests there are not problem but when I try to use PUT requests
the bundle does not UPDATE the data, it removes the records to insert new values so I get this error :
"detail": "translations [fr] .locale: This value is already used."
when I send this json to my endpoint:
{
"translations": [
{
"title": "natures",
"locale": "fr"
},
{
"title": "nature",
"locale": "en"
}
]
}
someone can give any hints ?
thank you in advance
Seems that SearchFilter
doesn't work out of the box. Do you have any suggestion on how to make it work? Ideally it should be as easy as this:
/products?locale=es&name=foo
where name
is a translated field.
Hello
I have a big problem
cant detect Translation object edit event because
run: insert event
why insert if already exist?
can i change this update?
symfony: 5.4.3
api-platform: 2.6.8
request body
{
"@context": "/contexts/EmailTemplate",
"@id": "/email_templates/1",
"@type": "EmailTemplate",
"id": 1,
"key": "confirmation",
"module": "admin",
"translations": {
"hu": {
"@type": "EmailTemplateTranslation",
"@id": "_:3955",
"id": 29,
"locale": "hu",
"name": "email_template.confirmation.name",
"subject": "email_template.confirmation.subject",
"content": "<p>email_template.confirmation.content33333</p>",
"tags": []
},
"en": {
"@type": "EmailTemplateTranslation",
"@id": "_:3957",
"id": 30,
"locale": "en",
"name": "email_template.confirmation.name",
"subject": "email_template.confirmation.subject",
"content": "email_template.confirmation.content",
"tags": []
}
},
"name": "email_template.confirmation.name",
"subject": "email_template.confirmation.subject",
"content": "<p>email_template.confirmation.content22222</p>"
}
I'm looking for a programmatic example in order to add new translations e.g. using a command, but I couldn't figure out how to get started. I'd be glad if you could give me a hint on how to start or extend the documentation with an example. Thanks!
Hello,
I've just updated my installation with doctrine3 and now i've got this error
Argument 1 passed to Locastic\ApiPlatformTranslationBundle\EventListener\AssignLocaleListener::postLoad() must be an instance of Doctrine\Common\Persistence\Event\LifecycleEventArgs, instance of Doctrine\ORM\Event\LifecycleEventArgs given, called in /var/www/html/api/vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php on line 58",
Any idea please ?
I did everything according to this instruction
https://github.com/Locastic/ApiPlatformTranslationBundle
And i get error after post method:
{ "@context": "/api/contexts/Error", "@type": "hydra:Error", "hydra:title": "An error occurred", "hydra:description": "No locale has been set and current locale is undefined.", "trace": [ { "namespace": "", "short_class": "", "class": "", "type": "", "function": "", "file": "/site/vendor/locastic/api-platform-translation-bundle/src/Model/TranslatableTrait.php", "line": 64, "args": [] }, .... }
(I use symfony5)
I have an issue on loading AliceBundleFixtures for translations managed by ApiPlatformTranslationBundle.
This is the trace on loading fixtures:
In SimpleObjectGenerator.php line 111:
An error occurred while generating the fixture "article_1_inventory2" (App\Entity\ArticleInventory): An error occurred while generating the fixture "article_color_blue_en" (App\Entity\ArticleColorT
ranslation): Call to a member function containsKey() on null
In SimpleObjectGenerator.php line 111:
An error occurred while generating the fixture "article_color_blue_en" (App\Entity\ArticleColorTranslation): Call to a member function containsKey() on null
In TranslatableTrait.php line 146:
Call to a member function containsKey() on null
This is my fixtures:
App\Entity\ArticleColorTranslation:
article_color_black_fr:
translatable: '@article_color_black'
locale: 'fr'
name: 'Noir'
article_color_black_en:
translatable: '@article_color_black'
locale: 'en'
name: 'Black'
article_color_white_fr:
translatable: '@article_color_white'
locale: 'fr'
name: 'Blanc'
article_color_white_en:
translatable: '@article_color_white'
locale: 'en'
name: 'White'
article_color_red_fr:
translatable: '@article_color_red'
locale: 'fr'
name: 'Rouge'
article_color_red_en:
translatable: '@article_color_red'
locale: 'en'
name: 'Red'
article_color_blue_fr:
translatable: '@article_color_blue'
locale: 'fr'
name: 'Bleu'
article_color_blue_en:
translatable: '@article_color_blue'
locale: 'en'
name: 'Blue'
article_color_yellow_fr:
translatable: '@article_color_yellow'
locale: 'fr'
name: 'Jaune'
article_color_yellow_en:
translatable: '@article_color_yellow'
locale: 'en'
name: 'Yellow'
article_color_green_fr:
translatable: '@article_color_green'
locale: 'fr'
name: 'Vert'
article_color_green_en:
translatable: '@article_color_green'
locale: 'en'
name: 'Green'
Thanks for your help..
Hello,
Do you have a release date for a new version? Especially for the last fix 09437e2
Is there any particular reason why this library depends on api-platform/api-pack
instead of api-platform/core
?
I've got a fresh template form https://github.com/api-platform/api-platform
and I got
api/config/api_platform/resources.yaml
resources:
App\Entity\Event:
itemOperations:
get:
method: GET
put:
method: PUT
normalization_context:
groups: [ 'translations' ]
collectionOperations:
get:
method: GET
post:
method: POST
normalization_context:
groups: [ 'translations' ]
attributes:
filters: [ 'translation.groups' ]
normalization_context:
groups: [ 'event_read' ]
denormalization_context:
groups: [ 'event_write' ]
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* Class Event
* @package App\Entity
* @ORM\Entity
*/
class Event extends AbstractTranslatable
{
/**
* @var int The entity Id
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @Groups({"event_read", "event_write"})
* @ORM\Column
*/
private $startAt;
/**
* @Groups({"event_read"})
* @ORM\Column
*/
private $title;
/**
* @Groups({"event_read"})
* @ORM\Column
*/
private $description;
/**
* @Groups({"event_write", "translations"})
*/
protected $translations;
public function getId()
{
return $this->id;
}
public function getStartAt(): \DateTime
{
return $this->startAt;
}
public function setStartAt(\DateTime $startAt)
{
$this->startAt = $startAt;
}
public function setTitle($title)
{
$this->getTranslation()->setTitle($title);
}
public function getTitle(): string
{
return $this->getTranslation()->getTitle();
}
public function setDescription($description)
{
$this->getTranslation()->setDescription($description);
}
public function getDescription(): string
{
return $this->getTranslation()->getDescription();
}
protected function createTranslation():TranslationInterface
{
return new EventTranslation();
}
}
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* Class EventTranslation
* @package App\Entity
* @ORM\Entity
*/
class EventTranslation extends AbstractTranslation
{
/**
* @var int The entity Id
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @var string
*
* @ORM\Column
* @Groups({"event_read", "event_write", "translations"})
*/
private $title;
/**
* @var string
* @ORM\Column
* @Groups({"event_read", "event_write", "translations"})
*/
private $description;
/**
* @var string
*
* @ORM\Column
* @Groups({"event_write", "translations"})
*/
protected $locale;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle(string $title): void
{
$this->title = $title;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @param string $description
*/
public function setDescription(string $description): void
{
$this->description = $description;
}
/**
* @return string
*/
public function getLocale(): string
{
return $this->locale;
}
/**
* @param string $locale
*/
public function setLocale(?string $locale): void
{
$this->locale = $locale;
}
}
got an errror
"hydra:description": "Could not denormalize object of type \"Locastic\\ApiPlatformTranslationBundle\\Model\\TranslationInterface[]\", no supporting normalizer found.",
well I try sec time
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
/**
* @ApiResource(
* itemOperations={
* "get"={"normalization_context"={"groups"={"get"}}},
* "put"={"normalization_context"={"groups"={"translations"}}}
* },
* collectionOperations={
* "get",
* "post"={"normalization_context"={"groups"={"translations"}}}
* },
* normalizationContext={"groups"={"post_read"}},
* denormalizationContext={"groups"={"post_write"}}
* )
* @ORM\Entity
*/
class Post extends AbstractTranslatable
{
/**
* @var int
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @ORM\Column(type="string")
* @Groups({"post_read"})
*/
private $title;
/**
* @Groups({"post_write", "translations"})
*/
protected $translations;
public function getId(): ?int
{
return $this->id;
}
public function setTitle($title)
{
$this->getTranslation()->setTitle($title);
return $this;
}
public function getTitle()
{
return $this->getTranslation()->getTitle();
}
/**
* Create resource translation model.
*
* @return TranslationInterface
*/
protected function createTranslation(): TranslationInterface
{
return new PostTranslation();
}
}
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
/**
* @ORM\Entity
*/
class PostTranslation extends AbstractTranslation
{
/**
* @var int
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @ORM\Column(type="string")
* @Groups({"post_read", "post_write", "translations"})
*/
private $title;
/**
* @var string
* @ORM\Column(type="string")
* @Groups({"post_write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getTitle()
{
return $this->title;
}
public function __toString()
{
return $this->title;
}
}
got the same error
I try to add the bundle in bundles.php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Locastic\ApiPlatformTranslationBundle\ApiPlatformTranslationBundle::class => ['all' => true]
];
And i have this error
PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "ApiPlatformTranslationBundle" from namespace "Locastic\ApiPlatformTranslationBundle".
It's normal ? I try this because the services of ApiPlatformTranslationBundle not load
Tried to install the package via composer, got an error:
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Installation request for locastic/api-platform-translation-bundle dev-master -> satisfiable by locastic/api-platform-translation-bundle[dev-master].
- locastic/api-platform-translation-bundle dev-master requires api-platform/api-pack ^1.1 -> satisfiable by api-platform/api-pack[1.1.0, v1.2.0] but these conflict with you
r requirements or minimum-stability.
Installation failed, reverting ./composer.json to its original content.
I guess it's only matter of incorrectly defined dependency on api-pack instead of api-core. Refering to #4. Can you please change composer.json to make it work?
I have to use Gedmo which philosophy and approach I cannot agree instead of Locastic. Thanks.
I'm playing with this bundle on Api Platform 3. I've setup some examples and my responses for a GET-Request on http://localhost/test_entities/91656d77-1db8-4294-a841-004dc365fbb6
look like
{
"@context": "/contexts/TestEntity",
"@type": "TestEntity",
"id": "91656d77-1db8-4294-a841-004dc365fbb6",
"name": "deutscher name",
"description": "deutsche beschreibung",
"products": [],
"translations": {
"de": {
"@type": "TestEntityTranslation",
"@id": "/.well-known/genid/6b50a3758975475ca57e",
"id": "192620a2-c8a1-45bd-8809-785e1a27f4cb",
"name": "deutscher name",
"description": "deutsche beschreibung",
"locale": "de"
},
"en": {
"@type": "TestEntityTranslation",
"@id": "/.well-known/genid/dc4fadb73b87e7475f3f",
"id": "e0d4ef95-f21c-4318-b182-9b00ed627de8",
"name": "english name",
"description": "english description",
"locale": "en"
}
},
"translation": {
"@type": "TestEntityTranslation",
"@id": "/.well-known/genid/6b50a3758975475ca57e",
"id": "192620a2-c8a1-45bd-8809-785e1a27f4cb",
"name": "deutscher name",
"description": "deutsche beschreibung",
"locale": "de"
},
"translationLocales": [
"de",
"en"
]
}
I'm not sure about if I missed something or it's just the way it works :). My expectation was to get something like the shown json but without these doubles. Additionally on extending the request with ?locale=de
I still get the whole response shown up here. Any hints for me so far without extracting the whole codebase here?
I am using API-Platform 3.1.17 and with Doctrine ORM 2.16.2.
First of all thanks for this extension, as it saved me a lot of time.
It would be cool to support PHP8 now that's been released. I tried the extension with ignore-platform-reqs
and it seems to work correctly
Thanks for your great work in advance!
The translations are working very well.
But when GraphQl is used there is an issue related to the Translation entity serialization proccess.
Entities related
@ApiResource
Workflow
The request made is described as follow:
// the parameters are the same suggested by GraphQl docs
mutation createRecipeSteps($cookOrder: Int!, $translations: [String]) {
createRecipeSteps(input: {cookOrder: $cookOrder, translations: $translations}) {
recipeSteps {
id
}
}
}
// set of variables
{
"translations": [
"/api/recipe_steps_translations/1863",
"/api/recipe_steps_translations/1864",
"/api/recipe_steps_translations/1865"
],
"cookOrder": 0
}
After send the query the next error is raised
{
"errors": [
{
"debugMessage": "The type of the key \"0\" must be \"string\", \"integer\" given.",
"message": "Internal server error",
"extensions": {
"category": "internal"
},
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createRecipeSteps"
],
"trace": [
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
"line": 737,
"call": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer::denormalizeCollection('translations', instance of ApiPlatform\\Core\\Metadata\\Property\\PropertyMetadata, instance of Symfony\\Component\\PropertyInfo\\Type, 'App\\Entity\\RecipeStepsTranslation', array(3), 'graphql', array(8))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
"line": 403,
"call": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer::createAttributeValue('translations', array(3), 'graphql', array(8))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\GraphQl\\Serializer\\ItemNormalizer.php",
"line": 128,
"call": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer::setAttributeValue(instance of App\\Entity\\RecipeSteps, 'translations', array(3), 'graphql', array(8))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\symfony\\serializer\\Normalizer\\AbstractObjectNormalizer.php",
"line": 336,
"call": "ApiPlatform\\Core\\GraphQl\\Serializer\\ItemNormalizer::setAttributeValue(instance of App\\Entity\\RecipeSteps, 'translations', array(3), 'graphql', array(8))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
"line": 250,
"call": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer::denormalize(array(2), 'App\\Entity\\RecipeSteps', 'graphql', array(8))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\Serializer\\ItemNormalizer.php",
"line": 70,
"call": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer::denormalize(array(2), 'App\\Entity\\RecipeSteps', 'graphql', array(7))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\symfony\\serializer\\Serializer.php",
"line": 208,
"call": "ApiPlatform\\Core\\Serializer\\ItemNormalizer::denormalize(array(2), 'App\\Entity\\RecipeSteps', 'graphql', array(6))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\GraphQl\\Resolver\\Stage\\DeserializeStage.php",
"line": 57,
"call": "Symfony\\Component\\Serializer\\Serializer::denormalize(array(2), 'App\\Entity\\RecipeSteps', 'graphql', array(6))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\GraphQl\\Resolver\\Factory\\ItemMutationResolverFactory.php",
"line": 98,
"call": "ApiPlatform\\Core\\GraphQl\\Resolver\\Stage\\DeserializeStage::__invoke(null, 'App\\Entity\\RecipeSteps', 'create', array(6))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 624,
"call": "ApiPlatform\\Core\\GraphQl\\Resolver\\Factory\\ItemMutationResolverFactory::ApiPlatform\\Core\\GraphQl\\Resolver\\Factory\\{closure}(null, array(1), null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 550,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 474,
"call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 858,
"call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'createRecipeSteps')"
},
{
"call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'createRecipeSteps')"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 860,
"function": "array_reduce(array(1), instance of Closure, array(0))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 490,
"call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 263,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\ReferenceExecutor.php",
"line": 215,
"call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\Executor\\Executor.php",
"line": 156,
"call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
"line": 162,
"call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, null, array(2), 'createRecipeSteps', null)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\webonyx\\graphql-php\\src\\GraphQL.php",
"line": 94,
"call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, 'mutation createRecipeSteps($cookOrder: Int!, $translations: [String]) {\n createRecipeSteps(input: {cookOrder: $cookOrder, translations: $translations}) {\n recipeSteps {\n id\n }\n }\n}\n', null, null, array(2), 'createRecipeSteps', null, null)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\GraphQl\\Executor.php",
"line": 34,
"call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, 'mutation createRecipeSteps($cookOrder: Int!, $translations: [String]) {\n createRecipeSteps(input: {cookOrder: $cookOrder, translations: $translations}) {\n recipeSteps {\n id\n }\n }\n}\n', null, null, array(2), 'createRecipeSteps', null, null)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\api-platform\\core\\src\\GraphQl\\Action\\EntrypointAction.php",
"line": 86,
"call": "ApiPlatform\\Core\\GraphQl\\Executor::executeQuery(instance of GraphQL\\Type\\Schema, 'mutation createRecipeSteps($cookOrder: Int!, $translations: [String]) {\n createRecipeSteps(input: {cookOrder: $cookOrder, translations: $translations}) {\n recipeSteps {\n id\n }\n }\n}\n', null, null, array(2), 'createRecipeSteps')"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\symfony\\http-kernel\\HttpKernel.php",
"line": 157,
"call": "ApiPlatform\\Core\\GraphQl\\Action\\EntrypointAction::__invoke(instance of Symfony\\Component\\HttpFoundation\\Request)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\symfony\\http-kernel\\HttpKernel.php",
"line": 79,
"call": "Symfony\\Component\\HttpKernel\\HttpKernel::handleRaw(instance of Symfony\\Component\\HttpFoundation\\Request, 1)"
},
{
"file": "W:\\projects\\apps\\sample-api\\vendor\\symfony\\http-kernel\\Kernel.php",
"line": 195,
"call": "Symfony\\Component\\HttpKernel\\HttpKernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request, 1, true)"
},
{
"file": "W:\\projects\\apps\\sample-api\\public\\index.php",
"line": 20,
"call": "Symfony\\Component\\HttpKernel\\Kernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request)"
}
]
}
],
"data": {
"createRecipeSteps": null
}
}
When I use another parameters as array of IRIs of previously saved objects this is not happening, only fails with translation.
Would it be posible the translations are not available to save this way when using GraphQl?
whatever I try to do I always get the error:
"Argument 1 passed to Locastic\ApiPlatformTranslationBundle\EventListener\AssignLocaleListener::prePersist() must be an instance of Doctrine\Common\Persistence\Event\LifecycleEventArgs, instance of Doctrine\ORM\Event\LifecycleEventArgs given, called in C:\xampp\htdocs\Symfony\project_4\vendor\symfony\doctrine-bridge\ContainerAwareEventManager.php on line 58"
I followed the readme and uses two different json formats to post. both resulting in the error above.
{
"translations": [
{
"title": "test",
"locale": "en"
}
]
}
{
"translations": {
"en":{
"title":"test",
"locale":"en"
},
"de":{
"title":"test de",
"locale":"de"
}
}
}
Here are my entity's:
Event.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\EventRepository;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ApiResource(
* itemOperations={
* "get"={"normalization_context"={"groups"={"get"}}},
* "put"={"normalization_context"={"groups"={"translations"}}}
* },
* collectionOperations={
* "get",
* "post"={"normalization_context"={"groups"={"translations"}}}
* },
* normalizationContext={"groups"={"post_read"}},
* denormalizationContext={"groups"={"post_write"}}
* )
* @ORM\Entity(repositoryClass=EventRepository::class)
*/
class Event extends AbstractTranslatable
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @Groups({"post_read"})
*/
private $title;
/**
* @ORM\OneToMany(targetEntity="EventTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"PERSIST"}, orphanRemoval=true)
*
* @Groups({"post_write", "translations"})
*/
protected $translations;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->getTranslation()->getLocale();
}
public function setTitle(string $title): void
{
$this->getTranslation()->setLocale($title);
}
protected function createTranslation(): TranslationInterface
{
return new EventTranslation();
}
}
EventTranslation.php
<?php
namespace App\Entity;
use App\Repository\EventTranslationRepository;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=EventTranslationRepository::class)
*/
class EventTranslation extends AbstractTranslation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Event", inversedBy="translations")
*/
protected $translatable;
/**
* @ORM\Column(type="string", length=255)
*
* @Groups({"post_read", "post_write", "translations"})
*/
private $title;
/**
* @ORM\Column(type="string", length=255)
*
* @Groups({"post_write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
}
Hi,
I want to user this bundle for first time.
I folow Implementation steps but i get load exception.
Class "App\Entity\PostTranslation" sub class of "Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation" is not a valid entity or mapped super class in . (which is being imported from "/Users/xxx/Code/www/symfony/apiTranslationTest/config/routes/api_platform.yaml"). Make sure there is a loader supporting the "api_platform" type.
Did I skip something in the implementation?
-- Post.php
<?php
namespace App\Entity;
use App\Repository\PostRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=PostRepository::class)
*/
class Post extends AbstractTranslatable
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"post_read", "post_write"})
*/
private $title;
/**
* @ORM\Column(type="string", length=255)
* @Gedmo\Slug(fields={"title"})
*/
private $slug;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"post_read", "post_write"})
*/
private $subtitle;
/**
* @ORM\Column(type="text", nullable=true)
* @Groups({"post_read", "post_write"})
*/
private $content;
/**
* @ORM\OneToMany(targetEntity="PostTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"PERSIST"}, orphanRemoval=true)
*
* @Groups({"post_write", "translations"})
*/
protected $translations;
protected function createTranslation(): TranslationInterface
{
return new PostTranslation();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->getTranslation()->getTitle();
}
public function setTitle(string $title): self
{
$this->getTranslation()->setTitle($title);
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getSubtitle(): ?string
{
return $this->subtitle;
}
public function setSubtitle(?string $subtitle): self
{
$this->subtitle = $subtitle;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(?string $content): self
{
$this->content = $content;
return $this;
}
}
-- PostTranslation.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
use Symfony\Component\Serializer\Annotation\Groups;
class PostTranslation extends AbstractTranslation
{
/**
* @ORM\ManyToOne(targetEntity="Post", inversedBy="translations")
*/
protected $translatable;
/**
* @ORM\Column(type="string")
*
* @Groups({"post_read", "post_write", "translations"})
*/
private $title;
/**
* @ORM\Column(type="string")
*
* @Groups({"post_write", "translations"})
*/
protected $locale;
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getTitle(): ?string
{
return $this->title;
}
}
hello !
i try to add unique constraint on my translations with name + locale fields to not have duplications on my translation entities but when i try to remplace my translations
collection of my translatable entity without any modifications, my constraint is trigged because doctrine try to add new translations ( instead of update existing ).
I analyse the unique constraint validator of symfony and i see i loss my id
on translations objects when valid
is called. someone have any idea ?
Rรฉdoine
Hello,
Is it possible to force locale have length of 2 chars: instead of fr_FR, i want only fr ?
Rรฉdoine
I decided use this bundle but I have no idea for using Easy Admin
Hello ,
I tried to use Constraints such as : length or NotBlank
but it seems be ignored
AreaTranslation.php
/**
* @ORM\Entity()
*/
class AreaTranslation extends AbstractTranslation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"Area_read","Area_write"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Area", inversedBy="translations")
* @Assert\NotNull
*/
protected $translatable;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank()
* @Assert\Length(max=1500)
* @Groups({"Area_read","Area_write","translations"})
*/
private $name;
/**
* @ORM\Column(type="string")
* @Assert\NotBlank
* @Assert\Language
* @Groups({"Area_read","Area_write","translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
Hello ,
I am using this package to do i18n with my APIs, it works fine but I would like to know what is the best way to prevent the user from translating the same word in the same language and several times .
I tried with slugs but I don't think it's better
Thank you in advance
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?
Hello !
I am trying to filter on current locale translations but i don't find any way to do that..
This is working for filter on all translations:
#[ApiFilter(SearchFilter::class, properties: ['translations.name'])]
I also want to order my list by current translation name:
'domains_list' => [ // @todo order
'method' => 'GET',
'path' => '/domains',
'access_control' => 'is_granted(\''.Account::ROLE_USER.'\')',
'normalization_context' => ['groups' => ['domains:list', 'domains:translations']],
'order' => ['translations.name' => 'ASC'], // <- here i want to order on current locale
],
Someone have any idea to do that without rewriting all filter class ?
Thanks :)
Suppose I have an Entity
with a translated field name
.
Could you please suggest a solution to the situation when I need to order the items collection (using OrderFilter
) by the name
field considering the current locale
? What should the annotations look like?
Hello,
I have an entity without any translation and i see in my Symfony profiler multiple requests to query current locale, and default locale, then if no translation, insert new translation.
The problem is i don't want to add new translation automatically.
I think, when i call $this->getTranslations('unavailable locale')->getName()
, i have a new translation object created with name = null
but with a $entityManager->flush()
for other reasons after this getTranslations(), this object is persisted to database.
This is my error:
An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"template\" of relation \"email_translation\" violates not-null constraint\nDETAIL: Failing row contains (1ecd36ca-d8ef-6572-bb2a-c1eda6aadb28, 1ecd23cc-a924-61aa-8575-d1e3b0741a73, fr, null).
there is an inconsistency of returning types when translation is empty it's returning array instead of object
no translations
{
"id":1,
"translations": []
}
there are translations
{
"id":1,
"translations": {
"en":{
"title":"test",
"content":"test",
"locale":"en"
},
"de":{
"title":"test de",
"content":"test de",
"locale":"de"
}
}
}
maybe it's should return {} or null if there are no translations
First of all thank you for this good bundle.
It works very well.
But looking more closely at the number of doctrine queries via the symfony profiler.
I notice that there are a lot of duplicate requests when we get the translations.
Example: if the collection contains 6 elements, we have 6 times the same request.
SELECT t0.id AS id_1, t0.title AS title_2, t0.content AS content_3, t0.meta_description AS meta_description_4, t0.seo_title AS seo_title_5, t0.slug AS slug_6, t0.locale AS locale_7, t0.translatable_id AS translatable_id_8 FROM blog_post_translation t0 WHERE (t0.locale = ? AND t0.translatable_id = ?)
Parameters:
[โผ
"en"
1
]
I had the same problem with Gedmo\Translatable and I used the TranslationWalker. Would it be possible to use something equivalent?
use Doctrine\ORM\Query;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
...
$query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
$query->setHint(Query::HINT_REFRESH, true);
Thanks
Hello,
Iโm trying to use LocasticApiTranslationBundle with doctrine but Iโm unable to make it work.
it tells me that there is no locale defined but I don't know where to declare the locale ?
Can you help me and tell me what Iโm doing wrong please ?
In attachment, the stacktrace,
the source entity (ReferenceMateriel) and the translation entity (ReferenceMaterielTranslation) I use.
Thank you in advance for your help.
Best regards
ReferenceMateriel.txt
ReferenceMaterielTranslation.txt
stacktrace.txt
I'm having a bit of trouble trying to edit translations.
It seems every time I make a PUT request to my resource to update one translation it begins a new database transaction, inserts new items and then deletes every translation item in the table.
I've followed the guide and here are my entities:
Product.php
/**
* @ApiResource(
* collectionOperations={
* "get"
* },
* itemOperations={
* "get",
* "put"={
* "security"="is_granted('ROLE_ADMIN')",
* "normalizationContext"={"groups"={"translations"}}
* },
* "delete"={"security"="is_granted('ROLE_ADMIN')"}
* },
* normalizationContext={"groups"={"product.read"}},
* denormalizationContext={"groups"={"product.write"}}
* )
* @ORM\Entity(repositoryClass=ProductRepository::class)
* @ORM\Table("products")
* @ORM\HasLifecycleCallbacks
*/
class Product extends AbstractTranslatable
{
/* more properties */
/**
* @ORM\OneToMany(
* targetEntity=ProductTranslation::class,
* mappedBy="translatable",
* fetch="EXTRA_LAZY",
* indexBy="locale",
* cascade={"persist"},
* orphanRemoval=true
* )
*
* @Groups({"product.read", "product.write", "translations"})
*/
protected $translations;
private string $title;
public function __construct()
{
$this->translations = new ArrayCollection();
}
protected function createTranslation(): TranslationInterface
{
return new ProductTranslation();
}
public function setTitle(string $title)
{
$this->getTranslation()->setTitle($title);
}
public function getTitle(): ?string
{
return $this->getTranslation()->getTitle();
}
}
ProductTranslation.php
/**
* @ORM\Entity(repositoryClass=ProductTranslationRepository::class)
* @ORM\Table("products_translations")
*/
class ProductTranslation extends AbstractTranslation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"product.read", "product.write", "translations"})
*/
private int $id;
/**
* @ORM\ManyToOne(targetEntity=Product::class, inversedBy="translations")
*/
protected $translatable;
/**
* @ORM\Column(type="string")
* @Groups({"product.read", "product.write", "translations"})
*/
private $title;
/**
* @ORM\Column(type="string")
* @Groups({"product.read", "product.write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getTitle(): ?string
{
return $this->title;
}
}
Dear maintainers,
could we kindly request the inclusion of support for doctrine/orm 3
? @konradkozaczenko has already submitted GH-68. Doctrine/orm 3
requires PHP 8.1+
, so this release may require updating the package.json
to reflect the change. Given the substantial implications, I think the release with this updates must be major version.
Best regards!
I am using this bundle but I have a problem that it is translating entity to locale set in Request, which is not the desired behaviour.
For example I don't have translations for en locale, I only have them for 'sl', but with request with header accept-language: en,sl
empty translations are returned even though default locale is set to 'sl'.
Is there a way to configure the bundle to only use predefined pool of locales (for example: sl, de), so it will ignore request locale but always use fixed one?
I just installed Locastic Translation Bundle in a new API Platform Symfony project. Everything works fine except POST requests, that return me an error.
I followed the detailed guide in the Readme and created my translatable entity, and setup the Locale.
I can translate existing content using PATCH requests, GET requests acknoledge my locale correctly and provide me translated informations in the right language with no error, so I think everything is setup correctly.
The error that POST requests return :
Return value of Locastic\\ApiPlatformTranslationBundle\\Model\\AbstractTranslatable::getTranslations() must implement interface Doctrine\\Common\\Collections\\Collection, null returned
"file": "C:\\Users\\user\\dev\\symfony-project\\vendor\\locastic\\api-platform-translation-bundle\\src\\Model\\TranslatableTrait.php",
"line": 138,
Here's my entity ( Challenge.php )
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\ChallengeRepository;
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"},
* "normalization_context"={"groups"={"chall:read"}},
* "denormalization_context"={"groups"={"chall:write"}}
* },
* collectionOperations={
* "get",
* "post"={
* "normalization_context"={"groups"={"translations"}}
* }
* },
* itemOperations={
* "get",
* "put"={
* "normalization_context"={"groups"={"translations"}}
* },
* "delete"
* }
* )
* @ORM\Entity(repositoryClass=ChallengeRepository::class)
*/
class Challenge extends AbstractTranslatable
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"chall:read"})
*/
private $title;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"chall:read", "chall:write"})
*/
private $code;
/**
* @ORM\Column(type="text")
* @Groups({"chall:read"})
*/
private $description;
/**
* @ORM\Column(type="datetime", nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $startAt;
/**
* @ORM\Column(type="datetime", nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $endAt;
/**
* @ORM\Column(type="datetime", nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $registryLimitDate;
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $maxSlots;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $image;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"chall:read", "chall:write"})
*/
private $status;
/**
* @ORM\ManyToMany(targetEntity=Course::class, mappedBy="challenges")
* @Groups({"chall:read", "chall:write"})
*/
private $courses;
/**
* @ORM\OneToMany(targetEntity="ChallengeTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"PERSIST"}, orphanRemoval=true)
* @Groups({"chall:write", "translations"})
*/
protected $translations;
public function __construct()
{
$this->courses = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->getTranslation()->getTitle();
}
public function setTitle(string $title): self
{
$this->getTranslation()->setTitle($title);
return $this;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
public function getDescription(): ?string
{
return $this->getTranslation()->getDescription();
}
public function setDescription(string $description): self
{
$this->getTranslation()->setDescription($title);
return $this;
}
public function getStartAt(): ?\DateTimeInterface
{
return $this->startAt;
}
public function setStartAt(?\DateTimeInterface $startAt): self
{
$this->startAt = $startAt;
return $this;
}
public function getEndAt(): ?\DateTimeInterface
{
return $this->endAt;
}
public function setEndAt(?\DateTimeInterface $endAt): self
{
$this->endAt = $endAt;
return $this;
}
public function getRegistryLimitDate(): ?\DateTimeInterface
{
return $this->registryLimitDate;
}
public function setRegistryLimitDate(?\DateTimeInterface $registryLimitDate): self
{
$this->registryLimitDate = $registryLimitDate;
return $this;
}
public function getMaxSlots(): ?int
{
return $this->maxSlots;
}
public function setMaxSlots(?int $maxSlots): self
{
$this->maxSlots = $maxSlots;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setImage(?string $image): self
{
$this->image = $image;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(?string $status): self
{
$this->status = $status;
return $this;
}
/**
* @return Collection|Course[]
*/
public function getCourses(): Collection
{
return $this->courses;
}
public function addCourse(Course $course): self
{
if (!$this->courses->contains($course)) {
$this->courses[] = $course;
$course->addChallenge($this);
}
return $this;
}
public function removeCourse(Course $course): self
{
if ($this->courses->contains($course)) {
$this->courses->removeElement($course);
$course->removeChallenge($this);
}
return $this;
}
protected function createTranslation(): TranslationInterface
{
return new ChallengeTranslation();
}
}
And my translated entity ( ChallengeTranslation.php )
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\ChallengeTranslationRepository;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
/**
* @ORM\Entity(repositoryClass=ChallengeTranslationRepository::class)
*/
class ChallengeTranslation extends AbstractTranslation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Challenge", inversedBy="translations")
*/
protected $translatable;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"chall:read", "chall:write", "translations"})
*/
private $title;
/**
* @ORM\Column(type="string", length=255)
* @Groups({"chall:read", "chall:write", "translations"})
*/
private $description;
/**
* @ORM\Column(type="string")
* @Groups({"chall:write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
}
Is this a common issue ? A little help would be much appreciated as almost everything is working !
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 ?
locastic/api-platform-translation-bundle[v1.3, ..., v1.3.4] require api-platform/core ^2.1 -> found api-platform/core[v2.1.0, ..., v2.7.0-rc.2] but it conflicts with your root composer.json require (^3.0@rc).
hello ,
i use symfony 4.4 and posgresql , but when i try to insert a new value using POST method , I get this error :
"hydra:description": "An exception occurred while executing 'INSERT INTO tag (id, title) VALUES (?, ?)' with params [5, null]:\n\nSQLSTATE[23502]: Not null violation
Tag.php
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiFilter;
use App\Entity\Translation\TagTranslation;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use App\Filter\FullTextSearchFilter;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass="App\Repository\TagRepository")
* @ApiResource(
* attributes={
* "filters"={"translation.groups"},
* "normalization_context"={"groups"={"Tag_read"}},
* "denormalization_context"={"groups"={"Tag_write"}}
* },
* collectionOperations={
* "get",
* "post"={
* "normalization_context"={"groups"={"translations"}}
* }
* },
* itemOperations={
* "get",
* "put"={
* "normalization_context"={"groups"={"translations"}}
* },
* "delete"
* }
* )
*/
class Tag extends AbstractTranslatable
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"Category_read","Tag_read","Image_read"})
*/
private $id;
/**
* @ORM\Column(type="string", length=255,unique=true)
* @Groups({"Category_read","Tag_read","Image_read"})
*/
private $title;
/**
* @ORM\ManyToMany(targetEntity=Image::class)
* @Groups({"Tag_read"})
*/
private $images;
/**
* @ORM\ManyToMany(targetEntity=Category::class, mappedBy="tags")
* @Groups({"Tag_read"})
*/
private $categories;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Translation\TagTranslation", mappedBy="translatable", fetch="EXTRA_LAZY", indexBy="locale", cascade={"PERSIST"}, orphanRemoval=true)
* @Groups({"Tag_write", "translations"})
*/
protected $translations;
public function __construct()
{
parent::__construct();
$this->images = new ArrayCollection();
$this->categories = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->getTranslation()->getTitle();
}
public function setTitle(string $title)
{
$this->getTranslation()->setTitle($title);
}
/**
* @return Collection|Image[]
*/
public function getImages(): Collection
{
return $this->images;
}
public function addImage(Image $image): self
{
if (!$this->images->contains($image)) {
$this->images[] = $image;
}
return $this;
}
public function removeImage(Image $image): self
{
if ($this->images->contains($image)) {
$this->images->removeElement($image);
}
return $this;
}
/**
* @return Collection|Category[]
*/
public function getCategories(): Collection
{
return $this->categories;
}
public function addCategory(Category $category): self
{
if (!$this->categories->contains($category)) {
$this->categories[] = $category;
$category->addTag($this);
}
return $this;
}
public function removeCategory(Category $category): self
{
if ($this->categories->contains($category)) {
$this->categories->removeElement($category);
$category->removeTag($this);
}
return $this;
}
protected function createTranslation(): TranslationInterface
{
return new TagTranslation();
}
}
and TagTranslation.php
<?php
namespace App\Entity\Translation;
use Doctrine\ORM\Mapping as ORM;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity()
*/
class TagTranslation extends AbstractTranslation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Tag", inversedBy="translations")
*/
protected $translatable;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Groups({"Tag_read","Tag_write"})
*/
private $title;
/**
* @ORM\Column(type="string")
*
* @Groups({"Tag_write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
}
Is this a common issue ?
Hi
How to use by examples from README?
My files:
App\Entity\Post
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Locastic\ApiPlatformTranslationBundle\Model\TranslationInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslatable;
/**
* @ApiResource(
* itemOperations={
* "get"={"normalization_context"={"groups"={"get"}}},
* "put"={"normalization_context"={"groups"={"translations"}}}
* },
* collectionOperations={
* "get",
* "post"={"normalization_context"={"groups"={"translations"}}}
* },
* normalizationContext={"groups"={"post_read"}},
* denormalizationContext={"groups"={"post_write"}}
* )
* @ORM\Entity
*/
class Post extends AbstractTranslatable
{
/**
* @var int
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @Groups({"post_read"})
*/
private $title;
/**
* @Groups({"post_write", "translations"})
*/
protected $translations;
public function getId(): ?int
{
return $this->id;
}
public function setTitle($title)
{
$this->getTranslation()->setTitle($title);
return $this;
}
public function getTitle()
{
return $this->getTranslation()->getTitle();
}
/**
* Create resource translation model.
*
* @return TranslationInterface
*/
protected function createTranslation(): TranslationInterface
{
return new PostTranslation();
}
}
App\Entity\PostTranslation
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation;
/**
* @ORM\Entity
*/
class PostTranslation extends AbstractTranslation
{
/**
* @var int
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
* @ORM\Column
* @Groups({"post_read", "post_write", "translations"})
*/
private $title;
/**
* @var string
* @ORM\Column
* @Groups({"post_write", "translations"})
*/
protected $locale;
public function getId(): ?int
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getTitle()
{
return $this->title;
}
}
After send POST to /api/posts
{
"translations": {
"ru": {
"title":"Some title"
},
"uk": {
"title":"Some title"
}
}
}
I'm receive error:
{
"@context": "/api/contexts/Error",
"@type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Could not denormalize object of type Locastic\\ApiPlatformTranslationBundle\\Model\\TranslationInterface[], no supporting normalizer found.",
...
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.