diff --git a/src/Collection/Functions/FetchPropertyFunction.php b/src/Collection/Functions/FetchPropertyFunction.php index 78d53282..1972d0a8 100644 --- a/src/Collection/Functions/FetchPropertyFunction.php +++ b/src/Collection/Functions/FetchPropertyFunction.php @@ -207,6 +207,18 @@ private function processTokens( foreach ($tokens as $tokenIndex => $token) { $property = $currentEntityMetadata->getProperty($token); if ($property->relationship !== null) { + $relType = $property->relationship->type; + $isMainSide = $relType === Relationship::MANY_HAS_ONE + || ($relType === Relationship::ONE_HAS_ONE && $property->relationship->isMain); + if ( + $isMainSide + && $tokenIndex === count($tokens) - 1 + && in_array($lastToken, $property->relationship->entityMetadata->getPrimaryKey(), strict: true) + ) { + $lastToken = $token; + break; + } + [ $currentAlias, $currentConventions, @@ -241,6 +253,36 @@ private function processTokens( throw new InvalidArgumentException("Property expression '$propertyExpression' does not fetch specific property."); } + if ($propertyMetadata->relationship !== null) { + $relType = $propertyMetadata->relationship->type; + if ( + ($relType === Relationship::ONE_HAS_ONE && !$propertyMetadata->relationship->isMain) || + $relType === Relationship::ONE_HAS_MANY || + $relType === Relationship::MANY_HAS_MANY + ) { + $allTokens = [...$tokens, $lastToken]; + [ + $currentAlias, + $currentConventions, + $currentEntityMetadata, + ] = $this->processRelationship( + $allTokens, + $joins, + $propertyMetadata, + $aggregator, + $currentConventions, + $currentMapper, + $currentAlias, + $lastToken, + count($allTokens) - 1, + $makeDistinct, + ); + $primaryKey = $currentEntityMetadata->getPrimaryKey(); + $lastToken = $primaryKey[0]; + $propertyMetadata = $currentEntityMetadata->getProperty($lastToken); + } + } + $column = $this->toColumnExpr( $currentEntityMetadata, $propertyMetadata, diff --git a/tests/cases/integration/Collection/collection.where.phpt b/tests/cases/integration/Collection/collection.where.phpt index c6789d07..103b102d 100644 --- a/tests/cases/integration/Collection/collection.where.phpt +++ b/tests/cases/integration/Collection/collection.where.phpt @@ -175,6 +175,40 @@ class CollectionWhereTest extends DataTestCase } + public function testFilterByNonMainSideRelationship(): void + { + // Non-main side of 1:1 relationship (Ean.book is non-main, FK ean_id is in books table) + $ean = new Ean(); + $ean->code = '1234'; + $ean->book = $this->orm->books->getByIdChecked(1); + $this->orm->eans->persistAndFlush($ean); + $this->orm->clear(); + + $fetched = $this->orm->eans->findBy(['book' => 1])->fetch(); + Assert::notNull($fetched); + Assert::equal('1234', $fetched->code); + + Assert::null($this->orm->eans->findBy(['book' => 2])->fetch()); + } + + + public function testFilterByNonMainSideRelationshipNull(): void + { + // Non-main side of 1:1 self-referential relationship (Book.previousPart is non-main side) + // Book 4 has next_part = 3, so Book 3 has a previousPart (= Book 4) + // Books 1, 2, 4 have no previousPart + + $booksWithNoPrevious = $this->orm->books->findBy(['previousPart' => null]); + Assert::count(3, $booksWithNoPrevious); + + $booksWithPrevious = $this->orm->books->findBy(['previousPart!=' => null]); + Assert::count(1, $booksWithPrevious); + $book = $booksWithPrevious->fetch(); + Assert::notNull($book); + Assert::equal(3, $book->id); + } + + private function moveToDifferentZone(DateTimeImmutable $dateTime): DateTimeImmutable { return $dateTime->setTimezone(new DateTimeZone("UTC")); diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionAggregationJoinTest_testAnyDependent.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionAggregationJoinTest_testAnyDependent.sql index 942b99a7..6cc87b1d 100644 --- a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionAggregationJoinTest_testAnyDependent.sql +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionAggregationJoinTest_testAnyDependent.sql @@ -5,13 +5,10 @@ FROM LEFT JOIN "books" AS "books_any" ON ( "authors"."id" = "books_any"."author_id" ) - LEFT JOIN "public"."authors" AS "books_translator_any" ON ( - "books_any"."translator_id" = "books_translator_any"."id" - ) WHERE ("books_any"."title" = 'Book 1') AND ( - "books_translator_any"."id" IS NULL + "books_any"."translator_id" IS NULL ) GROUP BY "authors"."id"; @@ -27,13 +24,10 @@ FROM LEFT JOIN "books" AS "books_any" ON ( "authors"."id" = "books_any"."author_id" ) - LEFT JOIN "public"."authors" AS "books_translator_any" ON ( - "books_any"."translator_id" = "books_translator_any"."id" - ) WHERE ("books_any"."title" = 'Book 1') AND ( - "books_translator_any"."id" IS NULL + "books_any"."translator_id" IS NULL ) GROUP BY "authors"."id" @@ -45,7 +39,10 @@ FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON ( ( - "authors"."id" = "books_count"."author_id" + ( + "authors"."id" = "books_count"."author_id" + ) + AND "books_count"."translator_id" IS NOT NULL ) OR ( ( @@ -54,18 +51,12 @@ FROM AND "books_count"."price" < 100 ) ) - LEFT JOIN "public"."authors" AS "books_translator_count" ON ( - ( - "books_count"."translator_id" = "books_translator_count"."id" - ) - AND "books_translator_count"."id" IS NOT NULL - ) GROUP BY "authors"."id" HAVING ( - COUNT("books_translator_count"."id") >= 1 - AND COUNT("books_translator_count"."id") <= 1 + COUNT("books_count"."id") >= 1 + AND COUNT("books_count"."id") <= 1 ) OR ( COUNT("books_count"."id") >= 1 @@ -82,7 +73,10 @@ FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON ( ( - "authors"."id" = "books_count"."author_id" + ( + "authors"."id" = "books_count"."author_id" + ) + AND "books_count"."translator_id" IS NOT NULL ) OR ( ( @@ -91,18 +85,12 @@ FROM AND "books_count"."price" < 100 ) ) - LEFT JOIN "public"."authors" AS "books_translator_count" ON ( - ( - "books_count"."translator_id" = "books_translator_count"."id" - ) - AND "books_translator_count"."id" IS NOT NULL - ) GROUP BY "authors"."id" HAVING ( - COUNT("books_translator_count"."id") >= 1 - AND COUNT("books_translator_count"."id") <= 1 + COUNT("books_count"."id") >= 1 + AND COUNT("books_count"."id") <= 1 ) OR ( COUNT("books_count"."id") >= 1 diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testCountStoredAndFutureFiltering.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testCountStoredAndFutureFiltering.sql index 59fa3124..2e909231 100644 --- a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testCountStoredAndFutureFiltering.sql +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testCountStoredAndFutureFiltering.sql @@ -1,26 +1,2 @@ -SELECT - COUNT(*) AS count -FROM - ( - SELECT - "books"."id" - FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) - WHERE - "author"."id" > 0 - ) temp; - -SELECT - "books".* -FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) -WHERE - "author"."id" > 0 -ORDER BY - "author"."id" ASC; +SELECT COUNT(*) AS count FROM (SELECT "books"."id" FROM "books" AS "books" WHERE "books"."author_id" > 0) temp; +SELECT "books".* FROM "books" AS "books" WHERE "books"."author_id" > 0 ORDER BY "books"."author_id" ASC; diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrdering.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrdering.sql index 44252c50..980a2ec9 100644 --- a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrdering.sql +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrdering.sql @@ -1,21 +1,2 @@ -SELECT - "books".* -FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) -ORDER BY - "author"."id" DESC, - "books"."title" ASC; - -SELECT - "books".* -FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) -ORDER BY - "author"."id" DESC, - "books"."title" DESC; +SELECT "books".* FROM "books" AS "books" ORDER BY "books"."author_id" DESC, "books"."title" ASC; +SELECT "books".* FROM "books" AS "books" ORDER BY "books"."author_id" DESC, "books"."title" DESC; diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrderingMultiple.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrderingMultiple.sql index 44252c50..980a2ec9 100644 --- a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrderingMultiple.sql +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionTest_testOrderingMultiple.sql @@ -1,21 +1,2 @@ -SELECT - "books".* -FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) -ORDER BY - "author"."id" DESC, - "books"."title" ASC; - -SELECT - "books".* -FROM - "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) -ORDER BY - "author"."id" DESC, - "books"."title" DESC; +SELECT "books".* FROM "books" AS "books" ORDER BY "books"."author_id" DESC, "books"."title" ASC; +SELECT "books".* FROM "books" AS "books" ORDER BY "books"."author_id" DESC, "books"."title" DESC; diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationship.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationship.sql new file mode 100644 index 00000000..aa945ce6 --- /dev/null +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationship.sql @@ -0,0 +1,21 @@ +SELECT "books".* FROM "books" AS "books" WHERE "books"."id" = 1; +START TRANSACTION; +INSERT INTO "eans" ("code", "type") VALUES ('1234', 2); +SELECT CURRVAL('public.eans_id_seq'); +UPDATE "books" SET "ean_id" = 1 WHERE "id" = 1; +COMMIT; +SELECT + "eans".* +FROM + "eans" AS "eans" + LEFT JOIN "books" AS "book" ON ("eans"."id" = "book"."ean_id") +WHERE + "book"."id" = 1; + +SELECT + "eans".* +FROM + "eans" AS "eans" + LEFT JOIN "books" AS "book" ON ("eans"."id" = "book"."ean_id") +WHERE + "book"."id" = 2; diff --git a/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationshipNull.sql b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationshipNull.sql new file mode 100644 index 00000000..c26d7954 --- /dev/null +++ b/tests/sqls/NextrasTests/Orm/Integration/Collection/CollectionWhereTest_testFilterByNonMainSideRelationshipNull.sql @@ -0,0 +1,19 @@ +SELECT + "books".* +FROM + "books" AS "books" + LEFT JOIN "books" AS "previousPart" ON ( + "books"."id" = "previousPart"."next_part" + ) +WHERE + "previousPart"."id" IS NULL; + +SELECT + "books".* +FROM + "books" AS "books" + LEFT JOIN "books" AS "previousPart" ON ( + "books"."id" = "previousPart"."next_part" + ) +WHERE + "previousPart"."id" IS NOT NULL; diff --git a/tests/sqls/NextrasTests/Orm/Integration/Relationships/RelationshipManyHasManyTest_testCountStoredOnManyHasManyRelationshipCondition.sql b/tests/sqls/NextrasTests/Orm/Integration/Relationships/RelationshipManyHasManyTest_testCountStoredOnManyHasManyRelationshipCondition.sql index 1f7c3de6..a55c611a 100644 --- a/tests/sqls/NextrasTests/Orm/Integration/Relationships/RelationshipManyHasManyTest_testCountStoredOnManyHasManyRelationshipCondition.sql +++ b/tests/sqls/NextrasTests/Orm/Integration/Relationships/RelationshipManyHasManyTest_testCountStoredOnManyHasManyRelationshipCondition.sql @@ -4,14 +4,11 @@ SELECT "books_x_tags"."tag_id" FROM "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ( "books_x_tags"."book_id" = "books"."id" ) WHERE - ("author"."id" = 1) + ("books"."author_id" = 1) AND ( "books_x_tags"."tag_id" IN (1) ); @@ -24,14 +21,11 @@ SELECT ) AS "count" FROM "books" AS "books" - LEFT JOIN "public"."authors" AS "author" ON ( - "books"."author_id" = "author"."id" - ) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ( "books_x_tags"."book_id" = "books"."id" ) WHERE - ("author"."id" = 1) + ("books"."author_id" = 1) AND ( "books_x_tags"."tag_id" IN (1) ) @@ -53,15 +47,12 @@ FROM LEFT JOIN "tag_followers" AS "author_tagFollowers_any" ON ( "author"."id" = "author_tagFollowers_any"."author_id" ) - LEFT JOIN "public"."authors" AS "author_tagFollowers_author_any" ON ( - "author_tagFollowers_any"."author_id" = "author_tagFollowers_author_any"."id" - ) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ( "books_x_tags"."book_id" = "books"."id" ) WHERE ( - "author_tagFollowers_author_any"."id" = 1 + "author_tagFollowers_any"."author_id" = 1 ) AND ( "books_x_tags"."tag_id" IN (1) @@ -89,15 +80,12 @@ FROM LEFT JOIN "tag_followers" AS "author_tagFollowers_any" ON ( "author"."id" = "author_tagFollowers_any"."author_id" ) - LEFT JOIN "public"."authors" AS "author_tagFollowers_author_any" ON ( - "author_tagFollowers_any"."author_id" = "author_tagFollowers_author_any"."id" - ) LEFT JOIN "books_x_tags" AS "books_x_tags" ON ( "books_x_tags"."book_id" = "books"."id" ) WHERE ( - "author_tagFollowers_author_any"."id" = 1 + "author_tagFollowers_any"."author_id" = 1 ) AND ( "books_x_tags"."tag_id" IN (1)