Completed
Push — master ( aba493...5356ed )
by Ruud
315:38 queued 305:00
created

Helper/Services/PagePartCreatorService.php (7 issues)

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\PagePartBundle\Helper\Services;
4
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\EntityManagerInterface;
7
use Kunstmaan\AdminBundle\Entity\EntityInterface;
8
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
9
use Kunstmaan\NodeBundle\Repository\NodeRepository;
10
use Kunstmaan\NodeBundle\Repository\NodeTranslationRepository;
11
use Kunstmaan\PagePartBundle\Entity\PageTemplateConfiguration;
12
use Kunstmaan\PagePartBundle\Helper\HasPagePartsInterface;
13
use Kunstmaan\PagePartBundle\Helper\HasPageTemplateInterface;
14
use Kunstmaan\PagePartBundle\Helper\PagePartInterface;
15
use Kunstmaan\PagePartBundle\Repository\PagePartRefRepository;
16
use Kunstmaan\PagePartBundle\Repository\PageTemplateConfigurationRepository;
17
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
18
19
/**
20
 * A class to facilitate the adding of PageParts to existing pages.
21
 *
22
 * NOTE: There is a similar implementation for adding pages. See the NodeBundle for more on this.
23
 */
24
class PagePartCreatorService
25
{
26
    /**
27
     * @var EntityManagerInterface|EntityManager
28
     */
29
    protected $em;
30
31
    /**
32
     * @var PagePartRefRepository
33
     */
34
    protected $pagePartRepo;
35
36
    /**
37
     * @var NodeTranslationRepository
38
     */
39
    protected $translationRepo;
40
41
    /**
42
     * @var NodeRepository
43
     */
44
    protected $nodeRepo;
45
46
    /**
47
     * Sets the EntityManager dependency.
48
     *
49
     * @param EntityManagerInterface $em
50
     */
51
    public function setEntityManager(EntityManagerInterface $em)
52
    {
53
        $this->em = $em;
54
        // Because these repositories are shared between the different functions it's
55
        // easier to make them available in the class.
56
        $this->pagePartRepo = $em->getRepository('KunstmaanPagePartBundle:PagePartRef');
57
        $this->translationRepo = $em->getRepository('KunstmaanNodeBundle:NodeTranslation');
58
        $this->nodeRepo = $em->getRepository('KunstmaanNodeBundle:Node');
59
    }
60
61
    /**
62
     * @return EntityManagerInterface
63
     */
64
    public function getEntityManager()
65
    {
66
        return $this->em;
67
    }
68
69
    /**
70
     * Add a single pagepart to an existing page for a specific language, in an optional position.
71
     *
72
     * @param mixed(Node|string)  $nodeOrInternalName
0 ignored issues
show
The doc-type mixed(Node|string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
73
     *                                                A Node instance or the internal name.
74
     *                                                When the internal name is passed we'll get the node instance.
75
     *                                                Based on the language we'll locate the correct Page instance.
76
     * @param pagePartInterface   $pagePart
77
     *                                                A completely configured pagepart for this language
78
     * @param string              $language
79
     *                                                The languagecode. nl|fr|en|.. . Just one.
80
     * @param string              $context
81
     *                                                Where you want the pagepart to be
82
     * @param mixed(integer\NULL) $position
0 ignored issues
show
The doc-type mixed(integer\NULL) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
83
     *                                                Leave null if you want to append at the end.
84
     *                                                Otherwise set a position you would like and it'll inject the pagepart in that position.
85
     *                                                It won't override pageparts but it will rather inject itself in that position and
86
     *                                                push the other pageparts down.
87
     */
88
    public function addPagePartToPage($nodeOrInternalName, PagePartInterface $pagePart, $language, $context = 'main', $position = null)
89
    {
90
        // Find the correct page instance.
91
        $node = $this->getNode($nodeOrInternalName);
92
        /** @var $translation NodeTranslation */
93
        $translation = $node->getNodeTranslation($language, true);
94
        /** @var HasPagePartsInterface $page */
95
        $page = $translation->getRef($this->em);
0 ignored issues
show
It seems like $this->em can also be of type object<Doctrine\ORM\EntityManagerInterface>; however, Kunstmaan\NodeBundle\Ent...deTranslation::getRef() does only seem to accept object<Doctrine\ORM\EntityManager>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
96
97
        // Find latest position.
98 View Code Duplication
        if (\is_null($position)) {
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...
99
            $pageParts = $this->pagePartRepo->getPagePartRefs($page, $context);
100
            $position = \count($pageParts) + 1;
101
        }
102
103
        $this->em->persist($pagePart);
104
        $this->em->flush();
105
106
        $this->pagePartRepo->addPagePart($page, $pagePart, $position, $context);
107
    }
108
109
    /**
110
     * A helper function to more easily append multiple pageparts in different manners.
111
     *
112
     * @param mixed(Node|string) $nodeOrInternalName
0 ignored issues
show
The doc-type mixed(Node|string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
113
     *                                               The node that you'd like to append the pageparts to. It's also possible to provide an internalname.
114
     * @param array              $structure
115
     *                                               The structure array is something like this:
116
     *
117
     *      array('main' => array(
118
     *          function() { return new DummyPagePart('A') }, function() { return new DummyPagePart('B') }
119
     *       ), 'banners' => array($awesomeBanner));
120
     *
121
     *      So it's an array containing the pageparts per region. Each pagepart is returned by a function.
122
     *      This is clean because we don't have to bother with variablenames which we have to remember to pass
123
     *      to the pagecreatorservice at the right time. With this method it's impossible to assign a wrong pagepart to a page.
124
     *      Unless you provide the incorrect page oviously ... .
125
     *
126
     *      You can also include variables in the pagepart arrays if you want.
127
     *
128
     *      Or optionally you can use the results of the getCreatorArgumentsForPagePartAndProperties function instead of an anonymous function.
129
     * @param string $language
130
     *                         The language of the translation you want to append to
131
     *
132
     * @throws \LogicException
133
     */
134
    public function addPagePartsToPage($nodeOrInternalName, array $structure, $language)
135
    {
136
        $node = $this->getNode($nodeOrInternalName);
137
138
        // First instantiate all PageParts. This way no PageParts will be saved if there is an issue instantiating some of them.
139
        $instantiatedPageParts = array();
140
        foreach ($structure as $context => $pageParts) {
141
            $instantiatedPageParts[$context] = array();
142
143
            foreach ($pageParts as $pagePartOrFunction) {
144
                if (\is_callable($pagePartOrFunction)) {
145
                    $pagePartOrFunction = $pagePartOrFunction();
146
147
                    if (!isset($pagePartOrFunction) || is_null($pagePartOrFunction)) {
148
                        throw new \LogicException('A function returned nothing for a pagepart. Make sure you return your instantiated pageparts in your anonymous functions.');
149
                    }
150
                }
151
                if (!$pagePartOrFunction instanceof PagePartInterface) {
152
                    throw new \LogicException('Detected a supposed pagepart that did not implement the PagePartInterface.');
153
                }
154
155
                $instantiatedPageParts[$context][] = $pagePartOrFunction;
156
            }
157
        }
158
159
        // All good. We can start saving.
160
        foreach ($instantiatedPageParts as $context => $pageParts) {
161
            foreach ($pageParts as $pagePart) {
162
                $this->addPagePartToPage($node, $pagePart, $language, $context);
163
            }
164
        }
165
    }
166
167
    /**
168
     * @param mixed(Node|string) $nodeOrInternalName
169
     * @param string             $language
170
     * @param string             $templateName
171
     */
172
    public function setPageTemplate($nodeOrInternalName, $language, $templateName)
173
    {
174
        $node = $this->getNode($nodeOrInternalName);
175
        /** @var $translation NodeTranslation */
176
        $translation = $node->getNodeTranslation($language, true);
177
        /** @var HasPageTemplateInterface|EntityInterface $page */
178
        $page = $translation->getRef($this->em);
179
180
        /** @var PageTemplateConfigurationRepository $repo */
181
        $repo = $this->em->getRepository('KunstmaanPagePartBundle:PageTemplateConfiguration');
182
        $pageTemplateConfiguration = $repo->findFor($page);
183
        if ($pageTemplateConfiguration) {
184
            $pageTemplateConfiguration->setPageTemplate($templateName);
185
        } else {
186
            $pageTemplateConfiguration = new PageTemplateConfiguration();
187
            $pageTemplateConfiguration->setPageId($page->getId());
188
            $pageTemplateConfiguration->setPageEntityName(ClassLookup::getClass($page));
189
            $pageTemplateConfiguration->setPageTemplate($templateName);
190
        }
191
192
        $this->em->persist($pageTemplateConfiguration);
193
        $this->em->flush();
194
    }
195
196
    /**
197
     * A helper function to express what pagepart you want.
198
     *
199
     * It just accepts a classname and an array of setter functions with their requested values.
200
     *
201
     * It'll return an anonymous function which instantiates the pagepart.
202
     *
203
     * @param string $pagePartClassName the full class name of the pagepart you want to instantiate
204
     * @param array  $setters           An array of setternames and their values. array('setName' => 'Kim', 'isDeveloper' => true)
0 ignored issues
show
Should the type for parameter $setters not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

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. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
205
     *
206
     * @return callable the function that will instantiate a pagepart
207
     */
208
    public function getCreatorArgumentsForPagePartAndProperties($pagePartClassName, array $setters = null)
209
    {
210
        return function () use ($pagePartClassName, $setters) {
211
            $pp = new $pagePartClassName();
212
213
            if (!\is_null($setters)) {
214
                foreach ($setters as $setter => $value) {
215
                    \call_user_func(array($pp, $setter), $value);
216
                }
217
            }
218
219
            return $pp;
220
        };
221
    }
222
223
    /**
224
     * @param mixed(string|Node) $nodeOrInternalName
0 ignored issues
show
The doc-type mixed(string|Node) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
225
     *
226
     * @return object
227
     */
228
    private function getNode($nodeOrInternalName)
229
    {
230
        if (\is_string($nodeOrInternalName)) {
231
            return $this->nodeRepo->findOneBy(array('internalName' => $nodeOrInternalName));
232
        }
233
234
        return $nodeOrInternalName;
235
    }
236
}
237