Skip to content

Commit

Permalink
feature #1468 [Autocomplete] Add support for doctrine/orm:^3.0 (evert…
Browse files Browse the repository at this point in the history
…harmeling)

This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[Autocomplete] Add support for doctrine/orm:^3.0

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes-ish?
| Issues        | Replaces #1459
| License       | MIT

Follow up on #1459 as it seemed a bit more complicated :)

Because in the current `getPropertyMetadata` function, [see](https://github.com/symfony/ux/blob/2.x/src/Autocomplete/src/Doctrine/EntityMetadata.php#L44) and below:

```php
public function getPropertyMetadata(string $propertyName): array
{
    if (\array_key_exists($propertyName, $this->metadata->fieldMappings)) {
        return $this->metadata->fieldMappings[$propertyName];
    }

    if (\array_key_exists($propertyName, $this->metadata->associationMappings)) {
        return $this->metadata->associationMappings[$propertyName];
    }

    throw new \InvalidArgumentException(sprintf('The "%s" field does not exist in the "%s" entity.', $propertyName, $this->metadata->getName()));
}
```

The function combines getting metadata from `fieldMappings` and `associationMappings`, that both return an `array` in **Doctrine ORM 2**.

This however changes in **Doctrine ORM 3** in `fieldMappings` returning a `FieldMapping` object and `associationMappings` in an `AssociationMapping` object.

To support both Doctrine ORM 2 and 3, I didn't changed the return type of the function for now, as we would otherwise need to bump the major version for the `Autocomplete` component.

To ease the transition to **Doctrine ORM 3** in the future, I've splitted `getPropertyMetadata` in `getFieldMetadata` and `getAssociationMetadata`.

## TODO
- [x] Should we keep the `getPropertyMetadata()` function as a proxy to the new methods? (I removed it for now as the method shouldn't be used directly as [mentioned](#1459 (comment)).
- [x] Should we retroactively add the ``@internal`` the `src/Autocomplete/src/Doctrine/EntityMetadata.php` class also (as [mentioned](#1459 (comment)))?
- [ ] Test on Doctrine ORM 3, currently blocked by zenstruck/foundry#556

Commits
-------

20f4dad [Autocomplete] Add support for doctrine/orm:^3.0
  • Loading branch information
weaverryan committed Feb 15, 2024
2 parents 6a08c6c + 20f4dad commit 2ba75f6
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/Autocomplete/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## Unreleased

- Add doctrine/orm 3 support.

## 2.14.0

- Fixed behavior of Autocomplete when the underlying `select` or `option`
Expand Down
3 changes: 2 additions & 1 deletion src/Autocomplete/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"require": {
"php": ">=8.1",
"symfony/dependency-injection": "^6.3|^7.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-foundation": "^6.3|^7.0",
"symfony/http-kernel": "^6.3|^7.0",
"symfony/property-access": "^6.3|^7.0",
Expand All @@ -34,7 +35,7 @@
"require-dev": {
"doctrine/collections": "^1.6.8|^2.0",
"doctrine/doctrine-bundle": "^2.4.3",
"doctrine/orm": "^2.9.4",
"doctrine/orm": "^2.9.4|^3.0",
"fakerphp/faker": "^1.22",
"mtdowling/jmespath.php": "^2.6",
"symfony/form": "^6.3|^7.0",
Expand Down
38 changes: 35 additions & 3 deletions src/Autocomplete/src/Doctrine/EntityMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,53 @@ public function isEmbeddedClassProperty(string $propertyName): bool
}

public function getPropertyMetadata(string $propertyName): array
{
trigger_deprecation('symfony/ux-autocomplete', '2.15.0', 'Calling EntityMetadata::getPropertyMetadata() is deprecated. You should stop using it, as it will be removed in the future.');

try {
return $this->getFieldMetadata($propertyName);
} catch (\InvalidArgumentException $e) {
return $this->getAssociationMetadata($propertyName);
}
}

/**
* @internal
*
* @return array<string, mixed>
*/
public function getFieldMetadata(string $propertyName): array
{
if (\array_key_exists($propertyName, $this->metadata->fieldMappings)) {
return $this->metadata->fieldMappings[$propertyName];
// Cast to array, because in doctrine/orm:^3.0; $metadata will be a FieldMapping object
return (array) $this->metadata->fieldMappings[$propertyName];
}

throw new \InvalidArgumentException(sprintf('The "%s" field does not exist in the "%s" entity.', $propertyName, $this->metadata->getName()));
}

/**
* @internal
*
* @return array<string, mixed>
*/
public function getAssociationMetadata(string $propertyName): array
{
if (\array_key_exists($propertyName, $this->metadata->associationMappings)) {
return $this->metadata->associationMappings[$propertyName];
// Cast to array, because in doctrine/orm:^3.0; $metadata will be an AssociationMapping object
return (array) $this->metadata->associationMappings[$propertyName];
}

throw new \InvalidArgumentException(sprintf('The "%s" field does not exist in the "%s" entity.', $propertyName, $this->metadata->getName()));
}

public function getPropertyDataType(string $propertyName): string
{
return $this->getPropertyMetadata($propertyName)['type'];
if (\array_key_exists($propertyName, $this->metadata->fieldMappings)) {
return $this->getFieldMetadata($propertyName)['type'];
}

return $this->getAssociationMetadata($propertyName)['type'];
}

public function getIdValue(object $entity): string
Expand Down
7 changes: 3 additions & 4 deletions src/Autocomplete/src/Doctrine/EntitySearchUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function addSearchClause(QueryBuilder $queryBuilder, string $query, strin
}

$originalPropertyName = $associatedProperties[0];
$originalPropertyMetadata = $entityMetadata->getPropertyMetadata($originalPropertyName);
$originalPropertyMetadata = $entityMetadata->getAssociationMetadata($originalPropertyName);
$associatedEntityDto = $this->metadataFactory->create($originalPropertyMetadata['targetEntity']);

for ($i = 0; $i < $numAssociatedProperties - 1; ++$i) {
Expand All @@ -75,9 +75,8 @@ public function addSearchClause(QueryBuilder $queryBuilder, string $query, strin
}

if ($i < $numAssociatedProperties - 2) {
$propertyMetadata = $associatedEntityDto->getPropertyMetadata($associatedPropertyName);
$targetEntity = $propertyMetadata['targetEntity'];
$associatedEntityDto = $this->metadataFactory->create($targetEntity);
$propertyMetadata = $associatedEntityDto->getAssociationMetadata($associatedPropertyName);
$associatedEntityDto = $this->metadataFactory->create($propertyMetadata['targetEntity']);
}
}

Expand Down
45 changes: 43 additions & 2 deletions src/Autocomplete/tests/Integration/Doctrine/EntityMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function testGetPropertyDataType(): void
$this->assertEquals(ClassMetadataInfo::MANY_TO_ONE, $metadata->getPropertyDataType('category'));
}

public function testGetPropertyMetadata(): void
public function testGetFieldMetadata(): void
{
$metadata = $this->getMetadata();
$this->assertSame([
Expand All @@ -66,7 +66,48 @@ public function testGetPropertyMetadata(): void
'nullable' => false,
'precision' => null,
'columnName' => 'name',
], $metadata->getPropertyMetadata('name'));
], $metadata->getFieldMetadata('name'));
}

public function testGetAssociationMetadata(): void
{
$metadata = $this->getMetadata();
$this->assertSame([
'fieldName' => 'category',
'joinColumns' => [
[
'name' => 'category_id',
'unique' => false,
'nullable' => false,
'onDelete' => null,
'columnDefinition' => null,
'referencedColumnName' => 'id',
],
],
'cascade' => [],
'inversedBy' => 'products',
'targetEntity' => 'Symfony\UX\Autocomplete\Tests\Fixtures\Entity\Category',
'fetch' => 2,
'type' => 2,
'mappedBy' => null,
'isOwningSide' => true,
'sourceEntity' => 'Symfony\UX\Autocomplete\Tests\Fixtures\Entity\Product',
'isCascadeRemove' => false,
'isCascadePersist' => false,
'isCascadeRefresh' => false,
'isCascadeMerge' => false,
'isCascadeDetach' => false,
'sourceToTargetKeyColumns' => [
'category_id' => 'id',
],
'joinColumnFieldNames' => [
'category_id' => 'category_id',
],
'targetToSourceKeyColumns' => [
'id' => 'category_id',
],
'orphanRemoval' => false,
], $metadata->getAssociationMetadata('category'));
}

public function testIsEmbeddedClassProperty(): void
Expand Down

0 comments on commit 2ba75f6

Please sign in to comment.