Completed
Push — master ( 798ee0...2329f6 )
by Lucas
08:18
created

DocumentModel::getEntityClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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 Graviton\SecurityBundle\Entities\SecurityUser;
11
use Symfony\Component\HttpFoundation\Request;
12
use Doctrine\ODM\MongoDB\Query\Builder;
13
use Graviton\Rql\Visitor\MongoOdm as Visitor;
14
use Xiag\Rql\Parser\Query;
15
use Graviton\ExceptionBundle\Exception\RecordOriginModifiedException;
16
17
/**
18
 * Use doctrine odm as backend
19
 *
20
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
21
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
22
 * @link     http://swisscom.ch
23
 */
24
class DocumentModel extends SchemaModel implements ModelInterface
25
{
26
    /**
27
     * @var string
28
     */
29
    protected $description;
30
    /**
31
     * @var string[]
32
     */
33
    protected $fieldTitles;
34
    /**
35
     * @var string[]
36
     */
37
    protected $fieldDescriptions;
38
    /**
39
     * @var string[]
40
     */
41
    protected $requiredFields = array();
42
    /**
43
     * @var DocumentRepository
44
     */
45
    private $repository;
46
    /**
47
     * @var Visitor
48
     */
49
    private $visitor;
50
    /**
51
     * @var array
52
     */
53
    protected $notModifiableOriginRecords;
54
    /**
55
     * @var  integer
56
     */
57
    private $paginationDefaultLimit;
58
59
    /**
60
     * @var boolean
61
     */
62
    protected $filterByAuthUser;
63 2
64
    /**
65 2
     * @var string
66 2
     */
67 2
    protected $filterByAuthField;
68 2
69 2
    /**
70
     * @param Visitor $visitor                    rql query visitor
71
     * @param array   $notModifiableOriginRecords strings with not modifiable recordOrigin values
72
     * @param integer $paginationDefaultLimit     amount of data records to be returned when in pagination context.
73
     */
74
    public function __construct(Visitor $visitor, $notModifiableOriginRecords, $paginationDefaultLimit)
75
    {
76
        parent::__construct();
77
        $this->visitor = $visitor;
78
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
79
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
80
    }
81
82
    /**
83
     * get repository instance
84
     *
85
     * @return DocumentRepository
86
     */
87
    public function getRepository()
88 2
    {
89
        return $this->repository;
90 2
    }
91
92 2
    /**
93
     * create new app model
94
     *
95
     * @param DocumentRepository $repository Repository of countries
96
     *
97
     * @return \Graviton\RestBundle\Model\DocumentModel
98
     */
99
    public function setRepository(DocumentRepository $repository)
100
    {
101
        $this->repository = $repository;
102 1
103
        return $this;
104 1
    }
105 1
106 1
    /**
107
     * {@inheritDoc}
108
     *
109 1
     * @param Request      $request The request object
110 1
     * @param SecurityUser $user    SecurityUser Object
111
     *
112
     * @return array
113 1
     */
114
    public function findAll(Request $request, SecurityUser $user = null)
115
    {
116
        $pageNumber = $request->query->get('page', 1);
117
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
118
        $startAt = ($pageNumber - 1) * $numberPerPage;
119
120
        /** @var \Doctrine\ODM\MongoDB\Query\Builder $queryBuilder */
121 1
        $queryBuilder = $this->repository
122
            ->createQueryBuilder();
123
124
        if ($this->filterByAuthUser && $user && $user->hasRole(SecurityUser::ROLE_USER)) {
125 1
            $queryBuilder->field($this->filterByAuthField)->equals($user->getUser()->getId());
126 1
        }
127 1
128
        // *** do we have an RQL expression, do we need to filter data?
129
        if ($request->attributes->get('hasRql', false)) {
130
            $queryBuilder = $this->doRqlQuery(
131 1
                $queryBuilder,
132 1
                $request->attributes->get('rqlQuery')
133 1
            );
134
        } else {
135
            // @todo [lapistano]: seems the offset is missing for this query.
136
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
137
            $queryBuilder->find($this->repository->getDocumentName());
138
        }
139
140
        // define offset and limit
141
        if (!array_key_exists('skip', $queryBuilder->getQuery()->getQuery())) {
142 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...
143 1
        } else {
144 1
            $startAt = (int) $queryBuilder->getQuery()->getQuery()['skip'];
145
        }
146
147 1
        if (!array_key_exists('limit', $queryBuilder->getQuery()->getQuery())) {
148 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...
149
        } else {
150 1
            $numberPerPage = (int) $queryBuilder->getQuery()->getQuery()['limit'];
151 1
        }
152 1
153 1
        /**
154
         * add a default sort on id if none was specified earlier
155
         *
156
         * not specifying something to sort on leads to very weird cases when fetching references
157
         */
158
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
159
            $queryBuilder->sort('_id');
160
        }
161
162 1
        // run query
163
        $query = $queryBuilder->getQuery();
164
        $records = array_values($query->execute()->toArray());
165
166
        $totalCount = $query->count();
167
        $numPages = (int) ceil($totalCount / $numberPerPage);
168
        $page = (int) ceil($startAt / $numberPerPage) + 1;
169
        if ($numPages > 1) {
170 1
            $request->attributes->set('paging', true);
171
            $request->attributes->set('page', $page);
172 1
            $request->attributes->set('numPages', $numPages);
173 1
            $request->attributes->set('startAt', $startAt);
174 1
            $request->attributes->set('perPage', $numberPerPage);
175 1
            $request->attributes->set('totalCount', $totalCount);
176
        }
177 1
178
        return $records;
179
    }
180
181
    /**
182
     * @param \Graviton\I18nBundle\Document\Translatable $entity entity to insert
183
     *
184
     * @return Object
185 1
     */
186 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...
187 1
    {
188
        $this->checkIfOriginRecord($entity);
189
        $manager = $this->repository->getDocumentManager();
190
        $manager->persist($entity);
191
        $manager->flush($entity);
192
193
        return $this->find($entity->getId());
194
    }
195
196
    /**
197
     * @param string $documentId id of entity to find
198 1
     *
199
     * @return Object
200 1
     */
201
    public function find($documentId)
202 1
    {
203 1
        return $this->repository->find($documentId);
204 1
    }
205 1
206
    /**
207 1
     * {@inheritDoc}
208
     *
209
     * @param string $documentId id of entity to update
210
     * @param Object $entity     new entity
211
     *
212
     * @return Object
213
     */
214 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...
215
    {
216
        $manager = $this->repository->getDocumentManager();
217 1
        // In both cases the document attribute named originRecord must not be 'core'
218
        $this->checkIfOriginRecord($entity);
219 1
        $this->checkIfOriginRecord($this->find($documentId));
220 1
        $entity = $manager->merge($entity);
221
        $manager->flush();
222 1
223 1
        return $entity;
224 1
    }
225 1
226 1
    /**
227 1
     * {@inheritDoc}
228 1
     *
229
     * @param string $documentId id of entity to delete
230 1
     *
231
     * @return null|Object
232
     */
233
    public function deleteRecord($documentId)
234
    {
235
        $manager = $this->repository->getDocumentManager();
236
        $entity = $this->find($documentId);
237
238 1
        $return = $entity;
239
        if ($entity) {
240 1
            $this->checkIfOriginRecord($entity);
241
            $manager->remove($entity);
242
            $manager->flush();
243
            $return = null;
244
        }
245
246
        return $return;
247
    }
248
249
    /**
250
     * get classname of entity
251
     *
252
     * @return string
253
     */
254
    public function getEntityClass()
255
    {
256
        return $this->repository->getDocumentName();
257
    }
258
259
    /**
260
     * {@inheritDoc}
261
     *
262
     * Currently this is being used to build the route id used for redirecting
263
     * to newly made documents. It might benefit from having a different name
264
     * for those purposes.
265
     *
266
     * We might use a convention based mapping here:
267
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
268
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
269
     *
270
     * @todo implement this in a more convention based manner
271
     *
272
     * @return string
273
     */
274
    public function getConnectionName()
275
    {
276
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
277
278
        return 'graviton.' . $bundle;
279
    }
280
281
    /**
282
     * Does the actual query using the RQL Bundle.
283
     *
284
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
285
     * @param Query   $query        query from parser
286
     *
287 7
     * @return array
288
     */
289
    protected function doRqlQuery($queryBuilder, Query $query)
290 7
    {
291 7
        $this->visitor->setBuilder($queryBuilder);
292 3
293 3
        return $this->visitor->visit($query);
294
    }
295 3
296 1
    /**
297
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
298 1
     *
299
     * @param Object $record record
300 2
     *
301 6
     * @return void
302
     */
303
    protected function checkIfOriginRecord($record)
304
    {
305
        if ($record instanceof RecordOriginInterface
306
            && !$record->isRecordOriginModifiable()
307
        ) {
308 1
            $values = $this->notModifiableOriginRecords;
309
            $originValue = strtolower(trim($record->getRecordOrigin()));
310 1
311 1
            if (in_array($originValue, $values)) {
312 1
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
313
314
                throw new RecordOriginModifiedException($msg);
315
            }
316
        }
317
    }
318
319
    /**
320
     * Determines the configured amount fo data records to be returned in pagination context.
321
     *
322
     * @return int
323
     */
324
    private function getDefaultLimit()
325
    {
326
        if (0 < $this->paginationDefaultLimit) {
327
            return $this->paginationDefaultLimit;
328
        }
329
330
        return 10;
331
    }
332
333
    /**
334
     * @param Boolean $active active
335
     * @param String  $field  field
336
     * @return void
337
     */
338
    public function setFilterByAuthUser($active, $field)
339
    {
340
        $this->filterByAuthUser = is_bool($active) ? $active : false;
341
        $this->filterByAuthField = $field;
342
    }
343
}
344