Completed
Pull Request — master (#309)
by Leny
06:35
created

QueryHelper::getCurrentUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Victoire\Bundle\QueryBundle\Helper;
4
5
use Doctrine\Common\Annotations\Reader;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\Mapping\ManyToMany;
8
use Doctrine\ORM\Mapping\ManyToOne;
9
use Doctrine\ORM\Mapping\OneToMany;
10
use Doctrine\ORM\Mapping\OneToOne;
11
use Doctrine\ORM\QueryBuilder;
12
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
13
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
14
use Victoire\Bundle\BusinessEntityBundle\Helper\BusinessEntityHelper;
15
use Victoire\Bundle\BusinessPageBundle\Entity\BusinessPage;
16
use Victoire\Bundle\CoreBundle\Helper\CurrentViewHelper;
17
18
/**
19
 * The QueryHelper helps to build query in Victoire's components
20
 * ref: victoire_query.query_helper.
21
 */
22
class QueryHelper
23
{
24
    protected $businessEntityHelper = null;
25
    protected $currentView;
26
    protected $reader;
27
    protected $tokenStorage;
28
29
    /**
30
     * Constructor.
31
     *
32
     * @param BusinessEntityHelper $businessEntityHelper
33
     * @param CurrentViewHelper    $currentView
34
     * @param Reader               $reader
35
     */
36
    public function __construct(BusinessEntityHelper $businessEntityHelper, CurrentViewHelper $currentView, Reader $reader, TokenStorage $tokenStorage)
37
    {
38
        $this->businessEntityHelper = $businessEntityHelper;
39
        $this->currentView = $currentView;
40
        $this->reader = $reader;
41
        $this->tokenStorage = $tokenStorage;
42
    }
43
44
    /**
45
     * Get the query builder base. This makes a "select  from item XXX"
46
     * use the item for doing the left join or where dql.
47
     *
48
     * @param \Victoire\Bundle\BusinessPageBundle\Entity\BusinessTemplate $containerEntity
49
     *
50
     * @throws \Exception
51
     *
52
     * @return QueryBuilder
53
     */
54
    public function getQueryBuilder($containerEntity, EntityManager $em)
55
    {
56
        if ($containerEntity === null) {
57
            throw new \Exception('The container entity parameter must not be null.');
58
        }
59
60
        //verify that the object has the query trait
61
        $this->checkObjectHasQueryTrait($containerEntity);
62
63
        //the business name of the container entity
64
        $businessEntityId = $containerEntity->getBusinessEntityId();
65
66
        //test that there is a business entity name
67
        if ($businessEntityId === null || $businessEntityId === '') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $businessEntityId (integer) and '' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
68
            $containerId = $containerEntity->getId();
69
            throw new \Exception('The container entity ['.$containerId.'] does not have any businessEntityId.');
70
        }
71
72
        //the business class of the container entity
73
        $businessEntity = $this->businessEntityHelper->findById(strtolower($businessEntityId));
74
75
        //test that there was a businessEntity
76
        if ($businessEntity === null) {
77
            throw new \Exception('The business entity was not found for the id:['.$businessEntityId.']');
78
        }
79
80
        $businessClass = $businessEntity->getClass();
81
82
        $itemsQueryBuilder = $em
83
            ->createQueryBuilder()
84
            ->select('main_item')
85
            ->from($businessClass, 'main_item');
86
87
        $refClass = new $businessClass();
88
        if (method_exists($refClass, 'getDeletedAt')) {
89
            $itemsQueryBuilder->where('main_item.deletedAt IS NULL');
90
        }
91
92
        return $itemsQueryBuilder;
93
    }
94
95
    /**
96
     * Check that the object is not null and has the query trait.
97
     *
98
     * @param \Victoire\Bundle\BusinessPageBundle\Entity\BusinessTemplate $containerEntity
99
     *
100
     * @throws \Exception
101
     */
102
    protected function checkObjectHasQueryTrait($containerEntity)
103
    {
104
        if ($containerEntity === null) {
105
            throw new \Exception('The container entity parameter must not be null.');
106
        }
107
108
        //test that the containerEntity has the trait
109
        if (!method_exists($containerEntity, 'getQuery') || !method_exists($containerEntity, 'getBusinessEntityId')) {
110
            throw new \Exception('The object '.get_class($containerEntity).' does not have the QueryTrait.');
111
        }
112
    }
113
114
    /**
115
     * Get the results from the sql after adding the.
116
     *
117
     * @param mixed        $containerEntity
118
     * @param QueryBuilder $itemsQueryBuilder
119
     *
120
     * @throws \Exception
121
     *
122
     * @return QueryBuilder The QB to list of objects
123
     */
124
    public function buildWithSubQuery($containerEntity, QueryBuilder $itemsQueryBuilder, EntityManager $em)
125
    {
126
        //test the container entity
127
        if ($containerEntity === null) {
128
            throw new \Exception('The container entity parameter must not be null.');
129
        }
130
131
        //verify that the object has the query trait
132
        //@todo please use an interface and cast with it in the method signature
133
        $this->checkObjectHasQueryTrait($containerEntity);
134
135
        //get the query of the container entity
136
        $query = $containerEntity->getQuery();
137
        if (method_exists($containerEntity, 'additionnalQueryPart')) {
138
            $query = $containerEntity->additionnalQueryPart();
139
        }
140
141
        if ($query !== '' && $query !== null) {
142
            $subQuery = $em->createQueryBuilder()
143
                                ->select('item.id')
144
                                ->from($itemsQueryBuilder->getRootEntities()[0], 'item');
145
146
            $itemsQueryBuilder
147
                ->andWhere('main_item.id IN ('.$subQuery->getQuery()->getDql().' '.$query.')');
148
        }
149
150
        //Add ORDER BY if set
151
        if (method_exists($containerEntity, 'getOrderBy')) {
152
            $orderBy = json_decode($containerEntity->getOrderBy(), true);
153
            if ($orderBy) {
154
                foreach ($orderBy as $addOrderBy) {
155
                    $reflectionClass = new \ReflectionClass($itemsQueryBuilder->getRootEntities()[0]);
156
                    $reflectionProperty = $reflectionClass->getProperty($addOrderBy['by']);
157
158
                    //If ordering field is an association, treat it as a boolean
159
                    if ($this->isAssociationField($reflectionProperty)) {
160
                        $itemsQueryBuilder->addSelect('CASE WHEN main_item.'.$addOrderBy['by'].' IS NULL THEN 0 ELSE 1 END AS HIDDEN caseOrder');
161
                        $itemsQueryBuilder->addOrderBy('caseOrder', $addOrderBy['order']);
162
                    } else {
163
                        $itemsQueryBuilder->addOrderBy('main_item.'.$addOrderBy['by'], $addOrderBy['order']);
164
                    }
165
                }
166
            }
167
        }
168
169
        $currentView = $this->currentView;
170
171
        // If the current page is a BEP, we parse all its properties and inject them as query parameters
172
        if ($currentView() && $currentView() instanceof BusinessPage && null !== $currentEntity = $currentView()->getBusinessEntity()) {
173
174
            // NEW
175
            $metadatas = $em->getClassMetadata(get_class($currentEntity));
176 View Code Duplication
            foreach ($metadatas->fieldMappings as $fieldName => $field) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
177
                if (strpos($query, ':'.$fieldName) !== false) {
178
                    $itemsQueryBuilder->setParameter($fieldName, $metadatas->getFieldValue($currentEntity, $fieldName));
179
                }
180
            }
181 View Code Duplication
            foreach ($metadatas->associationMappings as $fieldName => $field) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
182
                if (strpos($query, ':'.$fieldName) !== false) {
183
                    $itemsQueryBuilder->setParameter($fieldName, $metadatas->getFieldValue($currentEntity, $fieldName)->getId());
184
                }
185
            }
186
187
            if (strpos($query, ':currentEntity') !== false) {
188
                $itemsQueryBuilder->setParameter('currentEntity', $currentEntity->getId());
189
            }
190
        }
191
192
        if (strpos($query, ':currentUser') !== false && is_object($this->getCurrentUser())) {
193
            if (is_object($this->getCurrentUser())) {
194
                $itemsQueryBuilder->setParameter('currentUser', $this->getCurrentUser()->getId());
195
            } else {
196
                throw new AccessDeniedException();
197
            }
198
        }
199
200
        return $itemsQueryBuilder;
201
    }
202
203
    /**
204
     * Check if field is a OneToOne, OneToMany, ManyToOne or ManyToMany association.
205
     *
206
     * @param \ReflectionProperty $field
207
     *
208
     * @return bool
209
     */
210
    private function isAssociationField(\ReflectionProperty $field)
211
    {
212
        $annotations = $this->reader->getPropertyAnnotations($field);
213
        foreach ($annotations as $key => $annotationObj) {
214
            if ($annotationObj instanceof OneToOne || $annotationObj instanceof OneToMany || $annotationObj instanceof ManyToOne || $annotationObj instanceof ManyToMany) {
215
                return true;
216
            }
217
        }
218
219
        return false;
220
    }
221
222
    public function getCurrentUser() {
223
        return $this->tokenStorage->getToken()->getUser();
224
    }
225
}
226