Skip to content

Commit

Permalink
Extracted method for relationship handling / refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Steveb-p committed Oct 20, 2023
1 parent 7fa4751 commit 49df802
Showing 1 changed file with 73 additions and 63 deletions.
136 changes: 73 additions & 63 deletions src/lib/Gateway/ExpressionVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ final class ExpressionVisitor extends BaseExpressionVisitor
{
private const TRANSLATION_TABLE_ALIAS = 'translation';

/**
* Indicates that comparison should expand into a query against a table with relation to the current table.
*/
public const RELATIONSHIP_DELIMITER = '.';

private QueryBuilder $queryBuilder;

private DoctrineSchemaMetadataInterface $schemaMetadata;
Expand Down Expand Up @@ -79,67 +84,8 @@ public function walkComparison(Comparison $comparison)
{
$column = $comparison->getField();

if (str_contains($column, '.')) {
[
$foreignProperty,
$foreignClassProperty,
] = explode('.', $column, 2);

$relationship = $this->schemaMetadata->getRelationshipByForeignProperty($foreignProperty);
$relationshipMetadata = $this->registry->getMetadata($relationship->getRelationshipClass());

while (str_contains($foreignClassProperty, '.')) {
[
$foreignProperty,
$foreignClassProperty,
] = explode('.', $foreignClassProperty, 2);

$relationship = $relationshipMetadata->getRelationshipByForeignProperty($foreignProperty);
$relationshipMetadata = $this->registry->getMetadata($relationship->getRelationshipClass());
}

if (!$relationshipMetadata->hasColumn($foreignClassProperty)) {
throw new RuntimeMappingException(sprintf(
'"%s" does not exist as available column on "%s" class schema metadata. '
. 'Available columns: "%s". Available relationships: "%s"',
$foreignClassProperty,
$relationshipMetadata->getClassName(),
implode('", "', $relationshipMetadata->getColumns()),
implode('", "', array_map(
static fn (DoctrineRelationshipInterface $relationship): string => $relationship->getForeignProperty(),
$relationshipMetadata->getRelationships(),
)),
));
}

$relationshipType = get_class($relationship);

switch ($relationshipType) {
case DoctrineRelationship::class:
return $this->handleRelationship(
$relationshipMetadata,
$relationship->getForeignKeyColumn(),
$foreignClassProperty,
$comparison->getValue(),
);
case DoctrineOneToManyRelationship::class:
case PreJoinedDoctrineRelationship::class:
return $this->handleOneToManyRelationship(
$relationshipMetadata,
$foreignClassProperty,
$comparison->getValue(),
);
default:
throw new RuntimeMappingException(sprintf(
'Unhandled relationship metadata. Expected one of "%s". Received "%s".',
implode('", "', [
PreJoinedDoctrineRelationship::class,
DoctrineRelationship::class,
DoctrineOneToManyRelationship::class,
]),
$relationshipType,
));
}
if ($this->containsRelationshipDelimiter($column)) {
return $this->handleRelationshipComparison($column, $comparison);
}

if ($this->schemaMetadata->isTranslatedColumn($column)) {
Expand Down Expand Up @@ -259,6 +205,70 @@ public function walkCompositeExpression(CompositeExpression $expr): string
}
}

private function containsRelationshipDelimiter(string $column): bool
{
return str_contains($column, self::RELATIONSHIP_DELIMITER);
}

/**
* @throws \Ibexa\Contracts\CorePersistence\Exception\RuntimeMappingExceptionInterface
*/
private function handleRelationshipComparison(string $column, Comparison $comparison): string
{
do {
[
$foreignProperty,
$column,
] = explode(self::RELATIONSHIP_DELIMITER, $column, 2);

$relationship = $this->schemaMetadata->getRelationshipByForeignProperty($foreignProperty);
$relationshipMetadata = $this->registry->getMetadata($relationship->getRelationshipClass());
} while ($this->containsRelationshipDelimiter($column));

if (!$relationshipMetadata->hasColumn($column)) {
throw new RuntimeMappingException(sprintf(
'"%s" does not exist as available column on "%s" class schema metadata. '
. 'Available columns: "%s". Available relationships: "%s"',
$column,
$relationshipMetadata->getClassName(),
implode('", "', $relationshipMetadata->getColumns()),
implode('", "', array_map(
static fn (DoctrineRelationshipInterface $relationship): string => $relationship->getForeignProperty(),
$relationshipMetadata->getRelationships(),
)),
));
}

$relationshipType = get_class($relationship);

switch ($relationshipType) {
case DoctrineRelationship::class:
return $this->handleSubSelectQuery(
$relationshipMetadata,
$relationship->getForeignKeyColumn(),
$column,
$comparison->getValue(),
);
case DoctrineOneToManyRelationship::class:
case PreJoinedDoctrineRelationship::class:
return $this->handleJoinQuery(
$relationshipMetadata,
$column,
$comparison->getValue(),
);
default:
throw new RuntimeMappingException(sprintf(
'Unhandled relationship metadata. Expected one of "%s". Received "%s".',
implode('", "', [
PreJoinedDoctrineRelationship::class,
DoctrineRelationship::class,
DoctrineOneToManyRelationship::class,
]),
$relationshipType,
));
}
}

private function escapeSpecialSQLValues(Parameter $parameter): string
{
$platform = $this->queryBuilder->getConnection()->getDatabasePlatform();
Expand All @@ -271,7 +281,7 @@ private function expr(): ExpressionBuilder
return $this->queryBuilder->expr();
}

private function handleOneToManyRelationship(
private function handleJoinQuery(
DoctrineSchemaMetadataInterface $relationshipMetadata,
string $field,
Value $value
Expand All @@ -296,7 +306,7 @@ private function handleOneToManyRelationship(
return $this->expr()->eq($tableName . '.' . $field, $placeholder);
}

private function handleRelationship(
private function handleSubSelectQuery(
DoctrineSchemaMetadataInterface $relationshipMetadata,
string $foreignField,
string $field,
Expand Down

0 comments on commit 49df802

Please sign in to comment.