Skip to content

Commit

Permalink
Extend AttributeDriver allowing nested attributes on Table (doctr…
Browse files Browse the repository at this point in the history
…ine#11351)

This allows defining indices and unique constraints through the `Table`
attribute which already provides these properties but which aren't
respected by the `AttributeDriver`.
  • Loading branch information
DaDeather committed Mar 13, 2024
1 parent a809a71 commit e6fac25
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 3 deletions.
21 changes: 21 additions & 0 deletions docs/en/reference/attributes-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1078,19 +1078,40 @@ Required parameters:
Optional parameters:

- **schema**: Name of the schema the table lies in.
- **indexes**: A array with instances of :ref:`#[Index] <attrref_index>`
- **uniqueConstraints**: A array with instances of :ref:`#[UniqueConstraint] <attrref_uniqueconstraint>`

Example:

.. code-block:: php
<?php
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Index;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\UniqueConstraint;
#[Entity]
#[Table(name: "user", schema: "schema_name")]
class User { }
#[Entity]
#[Table(
name: "contact",
schema: "schema_name",
indexes: [
new Index(['some_column_1'], name: 'some_name_1'),
new Index(['some_column_2'], name: 'some_name_2'),
],
uniqueConstraints: [
new UniqueConstraint('some_unique_constraint', ['some_column_3']),
],
)]
class Contact
{
// [...]
}
.. _attrref_uniqueconstraint:

#[UniqueConstraint]
Expand Down
83 changes: 80 additions & 3 deletions src/Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use function assert;
use function class_exists;
use function constant;
use function count;
use function defined;
use function get_class;

Expand Down Expand Up @@ -163,9 +164,85 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
$primaryTable = [];

if (isset($classAttributes[Mapping\Table::class])) {
$tableAnnot = $classAttributes[Mapping\Table::class];
$primaryTable['name'] = $tableAnnot->name;
$primaryTable['schema'] = $tableAnnot->schema;
$tableAnnot = $classAttributes[Mapping\Table::class];
$primaryTable['name'] = $tableAnnot->name;
$primaryTable['schema'] = $tableAnnot->schema;
$primaryTable['indexes'] = [];
$primaryTable['uniqueConstraints'] = [];

foreach ($tableAnnot->indexes ?? [] as $indexAnnot) {
$index = [];

if (! empty($indexAnnot->columns)) {
$index['columns'] = $indexAnnot->columns;
}

if (! empty($indexAnnot->fields)) {
$index['fields'] = $indexAnnot->fields;
}

if (
isset($index['columns'], $index['fields'])
|| (
! isset($index['columns'])
&& ! isset($index['fields'])
)
) {
throw MappingException::invalidIndexConfiguration(
$className,
(string) ($indexAnnot->name ?? count($primaryTable['indexes']))
);
}

if (! empty($indexAnnot->flags)) {
$index['flags'] = $indexAnnot->flags;
}

if (! empty($indexAnnot->options)) {
$index['options'] = $indexAnnot->options;
}

if (! empty($indexAnnot->name)) {
$primaryTable['indexes'][$indexAnnot->name] = $index;
} else {
$primaryTable['indexes'][] = $index;
}
}

foreach ($tableAnnot->uniqueConstraints ?? [] as $uniqueConstraintAnnot) {
$uniqueConstraint = [];

if (! empty($uniqueConstraintAnnot->columns)) {
$uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns;
}

if (! empty($uniqueConstraintAnnot->fields)) {
$uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields;
}

if (
isset($uniqueConstraint['columns'], $uniqueConstraint['fields'])
|| (
! isset($uniqueConstraint['columns'])
&& ! isset($uniqueConstraint['fields'])
)
) {
throw MappingException::invalidUniqueConstraintConfiguration(
$className,
(string) ($uniqueConstraintAnnot->name ?? count($primaryTable['uniqueConstraints']))
);
}

if (! empty($uniqueConstraintAnnot->options)) {
$uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
}

if (! empty($uniqueConstraintAnnot->name)) {
$primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
} else {
$primaryTable['uniqueConstraints'][] = $uniqueConstraint;
}
}

if ($tableAnnot->options) {
$primaryTable['options'] = $tableAnnot->options;
Expand Down
32 changes: 32 additions & 0 deletions tests/Tests/ORM/Mapping/AttributeDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Doctrine\ORM\Mapping\MappingAttribute;
use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as PersistenceAnnotationDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Tests\ORM\Mapping\Fixtures\AttributeEntityWithIndicesAndUniqueConstraintInTableAttribute;
use Doctrine\Tests\ORM\Mapping\Fixtures\AttributeEntityWithNestedJoinColumns;
use stdClass;

Expand Down Expand Up @@ -115,6 +116,8 @@ public function testIsTransient(): void
self::assertFalse($driver->isTransient(AttributeEntityWithoutOriginalParents::class));

self::assertFalse($driver->isTransient(AttributeEntityStartingWithRepeatableAttributes::class));

self::assertFalse($driver->isTransient(AttributeEntityWithIndicesAndUniqueConstraintInTableAttribute::class));
}

public function testLegacyInheritance(): void
Expand Down Expand Up @@ -163,8 +166,37 @@ public function testManyToManyAssociationWithNestedJoinColumns(): void
$metadata->associationMappings['assoc']['joinTable']['inverseJoinColumns']
);
}

/**
* @requires PHP 8.1
*/
public function testTableAttributeWithIndicesAndUniqueConstraints(): void
{
$factory = $this->createClassMetadataFactory();

$metadata = $factory->getMetadataFor(AttributeEntityWithIndicesAndUniqueConstraintInTableAttribute::class);

self::assertEquals(
[
'bar' => [
'columns' => [0 => 'id'],
],
],
$metadata->table['indexes']
);

self::assertEquals(
[
'foo' => [
'columns' => [0 => 'id'],
],
],
$metadata->table['uniqueConstraints']
);
}
}


#[ORM\Entity]
#[ORM\UniqueConstraint(name: 'foo', columns: ['id'])]
#[ORM\Index(name: 'bar', columns: ['id'])]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Mapping\Fixtures;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(
indexes: [new ORM\Index(name: 'bar', columns: ['id'])],
uniqueConstraints: [new ORM\UniqueConstraint(name: 'foo', columns: ['id'])],
)]
class AttributeEntityWithIndicesAndUniqueConstraintInTableAttribute
{
/** @var int */
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue]
public $id;
}

0 comments on commit e6fac25

Please sign in to comment.