Completed
Push — master ( d54aa7...f819d0 )
by
unknown
15:37
created

DocumentModel::buildSearchTextQuery()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 11
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 6
nop 2
crap 20
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
use Graviton\RestBundle\Service\RestUtils;
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
    /** @var $collectionCache */
97
    protected $cache;
98
99
    /**
100
     * @var RestUtils
101
     */
102
    private $restUtils;
103
104
    /**
105
     * @param Visitor         $visitor                    rql query visitor
106
     * @param RestUtils       $restUtils                  Rest utils
107
     * @param EventDispatcher $eventDispatcher            Kernel event dispatcher
108
     * @param CollectionCache $collectionCache            Cache Service
109
     * @param array           $notModifiableOriginRecords strings with not modifiable recordOrigin values
110
     * @param integer         $paginationDefaultLimit     amount of data records to be returned when in pagination cnt
111
     */
112
    public function __construct(
113
        Visitor $visitor,
114
        RestUtils $restUtils,
115
        $eventDispatcher,
116
        CollectionCache $collectionCache,
117
        $notModifiableOriginRecords,
118
        $paginationDefaultLimit
119
    ) {
120
        parent::__construct();
121
        $this->visitor = $visitor;
122
        $this->eventDispatcher = $eventDispatcher;
123
        $this->notModifiableOriginRecords = $notModifiableOriginRecords;
124
        $this->paginationDefaultLimit = (int) $paginationDefaultLimit;
125
        $this->cache = $collectionCache;
126
        $this->restUtils = $restUtils;
127
    }
128
129
    /**
130
     * get repository instance
131
     *
132
     * @return DocumentRepository
133
     */
134
    public function getRepository()
135
    {
136
        return $this->repository;
137
    }
138
139
    /**
140
     * create new app model
141
     *
142
     * @param DocumentRepository $repository Repository of countries
143
     *
144
     * @return \Graviton\RestBundle\Model\DocumentModel
145
     */
146
    public function setRepository(DocumentRepository $repository)
147
    {
148
        $this->repository = $repository;
149
        $this->manager = $repository->getDocumentManager();
150
151
        return $this;
152
    }
153
154
    /**
155
     * {@inheritDoc}
156
     *
157
     * @param Request $request The request object
158
     *
159
     * @return array
160
     */
161
    public function findAll(Request $request)
162
    {
163
        $pageNumber = $request->query->get('page', 1);
164
        $numberPerPage = (int) $request->query->get('perPage', $this->getDefaultLimit());
165
        $startAt = ($pageNumber - 1) * $numberPerPage;
166
167
        /** @var XiagQuery $xiagQuery */
168
        $xiagQuery = $request->attributes->get('rqlQuery');
169
170
        /** @var MongoBuilder $queryBuilder */
171
        $queryBuilder = $this->repository
172
            ->createQueryBuilder();
173
174
        // Setting RQL Query
175
        if ($xiagQuery) {
176
            // Clean up Search rql param and set it as Doctrine query
177
            if ($xiagQuery->getQuery() && $this->hasCustomSearchIndex() && (float) $this->getMongoDBVersion() >= 2.6) {
178
                $searchQueries = $this->buildSearchQuery($xiagQuery, $queryBuilder);
179
                $xiagQuery = $searchQueries['xiagQuery'];
180
                $queryBuilder = $searchQueries['queryBuilder'];
181
            }
182
            $queryBuilder = $this->doRqlQuery(
183
                $queryBuilder,
184
                $xiagQuery
185
            );
186
        } else {
187
            // @todo [lapistano]: seems the offset is missing for this query.
188
            /** @var \Doctrine\ODM\MongoDB\Query\Builder $qb */
189
            $queryBuilder->find($this->repository->getDocumentName());
190
        }
191
192
        /** @var LimitNode $rqlLimit */
193
        $rqlLimit = $xiagQuery instanceof XiagQuery ? $xiagQuery->getLimit() : false;
194
195
        // define offset and limit
196
        if (!$rqlLimit || !$rqlLimit->getOffset()) {
197
            $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...
198
        } else {
199
            $startAt = (int) $rqlLimit->getOffset();
200
            $queryBuilder->skip($startAt);
201
        }
202
203
        if (!$rqlLimit || is_null($rqlLimit->getLimit())) {
204
            $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...
205
        } else {
206
            $numberPerPage = (int) $rqlLimit->getLimit();
207
            $queryBuilder->limit($numberPerPage);
208
        }
209
210
        // Limit can not be negative nor null.
211
        if ($numberPerPage < 1) {
212
            throw new RqlSyntaxErrorException('negative or null limit in rql');
213
        }
214
215
        /**
216
         * add a default sort on id if none was specified earlier
217
         *
218
         * not specifying something to sort on leads to very weird cases when fetching references.
219
         */
220
        if (!array_key_exists('sort', $queryBuilder->getQuery()->getQuery())) {
221
            $queryBuilder->sort('_id');
222
        }
223
224
        // run query
225
        $query = $queryBuilder->getQuery();
226
        $records = array_values($query->execute()->toArray());
227
228
        $totalCount = $query->count();
229
        $numPages = (int) ceil($totalCount / $numberPerPage);
230
        $page = (int) ceil($startAt / $numberPerPage) + 1;
231
        if ($numPages > 1) {
232
            $request->attributes->set('paging', true);
233
            $request->attributes->set('page', $page);
234
            $request->attributes->set('numPages', $numPages);
235
            $request->attributes->set('startAt', $startAt);
236
            $request->attributes->set('perPage', $numberPerPage);
237
            $request->attributes->set('totalCount', $totalCount);
238
        }
239
240
        return $records;
241
    }
242
243
    /**
244
     * @param XiagQuery    $xiagQuery    Xiag Builder
245
     * @param MongoBuilder $queryBuilder Mongo Doctrine query builder
246
     * @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...
247
     */
248
    private function buildSearchQuery(XiagQuery $xiagQuery, MongoBuilder $queryBuilder)
249
    {
250
        $innerQuery = $xiagQuery->getQuery();
251
        $hasSearch = false;
252
        $nodes = [];
253
        if ($innerQuery instanceof AbstractLogicOperatorNode) {
254
            foreach ($innerQuery->getQueries() as $key => $innerRql) {
255
                if ($innerRql instanceof SearchNode) {
256
                    if (!$hasSearch) {
257
                        $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerRql);
258
                        $hasSearch = true;
259
                    }
260
                } else {
261
                    $nodes[] = $innerRql;
262
                }
263
            }
264
        } elseif ($innerQuery instanceof SearchNode) {
265
            $queryBuilder = $this->repository->createQueryBuilder();
266
            $queryBuilder = $this->buildSearchTextQuery($queryBuilder, $innerQuery);
267
            $hasSearch = true;
268
        }
269
        // Remove the Search from RQL xiag
270
        if ($hasSearch && $nodes) {
271
            $newXiagQuery = new XiagQuery();
272
            if ($xiagQuery->getLimit()) {
273
                $newXiagQuery->setLimit($xiagQuery->getLimit());
274
            }
275
            if ($xiagQuery->getSelect()) {
276
                $newXiagQuery->setSelect($xiagQuery->getSelect());
277
            }
278
            if ($xiagQuery->getSort()) {
279
                $newXiagQuery->setSort($xiagQuery->getSort());
280
            }
281
            $binderClass = get_class($innerQuery);
282
            /** @var AbstractLogicOperatorNode $newBinder */
283
            $newBinder = new $binderClass();
284
            foreach ($nodes as $node) {
285
                $newBinder->addQuery($node);
286
            }
287
            $newXiagQuery->setQuery($newBinder);
288
            // Reset original query, so that there is no Search param
289
            $xiagQuery = $newXiagQuery;
290
        }
291
        if ($hasSearch) {
292
            $queryBuilder->sortMeta('score', 'textScore');
293
        }
294
        return [
295
            'xiagQuery'     => $xiagQuery,
296
            'queryBuilder'  => $queryBuilder
297
        ];
298
    }
299
300
    /**
301
     * Check if collection has search indexes in DB
302
     *
303
     * @param string $prefix the prefix for custom text search indexes
304
     * @return bool
305
     */
306
    private function hasCustomSearchIndex($prefix = 'search')
307
    {
308
        $metadata = $this->repository->getClassMetadata();
309
        $indexes = $metadata->getIndexes();
310
        if (count($indexes) < 1) {
311
            return false;
312
        }
313
        $collectionsName = substr($metadata->getName(), strrpos($metadata->getName(), '\\') + 1);
314
        $searchIndexName = $prefix.$collectionsName.'Index';
315
        // We reverse as normally the search index is the last.
316
        foreach (array_reverse($indexes) as $index) {
317
            if (array_key_exists('keys', $index) && array_key_exists($searchIndexName, $index['keys'])) {
318
                return true;
319
            }
320
        }
321
        return false;
322
    }
323
324
    /**
325
     * Build Search text index
326
     *
327
     * @param MongoBuilder $queryBuilder Doctrine mongo query builder object
328
     * @param SearchNode   $searchNode   Graviton Search node
329
     * @return MongoBuilder
330
     */
331
    private function buildSearchTextQuery(MongoBuilder $queryBuilder, SearchNode $searchNode)
332
    {
333
        $searchArr = [];
334
        foreach ($searchNode->getSearchTerms() as $string) {
335
            if (!empty(trim($string))) {
336
                $searchArr[] = "\"{$string}\"";
337
            }
338
        }
339
        if (!empty($searchArr)) {
340
            $queryBuilder->addAnd($queryBuilder->expr()->text(implode(' ', $searchArr)));
341
        }
342
        return $queryBuilder;
343
    }
344
345
    /**
346
     * @return string the version of the MongoDB as a string
347
     */
348
    private function getMongoDBVersion()
349
    {
350
        $buildInfo = $this->repository->getDocumentManager()->getDocumentDatabase(
351
            $this->repository->getClassName()
352
        )->command(['buildinfo'=>1]);
353
        if (isset($buildInfo['version'])) {
354
            return $buildInfo['version'];
355
        } else {
356
            return "unkown";
357
        }
358
    }
359
360
    /**
361
     * @param object $entity       entity to insert
362
     * @param bool   $returnEntity true to return entity
363
     * @param bool   $doFlush      if we should flush or not after insert
364
     *
365
     * @return Object|null
366
     */
367
    public function insertRecord($entity, $returnEntity = true, $doFlush = true)
368
    {
369
        $this->manager->persist($entity);
370
371
        if ($doFlush) {
372
            $this->manager->flush($entity);
373
        }
374
375
        // Fire ModelEvent
376
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_INSERT, $entity);
377
378
        if ($returnEntity) {
379
            return $this->find($entity->getId());
380
        }
381
        return null;
382
    }
383
384
    /**
385
     * @param string $documentId id of entity to find
386
     *
387
     * @throws NotFoundException
388
     * @return Object
389
     */
390
    public function find($documentId)
391
    {
392
        $result = $this->repository->find($documentId);
393
394
        if (empty($result)) {
395
            throw new NotFoundException("Entry with id " . $documentId . " not found!");
396
        }
397
398
        return $result;
399
    }
400
401
    /**
402
     * Will attempt to find Document by ID.
403
     * If config cache is enabled for document it will save it.
404
     *
405
     * @param string  $documentId id of entity to find
406
     * @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...
407
     *
408
     * @throws NotFoundException
409
     * @return string Serialised object
0 ignored issues
show
Documentation introduced by
Should the return type not be object|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.

Loading history...
410
     */
411
    public function getSerialised($documentId, Request $request = null)
412
    {
413
        if (($request instanceof Request)  &&
414
            ($query = $request->attributes->get('rqlQuery')) &&
415
            (($query instanceof XiagQuery))
416
        ) {
417
            /** @var MongoBuilder $queryBuilder */
418
            $queryBuilder = $this->doRqlQuery($this->repository->createQueryBuilder(), $query);
419
            $queryBuilder->field('id')->equals($documentId);
420
            $result = $queryBuilder->getQuery()->getSingleResult();
421
            if (empty($result)) {
422
                throw new NotFoundException("Entry with id " . $documentId . " not found!");
423
            }
424
            $document = $this->restUtils->serialize($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $queryBuilder->getQuery()->getSingleResult() on line 420 can also be of type array; however, Graviton\RestBundle\Service\RestUtils::serialize() does only seem to accept object|array<integer,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...
425
        } elseif ($cached = $this->cache->getByRepository($this->repository, $documentId)) {
426
            $document = $cached;
427
        } else {
428
            $this->cache->updateOperationCheck($this->repository, $documentId);
429
            $document = $this->restUtils->serialize($this->find($documentId));
430
            $this->cache->setByRepository($this->repository, $document, $documentId);
431
        }
432
433
        return $document;
434
    }
435
436
    /**
437
     * {@inheritDoc}
438
     *
439
     * @param string $documentId   id of entity to update
440
     * @param Object $entity       new entity
441
     * @param bool   $returnEntity true to return entity
442
     *
443
     * @return Object|null
444
     */
445
    public function updateRecord($documentId, $entity, $returnEntity = true)
446
    {
447
        if (!is_null($documentId)) {
448
            $this->deleteById($documentId);
449
            // detach so odm knows it's gone
450
            $this->manager->detach($entity);
451
            $this->manager->clear();
452
        }
453
454
        $entity = $this->manager->merge($entity);
455
456
        $this->manager->persist($entity);
457
        $this->manager->flush($entity);
458
459
        // Fire ModelEvent
460
        $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_UPDATE, $entity);
461
462
        if ($returnEntity) {
463
            return $entity;
464
        }
465
        return null;
466
    }
467
468
    /**
469
     * {@inheritDoc}
470
     *
471
     * @param string|object $id id of entity to delete or entity instance
472
     *
473
     * @return null|Object
474
     */
475
    public function deleteRecord($id)
476
    {
477
        // Check and wait if another update is being processed, avoid double delete
478
        $this->cache->updateOperationCheck($this->repository, $id);
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 475 can also be of type object; however, Graviton\DocumentBundle\...:updateOperationCheck() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
479
        $this->cache->addUpdateLock($this->repository, $id, 1);
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 475 can also be of type object; however, Graviton\DocumentBundle\...nCache::addUpdateLock() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
480
481
        if (is_object($id)) {
482
            $entity = $id;
483
        } else {
484
            $entity = $this->find($id);
485
        }
486
487
        $this->checkIfOriginRecord($entity);
488
        $return = $entity;
489
490
        if (is_callable([$entity, 'getId']) && $entity->getId() != null) {
491
            $this->deleteById($entity->getId());
492
            // detach so odm knows it's gone
493
            $this->manager->detach($entity);
494
            $this->manager->clear();
495
            // Dispatch ModelEvent
496
            $this->dispatchModelEvent(ModelEvent::MODEL_EVENT_DELETE, $return);
497
            $return = null;
498
        }
499
500
        $this->cache->releaseUpdateLock($this->repository, $id);
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 475 can also be of type object; however, Graviton\DocumentBundle\...he::releaseUpdateLock() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
501
502
        return $return;
503
    }
504
505
    /**
506
     * Triggers a flush on the DocumentManager
507
     *
508
     * @param null $document optional document
509
     *
510
     * @return void
511
     */
512
    public function flush($document = null)
513
    {
514
        $this->manager->flush($document);
515
    }
516
517
    /**
518
     * A low level delete without any checks
519
     *
520
     * @param mixed $id record id
521
     *
522
     * @return void
523
     */
524
    private function deleteById($id)
525
    {
526
        $builder = $this->repository->createQueryBuilder();
527
        $builder
528
            ->remove()
529
            ->field('id')->equals($id)
530
            ->getQuery()
531
            ->execute();
532
    }
533
534
    /**
535
     * Checks in a performant way if a certain record id exists in the database
536
     *
537
     * @param mixed $id record id
538
     *
539
     * @return bool true if it exists, false otherwise
540
     */
541
    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...
542
    {
543
        return is_array($this->selectSingleFields($id, ['id'], false));
544
    }
545
546
    /**
547
     * Returns a set of fields from an existing resource in a performant manner.
548
     * If you need to check certain fields on an object (and don't need everything), this
549
     * is a better way to get what you need.
550
     * If the record is not present, you will receive null. If you don't need an hydrated
551
     * instance, make sure to pass false there.
552
     *
553
     * @param mixed $id      record id
554
     * @param array $fields  list of fields you need.
555
     * @param bool  $hydrate whether to hydrate object or not
556
     *
557
     * @return array|null|object
558
     */
559
    public function selectSingleFields($id, array $fields, $hydrate = true)
560
    {
561
        $builder = $this->repository->createQueryBuilder();
562
        $idField = $this->repository->getClassMetadata()->getIdentifier()[0];
563
564
        $record = $builder
565
            ->field($idField)->equals($id)
566
            ->select($fields)
567
            ->hydrate($hydrate)
568
            ->getQuery()
569
            ->getSingleResult();
570
571
        return $record;
572
    }
573
574
    /**
575
     * get classname of entity
576
     *
577
     * @return string|null
578
     */
579
    public function getEntityClass()
580
    {
581
        if ($this->repository instanceof DocumentRepository) {
582
            return $this->repository->getDocumentName();
583
        }
584
585
        return null;
586
    }
587
588
    /**
589
     * {@inheritDoc}
590
     *
591
     * Currently this is being used to build the route id used for redirecting
592
     * to newly made documents. It might benefit from having a different name
593
     * for those purposes.
594
     *
595
     * We might use a convention based mapping here:
596
     * Graviton\CoreBundle\Document\App -> mongodb://graviton_core
597
     * Graviton\CoreBundle\Entity\Table -> mysql://graviton_core
598
     *
599
     * @todo implement this in a more convention based manner
600
     *
601
     * @return string
602
     */
603
    public function getConnectionName()
604
    {
605
        $bundle = strtolower(substr(explode('\\', get_class($this))[1], 0, -6));
606
607
        return 'graviton.' . $bundle;
608
    }
609
610
    /**
611
     * Does the actual query using the RQL Bundle.
612
     *
613
     * @param Builder $queryBuilder Doctrine ODM QueryBuilder
614
     * @param Query   $query        query from parser
615
     *
616
     * @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...
617
     */
618
    protected function doRqlQuery($queryBuilder, Query $query)
619
    {
620
        $this->visitor->setBuilder($queryBuilder);
621
622
        return $this->visitor->visit($query);
623
    }
624
625
    /**
626
     * Checks the recordOrigin attribute of a record and will throw an exception if value is not allowed
627
     *
628
     * @param Object $record record
629
     *
630
     * @return void
631
     */
632
    protected function checkIfOriginRecord($record)
633
    {
634
        if ($record instanceof RecordOriginInterface
635
            && !$record->isRecordOriginModifiable()
636
        ) {
637
            $values = $this->notModifiableOriginRecords;
638
            $originValue = strtolower(trim($record->getRecordOrigin()));
639
640
            if (in_array($originValue, $values)) {
641
                $msg = sprintf("Must not be one of the following keywords: %s", implode(', ', $values));
642
643
                throw new RecordOriginModifiedException($msg);
644
            }
645
        }
646
    }
647
648
    /**
649
     * Determines the configured amount fo data records to be returned in pagination context.
650
     *
651
     * @return int
652
     */
653
    private function getDefaultLimit()
654
    {
655
        if (0 < $this->paginationDefaultLimit) {
656
            return $this->paginationDefaultLimit;
657
        }
658
659
        return 10;
660
    }
661
662
    /**
663
     * Will fire a ModelEvent
664
     *
665
     * @param string $action     insert or update
666
     * @param Object $collection the changed Document
667
     *
668
     * @return void
669
     */
670
    private function dispatchModelEvent($action, $collection)
671
    {
672
        if (!($this->repository instanceof DocumentRepository)) {
673
            return;
674
        }
675
        if (!method_exists($collection, 'getId')) {
676
            return;
677
        }
678
679
        $event = new ModelEvent();
680
        $event->setCollectionId($collection->getId());
681
        $event->setActionByDispatchName($action);
682
        $event->setCollectionName($this->repository->getClassMetadata()->getCollection());
683
        $event->setCollectionClass($this->repository->getClassName());
684
        $event->setCollection($collection);
685
        
686
        $this->eventDispatcher->dispatch($action, $event);
687
    }
688
}
689