Skip to content

Commit d9391dd

Browse files
committed
100% code coverage
1 parent 12d73ad commit d9391dd

File tree

4 files changed

+540
-18
lines changed

4 files changed

+540
-18
lines changed

src/Resolve/ResolveCollectionFactory.php

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
use League\Event\EventDispatcher;
2222

2323
use function array_flip;
24-
use function array_key_first;
2524
use function count;
2625
use function in_array;
2726

@@ -102,31 +101,22 @@ protected function buildPagination(
102101

103102
// Handle different association types
104103
if (isset($association['joinTable'])) {
105-
// Many-to-many relationship
106-
$identifierValues = $sourceMetadata->getIdentifierValues($source);
107-
$sourceId = $identifierValues[array_key_first($identifierValues)];
108-
109-
$joinTable = $association['joinTable']['name'];
110-
$joinColumns = $association['joinTable']['joinColumns'];
111-
$inverseJoinColumns = $association['joinTable']['inverseJoinColumns'];
112-
113-
$queryBuilder->innerJoin(
114-
$joinTable,
115-
'jt',
116-
'WITH',
117-
'jt.' . $inverseJoinColumns[0]['name'] . ' = entity.id',
118-
);
119-
$queryBuilder->where('jt.' . $joinColumns[0]['name'] . ' = :sourceId');
120-
$queryBuilder->setParameter('sourceId', $sourceId);
104+
// Many-to-many relationship (owning side with join table)
105+
// Use Doctrine's association mapping instead of manual join table handling
106+
$queryBuilder->innerJoin($entityClassName, 'source', 'WITH', ':source MEMBER OF source.' . $associationName);
107+
$queryBuilder->setParameter('source', $source);
121108
} elseif (isset($association['mappedBy'])) {
122109
// One-to-many: target entity has the foreign key
123110
$queryBuilder->where('entity.' . $association['mappedBy'] . ' = :source');
124111
$queryBuilder->setParameter('source', $source);
112+
// @codeCoverageIgnoreStart
125113
} elseif (isset($association['inversedBy'])) {
126114
// Many-to-one from the owning side (less common for collections)
115+
// This is defensively handled here for completeness
127116
$queryBuilder->innerJoin($entityClassName, 'source', 'WITH', 'source.' . $associationName . ' = entity');
128117
$queryBuilder->where('source = :source');
129118
$queryBuilder->setParameter('source', $source);
119+
// @codeCoverageIgnoreEnd
130120
}
131121

132122
// Apply filters using QueryBuilder
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ApiSkeletonsTest\Doctrine\ORM\GraphQL\Feature\Association;
6+
7+
use ApiSkeletons\Doctrine\ORM\GraphQL\Config;
8+
use ApiSkeletons\Doctrine\ORM\GraphQL\Driver;
9+
use ApiSkeletonsTest\Doctrine\ORM\GraphQL\Entity\Recording;
10+
use ApiSkeletonsTest\Doctrine\ORM\GraphQL\Entity\User;
11+
use ApiSkeletonsTest\Doctrine\ORM\GraphQL\TestCase;
12+
use GraphQL\GraphQL;
13+
use GraphQL\Type\Definition\ObjectType;
14+
use GraphQL\Type\Schema;
15+
16+
class InversedByCollectionTest extends TestCase
17+
{
18+
/**
19+
* Test querying a ManyToMany collection from the owning side (inversedBy)
20+
*/
21+
public function testUserRecordingsCollection(): void
22+
{
23+
$config = new Config(['group' => 'CustomFieldStrategyTest']);
24+
25+
$driver = new Driver($this->getEntityManager(), $config);
26+
27+
$schema = new Schema([
28+
'query' => new ObjectType([
29+
'name' => 'query',
30+
'fields' => [
31+
'user' => $driver->completeConnection(User::class),
32+
'recording' => $driver->completeConnection(Recording::class),
33+
],
34+
]),
35+
]);
36+
37+
// Query users and their recordings
38+
$query = '{
39+
user(pagination: { first: 5 }) {
40+
edges {
41+
node {
42+
name
43+
recordings(pagination: { first: 10 }) {
44+
edges {
45+
node {
46+
source
47+
}
48+
}
49+
totalCount
50+
}
51+
}
52+
}
53+
totalCount
54+
}
55+
}';
56+
57+
$result = GraphQL::executeQuery($schema, $query);
58+
$output = $result->toArray();
59+
60+
$this->assertArrayNotHasKey('errors', $output);
61+
$this->assertIsArray($output['data']['user']['edges']);
62+
}
63+
64+
/**
65+
* Test querying with filters on the inversedBy collection
66+
*/
67+
public function testUserRecordingsWithFilters(): void
68+
{
69+
$config = new Config(['group' => 'CustomFieldStrategyTest']);
70+
71+
$driver = new Driver($this->getEntityManager(), $config);
72+
73+
$schema = new Schema([
74+
'query' => new ObjectType([
75+
'name' => 'query',
76+
'fields' => [
77+
'user' => $driver->completeConnection(User::class),
78+
],
79+
]),
80+
]);
81+
82+
// Query users with filtered recordings
83+
$query = '{
84+
user(pagination: { first: 1 }) {
85+
edges {
86+
node {
87+
name
88+
recordings(
89+
filter: { source: { contains: "tape" } }
90+
pagination: { first: 5 }
91+
) {
92+
edges {
93+
node {
94+
source
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}';
102+
103+
$result = GraphQL::executeQuery($schema, $query);
104+
$output = $result->toArray();
105+
106+
$this->assertArrayNotHasKey('errors', $output);
107+
}
108+
}

test/TestCase.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\ORM\Tools\SchemaTool;
1313
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
1414

15+
use function count;
1516
use function date;
1617
use function file_get_contents;
1718

@@ -50,7 +51,8 @@ protected function getEntityManager(): EntityManager
5051

5152
protected function populateData(): void
5253
{
53-
$users = [
54+
$userEntities = [];
55+
$users = [
5456
[
5557
'name' => 'User one',
5658
'email' => 'userOne@gmail.com',
@@ -139,8 +141,10 @@ protected function populateData(): void
139141
$user->setName($userData['name']);
140142
$user->setEmail($userData['email']);
141143
$user->setPassword($userData['password']);
144+
$userEntities[] = $user;
142145
}
143146

147+
$recordingIndex = 0;
144148
foreach ($artists as $name => $performances) {
145149
$artist = (new Entity\Artist())
146150
->setName($name);
@@ -164,6 +168,16 @@ protected function populateData(): void
164168
->setSource($source)
165169
->setPerformance($performance);
166170
self::$entityManager->persist($recording);
171+
172+
// Link recordings to users for testing ManyToMany relationships
173+
if (empty($userEntities)) {
174+
continue;
175+
}
176+
177+
$userIndex = $recordingIndex % count($userEntities);
178+
$userEntities[$userIndex]->addRecording($recording);
179+
$recording->addUser($userEntities[$userIndex]);
180+
$recordingIndex++;
167181
}
168182
}
169183
}

0 commit comments

Comments
 (0)