Completed
Push — feature/EVO-8323-configurable-... ( 44df39...80920d )
by
unknown
65:23
created

DocumentModel::updateRecord()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 12
cp 0
rs 9.3142
c 0
b 0
f 0
cc 3
eloc 11
nc 4
nop 3
crap 12
1
<?php
2
/**
3
 * Use doctrine odm as backend
4
 */
5
6
namespace Graviton\RestBundle\Model;
7
8
use Doctrine\ODM\MongoDB\DocumentManager;
9
use Doctrine\ODM\MongoDB\DocumentRepository;
10
use Graviton\DocumentBundle\Service\CollectionCache;
11
use Graviton\RestBundle\Event\ModelEvent;
12
use Graviton\Rql\Node\SearchNode;
13
use Graviton\SchemaBundle\Model\SchemaModel;
14
use Symfony\Component\HttpFoundation\Request;
15
use Doctrine\ODM\MongoDB\Query\Builder;
16
use Graviton\Rql\Visitor\MongoOdm as Visitor;
17
use Xiag\Rql\Parser\Node\LimitNode;
18
use Xiag\Rql\Parser\Node\Query\AbstractLogicOperatorNode;
19
use Xiag\Rql\Parser\Query;
20
use Graviton\ExceptionBundle\Exception\RecordOriginModifiedException;
21
use Xiag\Rql\Parser\Exception\SyntaxErrorException as RqlSyntaxErrorException;
22
use Xiag\Rql\Parser\Query as XiagQuery;
23
use \Doctrine\ODM\MongoDB\Query\Builder as MongoBuilder;
24
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher as EventDispatcher;
25
use Graviton\ExceptionBundle\Exception\NotFoundException;
26
27
/**
28
 * Use doctrine odm as backend
29
 *
30
 * @author  List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
31
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
32
 * @link    http://swisscom.ch
33
 */
34
class DocumentModel extends SchemaModel implements ModelInterface
35
{
36
    /**
37
     * @var string
38
     */
39
    protected $description;
40
    /**
41
     * @var string[]
42
     */
43
    protected $fieldTitles;
44
    /**
45
     * @var string[]
46
     */
47
    protected $fieldDescriptions;
48
    /**
49
     * @var string[]
50
     */
51
    protected $requiredFields = array();
52
    /**
53
     * @var string[]
54
     */
55
    protected $searchableFields = array();
56
    /**
57
     * @var string[]
58
     */
59
    protected $textIndexes = array();
60
    /**
61
     * @var DocumentRepository
62
     */
63
    private $repository;
64
    /**
65
     * @var Visitor
66
     */
67
    private $visitor;
68
    /**
69
     * @var array
70
     */
71
    protected $notModifiableOriginRecords;
72
    /**
73
     * @var  integer
74
     */
75
    private $paginationDefaultLimit;
76
77
    /**
78
     * @var boolean
79
     */
80
    protected $filterByAuthUser;
81
82
    /**
83
     * @var string
84
     */
85
    protected $filterByAuthField;
86
87
    /**
88
     * @var DocumentManager
89
     */
90
    protected $manager;
91
92
    /** @var EventDispatcher */
93
    protected $eventDispatcher;
94
95
    /** @var $collectionCache */
96
    protected $cache;
97
98
    /**
99
     * @param Visitor         $visitor                    rql query visitor
100
     * @param EventDispatcher $eventDispatcher            Kernel event dispatcher
101
     * @param CollectionCache $collectionCache            Cache Service
102
     * @param array           $notModifiableOriginRecords strings with not modifiable recordOrigin values
103
     * @param integer         $paginationDefaultLimit     amount of data records to be returned when in pagination cnt
104
     */
105
    public function __construct(
106
        Visitor $visitor,
107
        $eventDispatcher,
108
        CollectionCache $collectionCache,
109
        $notModifiableOriginRecords,
110
        $paginationDefaultLimit
111
    ) {
112
        parent::__construct();
113
        $this->visitor = $visitor;
114
        $this->eventDispatcher = $eventDispatcher;
115
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
116
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
117
        $this->cache = $collectionCache;
118
    }
119
120
    /**
121
     * get repository instance
122
     *
123
     * @return DocumentRepository
124
     */
125
    public function getRepository()
126
    {
127
        return $this->repository;
128
    }
129
130
    /**
131
     * create new app model
132
     *
133
     * @param DocumentRepository $repository Repository of countries
134
     *
135
     * @return \Graviton\RestBundle\Model\DocumentModel
136
     */
137
    public function setRepository(DocumentRepository $repository)
138
    {
139
        $this->repository = $repository;
140
        $this->manager = $repository->getDocumentManager();
141
142
        return $this;
143
    }
144
145
    /**
146
     * {@inheritDoc}
147
     *
148
     * @param Request $request The request object
149
     *
150
     * @return array
151
     */
152
    public function findAll(Request $request)
153
    {
154
        $pageNumber = $request->query->get('page', 1);
155
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
156
        $startAt = ($pageNumber - 1) * $numberPerPage;
157
158
        /** @var XiagQuery $xiagQuery */
159
        $xiagQuery = $request->attributes->get('rqlQuery');
160
161
        /** @var MongoBuilder $queryBuilder */
162
        $queryBuilder = $this->repository
163
            ->createQueryBuilder();
164
165
        // Setting RQL Query
166
        if ($xiagQuery) {
167
            // Clean up Search rql param and set it as Doctrine query
168
            if ($xiagQuery->getQuery() && $this->hasCustomSearchIndex() && (float) $this->getMongoDBVersion() >= 2.6) {
169
                $searchQueries = $this->buildSearchQuery($xiagQuery, $queryBuilder);
170
                $xiagQuery = $searchQueries['xiagQuery'];
171
                $queryBuilder = $searchQueries['queryBuilder'];
172
            }
173
            $queryBuilder = $this->doRqlQuery(
174
                $queryBuilder,
175
                $xiagQuery
176
            );
177
        } else {
178
            // @todo [lapistano]: seems the offset is missing for this query.
179
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
180
            $queryBuilder->find($this->repository->getDocumentName());
181
        }
182
183
        /** @var LimitNode $rqlLimit */
184
        $rqlLimit = $xiagQuery instanceof XiagQuery ? $xiagQuery->getLimit() : false;
185
186
        // define offset and limit
187
        if (!$rqlLimit || !$rqlLimit->getOffset()) {
188
            $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...
189
        } else {
190
            $startAt = (int) $rqlLimit->getOffset();
191
            $queryBuilder->skip($startAt);
192
        }
193
194
        if (!$rqlLimit || is_null($rqlLimit->getLimit())) {
195
            $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...
196
        } else {
197
            $numberPerPage = (int) $rqlLimit->getLimit();
198
            $queryBuilder->limit($numberPerPage);
199
        }
200
201
        // Limit can not be negative nor null.
202
        if ($numberPerPage < 1) {
203
            throw new RqlSyntaxErrorException('negative or null limit in rql');
204
        }
205
206
        /**
207
         * add a default sort on id if none was specified earlier
208
         *
209
         * not specifying something to sort on leads to very weird cases when fetching references.
210
         */
211
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
212
            $queryBuilder->sort('_id');
213
        }
214
215
        // run query
216
        $query = $queryBuilder->getQuery();
217
        $records = array_values($query->execute()->toArray());
218
219
        $totalCount = $query->count();
220
        $numPages = (int) ceil($totalCount / $numberPerPage);
221
        $page = (int) ceil($startAt / $numberPerPage) + 1;
222
        if ($numPages > 1) {
223
            $request->attributes->set('paging', true);
224
            $request->attributes->set('page', $page);
225
            $request->attributes->set('numPages', $numPages);
226
            $request->attributes->set('startAt', $startAt);
227
            $request->attributes->set('perPage', $numberPerPage);
228
            $request->attributes->set('totalCount', $totalCount);
229
        }
230
231
        return $records;
232
    }
233
234
    /**
235
     * @param XiagQuery    $xiagQuery    Xiag Builder
236
     * @param MongoBuilder $queryBuilder Mongo Doctrine query builder
237
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,Query|Builder>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
238
     */
239
    private function buildSearchQuery(XiagQuery $xiagQuery, MongoBuilder $queryBuilder)
240
    {
241
        $innerQuery = $xiagQuery->getQuery();
242
        $hasSearch = false;
243
        $nodes = [];
244
        if ($innerQuery instanceof AbstractLogicOperatorNode) {
245
            foreach ($innerQuery->getQueries() as $key => $innerRql) {
246
                if ($innerRql instanceof SearchNode) {
247
                    if (!$hasSearch) {
248
                        $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerRql);
249
                        $hasSearch = true;
250
                    }
251
                } else {
252
                    $nodes[] = $innerRql;
253
                }
254
            }
255
        } elseif ($innerQuery instanceof SearchNode) {
256
            $queryBuilder = $this->repository->createQueryBuilder();
257
            $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerQuery);
258
            $hasSearch = true;
259
        }
260
        // Remove the Search from RQL xiag
261
        if ($hasSearch && $nodes) {
262
            $newXiagQuery = new XiagQuery();
263
            if ($xiagQuery->getLimit()) {
264
                $newXiagQuery->setLimit($xiagQuery->getLimit());
265
            }
266
            if ($xiagQuery->getSelect()) {
267
                $newXiagQuery->setSelect($xiagQuery->getSelect());
268
            }
269
            if ($xiagQuery->getSort()) {
270
                $newXiagQuery->setSort($xiagQuery->getSort());
271
            }
272
            $binderClass = get_class($innerQuery);
273
            /** @var AbstractLogicOperatorNode $newBinder */
274
            $newBinder = new $binderClass();
275
            foreach ($nodes as $node) {
276
                $newBinder->addQuery($node);
277
            }
278
            $newXiagQuery->setQuery($newBinder);
279
            // Reset original query, so that there is no Search param
280
            $xiagQuery = $newXiagQuery;
281
        }
282
        if ($hasSearch) {
283
            $queryBuilder->sortMeta('score', 'textScore');
284
        }
285
        return [
286
            'xiagQuery'     => $xiagQuery,
287
            'queryBuilder'  => $queryBuilder
288
        ];
289
    }
290
291
    /**
292
     * Check if collection has search indexes in DB
293
     *
294
     * @param string $prefix the prefix for custom text search indexes
295
     * @return bool
296
     */
297
    private function hasCustomSearchIndex($prefix = 'search')
298
    {
299
        $metadata = $this->repository->getClassMetadata();
300
        $indexes = $metadata->getIndexes();
301
        if (count($indexes) < 1) {
302
            return false;
303
        }
304
        $collectionsName = substr($metadata->getName(), strrpos($metadata->getName(), '\\') + 1);
305
        $searchIndexName = $prefix.$collectionsName.'Index';
306
        // We reverse as normally the search index is the last.
307
        foreach (array_reverse($indexes) as $index) {
308
            if (array_key_exists('keys', $index) && array_key_exists($searchIndexName, $index['keys'])) {
309
                return true;
310
            }
311
        }
312
        return false;
313
    }
314
315
    /**
316
     * Build Search text index
317
     *
318
     * @param MongoBuilder $queryBuilder Doctrine mongo query builder object
319
     * @param SearchNode   $searchNode   Graviton Search node
320
     * @return MongoBuilder
321
     */
322
    private function buildSearchTextQuery(MongoBuilder $queryBuilder, SearchNode $searchNode)
323
    {
324
        $searchArr = [];
325
        foreach ($searchNode->getSearchTerms() as $string) {
326
            if (!empty(trim($string))) {
327
                $searchArr[] = (strpos($string, '.') !== false) ? "\"{$string}\"" : $string;
328
            }
329
        }
330
        if (!empty($searchArr)) {
331
            $queryBuilder->addAnd($queryBuilder->expr()->text(implode(' ', $searchArr)));
332
        }
333
        return $queryBuilder;
334
    }
335
336
    /**
337
     * @return string the version of the MongoDB as a string
338
     */
339
    private function getMongoDBVersion()
340
    {
341
        $buildInfo = $this->repository->getDocumentManager()->getDocumentDatabase(
342
            $this->repository->getClassName()
343
        )->command(['buildinfo'=>1]);
344
        if (isset($buildInfo['version'])) {
345
            return $buildInfo['version'];
346
        } else {
347
            return "unkown";
348
        }
349
    }
350
351
    /**
352
     * @param object $entity       entity to insert
353
     * @param bool   $returnEntity true to return entity
354
     * @param bool   $doFlush      if we should flush or not after insert
355
     *
356
     * @return Object|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
357
     */
358
    public function insertRecord($entity, $returnEntity = true, $doFlush = true)
359
    {
360
        $this->manager->persist($entity);
361
362
        if ($doFlush) {
363
            $this->manager->flush($entity);
364
        }
365
366
        // Fire ModelEvent
367
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_INSERT, $entity);
368
369
        if ($returnEntity) {
370
            return $this->find($entity->getId());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->find($entity->getId()); of type array|object adds the type array to the return on line 370 which is incompatible with the return type declared by the interface Graviton\RestBundle\Mode...Interface::insertRecord of type object.
Loading history...
371
        }
372
    }
373
374
    /**
375
     * @param string  $documentId id of entity to find
376
     * @param Request $request    request
0 ignored issues
show
Documentation introduced by
Should the type for parameter $request not be null|Request?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
377
     *
378
     * @throws NotFoundException
379
     * @return Object
0 ignored issues
show
Documentation introduced by
Should the return type not be array|object? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
380
     */
381
    public function find($documentId, Request $request = null)
382
    {
383
        if (($request instanceof Request)  &&
384
            ($query = $request->attributes->get('rqlQuery')) &&
385
            (($query instanceof XiagQuery))
386
        ) {
387
            /** @var MongoBuilder $queryBuilder */
388
            $queryBuilder = $this->doRqlQuery($this->repository->createQueryBuilder(), $query);
389
            $queryBuilder->field('id')->equals($documentId);
390
            $result = $queryBuilder->getQuery()->getSingleResult();
0 ignored issues
show
Bug Compatibility introduced by
The expression $queryBuilder->getQuery()->getSingleResult(); of type array|object|null adds the type array to the return on line 399 which is incompatible with the return type declared by the interface Graviton\RestBundle\Model\ModelInterface::find of type object.
Loading history...
391
        } else {
392
            $result = $this->repository->find($documentId);
393
        }
394
395
        if (empty($result)) {
396
            throw new NotFoundException("Entry with id " . $documentId . " not found!");
397
        }
398
399
        return $result;
400
    }
401
402
    /**
403
     * {@inheritDoc}
404
     *
405
     * @param string $documentId   id of entity to update
406
     * @param Object $entity       new entity
407
     * @param bool   $returnEntity true to return entity
408
     *
409
     * @return Object|null
410
     */
411
    public function updateRecord($documentId, $entity, $returnEntity = true)
412
    {
413
        if (!is_null($documentId)) {
414
            $this->deleteById($documentId);
415
            // detach so odm knows it's gone
416
            $this->manager->detach($entity);
417
            $this->manager->clear();
418
        }
419
420
        $entity = $this->manager->merge($entity);
421
422
        $this->manager->persist($entity);
423
        $this->manager->flush($entity);
424
425
        // Fire ModelEvent
426
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_UPDATE, $entity);
427
428
        if ($returnEntity) {
429
            return $entity;
430
        }
431
    }
432
433
    /**
434
     * {@inheritDoc}
435
     *
436
     * @param string|object $id id of entity to delete or entity instance
437
     *
438
     * @return null|Object
0 ignored issues
show
Documentation introduced by
Should the return type not be array|object|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
439
     */
440
    public function deleteRecord($id)
441
    {
442
        if (is_object($id)) {
443
            $entity = $id;
444
        } else {
445
            $entity = $this->find($id);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->find($id); of type array|object adds the type array to the return on line 461 which is incompatible with the return type declared by the interface Graviton\RestBundle\Mode...Interface::deleteRecord of type null|object.
Loading history...
446
        }
447
448
        $this->checkIfOriginRecord($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->find($id) on line 445 can also be of type array; however, Graviton\RestBundle\Mode...::checkIfOriginRecord() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
449
        $return = $entity;
450
451
        if (is_callable([$entity, 'getId']) && $entity->getId() != null) {
452
            $this->deleteById($entity->getId());
453
            // detach so odm knows it's gone
454
            $this->manager->detach($entity);
0 ignored issues
show
Bug introduced by
It seems like $entity defined by $this->find($id) on line 445 can also be of type array; however, Doctrine\ODM\MongoDB\DocumentManager::detach() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
455
            $this->manager->clear();
456
            // Dispatch ModelEvent
457
            $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_DELETE, $return);
0 ignored issues
show
Bug introduced by
It seems like $return defined by $entity on line 449 can also be of type array; however, Graviton\RestBundle\Mode...l::dispatchModelEvent() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
458
            $return = null;
459
        }
460
461
        return $return;
462
    }
463
464
    /**
465
     * Triggers a flush on the DocumentManager
466
     *
467
     * @param null $document optional document
468
     *
469
     * @return void
470
     */
471
    public function flush($document = null)
472
    {
473
        $this->manager->flush($document);
474
    }
475
476
    /**
477
     * A low level delete without any checks
478
     *
479
     * @param mixed $id record id
480
     *
481
     * @return void
482
     */
483
    private function deleteById($id)
484
    {
485
        $builder = $this->repository->createQueryBuilder();
486
        $builder
487
            ->remove()
488
            ->field('id')->equals($id)
489
            ->getQuery()
490
            ->execute();
491
    }
492
493
    /**
494
     * Checks in a performant way if a certain record id exists in the database
495
     *
496
     * @param mixed $id record id
497
     *
498
     * @return bool true if it exists, false otherwise
499
     */
500
    public function recordExists($id)
0 ignored issues
show
Coding Style introduced by
function recordExists() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
501
    {
502
        return is_array($this->selectSingleFields($id, ['id'], false));
503
    }
504
505
    /**
506
     * Returns a set of fields from an existing resource in a performant manner.
507
     * If you need to check certain fields on an object (and don't need everything), this
508
     * is a better way to get what you need.
509
     * If the record is not present, you will receive null. If you don't need an hydrated
510
     * instance, make sure to pass false there.
511
     *
512
     * @param mixed $id      record id
513
     * @param array $fields  list of fields you need.
514
     * @param bool  $hydrate whether to hydrate object or not
515
     *
516
     * @return array|null|object
517
     */
518
    public function selectSingleFields($id, array $fields, $hydrate = true)
519
    {
520
        $builder = $this->repository->createQueryBuilder();
521
        $idField = $this->repository->getClassMetadata()->getIdentifier()[0];
522
523
        $record = $builder
524
            ->field($idField)->equals($id)
525
            ->select($fields)
526
            ->hydrate($hydrate)
527
            ->getQuery()
528
            ->getSingleResult();
529
530
        return $record;
531
    }
532
533
    /**
534
     * get classname of entity
535
     *
536
     * @return string|null
537
     */
538
    public function getEntityClass()
539
    {
540
        if ($this->repository instanceof DocumentRepository) {
541
            return $this->repository->getDocumentName();
542
        }
543
544
        return null;
545
    }
546
547
    /**
548
     * {@inheritDoc}
549
     *
550
     * Currently this is being used to build the route id used for redirecting
551
     * to newly made documents. It might benefit from having a different name
552
     * for those purposes.
553
     *
554
     * We might use a convention based mapping here:
555
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
556
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
557
     *
558
     * @todo implement this in a more convention based manner
559
     *
560
     * @return string
561
     */
562
    public function getConnectionName()
563
    {
564
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
565
566
        return 'graviton.' . $bundle;
567
    }
568
569
    /**
570
     * Does the actual query using the RQL Bundle.
571
     *
572
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
573
     * @param Query   $query        query from parser
574
     *
575
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be Builder|\Doctrine\ODM\MongoDB\Query\Expr?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
576
     */
577
    protected function doRqlQuery($queryBuilder, Query $query)
578
    {
579
        $this->visitor->setBuilder($queryBuilder);
580
581
        return $this->visitor->visit($query);
582
    }
583
584
    /**
585
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
586
     *
587
     * @param Object $record record
588
     *
589
     * @return void
590
     */
591
    protected function checkIfOriginRecord($record)
592
    {
593
        if ($record instanceof RecordOriginInterface
594
            && !$record->isRecordOriginModifiable()
595
        ) {
596
            $values = $this->notModifiableOriginRecords;
597
            $originValue = strtolower(trim($record->getRecordOrigin()));
598
599
            if (in_array($originValue, $values)) {
600
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
601
602
                throw new RecordOriginModifiedException($msg);
603
            }
604
        }
605
    }
606
607
    /**
608
     * Determines the configured amount fo data records to be returned in pagination context.
609
     *
610
     * @return int
611
     */
612
    private function getDefaultLimit()
613
    {
614
        if (0 < $this->paginationDefaultLimit) {
615
            return $this->paginationDefaultLimit;
616
        }
617
618
        return 10;
619
    }
620
621
    /**
622
     * Will fire a ModelEvent
623
     *
624
     * @param string $action     insert or update
625
     * @param Object $collection the changed Document
626
     *
627
     * @return void
628
     */
629
    private function dispatchModelEvent($action, $collection)
630
    {
631
        if (!($this->repository instanceof DocumentRepository)) {
632
            return;
633
        }
634
        if (!method_exists($collection, 'getId')) {
635
            return;
636
        }
637
638
        $event = new ModelEvent();
639
        $event->setCollectionId($collection->getId());
640
        $event->setActionByDispatchName($action);
641
        $event->setCollectionName($this->repository->getClassMetadata()->getCollection());
642
        $event->setCollectionClass($this->repository->getClassName());
643
        $event->setCollection($collection);
644
        
645
        $this->eventDispatcher->dispatch($action, $event);
646
    }
647
}
648