Skip to content

Commit 1f99dc3

Browse files
committed
add real Ship pagination
1 parent 3c2f7eb commit 1f99dc3

File tree

8 files changed

+258
-5
lines changed

8 files changed

+258
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace AppBundle\Entity\Repository;
4+
5+
use Doctrine\ORM\EntityRepository;
6+
7+
class ShipRepository extends EntityRepository
8+
{
9+
public function countAllByFactionId($factionId)
10+
{
11+
return $this->shipByFactionIdQueryBuilder($factionId)
12+
->select('COUNT(s)')
13+
->getQuery()
14+
->getSingleScalarResult()
15+
;
16+
}
17+
18+
public function retrieveShipsByFactionId($factionId, $offset = 0, $limit = 0)
19+
{
20+
$qb = $this->shipByFactionIdQueryBuilder($factionId);
21+
22+
if ($limit > 0) {
23+
$qb->setMaxResults($limit);
24+
}
25+
26+
return $ships = $qb->select('s')
27+
->setFirstResult($offset)
28+
->getQuery()
29+
->getResult();
30+
}
31+
32+
private function shipByFactionIdQueryBuilder($factionId)
33+
{
34+
$qb = $this->createQueryBuilder('s')
35+
->innerJoin('s.factions', 'f')
36+
->where('f.id = :faction_id')
37+
->setParameter('faction_id', $factionId)
38+
;
39+
40+
return $qb;
41+
}
42+
43+
}

src/AppBundle/Entity/Ship.php

+48
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
namespace AppBundle\Entity;
4+
use Doctrine\Common\Collections\ArrayCollection;
45

56
/**
67
* Ship.
@@ -19,6 +20,19 @@ class Ship
1920
*/
2021
private $name;
2122

23+
/**
24+
* @var \Doctrine\Common\Collections\Collection
25+
*/
26+
private $factions;
27+
28+
/**
29+
* Constructor
30+
*/
31+
public function __construct()
32+
{
33+
$this->factions = new ArrayCollection();
34+
}
35+
2236
/**
2337
* Get id.
2438
*
@@ -63,4 +77,38 @@ public function getName()
6377
{
6478
return $this->name;
6579
}
80+
81+
/**
82+
* Add faction
83+
*
84+
* @param Faction $faction
85+
*
86+
* @return Ship
87+
*/
88+
public function addFaction(Faction $faction)
89+
{
90+
$this->factions[] = $faction;
91+
92+
return $this;
93+
}
94+
95+
/**
96+
* Remove faction
97+
*
98+
* @param Faction $faction
99+
*/
100+
public function removeFaction(Faction $faction)
101+
{
102+
$this->factions->removeElement($faction);
103+
}
104+
105+
/**
106+
* Get factions
107+
*
108+
* @return \Doctrine\Common\Collections\Collection
109+
*/
110+
public function getFactions()
111+
{
112+
return $this->factions;
113+
}
66114
}

src/AppBundle/GraphQL/Relay/Mutation/ShipMutation.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ public function createShip($shipName, $factionId)
2323

2424
$ship = new Ship();
2525
$ship->setName($shipName);
26-
27-
$faction->addShip($ship);
26+
$ship->addFaction($faction);
2827

2928
$em->persist($ship);
3029
$em->persist($faction);

src/AppBundle/GraphQL/Relay/Resolver/FactionResolver.php

+41-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace AppBundle\GraphQL\Relay\Resolver;
44

55
use AppBundle\Entity\Faction;
6+
use AppBundle\Entity\Repository\ShipRepository;
67
use Overblog\GraphQLBundle\Relay\Connection\Output\ConnectionBuilder;
78
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
89
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
@@ -27,7 +28,46 @@ public function resolveEmpire()
2728

2829
public function resolveShips(Faction $faction, $args)
2930
{
30-
return ConnectionBuilder::connectionFromArray($faction->getShips()->toArray(), $args);
31+
// $ships = $faction->getShips()->toArray();
32+
// $connection = ConnectionBuilder::connectionFromArray($ships, $args);
33+
// $connection->sliceSize = count($connection->edges);
34+
//
35+
// return $connection;
36+
37+
/** @var ShipRepository $repository */
38+
$repository = $this->container
39+
->get('doctrine.orm.default_entity_manager')
40+
->getRepository('AppBundle:Ship');
41+
42+
$arrayLength = $repository->countAllByFactionId($faction->getId());
43+
$beforeOffset = ConnectionBuilder::getOffsetWithDefault($args['before'], $arrayLength);
44+
$afterOffset = ConnectionBuilder::getOffsetWithDefault($args['after'], -1);
45+
46+
$startOffset = max($afterOffset, -1) + 1;
47+
$endOffset = min($beforeOffset, $arrayLength);
48+
49+
if (is_numeric($args['first'])) {
50+
$endOffset = min($endOffset, $startOffset + $args['first']);
51+
}
52+
if (is_numeric($args['last'])) {
53+
$startOffset = max($startOffset, $endOffset - $args['last']);
54+
}
55+
$offset = max($startOffset, 0);
56+
$limit = $endOffset - $startOffset;
57+
58+
$ships = $repository->retrieveShipsByFactionId($faction->getId(), $offset, $limit);
59+
60+
$connection = ConnectionBuilder::connectionFromArraySlice(
61+
$ships,
62+
$args,
63+
[
64+
'sliceStart' => $offset,
65+
'arrayLength' => $arrayLength,
66+
]
67+
);
68+
$connection->sliceSize = count($ships);
69+
70+
return $connection;
3171
}
3272

3373
private function getFactionByType($type)

src/AppBundle/Resources/config/doctrine/Faction.orm.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</id>
1010
<field name="name" type="string" column="name" length="255"/>
1111
<field name="type" type="string" column="type" length="255"/>
12-
<many-to-many field="ships" target-entity="AppBundle\Entity\Ship">
12+
<many-to-many field="ships" target-entity="AppBundle\Entity\Ship" inversed-by="factions">
1313
<join-table name="faction_ship">
1414
<join-columns>
1515
<join-column name="faction_id" referenced-column-name="id" />
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
3-
<entity name="AppBundle\Entity\Ship" table="ship">
3+
<entity
4+
name="AppBundle\Entity\Ship"
5+
table="ship"
6+
repository-class="AppBundle\Entity\Repository\ShipRepository">
47
<id name="id" type="integer" column="id">
58
<generator strategy="AUTO"/>
69
</id>
710
<field name="name" type="string" column="name" length="255"/>
11+
<many-to-many field="factions" mapped-by="ships" target-entity="AppBundle\Entity\Faction"/>
812
</entity>
913
</doctrine-mapping>

src/AppBundle/Resources/config/graphql/relay/ShipConnection.types.yml

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ ShipConnection:
1616
type: relay-connection
1717
config:
1818
nodeType: Ship
19+
connectionFields:
20+
sliceSize:
21+
type: "Int!"

tests/AppBundle/Functional/Relay/StarWarsConnectionTest.php

+116
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,122 @@ public function testIdentifiesTheEndOfTheList()
256256
}
257257
}
258258
}
259+
EOF;
260+
261+
$this->assertQuery($query, $jsonExpected);
262+
}
263+
264+
public function testFetchesShipsWithLastBeforeAndAfter()
265+
{
266+
$query = <<<EOF
267+
query EndOfRebelShipsWithLastAndBeforeAndAfterQuery {
268+
rebels {
269+
name
270+
ships: ships(last: 3, before: "YXJyYXljb25uZWN0aW9uOjQ=", after: "YXJyYXljb25uZWN0aW9uOjE=") {
271+
edges {
272+
cursor
273+
node {
274+
name
275+
}
276+
}
277+
pageInfo {
278+
hasNextPage
279+
hasPreviousPage
280+
}
281+
}
282+
}
283+
}
284+
EOF;
285+
286+
$jsonExpected = <<<EOF
287+
{
288+
"data": {
289+
"rebels": {
290+
"name": "Alliance to Restore the Republic",
291+
"ships": {
292+
"edges": [
293+
{
294+
"cursor": "YXJyYXljb25uZWN0aW9uOjI=",
295+
"node": {
296+
"name": "A-Wing"
297+
}
298+
},
299+
{
300+
"cursor": "YXJyYXljb25uZWN0aW9uOjM=",
301+
"node": {
302+
"name": "Millenium Falcon"
303+
}
304+
}
305+
],
306+
"pageInfo": {
307+
"hasNextPage": false,
308+
"hasPreviousPage": false
309+
}
310+
}
311+
}
312+
}
313+
}
314+
EOF;
315+
316+
$this->assertQuery($query, $jsonExpected);
317+
}
318+
319+
public function testFetchesShipsWithLastAfter()
320+
{
321+
$query = <<<EOF
322+
query EndOfRebelShipsWithLastAndAfterQuery {
323+
rebels {
324+
name
325+
ships: ships(last: 3, after: "YXJyYXljb25uZWN0aW9uOjE=") {
326+
edges {
327+
cursor
328+
node {
329+
name
330+
}
331+
}
332+
pageInfo {
333+
hasNextPage
334+
hasPreviousPage
335+
}
336+
}
337+
}
338+
}
339+
EOF;
340+
341+
$jsonExpected = <<<EOF
342+
{
343+
"data": {
344+
"rebels": {
345+
"name": "Alliance to Restore the Republic",
346+
"ships": {
347+
"edges": [
348+
{
349+
"cursor": "YXJyYXljb25uZWN0aW9uOjI=",
350+
"node": {
351+
"name": "A-Wing"
352+
}
353+
},
354+
{
355+
"cursor": "YXJyYXljb25uZWN0aW9uOjM=",
356+
"node": {
357+
"name": "Millenium Falcon"
358+
}
359+
},
360+
{
361+
"cursor": "YXJyYXljb25uZWN0aW9uOjQ=",
362+
"node": {
363+
"name": "Home One"
364+
}
365+
}
366+
],
367+
"pageInfo": {
368+
"hasNextPage": false,
369+
"hasPreviousPage": false
370+
}
371+
}
372+
}
373+
}
374+
}
259375
EOF;
260376

261377
$this->assertQuery($query, $jsonExpected);

0 commit comments

Comments
 (0)