diff --git a/src/Drivers/EloquentEntitySet.php b/src/Drivers/EloquentEntitySet.php index e75afbe78..6b41ade48 100644 --- a/src/Drivers/EloquentEntitySet.php +++ b/src/Drivers/EloquentEntitySet.php @@ -56,6 +56,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasOneOrMany; @@ -393,6 +394,7 @@ protected function getRelationships(): array $expansionSet->configureBuilder($builder); }; + $relations = array_merge( $relations, Collection::make($expansionSet->getRelationships()) @@ -541,24 +543,30 @@ public function discoverRelationship( $navigationProperty->setNullable($nullable); - if ($relation instanceof HasOneOrMany || $relation instanceof BelongsTo) { + if ($relation instanceof HasOneOrMany || $relation instanceof BelongsTo || $relation instanceof HasManyThrough) { $localProperty = null; $foreignProperty = null; + switch (true) { case $relation instanceof HasOneOrMany: - $localProperty = $this->getPropertyBySourceName($relation->getLocalKeyName()); - $foreignProperty = $right->getPropertyBySourceName($relation->getForeignKeyName()); + $localProperty = $this->getPropertyBySourceName(is_array($relation->getLocalKeyName()) ? $relation->getLocalKeyName()[0] : $relation->getLocalKeyName()); + $foreignProperty = $right->getPropertyBySourceName(is_array($relation->getForeignKeyName()) ? $relation->getForeignKeyName()[0] : $relation->getForeignKeyName()); break; case $relation instanceof BelongsTo: - $localProperty = $this->getPropertyBySourceName($relation->getForeignKeyName()); - $foreignProperty = $right->getPropertyBySourceName($relation->getOwnerKeyName()); + $localProperty = $this->getPropertyBySourceName(is_array($relation->getForeignKeyName()) ? $relation->getForeignKeyName()[0] : $relation->getForeignKeyName()); + $foreignProperty = $right->getPropertyBySourceName(is_array($relation->getOwnerKeyName()) ? $relation->getOwnerKeyName()[0] : $relation->getOwnerKeyName()); + break; + + case $relation instanceof HasManyThrough: + $localProperty = $this->getPropertyBySourceName(is_array($relation->getLocalKeyName()) ? $relation->getLocalKeyName()[0] : $relation->getLocalKeyName()); + $foreignProperty = $right->getPropertyBySourceName(is_array($relation->getForeignKeyName()) ? $relation->getForeignKeyName()[0] : $relation->getForeignKeyName()); break; } if ($localProperty && $foreignProperty) { - $referentialConstraint = new ReferentialConstraint($localProperty, $foreignProperty); + $referentialConstraint = new ReferentialConstraint($localProperty, $foreignProperty, $relation); $navigationProperty->addConstraint($referentialConstraint); } } @@ -813,4 +821,4 @@ public function discover(): self return $this; } -} +} \ No newline at end of file diff --git a/src/Drivers/SQL/SQLExpression.php b/src/Drivers/SQL/SQLExpression.php index d8d9b9bfd..3adeb812f 100644 --- a/src/Drivers/SQL/SQLExpression.php +++ b/src/Drivers/SQL/SQLExpression.php @@ -66,6 +66,7 @@ use Flat3\Lodata\NavigationProperty; use Flat3\Lodata\ReferentialConstraint; use Flat3\Lodata\Type; +use Illuminate\Database\Eloquent\Relations\HasManyThrough; /** * SQL Expression, with its associated parameters @@ -1317,16 +1318,29 @@ protected function lambdaExpression(Lambda $node): void $constraint = array_shift($constraints); $field = $this->entitySet->propertyToExpression($constraint->getProperty()); - $this->pushParameters($field->getParameters()); - - $this->pushStatement( - sprintf('( %s = %s ( SELECT %s from %s WHERE', - $field->getStatement(), - $node instanceof Lambda\Any ? 'ANY' : 'ALL', - $targetSet->propertyToExpression($constraint->getReferencedProperty())->getStatement(), - $targetSet->quoteSingleIdentifier($targetSet->getTable()), - ) - ); + + $relation = $constraint->getRelation(); + if (isset($relation) && $relation instanceof HasManyThrough) { + $constraint->getRelation()->select($relation->getQualifiedFirstKeyName()); + $where = preg_replace('/\s+where\s+.*$/i', '', $constraint->getRelation()->toSql()); + $this->pushStatement( + sprintf('( %s = %s ( %s WHERE', + $field->getStatement(), + $node instanceof Lambda\Any ? 'ANY' : 'ALL', + $where, + ) + ); + } else { + $this->pushParameters($field->getParameters()); + $this->pushStatement( + sprintf('( %s = %s ( SELECT %s from %s WHERE', + $field->getStatement(), + $node instanceof Lambda\Any ? 'ANY' : 'ALL', + $targetSet->propertyToExpression($constraint->getReferencedProperty())->getStatement(), + $targetSet->quoteSingleIdentifier($targetSet->getTable()), + ) + ); + } $operatingTargetSet = clone $targetSet; @@ -1335,6 +1349,7 @@ protected function lambdaExpression(Lambda $node): void $targetExpression->evaluate($lambdaExpression); $parser->popEntitySet(); + $this->pushStatement($targetExpression->getStatement()); $this->parameters = array_merge($this->parameters, $targetExpression->getParameters()); @@ -1345,4 +1360,4 @@ protected function lambdaExpression(Lambda $node): void } } } -} \ No newline at end of file +} diff --git a/src/ReferentialConstraint.php b/src/ReferentialConstraint.php index caf4cfcc6..88791c0d7 100644 --- a/src/ReferentialConstraint.php +++ b/src/ReferentialConstraint.php @@ -4,6 +4,8 @@ namespace Flat3\Lodata; +use Illuminate\Database\Eloquent\Relations\Relation; + /** * Referential Constraint * @link https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#_Toc38530370 @@ -23,10 +25,13 @@ class ReferentialConstraint */ protected $referencedProperty; - public function __construct(Property $property, Property $referenced_property) + private $relation; + + public function __construct(Property $property, Property $referenced_property, Relation $relation = null) { $this->property = $property; $this->referencedProperty = $referenced_property; + $this->relation = $relation; } /** @@ -47,6 +52,15 @@ public function getReferencedProperty(): Property return $this->referencedProperty; } + /** + * Get the referenced relation + * @return Relation|null + */ + public function getRelation(): ?Relation + { + return $this->relation; + } + /** * @return string */