Completed
Push — master ( 0be8e1...585ca2 )
by Daniel
59:26 queued 46:02
created

AbstractComponent::getParentContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
ccs 0
cts 6
cp 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Silverback\ApiComponentBundle\Entity\Component;
6
7
use ApiPlatform\Core\Annotation\ApiProperty;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\Common\Collections\Collection;
10
use Doctrine\ORM\Mapping as ORM;
11
use Ramsey\Uuid\Uuid;
12
use Silverback\ApiComponentBundle\Entity\Content\AbstractContent;
13
use Silverback\ApiComponentBundle\Entity\Content\ComponentGroup\ComponentGroup;
14
use Silverback\ApiComponentBundle\Entity\DeleteCascadeInterface;
15
use Silverback\ApiComponentBundle\Entity\ValidComponentTrait;
16
use Symfony\Component\Serializer\Annotation\Groups;
17
18
/**
19
 * Class AbstractComponent
20
 * @package Silverback\ApiComponentBundle\Entity\Component
21
 * @author Daniel West <[email protected]>
22
 * @ORM\Entity()
23
 * @ORM\Table(name="component")
24
 * @ORM\InheritanceType("SINGLE_TABLE")
25
 * @ORM\DiscriminatorColumn(name="type", type="string")
26
 * @ORM\DiscriminatorMap({
27
 *     "content" = "Silverback\ApiComponentBundle\Entity\Component\Content\Content",
28
 *     "form" = "Silverback\ApiComponentBundle\Entity\Component\Form\Form",
29
 *     "gallery" = "Silverback\ApiComponentBundle\Entity\Component\Gallery\Gallery",
30
 *     "gallery_item" = "Silverback\ApiComponentBundle\Entity\Component\Gallery\GalleryItem",
31
 *     "hero" = "Silverback\ApiComponentBundle\Entity\Component\Hero\Hero",
32
 *     "feature_columns" = "Silverback\ApiComponentBundle\Entity\Component\Feature\Columns\FeatureColumns",
33
 *     "feature_columns_item" = "Silverback\ApiComponentBundle\Entity\Component\Feature\Columns\FeatureColumnsItem",
34
 *     "feature_stacked" = "Silverback\ApiComponentBundle\Entity\Component\Feature\Stacked\FeatureStacked",
35
 *     "feature_stacked_item" = "Silverback\ApiComponentBundle\Entity\Component\Feature\Stacked\FeatureStackedItem",
36
 *     "feature_text_list" = "Silverback\ApiComponentBundle\Entity\Component\Feature\TextList\FeatureTextList",
37
 *     "feature_text_list_item" = "Silverback\ApiComponentBundle\Entity\Component\Feature\TextList\FeatureTextListItem",
38
 *     "nav_bar" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\NavBar\NavBar",
39
 *     "nav_bar_item" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\NavBar\NavBarItem",
40
 *     "tabs" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\Tabs\Tabs",
41
 *     "tabs_item" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\Tabs\TabsItem",
42
 *     "menu" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\Menu\Menu",
43
 *     "menu_item" = "Silverback\ApiComponentBundle\Entity\Component\Navigation\Menu\MenuItem",
44
 *     "collection" = "Silverback\ApiComponentBundle\Entity\Component\Collection\Collection",
45
 *     "layout_side_column" = "Silverback\ApiComponentBundle\Entity\Component\Layout\SideColumn",
46
 *     "simple_image" = "Silverback\ApiComponentBundle\Entity\Component\Image\SimpleImage"
47
 * })
48
 */
49
abstract class AbstractComponent implements ComponentInterface, DeleteCascadeInterface
50
{
51
    use ValidComponentTrait;
52
53
    /**
54
     * @ORM\Id()
55
     * @ORM\Column(type="string")
56
     * @var string
57
     */
58
    private $id;
59
60
    /**
61
     * @ORM\Column(nullable=true)
62
     * @Groups({"default"})
63
     * @var null|string
64
     */
65
    private $className;
66
67
    /**
68
     * @ORM\OneToMany(targetEntity="Silverback\ApiComponentBundle\Entity\Component\ComponentLocation", mappedBy="component")
69
     * @var Collection|ComponentLocation[]
70
     */
71
    protected $locations;
72
73
    /**
74
     * @ORM\OneToMany(targetEntity="Silverback\ApiComponentBundle\Entity\Content\ComponentGroup\ComponentGroup", mappedBy="parent", cascade={"persist"})
75
     * @ORM\OrderBy({"sort"="ASC"})
76
     * @ApiProperty(attributes={"fetchEager": false})
77
     * @Groups({"layout", "route", "content", "component"})
78
     * @var Collection|ComponentGroup[]
79
     */
80
    protected $componentGroups;
81
82
    /**
83
     * @ORM\Column(nullable=true)
84
     * @Groups({"route", "content", "component"})
85
     * @var string|null
86
     */
87
    protected $componentName;
88
89
    /**
90
     * AbstractComponent constructor.
91
     */
92
    public function __construct()
93 10
    {
94
        $this->id = Uuid::uuid4()->getHex();
95 10
        $this->locations = new ArrayCollection;
96 10
        $this->componentGroups = new ArrayCollection;
97 10
        $this->validComponents = new ArrayCollection;
98 10
    }
99 10
100
    /**
101
     * @return string
102
     */
103
    public function getId(): string
104
    {
105
        return $this->id;
106
    }
107
108
    /**
109
     * @return null|string
110
     */
111
    public function getClassName(): ?string
112
    {
113
        return $this->className;
114
    }
115
116
    /**
117
     * @param null|string $className
118
     * @return self
119
     */
120
    public function setClassName(?string $className): self
121
    {
122
        $this->className = $className;
123
        return $this;
124
    }
125
126
    /**
127
     * @return Collection|ComponentLocation[]
128
     */
129
    public function getLocations()
130
    {
131
        return $this->locations;
132
    }
133
134
    /**
135
     * @param ComponentLocation $componentLocation
136
     * @return self
137
     */
138
    public function addLocation(ComponentLocation $componentLocation): self
139
    {
140
        if (!$this->locations->contains($componentLocation)) {
141
            $componentLocation->setComponent($this);
142
            $this->locations->add($componentLocation);
143
        }
144
        return $this;
145
    }
146
147
    /**
148
     * @param ComponentLocation $componentLocation
149
     * @return self
150
     */
151
    public function removeLocation(ComponentLocation $componentLocation): self
152
    {
153
        if ($this->locations->contains($componentLocation)) {
154
            $this->locations->removeElement($componentLocation);
155
        }
156
        return $this;
157
    }
158
159
    /**
160
     * @param array $componentGroups
161
     * @return self
162
     */
163
    public function setComponentGroups(array $componentGroups): self
164
    {
165
        $this->componentGroups = new ArrayCollection;
166
        foreach ($componentGroups as $componentGroup) {
167
            $this->addComponentGroup($componentGroup);
168
        }
169
        return $this;
170
    }
171
172
    /**
173
     * @param ComponentGroup $componentGroup
174
     * @return self
175
     */
176
    public function addComponentGroup(ComponentGroup $componentGroup): self
177 3
    {
178
        if (!$this->componentGroups->contains($componentGroup)) {
179 3
            $this->componentGroups->add($componentGroup);
180 3
            $componentGroup->setParent($this);
181 3
        }
182
        return $this;
183 3
    }
184
185
    /**
186
     * @param ComponentGroup $componentGroup
187
     * @return self
188
     */
189
    public function removeComponentGroup(ComponentGroup $componentGroup): self
190
    {
191
        if ($this->componentGroups->contains($componentGroup)) {
192
            $this->componentGroups->removeElement($componentGroup);
193
        }
194
        return $this;
195
    }
196
197
    /**
198
     * @return Collection|\Silverback\ApiComponentBundle\Entity\Content\ComponentGroup\ComponentGroup[]
199
     */
200
    public function getComponentGroups(): Collection
201 3
    {
202
        return $this->componentGroups;
203 3
    }
204
205
    /**
206
     * @param null|string $componentName
207
     * @return self
208
     */
209
    public function setComponentName(?string $componentName): self
210
    {
211
        $this->componentName = $componentName;
212
        return $this;
213
    }
214
215
    /**
216
     * Return the component name for front-end to decipher
217
     * @return string
218
     */
219
    public function getComponentName(): string
220
    {
221
        if ($this->componentName) {
222
            return $this->componentName;
223
        }
224
        $explodedClass = explode('\\', static::class);
225
        return array_pop($explodedClass);
226
    }
227
228
    /**
229
     * @return bool
230
     */
231
    public function onDeleteCascade(): bool
232
    {
233
        return false;
234
    }
235
236
    /**
237
     * @Groups({"component_write"})
238
     * @param AbstractComponent $parent
239
     * @param int $componentGroupOffset
240
     * @return self
241
     */
242
    public function setParentComponent(AbstractComponent $parent, int $componentGroupOffset = 0): self
243
    {
244
        if (!\in_array($parent, $this->getParentComponents(), true)) {
245
            $componentGroup = $this->getComponentComponentGroup($parent, $componentGroupOffset);
246
            if (!$componentGroup->hasComponent($this)) {
247
                $componentGroup->addComponentLocation(new ComponentLocation($componentGroup, $this));
248
            }
249
        }
250
        return $this;
251
    }
252
253
    /**
254
     * @Groups({"component_write"})
255
     * @param ComponentGroup $componentGroup
256
     * @return self
257
     */
258
    public function setParentComponentGroup(ComponentGroup $componentGroup): self
259
    {
260
        if (!$componentGroup->hasComponent($this)) {
261
            $componentGroup->addComponentLocation(new ComponentLocation($componentGroup, $this));
262
        }
263
        return $this;
264
    }
265
266
    /**
267
     * @param AbstractContent $content
268
     * @return bool
269
     */
270
    public function hasParentContent(AbstractContent $content): bool
271
    {
272
        foreach ($this->locations as $location) {
273
            if ($location->getContent() === $content) {
274
                return true;
275
            }
276
        }
277
        return false;
278
    }
279
280
    /**
281
     * @param AbstractComponent $component
282
     * @param int $componentGroupOffset
283
     * @return ComponentGroup
284
     */
285
    private function getComponentComponentGroup(AbstractComponent $component, int $componentGroupOffset = 0): ComponentGroup
286
    {
287
        /** @var ComponentGroup $componentGroup */
288
        $componentGroup = $component->getComponentGroups()->get($componentGroupOffset);
289
        if (null === $componentGroup) {
290
            throw new \InvalidArgumentException(sprintf('There is no component group child of this component with the offset %d', $componentGroupOffset));
291
        }
292
        return $componentGroup;
293
    }
294
295
    /**
296
     * @return AbstractComponent[]
297
     */
298
    private function getParentComponents(): array
299
    {
300
        $parentContent = $this->getParentContent();
301
        return array_unique(
302
            array_filter(
303
                array_map(
304
                    function ($content) {
305
                        if ($content instanceof ComponentGroup) {
306
                            return $content->getParent();
307
                        }
308
                    },
309
                    $parentContent
310
                )
311
            )
312
        );
313
    }
314
315
    /**
316
     * @return AbstractContent[]
317
     */
318
    private function getParentContent(): array
319
    {
320
        return array_unique(
321
            array_filter(
322
                array_map(
323
                    function (ComponentLocation $loc) {
324
                        return $loc->getContent();
325
                    },
326
                    $this->locations->toArray()
327
                )
328
            )
329
        );
330
    }
331
}
332