Completed
Pull Request — develop (#353)
by Jan
10:35
created

DocumentModel::findAll()   C

Complexity

Conditions 9
Paths 64

Size

Total Lines 66
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 20.8082

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 66
ccs 18
cts 38
cp 0.4737
rs 6.4099
cc 9
eloc 37
nc 64
nop 2
crap 20.8082

How to fix   Long Method   

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
 * Use doctrine odm as backend
4
 */
5
6
namespace Graviton\RestBundle\Model;
7
8
use Doctrine\ODM\MongoDB\DocumentRepository;
9
use Graviton\SchemaBundle\Model\SchemaModel;
10
use Symfony\Component\HttpFoundation\Request;
11
use Doctrine\ODM\MongoDB\Query\Builder;
12
use Graviton\Rql\Visitor\MongoOdm as Visitor;
13
use Xiag\Rql\Parser\Query;
14
use Graviton\ExceptionBundle\Exception\RecordOriginModifiedException;
15
16
/**
17
 * Use doctrine odm as backend
18
 *
19
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
20
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
21
 * @link     http://swisscom.ch
22
 */
23
class DocumentModel extends SchemaModel implements ModelInterface
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $description;
29
    /**
30
     * @var string[]
31
     */
32
    protected $fieldTitles;
33
    /**
34
     * @var string[]
35
     */
36
    protected $fieldDescriptions;
37
    /**
38
     * @var string[]
39
     */
40
    protected $requiredFields = array();
41
    /**
42
     * @var DocumentRepository
43
     */
44
    private $repository;
45
    /**
46
     * @var Visitor
47
     */
48
    private $visitor;
49
    /**
50
     * @var array
51
     */
52
    protected $notModifiableOriginRecords;
53
    /**
54
     * @var  integer
55
     */
56
    private $paginationDefaultLimit;
57
58
    /**
59
     * @var boolean
60
     */
61
    protected $filterByAuthUser;
62
63 2
    /**
64
     * @var string
65 2
     */
66 2
    protected $filterByAuthField;
67 2
68 2
    /**
69 2
     * @param Visitor $visitor                    rql query visitor
70
     * @param array   $notModifiableOriginRecords strings with not modifiable recordOrigin values
71
     * @param integer $paginationDefaultLimit     amount of data records to be returned when in pagination context.
72
     */
73
    public function __construct(Visitor $visitor, $notModifiableOriginRecords, $paginationDefaultLimit)
74
    {
75
        parent::__construct();
76
        $this->visitor = $visitor;
77
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
78
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
79
    }
80
81
    /**
82
     * get repository instance
83
     *
84
     * @return DocumentRepository
85
     */
86
    public function getRepository()
87
    {
88 2
        return $this->repository;
89
    }
90 2
91
    /**
92 2
     * create new app model
93
     *
94
     * @param DocumentRepository $repository Repository of countries
95
     *
96
     * @return \Graviton\RestBundle\Model\DocumentModel
97
     */
98
    public function setRepository(DocumentRepository $repository)
99
    {
100
        $this->repository = $repository;
101
102 1
        return $this;
103
    }
104 1
105 1
    /**
106 1
     * {@inheritDoc}
107
     *
108
     * @param Request $request The request object
109 1
     * @param Object  $user    The user
110 1
     *
111
     * @return array
112
     */
113 1
    public function findAll(Request $request, $user = false)
114
    {
115
        $pageNumber = $request->query->get('page', 1);
116
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
117
        $startAt = ($pageNumber - 1) * $numberPerPage;
118
119
        /** @var \Doctrine\ODM\MongoDB\Query\Builder $queryBuilder */
120
        $queryBuilder = $this->repository
121 1
            ->createQueryBuilder();
122
123
        if ($this->filterByAuthUser && $user && $user->getId()) {
124
            $queryBuilder->field($this->filterByAuthField)->equals($user->getId());
125 1
        }
126 1
127 1
        // *** do we have an RQL expression, do we need to filter data?
128
        if ($request->attributes->get('hasRql', false)) {
129
            $queryBuilder = $this->doRqlQuery(
130
                $queryBuilder,
131 1
                $request->attributes->get('rqlQuery')
132 1
            );
133 1
        } else {
134
            // @todo [lapistano]: seems the offset is missing for this query.
135
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
136
            $queryBuilder->find($this->repository->getDocumentName());
137
        }
138
139
        // define offset and limit
140
        if (!array_key_exists('skip', $queryBuilder->getQuery()->getQuery())) {
141
            $queryBuilder->skip($startAt);
0 ignored issues
show
Bug introduced by
The method skip does only exist in Doctrine\ODM\MongoDB\Query\Builder, but not in Doctrine\ODM\MongoDB\Query\Expr.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
142 1
        } else {
143 1
            $startAt = (int) $queryBuilder->getQuery()->getQuery()['skip'];
144 1
        }
145
146
        if (!array_key_exists('limit', $queryBuilder->getQuery()->getQuery())) {
147 1
            $queryBuilder->limit($numberPerPage);
0 ignored issues
show
Bug introduced by
The method limit does only exist in Doctrine\ODM\MongoDB\Query\Builder, but not in Doctrine\ODM\MongoDB\Query\Expr.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
148 1
        } else {
149
            $numberPerPage = (int) $queryBuilder->getQuery()->getQuery()['limit'];
150 1
        }
151 1
152 1
        /**
153 1
         * add a default sort on id if none was specified earlier
154
         *
155
         * not specifying something to sort on leads to very weird cases when fetching references
156
         */
157
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
158
            $queryBuilder->sort('_id');
159
        }
160
161
        // run query
162 1
        $query = $queryBuilder->getQuery();
163
        $records = array_values($query->execute()->toArray());
164
165
        $totalCount = $query->count();
166
        $numPages = (int) ceil($totalCount / $numberPerPage);
167
        $page = (int) ceil($startAt / $numberPerPage) + 1;
168
        if ($numPages > 1) {
169
            $request->attributes->set('paging', true);
170
            $request->attributes->set('page', $page);
171
            $request->attributes->set('numPages', $numPages);
172
            $request->attributes->set('startAt', $startAt);
173
            $request->attributes->set('perPage', $numberPerPage);
174
            $request->attributes->set('totalCount', $totalCount);
175
        }
176
177
        return $records;
178
    }
179
180
    /**
181
     * @param \Graviton\I18nBundle\Document\Translatable $entity entity to insert
182
     *
183
     * @return Object
184
     */
185 1 View Code Duplication
    public function insertRecord($entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
    {
187 1
        $this->checkIfOriginRecord($entity);
188
        $manager = $this->repository->getDocumentManager();
189
        $manager->persist($entity);
190
        $manager->flush($entity);
191
192
        return $this->find($entity->getId());
193
    }
194
195
    /**
196
     * @param string $documentId id of entity to find
197
     *
198 1
     * @return Object
199
     */
200 1
    public function find($documentId)
201
    {
202 1
        return $this->repository->find($documentId);
203 1
    }
204 1
205 1
    /**
206
     * {@inheritDoc}
207 1
     *
208
     * @param string $documentId id of entity to update
209
     * @param Object $entity     new entity
210
     *
211
     * @return Object
212
     */
213 View Code Duplication
    public function updateRecord($documentId, $entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214
    {
215
        $manager = $this->repository->getDocumentManager();
216
        // In both cases the document attribute named originRecord must not be 'core'
217 1
        $this->checkIfOriginRecord($entity);
218
        $this->checkIfOriginRecord($this->find($documentId));
219 1
        $entity = $manager->merge($entity);
220 1
        $manager->flush();
221
222 1
        return $entity;
223 1
    }
224 1
225 1
    /**
226 1
     * {@inheritDoc}
227 1
     *
228 1
     * @param string $documentId id of entity to delete
229
     *
230 1
     * @return null|Object
231
     */
232
    public function deleteRecord($documentId)
233
    {
234
        $manager = $this->repository->getDocumentManager();
235
        $entity = $this->find($documentId);
236
237
        $return = $entity;
238 1
        if ($entity) {
239
            $this->checkIfOriginRecord($entity);
240 1
            $manager->remove($entity);
241
            $manager->flush();
242
            $return = null;
243
        }
244
245
        return $return;
246
    }
247
248
    /**
249
     * get classname of entity
250
     *
251
     * @return string
252
     */
253
    public function getEntityClass()
254
    {
255
        return $this->repository->getDocumentName();
256
    }
257
258
    /**
259
     * {@inheritDoc}
260
     *
261
     * Currently this is being used to build the route id used for redirecting
262
     * to newly made documents. It might benefit from having a different name
263
     * for those purposes.
264
     *
265
     * We might use a convention based mapping here:
266
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
267
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
268
     *
269
     * @todo implement this in a more convention based manner
270
     *
271
     * @return string
272
     */
273
    public function getConnectionName()
274
    {
275
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
276
277
        return 'graviton.' . $bundle;
278
    }
279
280
    /**
281
     * Does the actual query using the RQL Bundle.
282
     *
283
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
284
     * @param Query   $query        query from parser
285
     *
286
     * @return array
287 7
     */
288
    protected function doRqlQuery($queryBuilder, Query $query)
289
    {
290 7
        $this->visitor->setBuilder($queryBuilder);
291 7
292 3
        return $this->visitor->visit($query);
293 3
    }
294
295 3
    /**
296 1
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
297
     *
298 1
     * @param Object $record record
299
     *
300 2
     * @return void
301 6
     */
302
    protected function checkIfOriginRecord($record)
303
    {
304
        if ($record instanceof RecordOriginInterface
305
            && !$record->isRecordOriginModifiable()
306
        ) {
307
            $values = $this->notModifiableOriginRecords;
308 1
            $originValue = strtolower(trim($record->getRecordOrigin()));
309
310 1
            if (in_array($originValue, $values)) {
311 1
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
312 1
313
                throw new RecordOriginModifiedException($msg);
314
            }
315
        }
316
    }
317
318
    /**
319
     * Determines the configured amount fo data records to be returned in pagination context.
320
     *
321
     * @return int
322
     */
323
    private function getDefaultLimit()
324
    {
325
        if (0 < $this->paginationDefaultLimit) {
326
            return $this->paginationDefaultLimit;
327
        }
328
329
        return 10;
330
    }
331
332
    /**
333
     * @param Boolean $active active
334
     * @param String  $field  field
335
     * @return void
336
     */
337
    public function setFilterByAuthUser($active, $field)
338
    {
339
        $this->filterByAuthUser = is_bool($active) ? $active : false;
340
        $this->filterByAuthField = $field;
341
    }
342
}
343