Skip to content

Commit 7d87596

Browse files
committed
Merge remote-tracking branch 'origin/3.x'
2 parents 4f71ad7 + 9c3420b commit 7d87596

2 files changed

Lines changed: 131 additions & 9 deletions

File tree

src/Doctrine/TranslatableClassMetadata.php

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,27 +185,32 @@ private function findTranslatedProperties(ClassMetadata $cm, ClassMetadataFactor
185185
return;
186186
}
187187

188+
$reflectionService = $classMetadataFactory->getReflectionService();
188189
$translationClassMetadata = $classMetadataFactory->getMetadataFor($this->translationClass->getName());
189190

190-
foreach ($cm->fieldMappings as $fieldName => $mapping) {
191-
if (isset($mapping['declared'])) {
192-
// The association is inherited from a parent class
191+
/* Iterate all properties of the class, not only those mapped by Doctrine */
192+
foreach ($cm->getReflectionClass()->getProperties() as $reflectionProperty) {
193+
$propertyName = $reflectionProperty->name;
194+
195+
/*
196+
If the property is inherited from a parent class, and our parent entity class
197+
already contains that declaration, we need not include it.
198+
*/
199+
$declaringClass = $reflectionProperty->getDeclaringClass()->name;
200+
if ($declaringClass !== $cm->name && $cm->parentClasses && is_a($cm->parentClasses[0], $declaringClass, true)) {
193201
continue;
194202
}
195203

196-
$reflectionProperty = $cm->getReflectionProperty($fieldName);
197204
$attributes = $reflectionProperty->getAttributes(Attribute\Translatable::class);
198205

199206
if (!$attributes) {
200207
continue;
201208
}
202209

203210
$attribute = $attributes[0]->newInstance();
204-
$translationFieldname = $attribute->getTranslationFieldname() ?: $fieldName;
205-
$translationFieldReflectionProperty = $translationClassMetadata->getReflectionProperty($translationFieldname);
206-
207-
$this->translatedProperties[$fieldName] = $reflectionProperty;
208-
$this->translationFieldMapping[$fieldName] = $translationFieldReflectionProperty;
211+
$this->translatedProperties[$propertyName] = $reflectionService->getAccessibleProperty($cm->name, $propertyName);
212+
$translationFieldname = $attribute->getTranslationFieldname() ?: $propertyName;
213+
$this->translationFieldMapping[$propertyName] = $reflectionService->getAccessibleProperty($translationClassMetadata->name, $translationFieldname);
209214
}
210215
}
211216

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
namespace Webfactory\Bundle\PolyglotBundle\Tests\Functional;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
use Doctrine\ORM\Mapping as ORM;
8+
use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot;
9+
use Webfactory\Bundle\PolyglotBundle\Translatable;
10+
11+
/**
12+
* This test covers that also properties which are not mapped Doctrine fields
13+
* can be marked as translatable and will be handled by the PolyglotListener.
14+
*
15+
* This is useful when these fields are managed or updated by e. g. lifecycle callbacks
16+
* or other Doctrine event listeners.
17+
*/
18+
class TranslatingUnmappedPropertiesTest extends FunctionalTestBase
19+
{
20+
protected function setUp(): void
21+
{
22+
parent::setUp();
23+
$this->setupOrmInfrastructure([
24+
TranslatingUnmappedPropertiesTest_Entity::class,
25+
TranslatingUnmappedPropertiesTest_Translation::class,
26+
]);
27+
}
28+
29+
public function testPersistAndReloadEntity(): void
30+
{
31+
$entity = new TranslatingUnmappedPropertiesTest_Entity();
32+
$entity->text = new Translatable('base text');
33+
$entity->text->setTranslation('Basistext', 'de_DE');
34+
35+
$this->infrastructure->import($entity);
36+
37+
$loaded = $this->infrastructure->getEntityManager()->find(TranslatingUnmappedPropertiesTest_Entity::class, $entity->id);
38+
39+
self::assertSame('Basistext', $loaded->text->translate('de_DE'));
40+
self::assertSame('base text', $loaded->text->translate('en_GB'));
41+
}
42+
}
43+
44+
#[ORM\Entity]
45+
#[ORM\HasLifecycleCallbacks]
46+
#[Polyglot\Locale(primary: 'en_GB')]
47+
class TranslatingUnmappedPropertiesTest_Entity
48+
{
49+
#[ORM\Column(type: 'integer')]
50+
#[ORM\Id]
51+
#[ORM\GeneratedValue]
52+
public ?int $id = null;
53+
54+
#[ORM\OneToMany(targetEntity: TranslatingUnmappedPropertiesTest_Translation::class, mappedBy: 'entity')]
55+
#[Polyglot\TranslationCollection]
56+
protected Collection $translations;
57+
58+
#[ORM\Column(type: "string")]
59+
public $mappedText;
60+
61+
// (!) This field is unmapped from the ORM point of view
62+
#[Polyglot\Translatable]
63+
public $text;
64+
65+
public function __construct()
66+
{
67+
$this->translations = new ArrayCollection();
68+
$this->text = new Translatable();
69+
}
70+
71+
#[ORM\PreFlush]
72+
public function copyToMappedField(): void
73+
{
74+
$this->mappedText = $this->text;
75+
}
76+
77+
#[ORM\PostLoad]
78+
public function copyFromMappedField(): void
79+
{
80+
$this->text = $this->mappedText;
81+
}
82+
}
83+
84+
#[ORM\Entity]
85+
#[ORM\HasLifecycleCallbacks]
86+
class TranslatingUnmappedPropertiesTest_Translation
87+
{
88+
#[ORM\Column(type: 'integer')]
89+
#[ORM\Id]
90+
#[ORM\GeneratedValue]
91+
private ?int $id = null;
92+
93+
#[ORM\Column(type: "string")]
94+
#[Polyglot\Locale]
95+
private string $locale;
96+
97+
#[ORM\ManyToOne(targetEntity: TranslatingUnmappedPropertiesTest_Entity::class, inversedBy: 'translations')]
98+
private TranslatingUnmappedPropertiesTest_Entity $entity;
99+
100+
#[ORM\Column(type: "string")]
101+
private $mappedText;
102+
103+
// (!) This field is unmapped from the ORM point of view
104+
private $text;
105+
106+
#[ORM\PreFlush]
107+
public function copyToMappedField(): void
108+
{
109+
$this->mappedText = $this->text;
110+
}
111+
112+
#[ORM\PostLoad]
113+
public function copyFromMappedField(): void
114+
{
115+
$this->text = $this->mappedText;
116+
}
117+
}

0 commit comments

Comments
 (0)