Completed
Push — master ( 770316...74fc07 )
by Jeroen
09:08 queued 02:44
created

Helper/Services/PagePartCreatorService.php (2 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
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
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);
96
97
        // Find latest position.
98 View Code Duplication
        if (\is_null($position)) {
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
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);
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...
179
180
        /** @var PageTemplateConfigurationRepository $repo */
181
        $repo = $this->em->getRepository('KunstmaanPagePartBundle:PageTemplateConfiguration');
182
        $pageTemplateConfiguration = $repo->findFor($page);
0 ignored issues
show
It seems like $page defined by $translation->getRef($this->em) on line 178 can also be of type object<Kunstmaan\AdminBu...Entity\EntityInterface>; however, Kunstmaan\PagePartBundle...onRepository::findFor() does only seem to accept object<Kunstmaan\PagePar...sPageTemplateInterface>, 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...
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)
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
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