Skip to content

Commit

Permalink
IBX-6824: Fixed doctrine metadata property/foreign key relationship m…
Browse files Browse the repository at this point in the history
…ethods
  • Loading branch information
Steveb-p authored Oct 18, 2023
1 parent 62a007a commit b235b78
Show file tree
Hide file tree
Showing 9 changed files with 412 additions and 36 deletions.
2 changes: 1 addition & 1 deletion src/contracts/Exception/MappingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use Exception;

final class MappingException extends Exception
final class MappingException extends Exception implements MappingExceptionInterface
{
public static function singleIdNotAllowedOnCompositePrimaryKey(): self
{
Expand Down
15 changes: 15 additions & 0 deletions src/contracts/Exception/MappingExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\CorePersistence\Exception;

use Throwable;

interface MappingExceptionInterface extends Throwable
{
}
15 changes: 15 additions & 0 deletions src/contracts/Exception/RuntimeMappingException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\CorePersistence\Exception;

use RuntimeException;

final class RuntimeMappingException extends RuntimeException implements RuntimeMappingExceptionInterface
{
}
15 changes: 15 additions & 0 deletions src/contracts/Exception/RuntimeMappingExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\CorePersistence\Exception;

use Throwable;

interface RuntimeMappingExceptionInterface extends Throwable
{
}
72 changes: 46 additions & 26 deletions src/contracts/Gateway/DoctrineSchemaMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Type;
use Ibexa\Contracts\CorePersistence\Exception\MappingException;
use InvalidArgumentException;
use LogicException;
use RuntimeException;
use Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingException;

/**
* @internal
Expand Down Expand Up @@ -48,7 +46,12 @@ class DoctrineSchemaMetadata implements DoctrineSchemaMetadataInterface
/**
* @var array<non-empty-string, \Ibexa\Contracts\CorePersistence\Gateway\DoctrineRelationshipInterface>
*/
private array $relationshipColumns = [];
private array $propertyToRelationship = [];

/**
* @var array<non-empty-string, \Ibexa\Contracts\CorePersistence\Gateway\DoctrineRelationshipInterface>
*/
private array $columnToRelationship = [];

/** @var class-string|null */
private ?string $className;
Expand Down Expand Up @@ -116,9 +119,6 @@ public function getColumnType(string $column): Type
return $this->columnTypeCache[$column];
}

/**
* @throws \Doctrine\DBAL\Exception
*/
private function resolveColumnType(string $column): Type
{
if (isset($this->columnToTypesMap[$column])) {
Expand Down Expand Up @@ -167,7 +167,7 @@ private function resolveColumnType(string $column): Type
);
}

throw new InvalidArgumentException(sprintf(
throw new RuntimeMappingException(sprintf(
'Column "%s" does not exist in "%s" table. Available columns: "%s"',
$column,
$this->getTableName(),
Expand Down Expand Up @@ -206,9 +206,6 @@ public function isInheritedColumn(string $column): bool
return $this->getInheritanceMetadataWithColumn($column) !== null;
}

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\MappingException
*/
public function getIdentifierColumn(): string
{
if (count($this->identifierColumns) > 1) {
Expand Down Expand Up @@ -288,7 +285,7 @@ public function hasTranslationSchemaMetadata(): bool
public function getTranslationSchemaMetadata(): TranslationDoctrineSchemaMetadataInterface
{
if (!isset($this->translationMetadata)) {
throw new LogicException(sprintf(
throw new RuntimeMappingException(sprintf(
'%s does not contain translation metadata. Ensure that %1$s::%s has been called.',
DoctrineSchemaMetadata::class,
'setTranslationSchemaMetadata',
Expand Down Expand Up @@ -316,15 +313,15 @@ public function isTranslatedColumn(string $column): bool
public function getSubclassByDiscriminator(string $discriminator): DoctrineSchemaMetadataInterface
{
if (empty($this->discriminatorMap)) {
throw new RuntimeException(sprintf(
throw new RuntimeMappingException(sprintf(
'"%s" is not registered as a subclass for table "%s". There are no registered subclasses',
$discriminator,
$this->getTableName(),
));
}

if (!isset($this->discriminatorMap[$discriminator])) {
throw new RuntimeException(sprintf(
throw new RuntimeMappingException(sprintf(
'"%s" is not registered as a subclass for table "%s". Available discriminators: "%s"',
$discriminator,
$this->getTableName(),
Expand All @@ -347,7 +344,7 @@ public function addSubclass(string $discriminator, DoctrineSchemaMetadataInterfa
{
$this->inheritanceType = self::INHERITANCE_TYPE_JOINED;
if (isset($this->discriminatorMap[$discriminator])) {
throw new LogicException(sprintf(
throw new MappingException(sprintf(
'"%s" is already added as a discriminator for a subtype.',
$discriminator,
));
Expand All @@ -364,33 +361,56 @@ public function isInheritanceTypeJoined(): bool
public function addRelationship(DoctrineRelationshipInterface $relationship): void
{
$foreignProperty = $relationship->getForeignProperty();

if (isset($this->relationshipColumns[$foreignProperty])) {
throw new \LogicException(sprintf(
if (isset($this->propertyToRelationship[$foreignProperty])) {
throw new MappingException(sprintf(
'"%s" is already added as foreign property.',
$foreignProperty,
));
}

$this->relationshipColumns[$foreignProperty] = $relationship;
$this->propertyToRelationship[$foreignProperty] = $relationship;

$foreignColumn = $relationship->getForeignKeyColumn();
if (isset($this->columnToRelationship[$foreignColumn])) {
throw new MappingException(sprintf(
'"%s" is already added as foreign column.',
$foreignColumn,
));
}

$this->columnToRelationship[$foreignColumn] = $relationship;
}

public function getRelationships(): array
{
return $this->relationshipColumns;
return $this->propertyToRelationship;
}

public function getRelationshipByForeignKeyColumn(string $foreignProperty): DoctrineRelationshipInterface
public function getRelationshipByForeignProperty(string $foreignProperty): DoctrineRelationshipInterface
{
if (!isset($this->relationshipColumns[$foreignProperty])) {
throw new InvalidArgumentException(sprintf(
'"%s" does not exist as a relationship for "%s" class metadata. Available relationship: "%s"',
if (!isset($this->propertyToRelationship[$foreignProperty])) {
throw new RuntimeMappingException(sprintf(
'"%s" does not exist as a relationship for "%s" class metadata. Available relationship property: "%s"',
$foreignProperty,
$this->className,
implode('", "', array_keys($this->relationshipColumns)),
implode('", "', array_keys($this->propertyToRelationship)),
));
}

return $this->propertyToRelationship[$foreignProperty];
}

public function getRelationshipByForeignKeyColumn(string $foreignColumn): DoctrineRelationshipInterface
{
if (!isset($this->columnToRelationship[$foreignColumn])) {
throw new RuntimeMappingException(sprintf(
'"%s" does not exist as a relationship for "%s" class metadata. Available relationship columns: "%s"',
$foreignColumn,
$this->className,
implode('", "', array_keys($this->columnToRelationship)),
));
}

return $this->relationshipColumns[$foreignProperty];
return $this->columnToRelationship[$foreignColumn];
}
}
36 changes: 35 additions & 1 deletion src/contracts/Gateway/DoctrineSchemaMetadataInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public function getClassName(): ?string;
*/
public function getTableName(): string;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getColumnType(string $column): Type;

/**
Expand All @@ -43,6 +46,9 @@ public function getColumns(): array;

public function hasColumn(string $column): bool;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getColumn(string $column): string;

public function getInheritanceMetadataWithColumn(string $column): ?self;
Expand Down Expand Up @@ -70,13 +76,17 @@ public function convertToPHPValues(array $data): array;
* @param array<string, mixed> $data
*
* @return array<string, mixed>
*
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function convertToDatabaseValues(array $data): array;

/**
* @param array<string, mixed> $data
*
* @return array<string, int>
*
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getBindingTypesForData(array $data): array;

Expand All @@ -87,9 +97,13 @@ public function getIdentifierColumn(): string;

/**
* @throws \Doctrine\DBAL\Exception
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getBindingTypeForColumn(string $columnName): int;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\MappingExceptionInterface
*/
public function setParentMetadata(self $parentMetadata): void;

public function getParentMetadata(): ?self;
Expand All @@ -101,24 +115,44 @@ public function getSubclassByDiscriminator(string $discriminator): self;
*/
public function getSubclasses(): array;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\MappingExceptionInterface
*/
public function addSubclass(string $discriminator, self $doctrineSchemaMetadata): void;

public function isInheritanceTypeJoined(): bool;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\MappingExceptionInterface
*/
public function setTranslationSchemaMetadata(TranslationDoctrineSchemaMetadataInterface $translationMetadata): void;

public function hasTranslationSchemaMetadata(): bool;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getTranslationSchemaMetadata(): TranslationDoctrineSchemaMetadataInterface;

public function isTranslatedColumn(string $column): bool;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\MappingExceptionInterface
*/
public function addRelationship(DoctrineRelationshipInterface $relationship): void;

/**
* @return array<non-empty-string, \Ibexa\Contracts\CorePersistence\Gateway\DoctrineRelationshipInterface>
*/
public function getRelationships(): array;

public function getRelationshipByForeignKeyColumn(string $foreignProperty): DoctrineRelationshipInterface;
/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getRelationshipByForeignProperty(string $foreignProperty): DoctrineRelationshipInterface;

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function getRelationshipByForeignKeyColumn(string $foreignColumn): DoctrineRelationshipInterface;
}
22 changes: 20 additions & 2 deletions src/lib/Gateway/ExpressionVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use Doctrine\DBAL\Query\QueryBuilder;
use Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingException;
use Ibexa\Contracts\CorePersistence\Gateway\DoctrineOneToManyRelationship;
use Ibexa\Contracts\CorePersistence\Gateway\DoctrineRelationship;
use Ibexa\Contracts\CorePersistence\Gateway\DoctrineSchemaMetadataInterface;
Expand Down Expand Up @@ -69,6 +70,9 @@ public function clearParameters(): void
$this->parameters = [];
}

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
public function walkComparison(Comparison $comparison)
{
$column = $comparison->getField();
Expand All @@ -79,10 +83,10 @@ public function walkComparison(Comparison $comparison)
$foreignClassProperty,
] = explode('.', $column, 2);

$relationship = $this->schemaMetadata->getRelationshipByForeignKeyColumn($foreignProperty);
$relationship = $this->schemaMetadata->getRelationshipByForeignProperty($foreignProperty);
$relationshipMetadata = $this->registry->getMetadata($relationship->getRelationshipClass());
if (!$relationshipMetadata->hasColumn($foreignClassProperty)) {
throw new \InvalidArgumentException(sprintf(
throw new RuntimeMappingException(sprintf(
'"%s" does not exist as available column on "%s" class schema metadata. Available columns: "%s".',
$foreignClassProperty,
$relationshipMetadata->getClassName(),
Expand All @@ -106,13 +110,27 @@ public function walkComparison(Comparison $comparison)
$foreignClassProperty,
$comparison->getValue(),
);
default:
throw new RuntimeMappingException(sprintf(
'Unhandled relationship metadata. Expected one of "%s". Received "%s".',
implode('", "', [DoctrineRelationship::class, DoctrineOneToManyRelationship::class]),
$relationshipType,
));
}
}

if ($this->schemaMetadata->isTranslatedColumn($column)) {
return $this->handleTranslation($comparison);
}

if (!$this->schemaMetadata->hasColumn($column)) {
throw new RuntimeMappingException(sprintf(
'%s table metadata does not contain %s column.',
$this->schemaMetadata->getTableName(),
$column,
));
}

$parameterName = $column . '_' . count($this->parameters);
$placeholder = ':' . $parameterName;
$value = $this->walkValue($comparison->getValue());
Expand Down
Loading

0 comments on commit b235b78

Please sign in to comment.