These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Kunstmaan\NodeBundle\Repository; |
||
4 | |||
5 | use Gedmo\Tree\Entity\Repository\NestedTreeRepository; |
||
6 | use Kunstmaan\AdminBundle\Entity\BaseUser; |
||
7 | use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper; |
||
8 | use Kunstmaan\AdminBundle\Helper\Security\Acl\AclNativeHelper; |
||
9 | use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionDefinition; |
||
10 | use Kunstmaan\NodeBundle\Entity\HasNodeInterface; |
||
11 | use Kunstmaan\NodeBundle\Entity\Node; |
||
12 | use Kunstmaan\NodeBundle\Entity\NodeTranslation; |
||
13 | use Kunstmaan\NodeBundle\Entity\NodeVersion; |
||
14 | use Kunstmaan\NodeBundle\Helper\HiddenFromNavInterface; |
||
15 | use Kunstmaan\UtilitiesBundle\Helper\ClassLookup; |
||
16 | |||
17 | /** |
||
18 | * NodeRepository |
||
19 | */ |
||
20 | class NodeRepository extends NestedTreeRepository |
||
21 | { |
||
22 | /** |
||
23 | * @param string $lang The locale |
||
24 | * @param string $permission The permission (read, write, ...) |
||
25 | * @param AclHelper $aclHelper The acl helper |
||
26 | * @param bool $includeHiddenFromNav include the hiddenfromnav nodes |
||
27 | * or not |
||
28 | * |
||
29 | * @return Node[] |
||
30 | */ |
||
31 | public function getTopNodes( |
||
32 | $lang, |
||
33 | $permission, |
||
34 | AclHelper $aclHelper, |
||
35 | $includeHiddenFromNav = false |
||
36 | ) { |
||
37 | $result = $this->getChildNodes( |
||
38 | null, |
||
39 | $lang, |
||
40 | $permission, |
||
41 | $aclHelper, |
||
42 | $includeHiddenFromNav |
||
43 | ); |
||
44 | |||
45 | return $result; |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * @param int|null $parentId The parent node id |
||
50 | * @param string $lang The locale |
||
51 | * @param string $permission The permission (read, write, ...) |
||
52 | * @param AclHelper $aclHelper The acl helper |
||
53 | * @param bool $includeHiddenFromNav Include nodes hidden from |
||
54 | * navigation or not |
||
55 | * @param Node $rootNode Root node of the current tree |
||
56 | * |
||
57 | * @return Node[] |
||
58 | */ |
||
59 | public function getChildNodes( |
||
60 | $parentId, |
||
61 | $lang, |
||
62 | $permission, |
||
63 | AclHelper $aclHelper, |
||
64 | $includeHiddenFromNav = false, |
||
65 | $includeHiddenWithInternalName = false, |
||
66 | $rootNode = null |
||
67 | ) { |
||
68 | $qb = $this->createQueryBuilder('b') |
||
69 | ->select('b', 't', 'v') |
||
70 | ->leftJoin('b.nodeTranslations', 't', 'WITH', 't.lang = :lang') |
||
71 | ->leftJoin( |
||
72 | 't.publicNodeVersion', |
||
73 | 'v', |
||
74 | 'WITH', |
||
75 | 't.publicNodeVersion = v.id' |
||
76 | ) |
||
77 | ->where('b.deleted = 0') |
||
78 | ->setParameter('lang', $lang) |
||
79 | ->addOrderBy('t.weight', 'ASC') |
||
80 | ->addOrderBy('t.title', 'ASC'); |
||
81 | |||
82 | if (!$includeHiddenFromNav) { |
||
83 | if ($includeHiddenWithInternalName) { |
||
84 | $qb->andWhere( |
||
85 | '(b.hiddenFromNav != true OR b.internalName IS NOT NULL)' |
||
86 | ); |
||
87 | } else { |
||
88 | $qb->andWhere('b.hiddenFromNav != true'); |
||
89 | } |
||
90 | } |
||
91 | |||
92 | View Code Duplication | if (is_null($parentId)) { |
|
93 | $qb->andWhere('b.parent is NULL'); |
||
94 | } elseif ($parentId !== false) { |
||
95 | $qb->andWhere('b.parent = :parent') |
||
96 | ->setParameter('parent', $parentId); |
||
97 | } |
||
98 | if ($rootNode) { |
||
99 | $qb->andWhere('b.lft >= :left') |
||
100 | ->andWhere('b.rgt <= :right') |
||
101 | ->setParameter('left', $rootNode->getLeft()) |
||
102 | ->setParameter('right', $rootNode->getRight()); |
||
103 | } |
||
104 | |||
105 | $query = $aclHelper->apply( |
||
106 | $qb, |
||
107 | new PermissionDefinition(array($permission)) |
||
108 | ); |
||
109 | |||
110 | return $query->getResult(); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * @param HasNodeInterface $hasNode |
||
115 | * |
||
116 | * @return Node|null |
||
117 | */ |
||
118 | public function getNodeFor(HasNodeInterface $hasNode) |
||
119 | { |
||
120 | /* @var NodeVersion $nodeVersion */ |
||
121 | $nodeVersion = $this->getEntityManager()->getRepository( |
||
122 | 'KunstmaanNodeBundle:NodeVersion' |
||
123 | )->getNodeVersionFor( |
||
124 | $hasNode |
||
125 | ); |
||
126 | if (!is_null($nodeVersion)) { |
||
127 | /* @var NodeTranslation $nodeTranslation */ |
||
128 | $nodeTranslation = $nodeVersion->getNodeTranslation(); |
||
129 | if (!is_null($nodeTranslation)) { |
||
130 | return $nodeTranslation->getNode(); |
||
131 | } |
||
132 | } |
||
133 | |||
134 | return null; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * @param int $id The id |
||
139 | * @param string $entityName The class name |
||
140 | * |
||
141 | * @return Node|null |
||
142 | */ |
||
143 | public function getNodeForIdAndEntityname($id, $entityName) |
||
144 | { |
||
145 | /* @var NodeVersion $nodeVersion */ |
||
146 | $nodeVersion = $this->getEntityManager()->getRepository( |
||
147 | 'KunstmaanNodeBundle:NodeVersion' |
||
148 | )->findOneBy( |
||
149 | array('refId' => $id, 'refEntityName' => $entityName) |
||
150 | ); |
||
151 | if ($nodeVersion) { |
||
152 | return $nodeVersion->getNodeTranslation()->getNode(); |
||
153 | } |
||
154 | |||
155 | return null; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * @param Node $parentNode The parent node (may be null) |
||
160 | * @param string $slug The slug |
||
161 | * |
||
162 | * @return Node|null |
||
163 | */ |
||
164 | public function getNodeForSlug(Node $parentNode, $slug) |
||
165 | { |
||
166 | $slugParts = explode('/', $slug); |
||
167 | $result = null; |
||
168 | foreach ($slugParts as $slugPart) { |
||
169 | if ($parentNode) { |
||
170 | if ($r = $this->findOneBy( |
||
171 | array( |
||
172 | 'slug' => $slugPart, |
||
173 | 'parent.parent' => $parentNode->getId(), |
||
174 | ) |
||
175 | ) |
||
176 | ) { |
||
177 | $result = $r; |
||
178 | } |
||
179 | } else { |
||
180 | if ($r = $this->findOneBy(array('slug' => $slugPart))) { |
||
181 | $result = $r; |
||
182 | } |
||
183 | } |
||
184 | } |
||
185 | |||
186 | return $result; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * @param HasNodeInterface $hasNode The object to link to |
||
191 | * @param string $lang The locale |
||
192 | * @param BaseUser $owner The user |
||
193 | * @param string $internalName The internal name (may be null) |
||
194 | * |
||
195 | * @throws \InvalidArgumentException |
||
196 | * |
||
197 | * @return Node |
||
198 | */ |
||
199 | public function createNodeFor( |
||
200 | HasNodeInterface $hasNode, |
||
201 | $lang, |
||
202 | BaseUser $owner, |
||
203 | $internalName = null |
||
204 | ) { |
||
205 | $em = $this->getEntityManager(); |
||
206 | $node = new Node(); |
||
207 | $node->setRef($hasNode); |
||
208 | if (!$hasNode->getId() > 0) { |
||
209 | throw new \InvalidArgumentException( |
||
210 | 'the entity of class '. |
||
211 | $node->getRefEntityName( |
||
212 | ).' has no id, maybe you forgot to flush first' |
||
213 | ); |
||
214 | } |
||
215 | $node->setDeleted(false); |
||
216 | $node->setInternalName($internalName); |
||
217 | $parent = $hasNode->getParent(); |
||
218 | if ($parent) { |
||
219 | /* @var NodeVersion $parentNodeVersion */ |
||
220 | $parentNodeVersion = $em->getRepository( |
||
221 | 'KunstmaanNodeBundle:NodeVersion' |
||
222 | )->findOneBy( |
||
223 | array( |
||
224 | 'refId' => $parent->getId(), |
||
225 | 'refEntityName' => ClassLookup::getClass($parent), |
||
226 | ) |
||
227 | ); |
||
228 | if ($parentNodeVersion) { |
||
229 | $node->setParent( |
||
230 | $parentNodeVersion->getNodeTranslation()->getNode() |
||
231 | ); |
||
232 | } |
||
233 | } |
||
234 | if ($hasNode instanceof HiddenFromNavInterface) { |
||
235 | $node->setHiddenFromNav($hasNode->isHiddenFromNav()); |
||
236 | } |
||
237 | $em->persist($node); |
||
238 | $em->flush(); |
||
239 | $em->refresh($node); |
||
240 | $em->getRepository('KunstmaanNodeBundle:NodeTranslation') |
||
241 | ->createNodeTranslationFor( |
||
242 | $hasNode, |
||
243 | $lang, |
||
244 | $node, |
||
245 | $owner |
||
246 | ); |
||
247 | |||
248 | return $node; |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Get all the information needed to build a menu tree with one query. |
||
253 | * We only fetch the fields we need, instead of fetching full objects to |
||
254 | * limit the memory usage. |
||
255 | * |
||
256 | * @param string $lang The locale |
||
257 | * @param string $permission The permission (read, |
||
258 | * write, ...) |
||
259 | * @param AclNativeHelper $aclNativeHelper The acl helper |
||
260 | * @param bool $includeHiddenFromNav Include nodes hidden from |
||
261 | * navigation or not |
||
262 | * @param Node $rootNode The root node of the |
||
263 | * current site |
||
264 | * |
||
265 | * @return array |
||
266 | */ |
||
267 | public function getAllMenuNodes( |
||
268 | $lang, |
||
269 | $permission, |
||
270 | AclNativeHelper $aclNativeHelper, |
||
271 | $includeHiddenFromNav = false, |
||
272 | Node $rootNode = null |
||
273 | ) { |
||
274 | $connection = $this->_em->getConnection(); |
||
275 | $qb = $connection->createQueryBuilder(); |
||
276 | $databasePlatformName = $connection->getDatabasePlatform()->getName(); |
||
277 | $createIfStatement = function ( |
||
278 | $expression, |
||
279 | $trueValue, |
||
280 | $falseValue |
||
281 | ) use ($databasePlatformName) { |
||
282 | switch ($databasePlatformName) { |
||
283 | case 'sqlite': |
||
284 | $statement = 'CASE WHEN %s THEN %s ELSE %s END'; |
||
285 | |||
286 | break; |
||
287 | |||
288 | default: |
||
289 | $statement = 'IF(%s, %s, %s)'; |
||
290 | } |
||
291 | |||
292 | return sprintf($statement, $expression, $trueValue, $falseValue); |
||
293 | }; |
||
294 | |||
295 | $sql = <<<SQL |
||
296 | n.id, n.parent_id AS parent, t.url, t.id AS nt_id, |
||
297 | {$createIfStatement('t.weight IS NULL', 'v.weight', 't.weight')} AS weight, |
||
298 | {$createIfStatement('t.title IS NULL', 'v.title', 't.title')} AS title, |
||
299 | {$createIfStatement('t.online IS NULL', '0', 't.online')} AS online, |
||
300 | n.hidden_from_nav AS hidden, |
||
301 | n.ref_entity_name AS ref_entity_name |
||
302 | SQL; |
||
303 | |||
304 | $qb->select($sql) |
||
305 | ->from('kuma_nodes', 'n') |
||
306 | ->leftJoin( |
||
307 | 'n', |
||
308 | 'kuma_node_translations', |
||
309 | 't', |
||
310 | '(t.node_id = n.id AND t.lang = :lang)' |
||
311 | ) |
||
312 | ->leftJoin( |
||
313 | 'n', |
||
314 | 'kuma_node_translations', |
||
315 | 'v', |
||
316 | '(v.node_id = n.id AND v.lang <> :lang)' |
||
317 | ) |
||
318 | ->where('n.deleted = 0') |
||
319 | ->addGroupBy('n.id') |
||
320 | ->addOrderBy('t.weight', 'ASC') |
||
321 | ->addOrderBy('t.title', 'ASC'); |
||
322 | |||
323 | if (!$includeHiddenFromNav) { |
||
324 | $qb->andWhere('n.hidden_from_nav <> 0'); |
||
325 | } |
||
326 | |||
327 | if (!is_null($rootNode)) { |
||
328 | $qb->andWhere('n.lft >= :left') |
||
329 | ->andWhere('n.rgt <= :right'); |
||
330 | } |
||
331 | |||
332 | $permissionDef = new PermissionDefinition(array($permission)); |
||
333 | $permissionDef->setEntity('Kunstmaan\NodeBundle\Entity\Node'); |
||
334 | $permissionDef->setAlias('n'); |
||
335 | $qb = $aclNativeHelper->apply($qb, $permissionDef); |
||
336 | |||
337 | $stmt = $this->_em->getConnection()->prepare($qb->getSQL()); |
||
338 | $stmt->bindValue(':lang', $lang); |
||
339 | if (!is_null($rootNode)) { |
||
340 | $stmt->bindValue(':left', $rootNode->getLeft()); |
||
341 | $stmt->bindValue(':right', $rootNode->getRight()); |
||
342 | } |
||
343 | $stmt->execute(); |
||
344 | |||
345 | return $stmt->fetchAll(); |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Get all parents of a given node. We can go multiple levels up. |
||
350 | * |
||
351 | * @param Node $node |
||
352 | * @param string $lang |
||
0 ignored issues
–
show
|
|||
353 | * |
||
354 | * @return Node[] |
||
355 | */ |
||
356 | View Code Duplication | public function getAllParents(Node $node = null, $lang = null) |
|
0 ignored issues
–
show
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...
|
|||
357 | { |
||
358 | if (is_null($node)) { |
||
359 | return array(); |
||
360 | } |
||
361 | |||
362 | $qb = $this->createQueryBuilder('node'); |
||
363 | |||
364 | // Directly hydrate the nodeTranslation and nodeVersion |
||
365 | $qb->select('node', 't', 'v') |
||
366 | ->innerJoin('node.nodeTranslations', 't') |
||
367 | ->leftJoin( |
||
368 | 't.publicNodeVersion', |
||
369 | 'v', |
||
370 | 'WITH', |
||
371 | 't.publicNodeVersion = v.id' |
||
372 | ) |
||
373 | ->where('node.deleted = 0'); |
||
374 | |||
375 | if ($lang) { |
||
0 ignored issues
–
show
The expression
$lang of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
Loading history...
|
|||
376 | $qb->andWhere('t.lang = :lang') |
||
377 | ->setParameter('lang', $lang); |
||
378 | } |
||
379 | |||
380 | $qb->andWhere( |
||
381 | $qb->expr()->andX( |
||
382 | $qb->expr()->lte('node.lft', $node->getLeft()), |
||
383 | $qb->expr()->gte('node.rgt', $node->getRight()) |
||
384 | ) |
||
385 | ); |
||
386 | |||
387 | $qb->addOrderBy('node.lft', 'ASC'); |
||
388 | |||
389 | return $qb->getQuery()->getResult(); |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Get the root node of a given node. |
||
394 | * |
||
395 | * @param Node $node |
||
396 | * @param string $lang |
||
397 | * |
||
398 | * @return Node |
||
399 | */ |
||
400 | View Code Duplication | public function getRootNodeFor(Node $node = null, $lang = null) |
|
401 | { |
||
402 | if (is_null($node)) { |
||
403 | return null; |
||
404 | } |
||
405 | |||
406 | $qb = $this->createQueryBuilder('node'); |
||
407 | |||
408 | // Directly hydrate the nodeTranslation and nodeVersion |
||
409 | $qb->select('node', 't', 'v') |
||
410 | ->innerJoin('node.nodeTranslations', 't') |
||
411 | ->leftJoin( |
||
412 | 't.publicNodeVersion', |
||
413 | 'v', |
||
414 | 'WITH', |
||
415 | 't.publicNodeVersion = v.id' |
||
416 | ) |
||
417 | ->where('node.deleted = 0') |
||
418 | ->andWhere('node.parent IS NULL'); |
||
419 | |||
420 | if ($lang) { |
||
421 | $qb->andWhere('t.lang = :lang') |
||
422 | ->setParameter('lang', $lang); |
||
423 | } |
||
424 | |||
425 | $qb->andWhere( |
||
426 | $qb->expr()->andX( |
||
427 | $qb->expr()->lte('node.lft', $node->getLeft()), |
||
428 | $qb->expr()->gte('node.rgt', $node->getRight()) |
||
429 | ) |
||
430 | ); |
||
431 | |||
432 | return $qb->getQuery()->getOneOrNullResult(); |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * @return Node[] |
||
437 | */ |
||
438 | public function getAllTopNodes() |
||
439 | { |
||
440 | $qb = $this->createQueryBuilder('b') |
||
441 | ->select('b', 't', 'v') |
||
442 | ->leftJoin('b.nodeTranslations', 't') |
||
443 | ->leftJoin( |
||
444 | 't.publicNodeVersion', |
||
445 | 'v', |
||
446 | 'WITH', |
||
447 | 't.publicNodeVersion = v.id' |
||
448 | ) |
||
449 | ->where('b.deleted = 0') |
||
450 | ->andWhere('b.parent IS NULL'); |
||
451 | |||
452 | $result = $qb->getQuery()->getResult(); |
||
453 | |||
454 | return $result; |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * Get an array of Nodes based on the internal name. |
||
459 | * |
||
460 | * @param string $internalName The internal name of the node |
||
461 | * @param string $lang The locale |
||
462 | * @param int|null|bool $parentId The parent id |
||
463 | * @param bool $includeOffline Include offline nodes |
||
464 | * |
||
465 | * @return Node[] |
||
466 | */ |
||
467 | public function getNodesByInternalName( |
||
468 | $internalName, |
||
469 | $lang, |
||
470 | $parentId = false, |
||
471 | $includeOffline = false |
||
472 | ) { |
||
473 | $qb = $this->createQueryBuilder('n') |
||
474 | ->select('n', 't', 'v') |
||
475 | ->innerJoin('n.nodeTranslations', 't') |
||
476 | ->leftJoin( |
||
477 | 't.publicNodeVersion', |
||
478 | 'v', |
||
479 | 'WITH', |
||
480 | 't.publicNodeVersion = v.id' |
||
481 | ) |
||
482 | ->where('n.deleted = 0') |
||
483 | ->andWhere('n.internalName = :internalName') |
||
484 | ->setParameter('internalName', $internalName) |
||
485 | ->andWhere('t.lang = :lang') |
||
486 | ->setParameter('lang', $lang) |
||
487 | ->addOrderBy('t.weight', 'ASC') |
||
488 | ->addOrderBy('t.title', 'ASC'); |
||
489 | |||
490 | if (!$includeOffline) { |
||
491 | $qb->andWhere('t.online = true'); |
||
492 | } |
||
493 | |||
494 | View Code Duplication | if (is_null($parentId)) { |
|
0 ignored issues
–
show
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...
|
|||
495 | $qb->andWhere('n.parent is NULL'); |
||
496 | } elseif ($parentId === false) { |
||
0 ignored issues
–
show
This
elseif statement is empty, and could be removed.
This check looks for the bodies of These
Loading history...
|
|||
497 | // Do nothing |
||
498 | } else { |
||
499 | $qb->andWhere('n.parent = :parent') |
||
500 | ->setParameter('parent', $parentId); |
||
501 | } |
||
502 | |||
503 | $query = $qb->getQuery(); |
||
504 | |||
505 | return $query->getResult(); |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * Get a single node by internal name. |
||
510 | * |
||
511 | * @param string $internalName The internal name of the node |
||
512 | * |
||
513 | * @return Node |
||
514 | */ |
||
515 | public function getNodeByInternalName($internalName) |
||
516 | { |
||
517 | $qb = $this->createQueryBuilder('n') |
||
518 | ->select('n') |
||
519 | ->where('n.deleted = 0') |
||
520 | ->andWhere('n.internalName = :internalName') |
||
521 | ->setParameter('internalName', $internalName); |
||
522 | |||
523 | return $qb->getQuery()->getOneOrNullResult(); |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * Finds all different page classes currently registered as nodes |
||
528 | * |
||
529 | * @return string[] |
||
530 | */ |
||
531 | public function findAllDistinctPageClasses() |
||
532 | { |
||
533 | $qb = $this->createQueryBuilder('n') |
||
534 | ->select('n.refEntityName') |
||
535 | ->where('n.deleted = 0') |
||
536 | ->distinct(true); |
||
537 | |||
538 | return $qb->getQuery()->getArrayResult(); |
||
539 | } |
||
540 | } |
||
541 |
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.