Completed
Push — master ( 6d6774...64f3ed )
by Jeroen
11:23 queued 05:13
created

src/Kunstmaan/NodeBundle/Helper/NodeHelper.php (3 issues)

strict.coding_against_concrete_implementation

Bug Minor

Upgrade to new PHP Analysis Engine

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\Helper;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Kunstmaan\AdminBundle\Entity\User;
7
use Kunstmaan\AdminBundle\Helper\CloneHelper;
8
use Kunstmaan\AdminBundle\Helper\FormWidgets\Tabs\TabPane;
9
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
10
use Kunstmaan\NodeBundle\Entity\Node;
11
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
12
use Kunstmaan\NodeBundle\Entity\NodeVersion;
13
use Kunstmaan\NodeBundle\Event\CopyPageTranslationNodeEvent;
14
use Kunstmaan\NodeBundle\Event\Events;
15
use Kunstmaan\NodeBundle\Event\NodeEvent;
16
use Kunstmaan\NodeBundle\Event\RecopyPageTranslationNodeEvent;
17
use Kunstmaan\NodeBundle\Helper\NodeAdmin\NodeAdminPublisher;
18
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
20
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
21
22
/**
23
 * Class NodeHelper
24
 */
25
class NodeHelper
26
{
27
    /** @var EntityManagerInterface */
28
    private $em;
29
30
    /** @var NodeAdminPublisher */
31
    private $nodeAdminPublisher;
32
33
    /** @var TokenStorageInterface */
34
    private $tokenStorage;
35
36
    /** @var CloneHelper */
37
    private $cloneHelper;
38
39
    /** @var EventDispatcherInterface */
40
    private $eventDispatcher;
41
42
    /**
43
     * NodeHelper constructor.
44
     *
45
     * @param EntityManagerInterface   $em
46
     * @param NodeAdminPublisher       $nodeAdminPublisher
47
     * @param TokenStorageInterface    $tokenStorage
48
     * @param CloneHelper              $cloneHelper
49
     * @param EventDispatcherInterface $eventDispatcher
50
     */
51 12 View Code Duplication
    public function __construct(
52
        EntityManagerInterface $em,
53
        NodeAdminPublisher $nodeAdminPublisher,
54
        TokenStorageInterface $tokenStorage,
55
        CloneHelper $cloneHelper,
56
        EventDispatcherInterface $eventDispatcher
57
    ) {
58 12
        $this->em = $em;
59 12
        $this->nodeAdminPublisher = $nodeAdminPublisher;
60 12
        $this->tokenStorage = $tokenStorage;
61 12
        $this->cloneHelper = $cloneHelper;
62 12
        $this->eventDispatcher = $eventDispatcher;
63 12
    }
64
65
    /**
66
     * @param HasNodeInterface $page            The page
67
     * @param NodeTranslation  $nodeTranslation The node translation
68
     * @param NodeVersion      $nodeVersion     The node version
69
     *
70
     * @return NodeVersion
71
     */
72 1
    public function createDraftVersion(
73
        HasNodeInterface $page,
74
        NodeTranslation $nodeTranslation,
75
        NodeVersion $nodeVersion
76
    ) {
77 1
        $user = $this->getAdminUser();
78 1
        $publicPage = $this->cloneHelper->deepCloneAndSave($page);
79
80
        /* @var NodeVersion $publicNodeVersion */
81 1
        $publicNodeVersion = $this->em->getRepository('KunstmaanNodeBundle:NodeVersion')
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeVersionFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...y\NodeVersionRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
82 1
            ->createNodeVersionFor(
83 1
                $publicPage,
84
                $nodeTranslation,
85
                $user,
86 1
                $nodeVersion->getOrigin(),
87 1
                NodeVersion::PUBLIC_VERSION,
88 1
                $nodeVersion->getCreated()
89
            );
90
91 1
        $nodeTranslation->setPublicNodeVersion($publicNodeVersion);
92 1
        $nodeVersion->setType(NodeVersion::DRAFT_VERSION);
93 1
        $nodeVersion->setOrigin($publicNodeVersion);
94 1
        $nodeVersion->setCreated(new \DateTime());
95
96 1
        $this->em->persist($nodeTranslation);
97 1
        $this->em->persist($nodeVersion);
98 1
        $this->em->flush();
99
100 1
        $this->eventDispatcher->dispatch(
101 1
            Events::CREATE_DRAFT_VERSION,
102 1
            new NodeEvent(
103 1
                $nodeTranslation->getNode(),
104
                $nodeTranslation,
105
                $nodeVersion,
106
                $page
107
            )
108
        );
109
110 1
        return $nodeVersion;
111
    }
112
113
    /**
114
     * @param NodeVersion     $nodeVersion
115
     * @param NodeTranslation $nodeTranslation
116
     * @param int             $nodeVersionTimeout
117
     * @param bool            $nodeVersionIsLocked
118
     */
119 2
    public function prepareNodeVersion(NodeVersion $nodeVersion, NodeTranslation $nodeTranslation, $nodeVersionTimeout, $nodeVersionIsLocked)
120
    {
121 2
        $user = $this->getAdminUser();
122 2
        $thresholdDate = date('Y-m-d H:i:s', time() - $nodeVersionTimeout);
123 2
        $updatedDate = date('Y-m-d H:i:s', strtotime($nodeVersion->getUpdated()->format('Y-m-d H:i:s')));
124
125 2 View Code Duplication
        if ($thresholdDate >= $updatedDate || $nodeVersionIsLocked) {
126 2
            $page = $nodeVersion->getRef($this->em);
127 2
            if ($nodeVersion === $nodeTranslation->getPublicNodeVersion()) {
128 1
                $this->nodeAdminPublisher
129 1
                    ->createPublicVersion(
130 1
                        $page,
131
                        $nodeTranslation,
132
                        $nodeVersion,
133
                        $user
134
                    );
135
            } else {
136 1
                $this->createDraftVersion(
137 1
                    $page,
138
                    $nodeTranslation,
139
                    $nodeVersion
140
                );
141
            }
142
        }
143 2
    }
144
145
    /**
146
     * @param Node             $node
147
     * @param NodeTranslation  $nodeTranslation
148
     * @param NodeVersion      $nodeVersion
149
     * @param HasNodeInterface $page
150
     * @param bool             $isStructureNode
151
     * @param TabPane          $tabPane
152
     *
153
     * @return NodeTranslation
154
     */
155 1
    public function updatePage(
156
        Node $node,
157
        NodeTranslation $nodeTranslation,
158
        NodeVersion $nodeVersion,
159
        HasNodeInterface $page,
160
        $isStructureNode,
161
        TabPane $tabPane = null
162
    ) {
163 1
        $this->eventDispatcher->dispatch(
164 1
            Events::PRE_PERSIST,
165 1
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
166
        );
167
168 1
        $nodeTranslation->setTitle($page->getTitle());
169 1
        if ($isStructureNode) {
170
            $nodeTranslation->setSlug('');
171
        }
172
173 1
        $nodeVersion->setUpdated(new \DateTime());
174 1
        if ($nodeVersion->getType() == NodeVersion::PUBLIC_VERSION) {
175 1
            $nodeTranslation->setUpdated($nodeVersion->getUpdated());
176
        }
177 1
        $this->em->persist($nodeTranslation);
178 1
        $this->em->persist($nodeVersion);
179 1
        $this->em->persist($node);
180 1
        if (null !== $tabPane) {
181
            $tabPane->persist($this->em);
182
        }
183 1
        $this->em->flush();
184
185 1
        $this->eventDispatcher->dispatch(
186 1
            Events::POST_PERSIST,
187 1
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
188
        );
189
190 1
        return $nodeTranslation;
191
    }
192
193
    /**
194
     * @param string    $refEntityType
195
     * @param string    $pageTitle
196
     * @param string    $locale
197
     * @param Node|null $parentNode
198
     *
199
     * @return NodeTranslation
200
     */
201 1
    public function createPage(
202
        $refEntityType,
203
        $pageTitle,
204
        $locale,
205
        Node $parentNode = null)
206
    {
207 1
        $user = $this->getAdminUser();
208
209 1
        $newPage = $this->createNewPage($refEntityType, $pageTitle);
210 1
        if (null !== $parentNode) {
211 1
            $parentNodeTranslation = $parentNode->getNodeTranslation($locale, true);
212 1
            $parentNodeVersion = $parentNodeTranslation->getPublicNodeVersion();
213 1
            $parentPage = $parentNodeVersion->getRef($this->em);
214 1
            $newPage->setParent($parentPage);
215
        }
216
217
        /* @var Node $nodeNewPage */
218 1
        $nodeNewPage = $this->em->getRepository('KunstmaanNodeBundle:Node')->createNodeFor($newPage, $locale, $user);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
219 1
        $nodeTranslation = $nodeNewPage->getNodeTranslation($locale, true);
220 1
        if (null !== $parentNode) {
221 1
            $weight = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')->getMaxChildrenWeight(
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getMaxChildrenWeight() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
222 1
                    $parentNode,
223
                    $locale
224 1
                ) + 1;
225 1
            $nodeTranslation->setWeight($weight);
226
        }
227
228 1
        if ($newPage->isStructureNode()) {
229
            $nodeTranslation->setSlug('');
230
        }
231
232 1
        $this->em->persist($nodeTranslation);
233 1
        $this->em->flush();
234
235 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
236
237 1
        $this->eventDispatcher->dispatch(
238 1
            Events::ADD_NODE,
239 1
            new NodeEvent(
240 1
                $nodeNewPage, $nodeTranslation, $nodeVersion, $newPage
241
            )
242
        );
243
244 1
        return $nodeTranslation;
245
    }
246
247
    /**
248
     * @param Node   $node
249
     * @param string $locale
250
     *
251
     * @return NodeTranslation
252
     */
253 1
    public function deletePage(Node $node, $locale)
254
    {
255 1
        $nodeTranslation = $node->getNodeTranslation($locale, true);
256 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
257 1
        $page = $nodeVersion->getRef($this->em);
258
259 1
        $this->eventDispatcher->dispatch(
260 1
            Events::PRE_DELETE,
261 1
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
262
        );
263
264 1
        $node->setDeleted(true);
265 1
        $this->em->persist($node);
266
267 1
        $this->deleteNodeChildren($node, $locale);
268 1
        $this->em->flush();
269
270 1
        $this->eventDispatcher->dispatch(
271 1
            Events::POST_DELETE,
272 1
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
273
        );
274
275 1
        return $nodeTranslation;
276
    }
277
278
    /**
279
     * @param Node   $node
280
     * @param string $locale
281
     *
282
     * @return HasNodeInterface
283
     */
284 1
    public function getPageWithNodeInterface(Node $node, $locale)
285
    {
286 1
        $nodeTranslation = $node->getNodeTranslation($locale, true);
287 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
288
289 1
        return $nodeVersion->getRef($this->em);
290
    }
291
292
    /**
293
     * @param Node   $node
294
     * @param string $sourceLocale
295
     * @param string $locale
296
     *
297
     * @return NodeTranslation
298
     */
299 1 View Code Duplication
    public function copyPageFromOtherLanguage(Node $node, $sourceLocale, $locale)
300
    {
301 1
        $user = $this->getAdminUser();
302
303 1
        $sourceNodeTranslation = $node->getNodeTranslation($sourceLocale, true);
304 1
        $sourceNodeVersion = $sourceNodeTranslation->getPublicNodeVersion();
305 1
        $sourcePage = $sourceNodeVersion->getRef($this->em);
306 1
        $targetPage = $this->cloneHelper->deepCloneAndSave($sourcePage);
307
308
        /* @var NodeTranslation $nodeTranslation */
309 1
        $nodeTranslation = $this->em->getRepository(NodeTranslation::class)->createNodeTranslationFor($targetPage, $locale, $node, $user);
310 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
311
312 1
        $this->eventDispatcher->dispatch(
313 1
            Events::COPY_PAGE_TRANSLATION,
314 1
            new CopyPageTranslationNodeEvent(
315 1
                $node,
316
                $nodeTranslation,
317
                $nodeVersion,
318
                $targetPage,
319
                $sourceNodeTranslation,
320
                $sourceNodeVersion,
321
                $sourcePage,
322
                $sourceLocale
323
            )
324
        );
325
326 1
        return $nodeTranslation;
327
    }
328
329
    /**
330
     * @param Node   $node
331
     * @param string $locale
332
     * @param string $title
333
     *
334
     * @return NodeTranslation|null
335
     */
336 1
    public function duplicatePage(Node $node, $locale, $title = 'New page')
337
    {
338 1
        $user = $this->getAdminUser();
339
340 1
        $sourceNodeTranslations = $node->getNodeTranslation($locale, true);
341 1
        $sourcePage = $sourceNodeTranslations->getPublicNodeVersion()->getRef($this->em);
342 1
        $targetPage = $this->cloneHelper->deepCloneAndSave($sourcePage);
343 1
        $targetPage->setTitle($title);
344
345 1
        if ($node->getParent()) {
346 1
            $parentNodeTranslation = $node->getParent()->getNodeTranslation($locale, true);
347 1
            $parent = $parentNodeTranslation->getPublicNodeVersion()->getRef($this->em);
348 1
            $targetPage->setParent($parent);
349
        }
350 1
        $this->em->persist($targetPage);
351 1
        $this->em->flush();
352
353
        /* @var Node $nodeNewPage */
354 1
        $nodeNewPage = $this->em->getRepository(Node::class)->createNodeFor($targetPage, $locale, $user);
355
356 1
        $nodeTranslation = $nodeNewPage->getNodeTranslation($locale, true);
357 1
        if ($targetPage->isStructureNode()) {
358
            $nodeTranslation->setSlug('');
359
            $this->em->persist($nodeTranslation);
360
        }
361 1
        $this->em->flush();
362
363 1
        return $nodeTranslation;
364
    }
365
366
    /**
367
     * @param Node   $node
368
     * @param int    $sourceNodeTranslationId
369
     * @param string $locale
370
     *
371
     * @return NodeTranslation
372
     */
373 1 View Code Duplication
    public function createPageDraftFromOtherLanguage(Node $node, $sourceNodeTranslationId, $locale)
374
    {
375 1
        $user = $this->getAdminUser();
376
377 1
        $sourceNodeTranslation = $this->em->getRepository(NodeTranslation::class)->find($sourceNodeTranslationId);
378 1
        $sourceNodeVersion = $sourceNodeTranslation->getPublicNodeVersion();
379 1
        $sourcePage = $sourceNodeVersion->getRef($this->em);
380 1
        $targetPage = $this->cloneHelper->deepCloneAndSave($sourcePage);
381
382
        /* @var NodeTranslation $nodeTranslation */
383 1
        $nodeTranslation = $this->em->getRepository(NodeTranslation::class)->addDraftNodeVersionFor($targetPage, $locale, $node, $user);
384 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
385
386 1
        $this->eventDispatcher->dispatch(
387 1
            Events::RECOPY_PAGE_TRANSLATION,
388 1
            new RecopyPageTranslationNodeEvent(
389 1
                $node,
390
                $nodeTranslation,
391
                $nodeVersion,
392
                $targetPage,
393
                $sourceNodeTranslation,
394
                $sourceNodeVersion,
395
                $sourcePage,
396 1
                $sourceNodeTranslation->getLang()
397
            )
398
        );
399
400 1
        return $nodeTranslation;
401
    }
402
403
    /**
404
     * @param Node   $node
405
     * @param string $locale
406
     *
407
     * @return NodeTranslation
408
     */
409 1
    public function createEmptyPage(Node $node, $locale)
410
    {
411 1
        $user = $this->getAdminUser();
412
413 1
        $refEntityName = $node->getRefEntityName();
414 1
        $targetPage = $this->createNewPage($refEntityName);
415
416
        /* @var NodeTranslation $nodeTranslation */
417 1
        $nodeTranslation = $this->em->getRepository(NodeTranslation::class)->createNodeTranslationFor($targetPage, $locale, $node, $user);
418 1
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
419
420 1
        $this->eventDispatcher->dispatch(
421 1
            Events::ADD_EMPTY_PAGE_TRANSLATION,
422 1
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $targetPage)
423
        );
424
425 1
        return $nodeTranslation;
426
    }
427
428
    /**
429
     * @param string $entityType
430
     * @param string $title
431
     *
432
     * @return HasNodeInterface
433
     */
434 2
    protected function createNewPage($entityType, $title = 'No title')
435
    {
436
        /* @var HasNodeInterface $newPage */
437 2
        $newPage = new $entityType();
438 2
        $newPage->setTitle($title);
439
440 2
        $this->em->persist($newPage);
441 2
        $this->em->flush();
442
443 2
        return $newPage;
444
    }
445
446
    /**
447
     * @param Node   $node
448
     * @param string $locale
449
     */
450 1
    protected function deleteNodeChildren(Node $node, $locale)
451
    {
452 1
        $children = $node->getChildren();
453
454
        /* @var Node $childNode */
455 1
        foreach ($children as $childNode) {
456 1
            $childNodeTranslation = $childNode->getNodeTranslation($locale, true);
457 1
            $childNodeVersion = $childNodeTranslation->getPublicNodeVersion();
458 1
            $childNodePage = $childNodeVersion->getRef($this->em);
459
460 1
            $this->eventDispatcher->dispatch(
461 1
                Events::PRE_DELETE,
462 1
                new NodeEvent(
463 1
                    $childNode,
464
                    $childNodeTranslation,
465
                    $childNodeVersion,
466
                    $childNodePage
467
                )
468
            );
469
470 1
            $childNode->setDeleted(true);
471 1
            $this->em->persist($childNode);
472
473 1
            $this->deleteNodeChildren($childNode, $locale);
474
475 1
            $this->eventDispatcher->dispatch(
476 1
                Events::POST_DELETE,
477 1
                new NodeEvent(
478 1
                    $childNode,
479
                    $childNodeTranslation,
480
                    $childNodeVersion,
481
                    $childNodePage
482
                )
483
            );
484
        }
485 1
    }
486
487
    /**
488
     * @return mixed|null
489
     */
490 8
    protected function getUser()
491
    {
492 8
        $token = $this->tokenStorage->getToken();
493 8
        if ($token) {
494 8
            $user = $token->getUser();
495 8
            if ($user && $user !== 'anon.' && $user instanceof User) {
496 8
                return $user;
497
            }
498
        }
499
500
        return null;
501
    }
502
503
    /**
504
     * @return mixed
505
     */
506 8
    protected function getAdminUser()
507
    {
508 8
        $user = $this->getUser();
509 8
        if (!$user) {
510
            throw new AccessDeniedException('Access denied: User should be an admin user');
511
        }
512
513 8
        return $user;
514
    }
515
}
516