From 081dfde54d76c58c4bec27029a1e299d9f03cbe0 Mon Sep 17 00:00:00 2001 From: Dominik Grothaus Date: Tue, 2 Jul 2024 14:19:31 +0200 Subject: [PATCH 1/3] =?UTF-8?q?[Translatable]=20Ignore=20notInsertable=20c?= =?UTF-8?q?olumns=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in postPersist hook. Respect the column's metadata and don't attempt to insert a column that's flagged to be not-insertable. --- CHANGELOG.md | 2 ++ src/Translatable/Mapping/Event/Adapter/ODM.php | 10 ++++++++-- src/Translatable/Mapping/Event/Adapter/ORM.php | 11 ++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea079941e4..76b0695171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ a release. --- ## [Unreleased] +### Fixed +- Translatable: Ignore notInsertable columns in postPersist hook ## [3.16.1] ### Fixed diff --git a/src/Translatable/Mapping/Event/Adapter/ODM.php b/src/Translatable/Mapping/Event/Adapter/ODM.php index be64177ce9..774daa78de 100644 --- a/src/Translatable/Mapping/Event/Adapter/ODM.php +++ b/src/Translatable/Mapping/Event/Adapter/ODM.php @@ -139,9 +139,15 @@ public function insertTranslationRecord($translation) $data = []; foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) { - if (!$meta->isIdentifier($fieldName)) { - $data[$meta->getFieldMapping($fieldName)['name']] = $reflProp->getValue($translation); + if ($meta->isIdentifier($fieldName)) { + continue; } + $fieldMappings = $meta->getFieldMapping($fieldName); + if (($fieldMappings['notSaved'] ?? false) === true) { + continue; + } + + $data[$meta->getFieldMapping($fieldName)['name']] = $reflProp->getValue($translation); } $insertResult = $collection->insertOne($data); diff --git a/src/Translatable/Mapping/Event/Adapter/ORM.php b/src/Translatable/Mapping/Event/Adapter/ORM.php index 923f60cdc2..355e3ccce0 100644 --- a/src/Translatable/Mapping/Event/Adapter/ORM.php +++ b/src/Translatable/Mapping/Event/Adapter/ORM.php @@ -195,11 +195,16 @@ public function insertTranslationRecord($translation) $data = []; foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) { - if (!$meta->isIdentifier($fieldName)) { - $data[$meta->getColumnName($fieldName)] = $reflProp->getValue($translation); + if ($meta->isIdentifier($fieldName)) { + continue; + } + $fieldMappings = $meta->getFieldMapping($fieldName); + if (($fieldMappings['notInsertable'] ?? false) === true) { + continue; } - } + $data[$meta->getColumnName($fieldName)] = $reflProp->getValue($translation); + } $table = $meta->getTableName(); if (!$em->getConnection()->insert($table, $data)) { throw new RuntimeException('Failed to insert new Translation record'); From 4c88aa7a887cb64f11cd256051c58e2809cd780b Mon Sep 17 00:00:00 2001 From: Dominik Grothaus Date: Wed, 31 Jul 2024 09:07:56 +0200 Subject: [PATCH 2/3] Add a field to trigger ignores in test Adding this field to the test suit triggers the ignored flag for Doctrine fields. --- .../Fixture/PersonTranslation.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/Gedmo/Translatable/Fixture/PersonTranslation.php b/tests/Gedmo/Translatable/Fixture/PersonTranslation.php index cf1fc6d81b..98d3121e9a 100644 --- a/tests/Gedmo/Translatable/Fixture/PersonTranslation.php +++ b/tests/Gedmo/Translatable/Fixture/PersonTranslation.php @@ -11,6 +11,7 @@ namespace Gedmo\Tests\Translatable\Fixture; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation; use Gedmo\Translatable\Entity\Repository\TranslationRepository; @@ -33,4 +34,30 @@ #[ORM\UniqueConstraint(name: 'lookup_unique_idx', columns: ['locale', 'object_Class', 'foreign_key', 'field'])] class PersonTranslation extends AbstractTranslation { + /** + * @ORM\Column ( + * name: 'full_name', + * type: Types::STRING, + * length: 256, + * nullable: true, + * insertable: false, + * updatable: false, + * generated: 'ALWAYS' + * ) + */ + #[ORM\Column( + name: 'full_name', + type: Types::STRING, + length: 256, + nullable: true, + insertable: false, + updatable: false, + generated: 'ALWAYS' + )] + protected ?string $fullName = null; + + public function getFullName(): ?string + { + return $this->fullName; + } } From 3a909f495460ff0eb14558ddcada90a4ade958ca Mon Sep 17 00:00:00 2001 From: Dominik Grothaus Date: Wed, 31 Jul 2024 09:11:45 +0200 Subject: [PATCH 3/3] Add unit test for ODM translation repository Adding a unit test to test custom ODM translation repositories, as there already exist for ORM. --- .../EntityTranslationCollectionTest.php | 126 ++++++++++++++++++ .../Document/TranslationCollection/Person.php | 55 ++++++++ .../PersonTranslation.php | 74 ++++++++++ 3 files changed, 255 insertions(+) create mode 100644 tests/Gedmo/Translatable/EntityTranslationCollectionTest.php create mode 100644 tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/Person.php create mode 100644 tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/PersonTranslation.php diff --git a/tests/Gedmo/Translatable/EntityTranslationCollectionTest.php b/tests/Gedmo/Translatable/EntityTranslationCollectionTest.php new file mode 100644 index 0000000000..cf0c558811 --- /dev/null +++ b/tests/Gedmo/Translatable/EntityTranslationCollectionTest.php @@ -0,0 +1,126 @@ + http://www.gediminasm.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Gedmo\Tests\Translatable; + +use Doctrine\Common\EventManager; +use Doctrine\ODM\MongoDB\Repository\DocumentRepository; +use Gedmo\Tests\Tool\BaseTestCaseMongoODM; +use Gedmo\Tests\Translatable\Fixture\Document\TranslationCollection\Person; +use Gedmo\Tests\Translatable\Fixture\Document\TranslationCollection\PersonTranslation; +use Gedmo\Translatable\Document\Repository\TranslationRepository; +use Gedmo\Translatable\TranslatableListener; + +/** + * These are tests for translatable behavior + * + * @author Dominik Grothaus + */ +class EntityTranslationCollectionTest extends BaseTestCaseMongoODM +{ + private const PERSON = Person::class; + private const TRANSLATION = PersonTranslation::class; + + private TranslatableListener $translatableListener; + + protected function setUp(): void + { + parent::setUp(); + + $evm = new EventManager(); + $this->translatableListener = new TranslatableListener(); + $this->translatableListener->setDefaultLocale('en_US'); + $this->translatableListener->setTranslatableLocale('en_US'); + $evm->addEventSubscriber($this->translatableListener); + + $this->getDefaultDocumentManager($evm); + } + + public function testFixtureGeneratedTranslations(): void + { + $person = new (self::PERSON)(); + $person->setName('name in en'); + + $this->dm->persist($person); + $this->dm->flush(); + $this->dm->clear(); + + $repo = $this->dm->getRepository(self::TRANSLATION); + static::assertInstanceOf(TranslationRepository::class, $repo); + + $translations = $repo->findTranslations($person); + // As Translate locale and Default locale are the same, no records should be present in translations table + static::assertCount(0, $translations); + + // test second translations + $person = $this->dm->find(self::PERSON, $person->getId()); + $this->translatableListener->setTranslatableLocale('de_DE'); + $person->setName('name in de'); + + $this->dm->persist($person); + $this->dm->flush(); + $this->dm->clear(); + + $translations = $repo->findTranslations($person); + // Only one translation should be present + static::assertCount(1, $translations); + static::assertArrayHasKey('de_DE', $translations); + + static::assertArrayHasKey('name', $translations['de_DE']); + static::assertSame('name in de', $translations['de_DE']['name']); + + $this->translatableListener->setTranslatableLocale('en_US'); + } + + public function testShouldPersistDefaultLocaleValue(): void + { + $this->translatableListener->setPersistDefaultLocaleTranslation(true); + + $person = new (self::PERSON)(); + $person->setName('de_DE'); + + /** @var TranslationRepository $translationRepository */ + $translationRepository = $this->dm->getRepository(self::TRANSLATION); + $translationRepository + ->translate($person, 'name', 'de_DE', 'de_DE') + ->translate($person, 'name', 'en_US', 'en_US'); + $this->dm->persist($person); + $this->dm->flush(); + + $this->translatableListener->setTranslatableLocale('en_US'); + + /** @var DocumentRepository $personRepository */ + $personRepository = $this->dm->getRepository(self::PERSON); + $persons = $personRepository + ->createQueryBuilder() + ->hydrate(false) + ->find() + ->getQuery() + ->getIterator() + ->toArray() + ; + static::assertSame('en_US', $persons[0]['name']); + + $trans = $translationRepository + ->createQueryBuilder() + ->hydrate(false) + ->find() + ->getQuery() + ->getIterator() + ->toArray() + ; + static::assertCount(2, $trans); + foreach ($trans as $item) { + static::assertSame($item['locale'], $item['content']); + } + $this->translatableListener->setTranslatableLocale('en_US'); + } +} diff --git a/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/Person.php b/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/Person.php new file mode 100644 index 0000000000..f42b851c0f --- /dev/null +++ b/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/Person.php @@ -0,0 +1,55 @@ + http://www.gediminasm.org + * For the full copyright and licence information, please view the LICENCE + * file that was distributed with this source code. + */ + +namespace Gedmo\Tests\Translatable\Fixture\Document\TranslationCollection; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Gedmo\Mapping\Annotation as Gedmo; + +/** + * @ODM\Document(collection="persons") + */ +#[ODM\Document(collection: 'persons')] +#[Gedmo\TranslationEntity(class: PersonTranslation::class)] +class Person +{ + /** + * @var ?string + * @ODM\Id + */ + #[ODM\Id] + private ?string $id = null; + + /** + * @Gedmo\Translatable + * + * @ODM\Field(name="name", type="string") + */ + #[Gedmo\Translatable] + #[ODM\Field(name: 'name', type: Types::STRING)] + private ?string $name = null; + + public function getId(): ?string + { + return $this->id; + } + + public function setName(?string $name): void + { + $this->name = $name; + } + + public function getName(): ?string + { + return $this->name; + } +} \ No newline at end of file diff --git a/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/PersonTranslation.php b/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/PersonTranslation.php new file mode 100644 index 0000000000..24f58d73b6 --- /dev/null +++ b/tests/Gedmo/Translatable/Fixture/Document/TranslationCollection/PersonTranslation.php @@ -0,0 +1,74 @@ + http://www.gediminasm.org + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Gedmo\Tests\Translatable\Fixture\Document\TranslationCollection; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Gedmo\Translatable\Document\MappedSuperclass\AbstractTranslation; +use Gedmo\Translatable\Document\Repository\TranslationRepository; + +/** + * Gedmo\Translatable\Document\Translation + * + * @ODM\Document( + * collection="ext_translations", + * repositoryClass="Gedmo\Translatable\Document\Repository\TranslationRepository" + * ) + * @ODM\UniqueIndex(name="lookup_unique_idx", keys={ + * "locale": "asc", + * "object_class": "asc", + * "foreign_key": "asc", + * "field": "asc" + * }) + * @ODM\Index(name="translations_lookup_idx", keys={ + * "locale": "asc", + * "object_class": "asc", + * "foreign_key": "asc" + * }) + */ +#[ODM\Document(collection: 'ext_translations', repositoryClass: TranslationRepository::class)] +#[ODM\UniqueIndex(keys: [ + 'locale' => 'asc', + 'object_class' => 'asc', + 'foreign_key' => 'asc', + 'field' => 'asc', +], name: 'lookup_unique_idx' +)] +#[ODM\Index(keys: [ + 'locale' => 'asc', + 'object_class' => 'asc', + 'foreign_key' => 'asc', +], name: 'translations_lookup_idx' +)] +class PersonTranslation extends AbstractTranslation +{ + /** + * @ODM\Field( + * name="full_name", + * type= Types::STRING, + * nullable= true, + * notSaved= true, + * ) + */ + #[ODM\Field( + name: 'full_name', + type: Types::STRING, + nullable: true, + notSaved: true, + )] + protected ?string $fullName = null; + + public function getFullName(): ?string + { + return $this->fullName; + } +}