Completed
Push — master ( 91fdab...75a7b9 )
by
unknown
13:37
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
 * @package Kunstmaan\PagePartBundle\Helper\Services
25
 */
26
class PagePartCreatorService
27
{
28
    /**
29
     * @var EntityManagerInterface|EntityManager
30
     */
31
    protected $em;
32
33
    /**
34
     * @var PagePartRefRepository
35
     */
36
    protected $pagePartRepo;
37
38
    /**
39
     * @var NodeTranslationRepository
40
     */
41
    protected $translationRepo;
42
43
    /**
44
     * @var NodeRepository
45
     */
46
    protected $nodeRepo;
47
48
    /**
49
     * Sets the EntityManager dependency.
50
     *
51
     * @param EntityManagerInterface $em
52
     */
53
    public function setEntityManager(EntityManagerInterface $em)
54
    {
55
        $this->em = $em;
56
        // Because these repositories are shared between the different functions it's
57
        // easier to make them available in the class.
58
        $this->pagePartRepo = $em->getRepository('KunstmaanPagePartBundle:PagePartRef');
59
        $this->translationRepo = $em->getRepository('KunstmaanNodeBundle:NodeTranslation');
60
        $this->nodeRepo = $em->getRepository('KunstmaanNodeBundle:Node');
61
    }
62
63
    /**
64
     * @return EntityManagerInterface
65
     */
66
    public function getEntityManager()
67
    {
68
        return $this->em;
69
    }
70
71
    /**
72
     * Add a single pagepart to an existing page for a specific language, in an optional position.
73
     *
74
     * @param mixed(Node|string) $nodeOrInternalName
75
     *      A Node instance or the internal name.
76
     *      When the internal name is passed we'll get the node instance.
77
     *      Based on the language we'll locate the correct Page instance.
78
     * @param PagePartInterface $pagePart
79
     *      A completely configured pagepart for this language.
80
     * @param string $language
81
     *      The languagecode. nl|fr|en|.. . Just one.
82
     * @param string $context
83
     *      Where you want the pagepart to be.
84
     * @param mixed(integer\NULL) $position
85
     *      Leave null if you want to append at the end.
86
     *      Otherwise set a position you would like and it'll inject the pagepart in that position.
87
     *      It won't override pageparts but it will rather inject itself in that position and
88
     *      push the other pageparts down.
89
     */
90
    public function addPagePartToPage($nodeOrInternalName, PagePartInterface $pagePart, $language, $context = 'main', $position = null)
91
    {
92
        // Find the correct page instance.
93
        $node = $this->getNode($nodeOrInternalName);
94
        /** @var $translation NodeTranslation */
95
        $translation = $node->getNodeTranslation($language, true);
96
        /** @var HasPagePartsInterface $page */
97
        $page = $translation->getRef($this->em);
98
99
        // Find latest position.
100 View Code Duplication
        if (is_null($position)) {
101
            $pageParts = $this->pagePartRepo->getPagePartRefs($page, $context);
102
            $position = count($pageParts) + 1;
103
        }
104
105
        $this->em->persist($pagePart);
106
        $this->em->flush();
107
108
        $this->pagePartRepo->addPagePart($page, $pagePart, $position, $context);
109
    }
110
111
    /**
112
     * A helper function to more easily append multiple pageparts in different manners.
113
     *
114
     * @param mixed(Node|string) $nodeOrInternalName
115
     *      The node that you'd like to append the pageparts to. It's also possible to provide an internalname.
116
     * @param array $structure
117
     *      The structure array is something like this:
118
     *
119
     *      array('main' => array(
120
     *          function() { return new DummyPagePart('A') }, function() { return new DummyPagePart('B') }
121
     *       ), 'banners' => array($awesomeBanner));
122
     *
123
     *      So it's an array containing the pageparts per region. Each pagepart is returned by a function.
124
     *      This is clean because we don't have to bother with variablenames which we have to remember to pass
125
     *      to the pagecreatorservice at the right time. With this method it's impossible to assign a wrong pagepart to a page.
126
     *      Unless you provide the incorrect page oviously ... .
127
     *
128
     *      You can also include variables in the pagepart arrays if you want.
129
     *
130
     *      Or optionally you can use the results of the getCreatorArgumentsForPagePartAndProperties function instead of an anonymous function.
131
     * @param string $language
132
     *      The language of the translation you want to append to.
133
     *
134
     * @throws \LogicException
135
     */
136
    public function addPagePartsToPage($nodeOrInternalName, array $structure, $language)
137
    {
138
        $node = $this->getNode($nodeOrInternalName);
139
140
        // First instantiate all PageParts. This way no PageParts will be saved if there is an issue instantiating some of them.
141
        $instantiatedPageParts = array();
142
        foreach ($structure as $context => $pageParts) {
143
            $instantiatedPageParts[$context] = array();
144
145
            foreach ($pageParts as $pagePartOrFunction) {
146
                if (is_callable($pagePartOrFunction)) {
147
                    $pagePartOrFunction = $pagePartOrFunction();
148
149
                    if (!isset($pagePartOrFunction) || (is_null($pagePartOrFunction))) {
150
                        throw new \LogicException('A function returned nothing for a pagepart. Make sure you return your instantiated pageparts in your anonymous functions.');
151
                    }
152
                }
153
                if (!$pagePartOrFunction instanceof PagePartInterface) {
154
                    throw new \LogicException('Detected a supposed pagepart that did not implement the PagePartInterface.');
155
                }
156
157
                $instantiatedPageParts[$context][] = $pagePartOrFunction;
158
            }
159
        }
160
161
        // All good. We can start saving.
162
        foreach ($instantiatedPageParts as $context => $pageParts) {
163
            foreach ($pageParts as $pagePart) {
164
                $this->addPagePartToPage($node, $pagePart, $language, $context);
165
            }
166
        }
167
    }
168
169
    /**
170
     * @param mixed(Node|string) $nodeOrInternalName
171
     * @param string $language
172
     * @param string $templateName
173
     */
174
    public function setPageTemplate($nodeOrInternalName, $language, $templateName)
175
    {
176
        $node = $this->getNode($nodeOrInternalName);
177
        /** @var $translation NodeTranslation */
178
        $translation = $node->getNodeTranslation($language, true);
179
        /** @var HasPageTemplateInterface|EntityInterface $page */
180
        $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...
181
182
        /** @var PageTemplateConfigurationRepository $repo */
183
        $repo = $this->em->getRepository('KunstmaanPagePartBundle:PageTemplateConfiguration');
184
        $pageTemplateConfiguration = $repo->findFor($page);
0 ignored issues
show
It seems like $page defined by $translation->getRef($this->em) on line 180 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...
185
        if ($pageTemplateConfiguration) {
186
            $pageTemplateConfiguration->setPageTemplate($templateName);
187
        } else {
188
            $pageTemplateConfiguration = new PageTemplateConfiguration();
189
            $pageTemplateConfiguration->setPageId($page->getId());
190
            $pageTemplateConfiguration->setPageEntityName(ClassLookup::getClass($page));
191
            $pageTemplateConfiguration->setPageTemplate($templateName);
192
        }
193
194
        $this->em->persist($pageTemplateConfiguration);
195
        $this->em->flush();
196
    }
197
198
    /**
199
     * A helper function to express what pagepart you want.
200
     *
201
     * It just accepts a classname and an array of setter functions with their requested values.
202
     *
203
     * It'll return an anonymous function which instantiates the pagepart.
204
     *
205
     * @param string $pagePartClassName The full class name of the pagepart you want to instantiate.
206
     * @param array $setters An array of setternames and their values. array('setName' => 'Kim', 'isDeveloper' => true)
207
     *
208
     * @return callable The function that will instantiate a pagepart.
209
     */
210
    public function getCreatorArgumentsForPagePartAndProperties($pagePartClassName, array $setters = null)
211
    {
212
        return function() use ($pagePartClassName, $setters) {
213
            $pp = new $pagePartClassName;
214
215
            if (!is_null($setters)) {
216
                foreach ($setters as $setter => $value) {
217
                    call_user_func(array($pp, $setter), $value);
218
                }
219
            }
220
221
            return $pp;
222
        };
223
    }
224
225
    /**
226
     * @param mixed(string|Node) $nodeOrInternalName
227
     *
228
     * @return object
229
     */
230
    private function getNode($nodeOrInternalName)
231
    {
232
        if (is_string($nodeOrInternalName)) {
233
            return $this->nodeRepo->findOneBy(array('internalName' => $nodeOrInternalName));
234
        }
235
236
        return $nodeOrInternalName;
237
    }
238
}
239