Skip to content

Commit

Permalink
Add automatic type detection for Embedded.
Browse files Browse the repository at this point in the history
  • Loading branch information
Warxcell committed May 25, 2021
1 parent 46f0da9 commit dbab6cf
Show file tree
Hide file tree
Showing 14 changed files with 53 additions and 11 deletions.
2 changes: 1 addition & 1 deletion docs/en/reference/annotations-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ in order to specify that it is an embedded class.

Required attributes:

- **class**: The embeddable class
- **class**: The embeddable class. You can omit this value if you use a PHP property type instead.


.. code-block:: php
Expand Down
2 changes: 1 addition & 1 deletion doctrine-mapping.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@

<xs:complexType name="embedded">
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="required" />
<xs:attribute name="class" type="orm:fqcn" use="optional" />
<xs:attribute name="column-prefix" type="xs:string" use="optional" />
<xs:attribute name="use-column-prefix" type="xs:boolean" default="true" use="optional" />
</xs:complexType>
Expand Down
9 changes: 8 additions & 1 deletion lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -3605,9 +3605,16 @@ public function mapEmbedded(array $mapping)
{
$this->assertFieldNotMapped($mapping['fieldName']);

if (! isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) {
$type = $this->reflClass->getProperty($mapping['fieldName'])->getType();
if ($type) {
$mapping['class'] = $type->getName();
}
}

$this->embeddedClasses[$mapping['fieldName']] = [
'class' => $this->fullyQualifiedClassName($mapping['class']),
'columnPrefix' => $mapping['columnPrefix'],
'columnPrefix' => $mapping['columnPrefix'] ?? null,
'declaredField' => $mapping['declaredField'] ?? null,
'originalField' => $mapping['originalField'] ?? null,
];
Expand Down
4 changes: 3 additions & 1 deletion lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,11 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
? $this->evaluateBoolean($embeddedMapping['use-column-prefix'])
: true;

$class = isset($embeddedMapping['class']) ? (string) $embeddedMapping['class'] : null;

$mapping = [
'fieldName' => (string) $embeddedMapping['name'],
'class' => (string) $embeddedMapping['class'],
'class' => $class,
'columnPrefix' => $useColumnPrefix ? $columnPrefix : false,
];

Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
foreach ($element['embedded'] as $name => $embeddedMapping) {
$mapping = [
'fieldName' => $name,
'class' => $embeddedMapping['class'],
'class' => $embeddedMapping['class'] ?? null,
'columnPrefix' => $embeddedMapping['columnPrefix'] ?? null,
];
$metadata->mapEmbedded($mapping);
Expand Down
7 changes: 2 additions & 5 deletions lib/Doctrine/ORM/Mapping/Embedded.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,13 @@
#[Attribute(Attribute::TARGET_PROPERTY)]
final class Embedded implements Annotation
{
/**
* @Required
* @var string
*/
/** @var string|null */
public $class;

/** @var string|bool|null */
public $columnPrefix;

public function __construct(string $class, $columnPrefix = null)
public function __construct(?string $class = null, $columnPrefix = null)
{
$this->class = $class;
$this->columnPrefix = $columnPrefix;
Expand Down
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ parameters:

-
message: "#^Call to an undefined method ReflectionProperty\\:\\:getType\\(\\)\\.$#"
count: 2
count: 3
path: lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php

-
Expand Down
18 changes: 18 additions & 0 deletions tests/Doctrine/Tests/Models/TypedProperties/Contact.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\TypedProperties;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Embeddable()
*/
#[ORM\Embeddable]
class Contact
{
/** @Column() */
#[ORM\Column]
public ?string $email = null;
}
7 changes: 7 additions & 0 deletions tests/Doctrine/Tests/Models/TypedProperties/UserTyped.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class UserTyped
*/
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
public int $id;

/** @Column(length=50) */
#[ORM\Column(length: 50)]
public ?string $status;
Expand Down Expand Up @@ -67,6 +68,10 @@ class UserTyped
#[ORM\ManyToOne]
public ?CmsEmail $mainEmail;

/** @Embedded */
#[ORM\Embedded]
public ?Contact $contact = null;

public static function loadMetadata(ClassMetadataInfo $metadata): void
{
$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_NONE);
Expand Down Expand Up @@ -131,5 +136,7 @@ public static function loadMetadata(ClassMetadataInfo $metadata): void
$metadata->mapManyToOne(
['fieldName' => 'mainEmail']
);

$metadata->mapEmbedded(['fieldName' => 'contact']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use Doctrine\Tests\Models\DDC889\DDC889Entity;
use Doctrine\Tests\Models\DDC964\DDC964Admin;
use Doctrine\Tests\Models\DDC964\DDC964Guest;
use Doctrine\Tests\Models\TypedProperties\Contact;
use Doctrine\Tests\Models\TypedProperties\UserTyped;
use Doctrine\Tests\OrmTestCase;

Expand Down Expand Up @@ -281,6 +282,7 @@ public function testFieldIsNullableByType(): void

$this->assertEquals(CmsEmail::class, $class->getAssociationMapping('email')['targetEntity']);
$this->assertEquals(CmsEmail::class, $class->getAssociationMapping('mainEmail')['targetEntity']);
$this->assertEquals(Contact::class, $class->embeddedClasses['contact']['class']);
}

public function testFieldTypeFromReflection(): void
Expand Down
3 changes: 3 additions & 0 deletions tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ public function testFieldIsNullableByType(): void

$cm->mapManyToOne(['fieldName' => 'mainEmail']);
$this->assertEquals(CmsEmail::class, $cm->getAssociationMapping('mainEmail')['targetEntity']);

$cm->mapEmbedded(['fieldName' => 'contact']);
$this->assertEquals(TypedProperties\Contact::class, $cm->embeddedClasses['contact']['class']);
}

public function testFieldTypeFromReflection(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@
$metadata->mapManyToOne(
['fieldName' => 'mainEmail']
);

$metadata->mapEmbedded(['fieldName' => 'contact']);
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@
</one-to-one>

<many-to-one field="mainEmail"/>

<embedded name="contact" />
</entity>
</doctrine-mapping>
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Doctrine\Tests\Models\TypedProperties\UserTyped:
joinColumn: []
manyToOne:
mainEmail: []
embedded:
contact: ~

0 comments on commit dbab6cf

Please sign in to comment.