Completed
Push — master ( e25986...ffddd1 )
by Craig
07:06
created

AbstractRouteRepository::deleteByLastEditor()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 18
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 2
nop 4
dl 18
loc 18
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Routes.
4
 *
5
 * @copyright Zikula contributors (Zikula)
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 * @author Zikula contributors <[email protected]>.
8
 * @link http://www.zikula.org
9
 * @link http://zikula.org
10
 * @version Generated by ModuleStudio 0.7.4 (http://modulestudio.de).
11
 */
12
13
namespace Zikula\RoutesModule\Entity\Repository\Base;
14
15
use Doctrine\Common\Collections\ArrayCollection;
16
use Gedmo\Sortable\Entity\Repository\SortableRepository;
17
18
use Doctrine\ORM\Query;
19
use Doctrine\ORM\QueryBuilder;
20
use Doctrine\ORM\Tools\Pagination\Paginator;
21
use InvalidArgumentException;
22
use Symfony\Component\HttpFoundation\Request;
23
use Zikula\Component\FilterUtil\FilterUtil;
24
use Zikula\Component\FilterUtil\Config as FilterConfig;
25
use Zikula\Component\FilterUtil\PluginManager as FilterPluginManager;
26
use Psr\Log\LoggerInterface;
27
use ServiceUtil;
28
use Zikula\Common\Translator\TranslatorInterface;
29
use Zikula\UsersModule\Api\CurrentUserApi;
30
use Zikula\RoutesModule\Entity\RouteEntity;
31
32
/**
33
 * Repository class used to implement own convenience methods for performing certain DQL queries.
34
 *
35
 * This is the base repository class for route entities.
36
 */
37
abstract class AbstractRouteRepository extends SortableRepository
38
{
39
    
40
    /**
41
     * @var string The default sorting field/expression
42
     */
43
    protected $defaultSortingField = 'sort';
44
45
    /**
46
     * @var Request The request object given by the calling controller
47
     */
48
    protected $request;
49
    
50
51
    /**
52
     * Retrieves an array with all fields which can be used for sorting instances.
53
     *
54
     * @return array Sorting fields array
55
     */
56
    public function getAllowedSortingFields()
57
    {
58
        return [
59
            'routeType',
60
            'replacedRouteName',
61
            'bundle',
62
            'controller',
63
            'action',
64
            'path',
65
            'host',
66
            'schemes',
67
            'methods',
68
            'prependBundlePrefix',
69
            'translatable',
70
            'translationPrefix',
71
            'condition',
72
            'description',
73
            'sort',
74
            'group',
75
            'createdBy',
76
            'createdDate',
77
            'updatedBy',
78
            'updatedDate',
79
        ];
80
    }
81
82
    /**
83
     * Returns the default sorting field.
84
     *
85
     * @return string
86
     */
87
    public function getDefaultSortingField()
88
    {
89
        return $this->defaultSortingField;
90
    }
91
    
92
    /**
93
     * Sets the default sorting field.
94
     *
95
     * @param string $defaultSortingField
96
     *
97
     * @return void
98
     */
99
    public function setDefaultSortingField($defaultSortingField)
100
    {
101
        $this->defaultSortingField = $defaultSortingField;
102
    }
103
    
104
    /**
105
     * Returns the request.
106
     *
107
     * @return Request
108
     */
109
    public function getRequest()
110
    {
111
        return $this->request;
112
    }
113
    
114
    /**
115
     * Sets the request.
116
     *
117
     * @param Request $request
118
     *
119
     * @return void
120
     */
121
    public function setRequest($request)
122
    {
123
        $this->request = $request;
124
    }
125
    
126
127
    /**
128
     * Returns name of the field used as title / name for entities of this repository.
129
     *
130
     * @return string Name of field to be used as title
131
     */
132
    public function getTitleFieldName()
133
    {
134
        $fieldName = 'replacedRouteName';
135
    
136
        return $fieldName;
137
    }
138
    
139
    /**
140
     * Returns name of the field used for describing entities of this repository.
141
     *
142
     * @return string Name of field to be used as description
143
     */
144
    public function getDescriptionFieldName()
145
    {
146
        $fieldName = 'bundle';
147
    
148
        return $fieldName;
149
    }
150
    
151
    /**
152
     * Returns name of first upload field which is capable for handling images.
153
     *
154
     * @return string Name of field to be used for preview images
155
     */
156
    public function getPreviewFieldName()
157
    {
158
        $fieldName = '';
159
    
160
        return $fieldName;
161
    }
162
    
163
    /**
164
     * Returns name of the date(time) field to be used for representing the start
165
     * of this object. Used for providing meta data to the tag module.
166
     *
167
     * @return string Name of field to be used as date
168
     */
169
    public function getStartDateFieldName()
170
    {
171
        $fieldName = 'createdDate';
172
    
173
        return $fieldName;
174
    }
175
176
    /**
177
     * Returns an array of additional template variables which are specific to the object type treated by this repository.
178
     *
179
     * @param ImageHelper $imageHelper ImageHelper service instance
0 ignored issues
show
Bug introduced by
There is no parameter named $imageHelper. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
180
     * @param string      $context     Usage context (allowed values: controllerAction, api, actionHandler, block, contentType)
181
     * @param array       $args        Additional arguments
182
     *
183
     * @return array List of template variables to be assigned
184
     */
185
    public function getAdditionalTemplateParameters($context = '', $args = [])
186
    {
187
        if (!in_array($context, ['controllerAction', 'api', 'actionHandler', 'block', 'contentType'])) {
188
            $context = 'controllerAction';
189
        }
190
    
191
        $templateParameters = [];
192
    
193
        if ($context == 'controllerAction') {
194
            if (!isset($args['action'])) {
195
                $args['action'] = $this->getRequest()->query->getAlpha('func', 'index');
196
            }
197
            if (in_array($args['action'], ['index', 'view'])) {
198
                $templateParameters = $this->getViewQuickNavParameters($context, $args);
199
            }
200
        }
201
    
202
        // in the concrete child class you could do something like
203
        // $parameters = parent::getAdditionalTemplateParameters($context, $args);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
204
        // $parameters['myvar'] = 'myvalue';
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
205
        // return $parameters;
206
    
207
        return $templateParameters;
208
    }
209
    /**
210
     * Returns an array of additional template variables for view quick navigation forms.
211
     *
212
     * @param string $context Usage context (allowed values: controllerAction, api, actionHandler, block, contentType)
213
     * @param array  $args    Additional arguments
214
     *
215
     * @return array List of template variables to be assigned
216
     */
217
    protected function getViewQuickNavParameters($context = '', $args = [])
218
    {
219
        if (!in_array($context, ['controllerAction', 'api', 'actionHandler', 'block', 'contentType'])) {
220
            $context = 'controllerAction';
0 ignored issues
show
Unused Code introduced by
$context is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
221
        }
222
    
223
        $parameters = [];
224
        $parameters['workflowState'] = $this->getRequest()->query->get('workflowState', '');
225
        $parameters['routeType'] = $this->getRequest()->query->get('routeType', '');
226
        $parameters['schemes'] = $this->getRequest()->query->get('schemes', '');
227
        $parameters['methods'] = $this->getRequest()->query->get('methods', '');
228
        $parameters['q'] = $this->getRequest()->query->get('q', '');
229
        
230
        $parameters['prependBundlePrefix'] = $this->getRequest()->query->get('prependBundlePrefix', '');
231
        $parameters['translatable'] = $this->getRequest()->query->get('translatable', '');
232
    
233
        // in the concrete child class you could do something like
234
        // $parameters = parent::getViewQuickNavParameters($context, $args);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
235
        // $parameters['myvar'] = 'myvalue';
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
236
        // return $parameters;
237
    
238
        return $parameters;
239
    }
240
241
    /**
242
     * Helper method for truncating the table.
243
     * Used during installation when inserting default data.
244
     *
245
     * @param LoggerInterface $logger Logger service instance
246
     *
247
     * @return void
248
     */
249
    public function truncateTable(LoggerInterface $logger)
250
    {
251
        $qb = $this->getEntityManager()->createQueryBuilder();
252
        $qb->delete('Zikula\RoutesModule\Entity\RouteEntity', 'tbl');
253
        $query = $qb->getQuery();
254
    
255
        $query->execute();
256
    
257
        $logArgs = ['app' => 'ZikulaRoutesModule', 'entity' => 'route'];
258
        $logger->debug('{app}: Truncated the {entity} entity table.', $logArgs);
259
    }
260
    /**
261
     * Updates the creator of all objects created by a certain user.
262
     *
263
     * @param integer             $userId         The userid of the creator to be replaced
264
     * @param integer             $newUserId      The new userid of the creator as replacement
265
     * @param TranslatorInterface $translator     Translator service instance
266
     * @param LoggerInterface     $logger         Logger service instance
267
     * @param CurrentUserApi      $currentUserApi CurrentUserApi service instance
268
     *
269
     * @return void
270
     *
271
     * @throws InvalidArgumentException Thrown if invalid parameters are received
272
     */
273 View Code Duplication
    public function updateCreator($userId, $newUserId, TranslatorInterface $translator, LoggerInterface $logger, CurrentUserApi $currentUserApi)
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...
274
    {
275
        // check id parameter
276
        if ($userId == 0 || !is_numeric($userId)
277
         || $newUserId == 0 || !is_numeric($newUserId)) {
278
            throw new InvalidArgumentException($translator->__('Invalid user identifier received.'));
279
        }
280
    
281
        $qb = $this->getEntityManager()->createQueryBuilder();
282
        $qb->update('Zikula\RoutesModule\Entity\RouteEntity', 'tbl')
283
           ->set('tbl.createdBy', $newUserId)
284
           ->where('tbl.createdBy= :creator')
285
           ->setParameter('creator', $userId);
286
        $query = $qb->getQuery();
287
        $query->execute();
288
    
289
        $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $currentUserApi->get('uname'), 'entities' => 'routes', 'userid' => $userId];
290
        $logger->debug('{app}: User {user} updated {entities} created by user id {userid}.', $logArgs);
291
    }
292
    
293
    /**
294
     * Updates the last editor of all objects updated by a certain user.
295
     *
296
     * @param integer             $userId         The userid of the last editor to be replaced
297
     * @param integer             $newUserId      The new userid of the last editor as replacement
298
     * @param TranslatorInterface $translator     Translator service instance
299
     * @param LoggerInterface     $logger         Logger service instance
300
     * @param CurrentUserApi      $currentUserApi CurrentUserApi service instance
301
     *
302
     * @return void
303
     *
304
     * @throws InvalidArgumentException Thrown if invalid parameters are received
305
     */
306 View Code Duplication
    public function updateLastEditor($userId, $newUserId, TranslatorInterface $translator, LoggerInterface $logger, CurrentUserApi $currentUserApi)
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...
307
    {
308
        // check id parameter
309
        if ($userId == 0 || !is_numeric($userId)
310
         || $newUserId == 0 || !is_numeric($newUserId)) {
311
            throw new InvalidArgumentException($translator->__('Invalid user identifier received.'));
312
        }
313
    
314
        $qb = $this->getEntityManager()->createQueryBuilder();
315
        $qb->update('Zikula\RoutesModule\Entity\RouteEntity', 'tbl')
316
           ->set('tbl.updatedBy', $newUserId)
317
           ->where('tbl.updatedBy = :editor')
318
           ->setParameter('editor', $userId);
319
        $query = $qb->getQuery();
320
        $query->execute();
321
    
322
        $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $currentUserApi->get('uname'), 'entities' => 'routes', 'userid' => $userId];
323
        $logger->debug('{app}: User {user} updated {entities} edited by user id {userid}.', $logArgs);
324
    }
325
    
326
    /**
327
     * Deletes all objects created by a certain user.
328
     *
329
     * @param integer             $userId         The userid of the creator to be removed
330
     * @param TranslatorInterface $translator     Translator service instance
331
     * @param LoggerInterface     $logger         Logger service instance
332
     * @param CurrentUserApi      $currentUserApi CurrentUserApi service instance
333
     *
334
     * @return void
335
     *
336
     * @throws InvalidArgumentException Thrown if invalid parameters are received
337
     */
338 View Code Duplication
    public function deleteByCreator($userId, TranslatorInterface $translator, LoggerInterface $logger, CurrentUserApi $currentUserApi)
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...
339
    {
340
        // check id parameter
341
        if ($userId == 0 || !is_numeric($userId)) {
342
            throw new InvalidArgumentException($translator->__('Invalid user identifier received.'));
343
        }
344
    
345
        $qb = $this->getEntityManager()->createQueryBuilder();
346
        $qb->delete('Zikula\RoutesModule\Entity\RouteEntity', 'tbl')
347
           ->where('tbl.createdBy = :creator')
348
           ->setParameter('creator', $userId);
349
        $query = $qb->getQuery();
350
    
351
        $query->execute();
352
    
353
        $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $currentUserApi->get('uname'), 'entities' => 'routes', 'userid' => $userId];
354
        $logger->debug('{app}: User {user} deleted {entities} created by user id {userid}.', $logArgs);
355
    }
356
    
357
    /**
358
     * Deletes all objects updated by a certain user.
359
     *
360
     * @param integer             $userId         The userid of the last editor to be removed
361
     * @param TranslatorInterface $translator     Translator service instance
362
     * @param LoggerInterface     $logger         Logger service instance
363
     * @param CurrentUserApi      $currentUserApi CurrentUserApi service instance
364
     *
365
     * @return void
366
     *
367
     * @throws InvalidArgumentException Thrown if invalid parameters are received
368
     */
369 View Code Duplication
    public function deleteByLastEditor($userId, TranslatorInterface $translator, LoggerInterface $logger, CurrentUserApi $currentUserApi)
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...
370
    {
371
        // check id parameter
372
        if ($userId == 0 || !is_numeric($userId)) {
373
            throw new InvalidArgumentException($translator->__('Invalid user identifier received.'));
374
        }
375
    
376
        $qb = $this->getEntityManager()->createQueryBuilder();
377
        $qb->delete('Zikula\RoutesModule\Entity\RouteEntity', 'tbl')
378
           ->where('tbl.updatedBy = :editor')
379
           ->setParameter('editor', $userId);
380
        $query = $qb->getQuery();
381
    
382
        $query->execute();
383
    
384
        $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $currentUserApi->get('uname'), 'entities' => 'routes', 'userid' => $userId];
385
        $logger->debug('{app}: User {user} deleted {entities} edited by user id {userid}.', $logArgs);
386
    }
387
388
    /**
389
     * Adds an array of id filters to given query instance.
390
     *
391
     * @param mixed        $idList The array of ids to use to retrieve the object
392
     * @param QueryBuilder $qb     Query builder to be enhanced
393
     *
394
     * @return QueryBuilder Enriched query builder instance
395
     */
396
    protected function addIdListFilter($idList, QueryBuilder $qb)
397
    {
398
        $orX = $qb->expr()->orX();
399
    
400
        foreach ($idList as $id) {
401
            // check id parameter
402
            if ($id == 0) {
403
                throw new InvalidArgumentException('Invalid identifier received.');
404
            }
405
    
406
            if (is_array($id)) {
407
                $andX = $qb->expr()->andX();
408
                foreach ($id as $fieldName => $fieldValue) {
409
                    $andX->add($qb->expr()->eq('tbl.' . $fieldName, $fieldValue));
410
                }
411
                $orX->add($andX);
412
            } else {
413
                $orX->add($qb->expr()->eq('tbl.id', $id));
414
            }
415
        }
416
    
417
        $qb->andWhere($orX);
418
    
419
        return $qb;
420
    }
421
    
422
    /**
423
     * Selects an object from the database.
424
     *
425
     * @param mixed   $id       The id (or array of ids) to use to retrieve the object (optional) (default=0)
426
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
427
     * @param boolean $slimMode If activated only some basic fields are selected without using any joins (optional) (default=false)
428
     *
429
     * @return array|routeEntity retrieved data array or routeEntity instance
430
     *
431
     * @throws InvalidArgumentException Thrown if invalid parameters are received
432
     */
433
    public function selectById($id = 0, $useJoins = true, $slimMode = false)
434
    {
435
        $results = $this->selectByIdList(is_array($id) ? $id : [$id], $useJoins, $slimMode);
436
    
437
        return count($results) > 0 ? $results[0] : null;
438
    }
439
    
440
    /**
441
     * Selects a list of objects with an array of ids
442
     *
443
     * @param mixed   $idList   The array of ids to use to retrieve the objects (optional) (default=0)
444
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
445
     * @param boolean $slimMode If activated only some basic fields are selected without using any joins (optional) (default=false)
446
     *
447
     * @return ArrayCollection collection containing retrieved routeEntity instances
448
     *
449
     * @throws InvalidArgumentException Thrown if invalid parameters are received
450
     */
451 View Code Duplication
    public function selectByIdList($idList = [0], $useJoins = true, $slimMode = false)
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...
452
    {
453
        $qb = $this->genericBaseQuery('', '', $useJoins, $slimMode);
454
        $qb = $this->addIdListFilter($idList, $qb);
455
    
456
        $query = $this->getQueryFromBuilder($qb);
457
    
458
        $results = $query->getResult();
459
    
460
        return (count($results) > 0) ? $results : null;
461
    }
462
463
    /**
464
     * Adds where clauses excluding desired identifiers from selection.
465
     *
466
     * @param QueryBuilder $qb        Query builder to be enhanced
467
     * @param integer      $excludeId The id to be excluded from selection
468
     *
469
     * @return QueryBuilder Enriched query builder instance
470
     */
471
    protected function addExclusion(QueryBuilder $qb, $excludeId)
472
    {
473
        if ($excludeId > 0) {
474
            $qb->andWhere('tbl.id != :excludeId')
475
               ->setParameter('excludeId', $excludeId);
476
        }
477
    
478
        return $qb;
479
    }
480
481
    /**
482
     * Adds a filter for the createdBy field.
483
     *
484
     * @param QueryBuilder $qb Query builder to be enhanced
485
     * @param integer      $userId The user identifier used for filtering (optional)
486
     *
487
     * @return QueryBuilder Enriched query builder instance
488
     */
489
    public function addCreatorFilter(QueryBuilder $qb, $userId = null)
490
    {
491
        if (null === $userId) {
492
            $currentUserApi = ServiceUtil::get('zikula_users_module.current_user');
493
            $userId = $currentUserApi->isLoggedIn() ? $currentUserApi->get('uid') : 1;
494
        }
495
    
496
        if (is_array($userId)) {
497
            $qb->andWhere('tbl.createdBy IN (:userIds)')
498
               ->setParameter('userIds', $userId);
499
        } else {
500
            $qb->andWhere('tbl.createdBy = :userId')
501
               ->setParameter('userId', $userId);
502
        }
503
    
504
        return $qb;
505
    }
506
507
    /**
508
     * Returns query builder for selecting a list of objects with a given where clause.
509
     *
510
     * @param string  $where    The where clause to use when retrieving the collection (optional) (default='')
511
     * @param string  $orderBy  The order-by clause to use when retrieving the collection (optional) (default='')
512
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
513
     * @param boolean $slimMode If activated only some basic fields are selected without using any joins (optional) (default=false)
514
     *
515
     * @return QueryBuilder query builder for the given arguments
516
     */
517
    public function getListQueryBuilder($where = '', $orderBy = '', $useJoins = true, $slimMode = false)
518
    {
519
        $qb = $this->genericBaseQuery($where, $orderBy, $useJoins, $slimMode);
520
        if (!$useJoins || !$slimMode) {
521
            $qb = $this->addCommonViewFilters($qb);
522
        }
523
    
524
        return $qb;
525
    }
526
    
527
    /**
528
     * Selects a list of objects with a given where clause.
529
     *
530
     * @param string  $where    The where clause to use when retrieving the collection (optional) (default='')
531
     * @param string  $orderBy  The order-by clause to use when retrieving the collection (optional) (default='')
532
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
533
     * @param boolean $slimMode If activated only some basic fields are selected without using any joins (optional) (default=false)
534
     *
535
     * @return ArrayCollection collection containing retrieved routeEntity instances
536
     */
537
    public function selectWhere($where = '', $orderBy = '', $useJoins = true, $slimMode = false)
538
    {
539
        $qb = $this->getListQueryBuilder($where, $orderBy, $useJoins, $slimMode);
540
    
541
        $query = $this->getQueryFromBuilder($qb);
542
    
543
        return $this->retrieveCollectionResult($query, $orderBy, false);
544
    }
545
546
    /**
547
     * Returns query builder instance for retrieving a list of objects with a given where clause and pagination parameters.
548
     *
549
     * @param QueryBuilder $qb             Query builder to be enhanced
550
     * @param integer      $currentPage    Where to start selection
551
     * @param integer      $resultsPerPage Amount of items to select
552
     *
553
     * @return Query Created query instance
554
     */
555
    public function getSelectWherePaginatedQuery(QueryBuilder $qb, $currentPage = 1, $resultsPerPage = 25)
556
    {
557
        $qb = $this->addCommonViewFilters($qb);
558
    
559
        $query = $this->getQueryFromBuilder($qb);
560
        $offset = ($currentPage-1) * $resultsPerPage;
561
    
562
        $query->setFirstResult($offset)
563
              ->setMaxResults($resultsPerPage);
564
    
565
        return $query;
566
    }
567
    
568
    /**
569
     * Selects a list of objects with a given where clause and pagination parameters.
570
     *
571
     * @param string  $where          The where clause to use when retrieving the collection (optional) (default='')
572
     * @param string  $orderBy        The order-by clause to use when retrieving the collection (optional) (default='')
573
     * @param integer $currentPage    Where to start selection
574
     * @param integer $resultsPerPage Amount of items to select
575
     * @param boolean $useJoins       Whether to include joining related objects (optional) (default=true)
576
     * @param boolean $slimMode       If activated only some basic fields are selected without using any joins (optional) (default=false)
577
     *
578
     * @return array with retrieved collection and amount of total records affected by this query
579
     */
580 View Code Duplication
    public function selectWherePaginated($where = '', $orderBy = '', $currentPage = 1, $resultsPerPage = 25, $useJoins = true, $slimMode = false)
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...
581
    {
582
        $qb = $this->genericBaseQuery($where, $orderBy, $useJoins, $slimMode);
583
    
584
        $page = $currentPage;
585
        
586
        $query = $this->getSelectWherePaginatedQuery($qb, $page, $resultsPerPage);
587
    
588
        return $this->retrieveCollectionResult($query, $orderBy, true);
589
    }
590
    
591
    /**
592
     * Adds quick navigation related filter options as where clauses.
593
     *
594
     * @param QueryBuilder $qb Query builder to be enhanced
595
     *
596
     * @return QueryBuilder Enriched query builder instance
597
     */
598
    public function addCommonViewFilters(QueryBuilder $qb)
599
    {
600
        if (null === $this->getRequest()) {
601
            // if no request is set we return (#433)
602
            return $qb;
603
        }
604
    
605
        $routeName = $this->getRequest()->get('_route');
606
        if (false !== strpos($routeName, 'edit')) {
607
            return $qb;
608
        }
609
    
610
        $parameters = $this->getViewQuickNavParameters('', []);
611
        foreach ($parameters as $k => $v) {
612
            if (in_array($k, ['q', 'searchterm'])) {
613
                // quick search
614
                if (!empty($v)) {
615
                    $qb = $this->addSearchFilter($qb, $v);
616
                }
617
            } elseif (in_array($k, ['prependBundlePrefix', 'translatable'])) {
618
                // boolean filter
619
                if ($v == 'no') {
620
                    $qb->andWhere('tbl.' . $k . ' = 0');
621
                } elseif ($v == 'yes' || $v == '1') {
622
                    $qb->andWhere('tbl.' . $k . ' = 1');
623
                }
624
            } else if (!is_array($v)) {
625
                // field filter
626
                if ((!is_numeric($v) && $v != '') || (is_numeric($v) && $v > 0)) {
627
                    if ($k == 'workflowState' && substr($v, 0, 1) == '!') {
628
                        $qb->andWhere('tbl.' . $k . ' != :' . $k)
629
                           ->setParameter($k, substr($v, 1, strlen($v)-1));
630
                    } elseif (substr($v, 0, 1) == '%') {
631
                        $qb->andWhere('tbl.' . $k . ' LIKE :' . $k)
632
                           ->setParameter($k, '%' . $v . '%');
633
                    } else {
634
                        $qb->andWhere('tbl.' . $k . ' = :' . $k)
635
                           ->setParameter($k, $v);
636
                   }
637
                }
638
            }
639
        }
640
    
641
        $qb = $this->applyDefaultFilters($qb, $parameters);
642
    
643
        return $qb;
644
    }
645
    
646
    /**
647
     * Adds default filters as where clauses.
648
     *
649
     * @param QueryBuilder $qb         Query builder to be enhanced
650
     * @param array        $parameters List of determined filter options
651
     *
652
     * @return QueryBuilder Enriched query builder instance
653
     */
654
    protected function applyDefaultFilters(QueryBuilder $qb, $parameters = [])
655
    {
656
    
657
        return $qb;
658
    }
659
660
    /**
661
     * Selects entities by a given search fragment.
662
     *
663
     * @param string  $fragment       The fragment to search for
664
     * @param array   $exclude        List with identifiers to be excluded from search
665
     * @param string  $orderBy        The order-by clause to use when retrieving the collection (optional) (default='')
666
     * @param integer $currentPage    Where to start selection
667
     * @param integer $resultsPerPage Amount of items to select
668
     * @param boolean $useJoins       Whether to include joining related objects (optional) (default=true)
669
     *
670
     * @return array with retrieved collection and amount of total records affected by this query
671
     */
672
    public function selectSearch($fragment = '', $exclude = [], $orderBy = '', $currentPage = 1, $resultsPerPage = 25, $useJoins = true)
673
    {
674
        $qb = $this->genericBaseQuery('', $orderBy, $useJoins);
675
        if (count($exclude) > 0) {
676
            $qb = $this->addExclusion($qb, $exclude);
677
        }
678
    
679
        $qb = $this->addSearchFilter($qb, $fragment);
680
    
681
        $query = $this->getSelectWherePaginatedQuery($qb, $currentPage, $resultsPerPage);
682
    
683
        return $this->retrieveCollectionResult($query, $orderBy, true);
684
    }
685
    
686
    /**
687
     * Adds where clause for search query.
688
     *
689
     * @param QueryBuilder $qb       Query builder to be enhanced
690
     * @param string       $fragment The fragment to search for
691
     *
692
     * @return QueryBuilder Enriched query builder instance
693
     */
694
    protected function addSearchFilter(QueryBuilder $qb, $fragment = '')
695
    {
696
        if ($fragment == '') {
697
            return $qb;
698
        }
699
    
700
        $fragment = str_replace('\'', '', \DataUtil::formatForStore($fragment));
701
        $fragmentIsNumeric = is_numeric($fragment);
702
    
703
        $where = '';
704
        if (!$fragmentIsNumeric) {
705
            $where .= ((!empty($where)) ? ' OR ' : '');
706
            $where .= 'tbl.routeType = \'' . $fragment . '\'';
707
            $where .= ((!empty($where)) ? ' OR ' : '');
708
            $where .= 'tbl.replacedRouteName LIKE \'%' . $fragment . '%\'';
709
            $where .= ((!empty($where)) ? ' OR ' : '');
710
            $where .= 'tbl.bundle LIKE \'%' . $fragment . '%\'';
711
            $where .= ((!empty($where)) ? ' OR ' : '');
712
            $where .= 'tbl.controller LIKE \'%' . $fragment . '%\'';
713
            $where .= ((!empty($where)) ? ' OR ' : '');
714
            $where .= 'tbl.action LIKE \'%' . $fragment . '%\'';
715
            $where .= ((!empty($where)) ? ' OR ' : '');
716
            $where .= 'tbl.path LIKE \'%' . $fragment . '%\'';
717
            $where .= ((!empty($where)) ? ' OR ' : '');
718
            $where .= 'tbl.host LIKE \'%' . $fragment . '%\'';
719
            $where .= ((!empty($where)) ? ' OR ' : '');
720
            $where .= 'tbl.schemes = \'' . $fragment . '\'';
721
            $where .= ((!empty($where)) ? ' OR ' : '');
722
            $where .= 'tbl.methods = \'' . $fragment . '\'';
723
            $where .= ((!empty($where)) ? ' OR ' : '');
724
            $where .= 'tbl.translationPrefix LIKE \'%' . $fragment . '%\'';
725
            $where .= ((!empty($where)) ? ' OR ' : '');
726
            $where .= 'tbl.condition LIKE \'%' . $fragment . '%\'';
727
            $where .= ((!empty($where)) ? ' OR ' : '');
728
            $where .= 'tbl.description LIKE \'%' . $fragment . '%\'';
729
            $where .= ((!empty($where)) ? ' OR ' : '');
730
            $where .= 'tbl.group LIKE \'%' . $fragment . '%\'';
731
        } else {
732
            $where .= ((!empty($where)) ? ' OR ' : '');
733
            $where .= 'tbl.routeType = \'' . $fragment . '\'';
734
            $where .= ((!empty($where)) ? ' OR ' : '');
735
            $where .= 'tbl.replacedRouteName LIKE \'%' . $fragment . '%\'';
736
            $where .= ((!empty($where)) ? ' OR ' : '');
737
            $where .= 'tbl.bundle LIKE \'%' . $fragment . '%\'';
738
            $where .= ((!empty($where)) ? ' OR ' : '');
739
            $where .= 'tbl.controller LIKE \'%' . $fragment . '%\'';
740
            $where .= ((!empty($where)) ? ' OR ' : '');
741
            $where .= 'tbl.action LIKE \'%' . $fragment . '%\'';
742
            $where .= ((!empty($where)) ? ' OR ' : '');
743
            $where .= 'tbl.path LIKE \'%' . $fragment . '%\'';
744
            $where .= ((!empty($where)) ? ' OR ' : '');
745
            $where .= 'tbl.host LIKE \'%' . $fragment . '%\'';
746
            $where .= ((!empty($where)) ? ' OR ' : '');
747
            $where .= 'tbl.schemes = \'' . $fragment . '\'';
748
            $where .= ((!empty($where)) ? ' OR ' : '');
749
            $where .= 'tbl.methods = \'' . $fragment . '\'';
750
            $where .= ((!empty($where)) ? ' OR ' : '');
751
            $where .= 'tbl.translationPrefix LIKE \'%' . $fragment . '%\'';
752
            $where .= ((!empty($where)) ? ' OR ' : '');
753
            $where .= 'tbl.condition LIKE \'%' . $fragment . '%\'';
754
            $where .= ((!empty($where)) ? ' OR ' : '');
755
            $where .= 'tbl.description LIKE \'%' . $fragment . '%\'';
756
            $where .= ((!empty($where)) ? ' OR ' : '');
757
            $where .= 'tbl.sort = \'' . $fragment . '\'';
758
            $where .= ((!empty($where)) ? ' OR ' : '');
759
            $where .= 'tbl.group LIKE \'%' . $fragment . '%\'';
760
        }
761
        $where = '(' . $where . ')';
762
    
763
        $qb->andWhere($where);
764
    
765
        return $qb;
766
    }
767
768
    /**
769
     * Performs a given database selection and post-processed the results.
770
     *
771
     * @param Query   $query       The Query instance to be executed
772
     * @param string  $orderBy     The order-by clause to use when retrieving the collection (optional) (default='')
773
     * @param boolean $isPaginated Whether the given query uses a paginator or not (optional) (default=false)
774
     *
775
     * @return array with retrieved collection and (for paginated queries) the amount of total records affected
776
     */
777
    public function retrieveCollectionResult(Query $query, $orderBy = '', $isPaginated = false)
778
    {
779
        $count = 0;
780
        if (!$isPaginated) {
781
            $result = $query->getResult();
782
        } else {
783
            $paginator = new Paginator($query, false);
784
    
785
            $count = count($paginator);
786
            $result = $paginator;
787
        }
788
    
789
        if (!$isPaginated) {
790
            return $result;
791
        }
792
    
793
        return [$result, $count];
794
    }
795
796
    /**
797
     * Returns query builder instance for a count query.
798
     *
799
     * @param string  $where    The where clause to use when retrieving the object count (optional) (default='')
800
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
801
     *
802
     * @return QueryBuilder Created query builder instance
803
     * @TODO fix usage of joins; please remove the first line and test
804
     */
805
    protected function getCountQuery($where = '', $useJoins = true)
806
    {
807
        $useJoins = false;
808
    
809
        $selection = 'COUNT(tbl.id) AS numRoutes';
810
        if (true === $useJoins) {
811
            $selection .= $this->addJoinsToSelection();
812
        }
813
    
814
        $qb = $this->getEntityManager()->createQueryBuilder();
815
        $qb->select($selection)
816
           ->from('Zikula\RoutesModule\Entity\RouteEntity', 'tbl');
817
    
818
        if (true === $useJoins) {
819
            $this->addJoinsToFrom($qb);
0 ignored issues
show
Unused Code introduced by
The call to the method Zikula\RoutesModule\Enti...itory::addJoinsToFrom() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
820
        }
821
    
822
        $this->genericBaseQueryAddWhere($qb, $where);
823
    
824
        return $qb;
825
    }
826
    
827
    /**
828
     * Selects entity count with a given where clause.
829
     *
830
     * @param string  $where      The where clause to use when retrieving the object count (optional) (default='')
831
     * @param boolean $useJoins   Whether to include joining related objects (optional) (default=true)
832
     * @param array   $parameters List of determined filter options
833
     *
834
     * @return integer amount of affected records
835
     */
836
    public function selectCount($where = '', $useJoins = true, $parameters = [])
837
    {
838
        $qb = $this->getCountQuery($where, $useJoins);
839
    
840
        $qb = $this->applyDefaultFilters($qb, $parameters);
841
    
842
        $query = $qb->getQuery();
843
    
844
        return $query->getSingleScalarResult();
845
    }
846
847
848
    /**
849
     * Checks for unique values.
850
     *
851
     * @param string $fieldName  The name of the property to be checked
852
     * @param string $fieldValue The value of the property to be checked
853
     * @param int    $excludeId  Id of routes to exclude (optional)
854
     *
855
     * @return boolean result of this check, true if the given route does not already exist
856
     */
857
    public function detectUniqueState($fieldName, $fieldValue, $excludeId = 0)
858
    {
859
        $qb = $this->getCountQuery('', false);
860
        $qb->andWhere('tbl.' . $fieldName . ' = :' . $fieldName)
861
           ->setParameter($fieldName, $fieldValue);
862
    
863
        $qb = $this->addExclusion($qb, $excludeId);
864
    
865
        $query = $qb->getQuery();
866
    
867
        $count = $query->getSingleScalarResult();
868
    
869
        return ($count == 0);
870
    }
871
872
    /**
873
     * Builds a generic Doctrine query supporting WHERE and ORDER BY.
874
     *
875
     * @param string  $where    The where clause to use when retrieving the collection (optional) (default='')
876
     * @param string  $orderBy  The order-by clause to use when retrieving the collection (optional) (default='')
877
     * @param boolean $useJoins Whether to include joining related objects (optional) (default=true)
878
     * @param boolean $slimMode If activated only some basic fields are selected without using any joins (optional) (default=false)
879
     *
880
     * @return QueryBuilder query builder instance to be further processed
881
     */
882
    public function genericBaseQuery($where = '', $orderBy = '', $useJoins = true, $slimMode = false)
883
    {
884
        // normally we select the whole table
885
        $selection = 'tbl';
886
    
887
        if (true === $slimMode) {
888
            // but for the slim version we select only the basic fields, and no joins
889
    
890
            $selection = 'tbl.id';
891
            
892
            
893
            $selection .= ', tbl.path';
894
            
895
            
896
            $selection .= ', tbl.sort';
897
            
898
            $useJoins = false;
899
        }
900
    
901
        if (true === $useJoins) {
902
            $selection .= $this->addJoinsToSelection();
903
        }
904
    
905
        $qb = $this->getEntityManager()->createQueryBuilder();
906
        $qb->select($selection)
907
           ->from('Zikula\RoutesModule\Entity\RouteEntity', 'tbl');
908
    
909
        if (true === $useJoins) {
910
            $this->addJoinsToFrom($qb);
0 ignored issues
show
Unused Code introduced by
The call to the method Zikula\RoutesModule\Enti...itory::addJoinsToFrom() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
911
        }
912
    
913
        $this->genericBaseQueryAddWhere($qb, $where);
914
        $this->genericBaseQueryAddOrderBy($qb, $orderBy);
915
    
916
        return $qb;
917
    }
918
919
    /**
920
     * Adds WHERE clause to given query builder.
921
     *
922
     * @param QueryBuilder $qb    Given query builder instance
923
     * @param string       $where The where clause to use when retrieving the collection (optional) (default='')
924
     *
925
     * @return QueryBuilder query builder instance to be further processed
926
     */
927
    protected function genericBaseQueryAddWhere(QueryBuilder $qb, $where = '')
928
    {
929
        if (!empty($where)) {
930
            // Use FilterUtil to support generic filtering.
931
            //$qb->where($where);
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
932
    
933
            // Create filter configuration.
934
            $filterConfig = new FilterConfig($qb);
935
    
936
            // Define plugins to be used during filtering.
937
            $filterPluginManager = new FilterPluginManager(
938
                $filterConfig,
939
    
940
                // Array of plugins to load.
941
                // If no plugin with default = true given the compare plugin is loaded and used for unconfigured fields.
942
                // Multiple objects of the same plugin with different configurations are possible.
943
                [
944
                ],
945
    
946
                // Allowed operators per field.
947
                // Array in the form "field name => operator array".
948
                // If a field is not set in this array all operators are allowed.
949
                []
950
            );
951
    
952
            // Request object to obtain the filter string (only needed if the filter is set via GET or it reads values from GET).
953
            // We do this not per default (for now) to prevent problems with explicite filters set by blocks or content types.
954
            // TODO readd automatic request processing (basically replacing applyDefaultFilters() and addCommonViewFilters()).
955
            $request = null;
956
    
957
            // Name of filter variable(s) (filterX).
958
            $filterKey = 'filter';
959
    
960
            // initialise FilterUtil and assign both query builder and configuration
961
            $filterUtil = new FilterUtil($filterPluginManager, $request, $filterKey);
962
    
963
            // set our given filter
964
            $filterUtil->setFilter($where);
965
    
966
            // you could add explicit filters at this point, something like
967
            // $filterUtil->addFilter('foo:eq:something,bar:gt:100');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
968
            // read more at https://github.com/zikula/core/tree/1.4/src/docs/Core-2.0/FilterUtil
969
    
970
            // now enrich the query builder
971
            $filterUtil->enrichQuery();
972
        }
973
    
974
        if (null === $this->getRequest()) {
975
            // if no request is set we return (#783)
976
            return $qb;
977
        }
978
    
979
        
980
        $showOnlyOwnEntries = $this->getRequest()->query->getInt('own', 0);
981
        if ($showOnlyOwnEntries == 1) {
982
            
983
            $userId = $this->getRequest()->getSession()->get('uid');
984
            $qb->andWhere('tbl.createdBy = :creator')
985
               ->setParameter('creator', $userId);
986
        }
987
    
988
        return $qb;
989
    }
990
991
    /**
992
     * Adds ORDER BY clause to given query builder.
993
     *
994
     * @param QueryBuilder $qb      Given query builder instance
995
     * @param string       $orderBy The order-by clause to use when retrieving the collection (optional) (default='')
996
     *
997
     * @return QueryBuilder query builder instance to be further processed
998
     */
999
    protected function genericBaseQueryAddOrderBy(QueryBuilder $qb, $orderBy = '')
1000
    {
1001
        if ($orderBy == 'RAND()') {
1002
            // random selection
1003
            $qb->addSelect('MOD(tbl.id, ' . mt_rand(2, 15) . ') AS HIDDEN randomIdentifiers')
1004
               ->add('orderBy', 'randomIdentifiers');
1005
            $orderBy = '';
1006
        } elseif (empty($orderBy)) {
1007
            $orderBy = $this->defaultSortingField;
1008
        }
1009
    
1010
        // add order by clause
1011
        if (!empty($orderBy)) {
1012
            if (false === strpos($orderBy, '.')) {
1013
                $orderBy = 'tbl.' . $orderBy;
1014
            }
1015
            if (false !== strpos($orderBy, 'tbl.createdBy')) {
1016
                $qb->addSelect('tblCreator')
1017
                   ->leftJoin('tbl.createdBy', 'tblCreator');
1018
                $orderBy = str_replace('tbl.createdBy', 'tblCreator.uname', $orderBy);
1019
            }
1020
            if (false !== strpos($orderBy, 'tbl.updatedBy')) {
1021
                $qb->addSelect('tblUpdater')
1022
                   ->leftJoin('tbl.updatedBy', 'tblUpdater');
1023
                $orderBy = str_replace('tbl.updatedBy', 'tblUpdater.uname', $orderBy);
1024
            }
1025
            $qb->add('orderBy', $orderBy);
1026
        }
1027
    
1028
        return $qb;
1029
    }
1030
1031
    /**
1032
     * Retrieves Doctrine query from query builder, applying FilterUtil and other common actions.
1033
     *
1034
     * @param QueryBuilder $qb Query builder instance
1035
     *
1036
     * @return Query query instance to be further processed
1037
     */
1038
    public function getQueryFromBuilder(QueryBuilder $qb)
1039
    {
1040
        $query = $qb->getQuery();
1041
    
1042
        return $query;
1043
    }
1044
1045
    /**
1046
     * Helper method to add join selections.
1047
     *
1048
     * @return String Enhancement for select clause
1049
     */
1050
    protected function addJoinsToSelection()
1051
    {
1052
        $selection = '';
1053
    
1054
        return $selection;
1055
    }
1056
    
1057
    /**
1058
     * Helper method to add joins to from clause.
1059
     *
1060
     * @param QueryBuilder $qb Query builder instance used to create the query
1061
     *
1062
     * @return QueryBuilder The query builder enriched by additional joins
1063
     */
1064
    protected function addJoinsToFrom(QueryBuilder $qb)
1065
    {
1066
    
1067
        return $qb;
1068
    }
1069
}
1070