Completed
Push — feature/EVO-7278-tracking-info... ( d73a1e...c12eb7 )
by
unknown
66:18
created

DocumentModel   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 613
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
wmc 70
c 0
b 0
f 0
lcom 1
cbo 20
dl 0
loc 613
rs 2.1006

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A getRepository() 0 4 1
A setRepository() 0 7 1
D findAll() 0 81 13
C buildSearchQuery() 0 51 13
B hasCustomSearchIndex() 0 17 5
B buildSearchTextQuery() 0 13 5
A getMongoDBVersion() 0 11 2
A insertRecord() 0 15 3
B find() 0 27 3
A updateRecord() 0 21 3
B deleteRecord() 0 24 4
A flush() 0 4 1
A deleteById() 0 9 1
A recordExists() 0 4 1
A selectSingleFields() 0 14 1
A getEntityClass() 0 8 2
A getConnectionName() 0 6 1
A doRqlQuery() 0 6 1
A checkIfOriginRecord() 0 15 4
A getDefaultLimit() 0 8 2
A dispatchModelEvent() 0 15 2

How to fix   Complexity   

Complex Class

Complex classes like DocumentModel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DocumentModel, and based on these observations, apply Extract Interface, too.

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\RestBundle\Event\ModelEvent;
11
use Graviton\Rql\Node\SearchNode;
12
use Graviton\SchemaBundle\Model\SchemaModel;
13
use Graviton\SecurityBundle\Entities\SecurityUser;
14
use Symfony\Bridge\Monolog\Logger;
15
use Symfony\Component\HttpFoundation\Request;
16
use Doctrine\ODM\MongoDB\Query\Builder;
17
use Graviton\Rql\Visitor\MongoOdm as Visitor;
18
use Xiag\Rql\Parser\Node\LimitNode;
19
use Xiag\Rql\Parser\Node\Query\AbstractLogicOperatorNode;
20
use Xiag\Rql\Parser\Query;
21
use Graviton\ExceptionBundle\Exception\RecordOriginModifiedException;
22
use Xiag\Rql\Parser\Exception\SyntaxErrorException as RqlSyntaxErrorException;
23
use Graviton\SchemaBundle\Document\Schema as SchemaDocument;
24
use Xiag\Rql\Parser\Query as XiagQuery;
25
use \Doctrine\ODM\MongoDB\Query\Builder as MongoBuilder;
26
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher as EventDispatcher;
27
28
/**
29
 * Use doctrine odm as backend
30
 *
31
 * @author  List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
32
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
33
 * @link    http://swisscom.ch
34
 */
35
class DocumentModel extends SchemaModel implements ModelInterface
36
{
37
    /**
38
     * @var string
39
     */
40
    protected $description;
41
    /**
42
     * @var string[]
43
     */
44
    protected $fieldTitles;
45
    /**
46
     * @var string[]
47
     */
48
    protected $fieldDescriptions;
49
    /**
50
     * @var string[]
51
     */
52
    protected $requiredFields = array();
53
    /**
54
     * @var string[]
55
     */
56
    protected $searchableFields = array();
57
    /**
58
     * @var string[]
59
     */
60
    protected $textIndexes = array();
61
    /**
62
     * @var DocumentRepository
63
     */
64
    private $repository;
65
    /**
66
     * @var Visitor
67
     */
68
    private $visitor;
69
    /**
70
     * @var array
71
     */
72
    protected $notModifiableOriginRecords;
73
    /**
74
     * @var  integer
75
     */
76
    private $paginationDefaultLimit;
77
78
    /**
79
     * @var boolean
80
     */
81
    protected $filterByAuthUser;
82
83
    /**
84
     * @var string
85
     */
86
    protected $filterByAuthField;
87
88
    /**
89
     * @var DocumentManager
90
     */
91
    protected $manager;
92
93
    /** @var EventDispatcher */
94
    protected $eventDispatcher;
95
96
    /**
97
     * @param Visitor         $visitor                    rql query visitor
98
     * @param EventDispatcher $eventDispatcher            Kernel event dispatcher
99
     * @param array           $notModifiableOriginRecords strings with not modifiable recordOrigin values
100
     * @param integer         $paginationDefaultLimit     amount of data records to be returned when in pagination cnt
101
     */
102
    public function __construct(
103
        Visitor $visitor,
104
        $eventDispatcher,
105
        $notModifiableOriginRecords,
106
        $paginationDefaultLimit
107
    ) {
108
        parent::__construct();
109
        $this->visitor = $visitor;
110
        $this->eventDispatcher = $eventDispatcher;
111
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
112
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
113
    }
114
115
    /**
116
     * get repository instance
117
     *
118
     * @return DocumentRepository
119
     */
120
    public function getRepository()
121
    {
122
        return $this->repository;
123
    }
124
125
    /**
126
     * create new app model
127
     *
128
     * @param DocumentRepository $repository Repository of countries
129
     *
130
     * @return \Graviton\RestBundle\Model\DocumentModel
131
     */
132
    public function setRepository(DocumentRepository $repository)
133
    {
134
        $this->repository = $repository;
135
        $this->manager = $repository->getDocumentManager();
136
137
        return $this;
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     *
143
     * @param Request      $request The request object
144
     * @param SecurityUser $user    SecurityUser Object
0 ignored issues
show
Documentation introduced by
Should the type for parameter $user not be null|SecurityUser?

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...
145
     *
146
     * @return array
147
     */
148
    public function findAll(Request $request, SecurityUser $user = null)
149
    {
150
        $pageNumber = $request->query->get('page', 1);
151
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
152
        $startAt = ($pageNumber - 1) * $numberPerPage;
153
154
        /** @var XiagQuery $xiagQuery */
155
        $xiagQuery = $request->attributes->get('rqlQuery');
156
157
        /** @var MongoBuilder $queryBuilder */
158
        $queryBuilder = $this->repository
159
            ->createQueryBuilder();
160
161
        // Setting RQL Query
162
        if ($xiagQuery) {
163
            // Clean up Search rql param and set it as Doctrine query
164
            if ($xiagQuery->getQuery() && $this->hasCustomSearchIndex() && (float) $this->getMongoDBVersion() >= 2.6) {
165
                $searchQueries = $this->buildSearchQuery($xiagQuery, $queryBuilder);
166
                $xiagQuery = $searchQueries['xiagQuery'];
167
                $queryBuilder = $searchQueries['queryBuilder'];
168
            }
169
            $queryBuilder = $this->doRqlQuery(
170
                $queryBuilder,
171
                $xiagQuery
172
            );
173
        } else {
174
            // @todo [lapistano]: seems the offset is missing for this query.
175
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
176
            $queryBuilder->find($this->repository->getDocumentName());
177
        }
178
179
        /** @var LimitNode $rqlLimit */
180
        $rqlLimit = $xiagQuery instanceof XiagQuery ? $xiagQuery->getLimit() : false;
181
182
        // define offset and limit
183
        if (!$rqlLimit || !$rqlLimit->getOffset()) {
184
            $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...
185
        } else {
186
            $startAt = (int) $rqlLimit->getOffset();
187
            $queryBuilder->skip($startAt);
188
        }
189
190
        if (!$rqlLimit || is_null($rqlLimit->getLimit())) {
191
            $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...
192
        } else {
193
            $numberPerPage = (int) $rqlLimit->getLimit();
194
            $queryBuilder->limit($numberPerPage);
195
        }
196
197
        // Limit can not be negative nor null.
198
        if ($numberPerPage < 1) {
199
            throw new RqlSyntaxErrorException('negative or null limit in rql');
200
        }
201
202
        /**
203
         * add a default sort on id if none was specified earlier
204
         *
205
         * not specifying something to sort on leads to very weird cases when fetching references.
206
         */
207
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
208
            $queryBuilder->sort('_id');
209
        }
210
211
        // run query
212
        $query = $queryBuilder->getQuery();
213
        $records = array_values($query->execute()->toArray());
214
215
        $totalCount = $query->count();
216
        $numPages = (int) ceil($totalCount / $numberPerPage);
217
        $page = (int) ceil($startAt / $numberPerPage) + 1;
218
        if ($numPages > 1) {
219
            $request->attributes->set('paging', true);
220
            $request->attributes->set('page', $page);
221
            $request->attributes->set('numPages', $numPages);
222
            $request->attributes->set('startAt', $startAt);
223
            $request->attributes->set('perPage', $numberPerPage);
224
            $request->attributes->set('totalCount', $totalCount);
225
        }
226
227
        return $records;
228
    }
229
230
    /**
231
     * @param XiagQuery    $xiagQuery    Xiag Builder
232
     * @param MongoBuilder $queryBuilder Mongo Doctrine query builder
233
     * @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...
234
     */
235
    private function buildSearchQuery(XiagQuery $xiagQuery, MongoBuilder $queryBuilder)
236
    {
237
        $innerQuery = $xiagQuery->getQuery();
238
        $hasSearch = false;
239
        $nodes = [];
240
        if ($innerQuery instanceof AbstractLogicOperatorNode) {
241
            foreach ($innerQuery->getQueries() as $key => $innerRql) {
242
                if ($innerRql instanceof SearchNode) {
243
                    if (!$hasSearch) {
244
                        $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerRql);
245
                        $hasSearch = true;
246
                    }
247
                } else {
248
                    $nodes[] = $innerRql;
249
                }
250
            }
251
        } elseif ($innerQuery instanceof SearchNode) {
252
            $queryBuilder = $this->repository->createQueryBuilder();
253
            $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerQuery);
254
            $hasSearch = true;
255
        }
256
        // Remove the Search from RQL xiag
257
        if ($hasSearch && $nodes) {
258
            $newXiagQuery = new XiagQuery();
259
            if ($xiagQuery->getLimit()) {
260
                $newXiagQuery->setLimit($xiagQuery->getLimit());
261
            }
262
            if ($xiagQuery->getSelect()) {
263
                $newXiagQuery->setSelect($xiagQuery->getSelect());
264
            }
265
            if ($xiagQuery->getSort()) {
266
                $newXiagQuery->setSort($xiagQuery->getSort());
267
            }
268
            $binderClass = get_class($innerQuery);
269
            /** @var AbstractLogicOperatorNode $newBinder */
270
            $newBinder = new $binderClass();
271
            foreach ($nodes as $node) {
272
                $newBinder->addQuery($node);
273
            }
274
            $newXiagQuery->setQuery($newBinder);
275
            // Reset original query, so that there is no Search param
276
            $xiagQuery = $newXiagQuery;
277
        }
278
        if ($hasSearch) {
279
            $queryBuilder->sortMeta('score', 'textScore');
280
        }
281
        return [
282
            'xiagQuery'     => $xiagQuery,
283
            'queryBuilder'  => $queryBuilder
284
        ];
285
    }
286
287
    /**
288
     * Check if collection has search indexes in DB
289
     *
290
     * @param string $prefix the prefix for custom text search indexes
291
     * @return bool
292
     */
293
    private function hasCustomSearchIndex($prefix = 'search')
294
    {
295
        $metadata = $this->repository->getClassMetadata();
296
        $indexes = $metadata->getIndexes();
297
        if (count($indexes) < 1) {
298
            return false;
299
        }
300
        $collectionsName = substr($metadata->getName(), strrpos($metadata->getName(), '\\') + 1);
301
        $searchIndexName = $prefix.$collectionsName.'Index';
302
        // We reverse as normally the search index is the last.
303
        foreach (array_reverse($indexes) as $index) {
304
            if (array_key_exists('keys', $index) && array_key_exists($searchIndexName, $index['keys'])) {
305
                return true;
306
            }
307
        }
308
        return false;
309
    }
310
311
    /**
312
     * Build Search text index
313
     *
314
     * @param MongoBuilder $queryBuilder Doctrine mongo query builder object
315
     * @param SearchNode   $searchNode   Graviton Search node
316
     * @return MongoBuilder
317
     */
318
    private function buildSearchTextQuery(MongoBuilder $queryBuilder, SearchNode $searchNode)
319
    {
320
        $searchArr = [];
321
        foreach ($searchNode->getSearchTerms() as $string) {
322
            if (!empty(trim($string))) {
323
                $searchArr[] = (strpos($string, '.') !== false) ? "\"{$string}\"" : $string;
324
            }
325
        }
326
        if (!empty($searchArr)) {
327
            $queryBuilder->addAnd($queryBuilder->expr()->text(implode(' ', $searchArr)));
328
        }
329
        return $queryBuilder;
330
    }
331
332
    /**
333
     * @return string the version of the MongoDB as a string
334
     */
335
    private function getMongoDBVersion()
336
    {
337
        $buildInfo = $this->repository->getDocumentManager()->getDocumentDatabase(
338
            $this->repository->getClassName()
339
        )->command(['buildinfo'=>1]);
340
        if (isset($buildInfo['version'])) {
341
            return $buildInfo['version'];
342
        } else {
343
            return "unkown";
344
        }
345
    }
346
347
    /**
348
     * @param object $entity       entity to insert
349
     * @param bool   $returnEntity true to return entity
350
     * @param bool   $doFlush      if we should flush or not after insert
351
     *
352
     * @return Object|null
353
     */
354
    public function insertRecord($entity, $returnEntity = true, $doFlush = true)
355
    {
356
        $this->manager->persist($entity);
357
358
        if ($doFlush) {
359
            $this->manager->flush($entity);
360
        }
361
362
        // Fire ModelEvent
363
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_INSERT, $entity);
364
365
        if ($returnEntity) {
366
            return $this->find($entity->getId());
367
        }
368
    }
369
370
    /**
371
     * @param string  $documentId id of entity to find
372
     * @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...
373
     *
374
     * @return Object
375
     */
376
    public function find($documentId, Request $request = null)
377
    {
378
        if ($request instanceof Request) {
379
            // if we are provided a Request, we apply RQL
380
381
            /** @var MongoBuilder $queryBuilder */
382
            $queryBuilder = $this->repository
383
                ->createQueryBuilder();
384
385
            /** @var XiagQuery $query */
386
            $query = $request->attributes->get('rqlQuery');
387
388
            if ($query instanceof XiagQuery) {
389
                $queryBuilder = $this->doRqlQuery(
390
                    $queryBuilder,
391
                    $query
392
                );
393
            }
394
395
            $queryBuilder->field('id')->equals($documentId);
396
397
            $query = $queryBuilder->getQuery();
398
            return $query->getSingleResult();
399
        }
400
401
        return $this->repository->find($documentId);
402
    }
403
404
    /**
405
     * {@inheritDoc}
406
     *
407
     * @param string $documentId   id of entity to update
408
     * @param Object $entity       new entity
409
     * @param bool   $returnEntity true to return entity
410
     *
411
     * @return Object|null
412
     */
413
    public function updateRecord($documentId, $entity, $returnEntity = true)
414
    {
415
        if (!is_null($documentId)) {
416
            $this->deleteById($documentId);
417
            // detach so odm knows it's gone
418
            $this->manager->detach($entity);
419
            $this->manager->clear();
420
        }
421
422
        $entity = $this->manager->merge($entity);
423
424
        $this->manager->persist($entity);
425
        $this->manager->flush($entity);
426
        
427
        // Fire ModelEvent
428
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_UPDATE, $entity);
429
430
        if ($returnEntity) {
431
            return $entity;
432
        }
433
    }
434
435
    /**
436
     * {@inheritDoc}
437
     *
438
     * @param string|object $id id of entity to delete or entity instance
439
     *
440
     * @return null|Object
441
     */
442
    public function deleteRecord($id)
443
    {
444
        if (is_object($id)) {
445
            $entity = $id;
446
        } else {
447
            $entity = $this->find($id);
448
        }
449
450
        $this->checkIfOriginRecord($entity);
451
        $return = $entity;
452
453
        if (is_callable([$entity, 'getId']) && $entity->getId() != null) {
454
            $this->deleteById($entity->getId());
455
            // detach so odm knows it's gone
456
            $this->manager->detach($entity);
457
            $this->manager->clear();
458
            $return = null;
459
            
460
            // Dispatch ModelEvent
461
            $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_DELETE, $return);
0 ignored issues
show
Documentation introduced by
$return is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
462
        }
463
464
        return $return;
465
    }
466
467
    /**
468
     * Triggers a flush on the DocumentManager
469
     *
470
     * @param null $document optional document
471
     *
472
     * @return void
473
     */
474
    public function flush($document = null)
475
    {
476
        $this->manager->flush($document);
477
    }
478
479
    /**
480
     * A low level delete without any checks
481
     *
482
     * @param mixed $id record id
483
     *
484
     * @return void
485
     */
486
    private function deleteById($id)
487
    {
488
        $builder = $this->repository->createQueryBuilder();
489
        $builder
490
            ->remove()
491
            ->field('id')->equals($id)
492
            ->getQuery()
493
            ->execute();
494
    }
495
496
    /**
497
     * Checks in a performant way if a certain record id exists in the database
498
     *
499
     * @param mixed $id record id
500
     *
501
     * @return bool true if it exists, false otherwise
502
     */
503
    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...
504
    {
505
        return is_array($this->selectSingleFields($id, ['id'], false));
506
    }
507
508
    /**
509
     * Returns a set of fields from an existing resource in a performant manner.
510
     * If you need to check certain fields on an object (and don't need everything), this
511
     * is a better way to get what you need.
512
     * If the record is not present, you will receive null. If you don't need an hydrated
513
     * instance, make sure to pass false there.
514
     *
515
     * @param mixed $id      record id
516
     * @param array $fields  list of fields you need.
517
     * @param bool  $hydrate whether to hydrate object or not
518
     *
519
     * @return array|null|object
520
     */
521
    public function selectSingleFields($id, array $fields, $hydrate = true)
522
    {
523
        $builder = $this->repository->createQueryBuilder();
524
        $idField = $this->repository->getClassMetadata()->getIdentifier()[0];
525
526
        $record = $builder
527
            ->field($idField)->equals($id)
528
            ->select($fields)
529
            ->hydrate($hydrate)
530
            ->getQuery()
531
            ->getSingleResult();
532
533
        return $record;
534
    }
535
536
    /**
537
     * get classname of entity
538
     *
539
     * @return string|null
540
     */
541
    public function getEntityClass()
542
    {
543
        if ($this->repository instanceof DocumentRepository) {
544
            return $this->repository->getDocumentName();
545
        }
546
547
        return null;
548
    }
549
550
    /**
551
     * {@inheritDoc}
552
     *
553
     * Currently this is being used to build the route id used for redirecting
554
     * to newly made documents. It might benefit from having a different name
555
     * for those purposes.
556
     *
557
     * We might use a convention based mapping here:
558
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
559
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
560
     *
561
     * @todo implement this in a more convention based manner
562
     *
563
     * @return string
564
     */
565
    public function getConnectionName()
566
    {
567
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
568
569
        return 'graviton.' . $bundle;
570
    }
571
572
    /**
573
     * Does the actual query using the RQL Bundle.
574
     *
575
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
576
     * @param Query   $query        query from parser
577
     *
578
     * @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...
579
     */
580
    protected function doRqlQuery($queryBuilder, Query $query)
581
    {
582
        $this->visitor->setBuilder($queryBuilder);
583
584
        return $this->visitor->visit($query);
585
    }
586
587
    /**
588
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
589
     *
590
     * @param Object $record record
591
     *
592
     * @return void
593
     */
594
    protected function checkIfOriginRecord($record)
595
    {
596
        if ($record instanceof RecordOriginInterface
597
            && !$record->isRecordOriginModifiable()
598
        ) {
599
            $values = $this->notModifiableOriginRecords;
600
            $originValue = strtolower(trim($record->getRecordOrigin()));
601
602
            if (in_array($originValue, $values)) {
603
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
604
605
                throw new RecordOriginModifiedException($msg);
606
            }
607
        }
608
    }
609
610
    /**
611
     * Determines the configured amount fo data records to be returned in pagination context.
612
     *
613
     * @return int
614
     */
615
    private function getDefaultLimit()
616
    {
617
        if (0 < $this->paginationDefaultLimit) {
618
            return $this->paginationDefaultLimit;
619
        }
620
621
        return 10;
622
    }
623
624
    /**
625
     * Will fire a ModelEvent
626
     *
627
     * @param string $action     insert or update
628
     * @param Object $collection the changed Document
629
     *
630
     * @return void
631
     */
632
    private function dispatchModelEvent($action, $collection)
633
    {
634
        if (!($this->repository instanceof DocumentRepository)) {
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