Completed
Pull Request — develop (#352)
by Samuel
06:45 queued 10s
created

DocumentModel::findAll()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 62
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7.7999
Metric Value
dl 0
loc 62
ccs 24
cts 38
cp 0.6316
rs 8.6652
cc 6
eloc 35
nc 32
nop 1
crap 7.7999

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
     * @param Visitor $visitor                    rql query visitor
60
     * @param array   $notModifiableOriginRecords strings with not modifiable recordOrigin values
61
     * @param integer $paginationDefaultLimit     amount of data records to be returned when in pagination context.
62
     */
63 2
    public function __construct(Visitor $visitor, $notModifiableOriginRecords, $paginationDefaultLimit)
64
    {
65 2
        parent::__construct();
66 2
        $this->visitor = $visitor;
67 2
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
68 2
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
69 2
    }
70
71
    /**
72
     * get repository instance
73
     *
74
     * @return DocumentRepository
75
     */
76
    public function getRepository()
77
    {
78
        return $this->repository;
79
    }
80
81
    /**
82
     * create new app model
83
     *
84
     * @param DocumentRepository $repository Repository of countries
85
     *
86
     * @return \Graviton\RestBundle\Model\DocumentModel
87
     */
88 2
    public function setRepository(DocumentRepository $repository)
89
    {
90 2
        $this->repository = $repository;
91
92 2
        return $this;
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     *
98
     * @param \Symfony\Component\HttpFoundation\Request $request Request object
99
     *
100
     * @return array
101
     */
102 1
    public function findAll(Request $request)
103
    {
104 1
        $pageNumber = $request->query->get('page', 1);
105 1
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
106 1
        $startAt = ($pageNumber - 1) * $numberPerPage;
107
108
        /** @var \Doctrine\ODM\MongoDB\Query\Builder $queryBuilder */
109 1
        $queryBuilder = $this->repository
110 1
            ->createQueryBuilder();
111
112
        // *** do we have an RQL expression, do we need to filter data?
113 1
        if ($request->attributes->get('hasRql', false)) {
114
            $queryBuilder = $this->doRqlQuery(
115
                $queryBuilder,
116
                $request->attributes->get('rqlQuery')
117
            );
118
        } else {
119
            // @todo [lapistano]: seems the offset is missing for this query.
120
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
121 1
            $queryBuilder->find($this->repository->getDocumentName());
122
        }
123
124
        // define offset and limit
125 1
        if (!array_key_exists('skip', $queryBuilder->getQuery()->getQuery())) {
126 1
            $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...
127 1
        } else {
128
            $startAt = (int) $queryBuilder->getQuery()->getQuery()['skip'];
129
        }
130
131 1
        if (!array_key_exists('limit', $queryBuilder->getQuery()->getQuery())) {
132 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...
133 1
        } else {
134
            $numberPerPage = (int) $queryBuilder->getQuery()->getQuery()['limit'];
135
        }
136
137
        /**
138
         * add a default sort on id if none was specified earlier
139
         *
140
         * not specifying something to sort on leads to very weird cases when fetching references
141
         */
142 1
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
143 1
            $queryBuilder->sort('_id');
144 1
        }
145
146
        // run query
147 1
        $query = $queryBuilder->getQuery();
148 1
        $records = array_values($query->execute()->toArray());
149
150 1
        $totalCount = $query->count();
151 1
        $numPages = (int) ceil($totalCount / $numberPerPage);
152 1
        $page = (int) ceil($startAt / $numberPerPage) + 1;
153 1
        if ($numPages > 1) {
154
            $request->attributes->set('paging', true);
155
            $request->attributes->set('page', $page);
156
            $request->attributes->set('numPages', $numPages);
157
            $request->attributes->set('startAt', $startAt);
158
            $request->attributes->set('perPage', $numberPerPage);
159
            $request->attributes->set('totalCount', $totalCount);
160
        }
161
162 1
        return $records;
163
    }
164
165
    /**
166
     * @param \Graviton\I18nBundle\Document\Translatable $entity entity to insert
167
     *
168
     * @return Object
169
     */
170 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...
171
    {
172
        $this->checkIfOriginRecord($entity);
173
        $manager = $this->repository->getDocumentManager();
174
        $manager->persist($entity);
175
        $manager->flush($entity);
176
177
        return $this->find($entity->getId());
178
    }
179
180
    /**
181
     * @param string $documentId id of entity to find
182
     *
183
     * @return Object
184
     */
185 1
    public function find($documentId)
186
    {
187 1
        return $this->repository->find($documentId);
188
    }
189
190
    /**
191
     * {@inheritDoc}
192
     *
193
     * @param string $documentId id of entity to update
194
     * @param Object $entity     new entity
195
     *
196
     * @return Object
197
     */
198 1 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...
199
    {
200 1
        $manager = $this->repository->getDocumentManager();
201
        // In both cases the document attribute named originRecord must not be 'core'
202 1
        $this->checkIfOriginRecord($entity);
203 1
        $this->checkIfOriginRecord($this->find($documentId));
204 1
        $entity = $manager->merge($entity);
205 1
        $manager->flush();
206
207 1
        return $entity;
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     *
213
     * @param string $documentId id of entity to delete
214
     *
215
     * @return null|Object
216
     */
217 1
    public function deleteRecord($documentId)
218
    {
219 1
        $manager = $this->repository->getDocumentManager();
220 1
        $entity = $this->find($documentId);
221
222 1
        $return = $entity;
223 1
        if ($entity) {
224 1
            $this->checkIfOriginRecord($entity);
225 1
            $manager->remove($entity);
226 1
            $manager->flush();
227 1
            $return = null;
228 1
        }
229
230 1
        return $return;
231
    }
232
233
    /**
234
     * get classname of entity
235
     *
236
     * @return string
237
     */
238 1
    public function getEntityClass()
239
    {
240 1
        return $this->repository->getDocumentName();
241
    }
242
243
    /**
244
     * {@inheritDoc}
245
     *
246
     * Currently this is being used to build the route id used for redirecting
247
     * to newly made documents. It might benefit from having a different name
248
     * for those purposes.
249
     *
250
     * We might use a convention based mapping here:
251
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
252
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
253
     *
254
     * @todo implement this in a more convention based manner
255
     *
256
     * @return string
257
     */
258
    public function getConnectionName()
259
    {
260
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
261
262
        return 'graviton.' . $bundle;
263
    }
264
265
    /**
266
     * Does the actual query using the RQL Bundle.
267
     *
268
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
269
     * @param Query   $query        query from parser
270
     *
271
     * @return array
272
     */
273
    protected function doRqlQuery($queryBuilder, Query $query)
274
    {
275
        $this->visitor->setBuilder($queryBuilder);
276
277
        return $this->visitor->visit($query);
278
    }
279
280
    /**
281
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
282
     *
283
     * @param Object $record record
284
     *
285
     * @return void
286
     */
287 7
    protected function checkIfOriginRecord($record)
288
    {
289
        if ($record instanceof RecordOriginInterface
290 7
            && !$record->isRecordOriginModifiable()
291 7
        ) {
292 3
            $values = $this->notModifiableOriginRecords;
293 3
            $originValue = strtolower(trim($record->getRecordOrigin()));
294
295 3
            if (in_array($originValue, $values)) {
296 1
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
297
298 1
                throw new RecordOriginModifiedException($msg);
299
            }
300 2
        }
301 6
    }
302
303
    /**
304
     * Determines the configured amount fo data records to be returned in pagination context.
305
     *
306
     * @return int
307
     */
308 1
    private function getDefaultLimit()
309
    {
310 1
        if (0 < $this->paginationDefaultLimit) {
311 1
            return $this->paginationDefaultLimit;
312 1
        }
313
314
        return 10;
315
    }
316
}
317