Passed
Push — master ( 5cd32c...1a1aa9 )
by Rafael
05:46
created

AllNodesWithPagination::__invoke()   C

Complexity

Conditions 11
Paths 80

Size

Total Lines 61
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 16.1878

Importance

Changes 0
Metric Value
dl 0
loc 61
ccs 26
cts 40
cp 0.65
rs 6.2318
c 0
b 0
f 0
cc 11
eloc 39
nc 80
nop 1
crap 16.1878

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Query\Node;
12
13
use Doctrine\ORM\Query\Expr\Orx;
14
use Doctrine\ORM\QueryBuilder;
15
use GraphQL\Error\Error;
16
use Ynlo\GraphQLBundle\Definition\Plugin\PaginationDefinitionPlugin;
17
use Ynlo\GraphQLBundle\Model\ConnectionInterface;
18
use Ynlo\GraphQLBundle\Model\ID;
19
use Ynlo\GraphQLBundle\Model\NodeConnection;
20
use Ynlo\GraphQLBundle\Model\NodeInterface;
21
use Ynlo\GraphQLBundle\Pagination\DoctrineCursorPaginatorInterface;
22
use Ynlo\GraphQLBundle\Pagination\DoctrineOffsetCursorPaginator;
23
use Ynlo\GraphQLBundle\Pagination\PaginationRequest;
24
25
/**
26
 * Base class to fetch nodes
27
 */
28
class AllNodesWithPagination extends AllNodes
29
{
30
    /**
31
     * @param array[] $args
32
     *
33
     * @return mixed
34
     *
35
     * @throws Error
36
     */
37 10
    public function __invoke($args = [])
38
    {
39 10
        $orderBy = $args['orderBy'] ?? [];
40 10
        $first = $args['first'] ?? null;
41 10
        $last = $args['last'] ?? null;
42 10
        $before = $args['before'] ?? null;
43 10
        $after = $args['after'] ?? null;
44 10
        $search = $args['search'] ?? null;
45 10
        $filters = $args['filters'] ?? null;
46
47 10
        $this->initialize();
48
49 10
        $qb = $this->createQuery();
50 10
        $this->applyOrderBy($qb, $orderBy);
51
52 10
        if ($this->getContext()->getRoot()) {
53 3
            $this->applyFilterByParent($qb, $this->getContext()->getRoot());
54
        }
55
56 10
        if ($search) {
57
            $this->search($qb, $search);
58
        }
59
60 10
        if ($filters) {
61
            $this->applyFilters($qb, $filters);
62
        }
63
64 10
        $this->configureQuery($qb);
65 10
        foreach ($this->extensions as $extension) {
66 4
            $extension->configureQuery($qb, $this, $this->context);
67
        }
68
69 10
        if (!$first && !$last) {
70
            $error = sprintf('You must provide a `first` or `last` value to properly paginate records in "%s" connection.', $this->queryDefinition->getName());
71
            throw new Error($error);
72
        }
73
74 10
        if ($this->queryDefinition->hasMeta('pagination')) {
75 10
            $limitAllowed = $this->queryDefinition->getMeta('pagination')['limit'];
76
77 10
            if ($first > $limitAllowed || $last > $limitAllowed) {
78
                $current = $first ?? $last;
79
                $where = $first ? 'first' : 'last';
80
                $error = sprintf(
81
                    'Requesting %s records for `%s` exceeds the `%s` limit of %s records for "%s" connection',
82
                    $current,
83
                    $this->queryDefinition->getName(),
84
                    $where,
85
                    $limitAllowed,
86
                    $this->queryDefinition->getName()
87
                );
88
                throw new Error($error);
89
            }
90
        }
91
92 10
        $paginator = $this->createPaginator();
93
94 10
        $connection = $this->createConnection();
95 10
        $paginator->paginate($qb, new PaginationRequest($first, $last, $after, $before), $connection);
96
97 10
        return $connection;
98
    }
99
100 10
    protected function createConnection(): ConnectionInterface
101
    {
102 10
        return new NodeConnection();
103
    }
104
105 10
    protected function createPaginator(): DoctrineCursorPaginatorInterface
106
    {
107 10
        return new DoctrineOffsetCursorPaginator();
108
    }
109
110
    /**
111
     * Apply advanced filters
112
     */
113
    protected function applyFilters(QueryBuilder $qb, array $filters)
114
    {
115
        $definition = $this->objectDefinition;
116
        foreach ($filters as $field => $value) {
117
            if (!$definition->hasField($field) || !$prop = $definition->getField($field)->getOriginName()) {
118
                continue;
119
            }
120
121
            $entityField = sprintf('%s.%s', $this->queryAlias, $prop);
122
123
            switch (gettype($value)) {
124
                case 'string':
125
                    $qb->andWhere($qb->expr()->eq($entityField, $qb->expr()->literal($value)));
126
                    break;
127
                case 'integer':
128
                case 'double':
129
                    $qb->andWhere($qb->expr()->eq($entityField, $value));
130
                    break;
131
                case 'boolean':
132
                    $qb->andWhere($qb->expr()->eq($entityField, (int) $value));
133
                    break;
134
                case 'array':
135
                    if (empty($value)) {
136
                        $qb->andWhere($qb->expr()->isNull($entityField));
137
                    } else {
138
                        $qb->andWhere($qb->expr()->in($entityField, $value));
139
                    }
140
                    break;
141
                case 'NULL':
142
                    $qb->andWhere($qb->expr()->isNull($entityField));
143
                    break;
144
            }
145
        }
146
    }
147
148
    /**
149
     * Filter some columns with simple string.
150
     */
151
    protected function search(QueryBuilder $qb, string $search)
152
    {
153
        //search every word separate
154
        $searchArray = explode(' ', $search);
155
156
        $alias = $qb->getRootAliases()[0];
157
158
        //TODO: allow some config to customize search fields
159
        $em = $this->getManager();
160
        $metadata = $em->getClassMetadata($this->entity);
161
        $searchFields = $metadata->getFieldNames();
162
163
        if (count($searchFields) > 0) {
164
            $meta = $qb->getEntityManager()->getClassMetadata($qb->getRootEntities()[0]);
165
            foreach ($searchArray as $q) {
166
                $q = trim(rtrim($q));
167
                $id = md5($q);
168
                $orx = new Orx();
169
                foreach ($searchFields as $field) {
170
                    if (strpos($field, '.') !== false && !isset($meta->embeddedClasses[explode('.', $field)[0]])) {
171
                        $orx->add("$field LIKE :search_$id");
172
                    } else { //append current alias
173
                        $orx->add("$alias.$field LIKE :search_$id");
174
                    }
175
                }
176
                $qb->andWhere($orx);
177
                $qb->setParameter("search_$id", "%$q%");
178
            }
179
        }
180
    }
181
182 3
    protected function applyFilterByParent(QueryBuilder $qb, NodeInterface $root)
183
    {
184 3
        $parentField = null;
185 3
        if ($this->queryDefinition->hasMeta('pagination')) {
186 3
            $parentField = $this->queryDefinition->getMeta('pagination')['parent_field'] ?? null;
187
        }
188 3
        if (!$parentField) {
189
            throw new \RuntimeException(
190
                sprintf(
191
                    'Missing parent field to filter "%s" by given parent.
192
             The "parent_field" should be specified.',
193
                    $this->queryDefinition->getName()
194
                )
195
            );
196
        }
197
198 3
        if ($this->objectDefinition->hasField($parentField)) {
199 3
            $parentField = $this->objectDefinition->getField($parentField)->getOriginName();
200
        }
201
202 3
        $paramName = 'root'.mt_rand();
203 3
        if ($this->queryDefinition->getMeta('pagination')['parent_relation'] === PaginationDefinitionPlugin::MANY_TO_MANY) {
204 2
            $qb->andWhere(sprintf(':%s MEMBER OF %s.%s', $paramName, $this->queryAlias, $parentField))
205 2
               ->setParameter($paramName, $root);
206
        } else {
207 1
            $qb->andWhere(sprintf('%s.%s = :%s', $this->queryAlias, $parentField, $paramName))
208 1
               ->setParameter($paramName, $root);
209
        }
210 3
    }
211
}
212