Completed
Push — develop ( 38da12...667cd2 )
by Vladimir
02:29
created

PageManager   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 355
Duplicated Lines 7.61 %

Coupling/Cohesion

Components 2
Dependencies 23

Test Coverage

Coverage 82.68%

Importance

Changes 0
Metric Value
dl 27
loc 355
rs 9.6
c 0
b 0
f 0
ccs 105
cts 127
cp 0.8268
wmc 35
lcom 2
cbo 23

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 22 1
A compileManager() 0 4 1
A parsePageViews() 0 22 2
A getPageViews() 0 4 1
A getPageViewsFlattened() 0 4 1
A getStaticPageViews() 0 4 1
A getJailedStaticPageViews() 0 11 2
A getJailedRepeaterPageViews() 0 11 2
A trackNewPageView() 0 27 4
A trackNewContentItem() 0 5 1
A handleTrackableItem() 0 10 1
A handleTrackableStaticPageView() 13 13 2
B handleTrackableDynamicPageView() 0 49 7
A handleTrackableRepeaterPageView() 14 14 2
B registerExplicitAssets() 0 36 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Manager;
9
10
use allejo\stakx\Configuration;
11
use allejo\stakx\Document\BasePageView;
12
use allejo\stakx\Document\ContentItem;
13
use allejo\stakx\Document\DataItem;
14
use allejo\stakx\Document\DynamicPageView;
15
use allejo\stakx\Document\JailedDocument;
16
use allejo\stakx\Document\RepeaterPageView;
17
use allejo\stakx\Document\StaticPageView;
18
use allejo\stakx\Event\CollectionItemFinalized;
19
use allejo\stakx\Event\PageManagerPostProcess;
20
use allejo\stakx\Event\PageManagerPreProcess;
21
use allejo\stakx\Event\PageViewAdded;
22
use allejo\stakx\Event\PageViewDefinitionAdded;
23
use allejo\stakx\Exception\CollectionNotFoundException;
24
use allejo\stakx\Filesystem\File;
25
use allejo\stakx\Filesystem\FileExplorer;
26
use allejo\stakx\Filesystem\FileExplorerDefinition;
27
use allejo\stakx\Filesystem\FilesystemLoader as fs;
28
use allejo\stakx\Filesystem\Folder;
29
use Psr\Log\LoggerInterface;
30
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
31
32
/**
33
 * This class is responsible for handling all of the PageViews within a website.
34
 *
35
 * PageManager will parse all available dynamic and static PageViews. After, dynamic PageViews will be prepared by
36
 * setting the appropriate values for each ContentItem such as permalinks.
37
 */
38
class PageManager extends TrackingManager
39
{
40
    /** @var StaticPageView[] A place to store a reference to static PageViews with titles. */
41
    private $staticPages;
42
    /** @var RepeaterPageView[] A place to store a reference to repeater PageViews with titles. */
43
    private $repeaterPages;
44
    private $configuration;
45
    private $collectionManager;
46
    private $dataManager;
47
    private $eventDispatcher;
48
    private $logger;
49
    /** @var AssetManager */
50
    private $assetManager;
51
52
    /**
53
     * PageManager constructor.
54
     */
55 20
    public function __construct(
56
        Configuration $configuration,
57
        CollectionManager $collectionManager,
58
        DataManager $dataManager,
59
        AssetManager $assetManager,
60
        EventDispatcherInterface $eventDispatcher,
61
        LoggerInterface $logger
62
    ) {
63 20
        $this->trackedItems = [
64
            BasePageView::STATIC_TYPE => [],
65
            BasePageView::DYNAMIC_TYPE => [],
66
            BasePageView::REPEATER_TYPE => [],
67
        ];
68 20
        $this->staticPages = [];
69 20
        $this->repeaterPages = [];
70 20
        $this->configuration = $configuration;
71 20
        $this->collectionManager = $collectionManager;
72 20
        $this->dataManager = $dataManager;
73 20
        $this->eventDispatcher = $eventDispatcher;
74 20
        $this->logger = $logger;
75 20
        $this->assetManager = $assetManager;
76 20
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 7
    public function compileManager()
82
    {
83 7
        $this->parsePageViews($this->configuration->getPageViewFolders());
84 5
    }
85
86
    /**
87
     * Go through all of the PageView directories and create a respective PageView for each and classify them by type.
88
     *
89
     * @param string[] $pageViewFolders
90
     *
91
     * @since 0.1.0
92
     */
93 19
    public function parsePageViews(array $pageViewFolders)
94
    {
95 19
        $preEvent = new PageManagerPreProcess($this);
96 19
        $this->eventDispatcher->dispatch(PageManagerPreProcess::NAME, $preEvent);
97
98 19
        foreach ($pageViewFolders as $pageViewFolderName)
99
        {
100 19
            $folder = new Folder($pageViewFolderName);
101
102 18
            $event = new PageViewDefinitionAdded($folder);
103 18
            $this->eventDispatcher->dispatch(PageViewDefinitionAdded::NAME, $event);
104
105 18
            $def = new FileExplorerDefinition($folder);
106 18
            $def->flags = FileExplorer::INCLUDE_ONLY_FILES;
107 18
            $def->includes = ['/.html$/', '/.twig$/'];
108
109 18
            $this->scanTrackableItems($def);
110
        }
111
112 17
        $postEvent = new PageManagerPostProcess($this);
113 17
        $this->eventDispatcher->dispatch(PageManagerPostProcess::NAME, $postEvent);
114 17
    }
115
116
    /**
117
     * Get all of the PageViews in an associative array with PageView types as the keys.
118
     *
119
     * @since  0.1.1
120
     *
121
     * @return BasePageView[][]
122
     */
123 1
    public function &getPageViews()
124
    {
125 1
        return $this->trackedItems;
126
    }
127
128
    /**
129
     * Get all of the PageViews in flat array.
130
     *
131
     * @since  0.1.1
132
     *
133
     * @return BasePageView[]
134
     */
135 16
    public function &getPageViewsFlattened()
136
    {
137 16
        return $this->trackedItemsFlattened;
138
    }
139
140
    /**
141
     * Get the static PageViews tracked by this manager indexed by their title.
142
     *
143
     * @since 0.1.0
144
     *
145
     * @return StaticPageView[]
146
     */
147 1
    public function getStaticPageViews()
148
    {
149 1
        return $this->staticPages;
150
    }
151
152
    /**
153
     * Get the jailed version of the static PageViews indexed by their title.
154
     *
155
     * @since 0.1.0
156
     *
157
     * @return JailedDocument[]
158
     */
159 13
    public function getJailedStaticPageViews()
160
    {
161 13
        $jailedObjects = [];
162
163 13
        foreach ($this->staticPages as $key => $value)
164
        {
165 1
            $jailedObjects[$key] = $value->createJail();
166
        }
167
168 13
        return $jailedObjects;
169
    }
170
171
    /**
172
     * Get the jailed version of repeater PageViews indexed by their title.
173
     */
174 12
    public function getJailedRepeaterPageViews()
175
    {
176 12
        $jailedObjects = [];
177
178 12
        foreach ($this->repeaterPages as $key => $value)
179
        {
180
            $jailedObjects[$key] = $value->createJail();
181
        }
182
183 12
        return $jailedObjects;
184
    }
185
186
    /**
187
     * Add a PageView for this PageManager to track.
188
     *
189
     * @param BasePageView|StaticPageView|DynamicPageView|RepeaterPageView $pageView
190
     *
191
     * @throws \LogicException             When a PageView is treated as the wrong type
192
     * @throws CollectionNotFoundException When the collection specified in a Dynamic PageView isn't found
193
     * @throws \Exception                  The permalink could not be built correctly
194
     *
195
     * @since 0.2.0
196
     */
197 19
    public function trackNewPageView(BasePageView $pageView)
198
    {
199 19
        $namespace = $pageView->getType();
200
201 19
        switch ($namespace)
202
        {
203
            case BasePageView::STATIC_TYPE:
204 12
                $this->handleTrackableStaticPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\StaticPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
205 12
                break;
206
207
            case BasePageView::DYNAMIC_TYPE:
208 4
                $this->handleTrackableDynamicPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\DynamicPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
209 3
                break;
210
211
            case BasePageView::REPEATER_TYPE:
212 3
                $this->handleTrackableRepeaterPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\RepeaterPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
213 3
                break;
214
215
            default:
216
                break;
217
        }
218
219 18
        $event = new PageViewAdded($pageView);
220 18
        $this->eventDispatcher->dispatch(PageViewAdded::NAME, $event);
221
222 18
        $this->addObjectToTracker($pageView, $namespace);
223 18
    }
224
225
    /**
226
     * Add a new ContentItem to the respective parent PageView of the ContentItem.
227
     *
228
     * @param ContentItem $contentItem
229
     *
230
     * @since 0.1.0
231
     */
232 1
    public function trackNewContentItem(&$contentItem)
233
    {
234 1
        $collection = $contentItem->getNamespace();
235 1
        $this->trackedItems[BasePageView::DYNAMIC_TYPE][$collection]->addCollectableItem($contentItem);
236 1
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241 18
    protected function &handleTrackableItem(File $filePath, array $options = [])
242
    {
243 18
        $pageView = BasePageView::create($filePath, [
244 18
            'site' => $this->configuration->getConfiguration(),
245
        ]);
246
247 18
        $this->trackNewPageView($pageView);
248
249 17
        return $pageView;
250
    }
251
252
    /**
253
     * Handle special behavior and treatment for static PageViews while we're iterating through them.
254
     *
255
     * @param StaticPageView $pageView
256
     *
257
     * @since 0.1.0
258
     */
259 12 View Code Duplication
    private function handleTrackableStaticPageView(&$pageView)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
260
    {
261 12
        $pageView->evaluateFrontMatter([], [
262 12
            'site' => $this->configuration->getConfiguration(),
263
        ]);
264
265 12
        if (empty($pageView['title']))
266
        {
267 12
            return;
268
        }
269
270 2
        $this->staticPages[$pageView['title']] = &$pageView;
271 2
    }
272
273
    /**
274
     * Handle special behavior and treatment for dynamic PageViews while we're iterating through them.
275
     *
276
     * @param DynamicPageView $pageView
277
     *
278
     * @since 0.1.0
279
     *
280
     * @throws \LogicException             An invalid PageView has been given as a Dynamic PageView
281
     * @throws CollectionNotFoundException When a collection or dataset specified in a Dynamic PageView doesn't exist
282
     * @throws \Exception                  When the permalink for the given PageView hasn't been set
283
     */
284 4
    private function handleTrackableDynamicPageView(&$pageView)
285
    {
286 4
        $frontMatter = $pageView->getRawFrontMatter();
287 4
        $dataSource = null;
288 4
        $namespace = null;
289
290 4
        if (isset($frontMatter['collection']))
291
        {
292 4
            $dataSource = &$this->collectionManager->getCollections();
293 4
            $namespace = 'collection';
294
        }
295
        elseif (isset($frontMatter['dataset']))
296
        {
297
            $dataSource = &$this->dataManager->getDataItems();
298
            $namespace = 'dataset';
299
        }
300
301 4
        if ($dataSource === null)
302
        {
303
            throw new \LogicException('Invalid Dynamic PageView defined');
304
        }
305
306 4
        $collection = $frontMatter[$namespace];
307
308 4
        if (!isset($dataSource[$collection]))
309
        {
310 1
            throw new CollectionNotFoundException("The '$collection' $namespace is not defined");
311
        }
312
313
        /** @var ContentItem|DataItem $item */
314 3
        foreach ($dataSource[$collection] as &$item)
315
        {
316 3
            $item->evaluateFrontMatter($frontMatter, [
317 3
                'site' => $this->configuration->getConfiguration(),
318
            ]);
319 3
            $item->saveParentPageView($pageView);
320 3
            $item->buildPermalink(true);
321
322 3
            if ($item instanceof ContentItem)
323
            {
324 3
                $this->registerExplicitAssets($item);
325
            }
326
327 3
            $event = new CollectionItemFinalized($item);
328 3
            $this->eventDispatcher->dispatch(CollectionItemFinalized::NAME, $event);
329
330 3
            $pageView->addCollectableItem($item);
331
        }
332 3
    }
333
334
    /**
335
     * Handle special behavior and treatment for repeater PageViews while we're iterating through them.
336
     *
337
     * @param RepeaterPageView $pageView
338
     *
339
     * @since 0.2.0
340
     */
341 3 View Code Duplication
    private function handleTrackableRepeaterPageView(&$pageView)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
342
    {
343 3
        $pageView->evaluateFrontMatter([], [
344 3
            'site' => $this->configuration->getConfiguration(),
345
        ]);
346 3
        $pageView->configurePermalinks();
347
348 3
        if (empty($pageView['title']))
349
        {
350 3
            return;
351
        }
352
353
        $this->repeaterPages[$pageView['title']] = &$pageView;
354
    }
355
356 3
    private function registerExplicitAssets(ContentItem $contentItem)
357
    {
358 3
        $assets = $contentItem['assets'];
359
360 3
        if (!is_array($assets) && $assets !== null)
361
        {
362
            $this->logger->warning('The "assets" directive in FrontMatter must be null or an array of strings.');
363
            return;
364
        }
365
366 3
        if ($assets === null || count($assets) === 0)
367
        {
368 3
            return;
369
        }
370
371
        $sourceFolder = fs::path($contentItem->getAbsoluteFilePath())->getParentDirectory();
0 ignored issues
show
Documentation introduced by
$contentItem->getAbsoluteFilePath() is of type string, but the function expects a object<string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
372
        $permalinkFolder = fs::path($contentItem->getTargetFile())->getParentDirectory();
0 ignored issues
show
Documentation introduced by
$contentItem->getTargetFile() is of type string, but the function expects a object<string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
373
374
        foreach ($assets as $i => $asset)
375
        {
376
            if (!is_string($asset))
377
            {
378
                $this->logger->warning('{path}: Item #{index} of the "assets" array is not a string', [
379
                    'path' => $contentItem->getRelativeFilePath(),
380
                    'index' => $i,
381
                ]);
382
383
                continue;
384
            }
385
386
            $assetFile = new File($sourceFolder->generatePath($asset));
387
            $assetPermalink = fs::getRelativePath($permalinkFolder->generatePath($asset));
0 ignored issues
show
Documentation introduced by
$permalinkFolder->generatePath($asset) is of type object<allejo\stakx\Filesystem\FilesystemPath>, but the function expects a object<string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
388
389
            $this->assetManager->addExplicitAsset($assetPermalink, $assetFile);
390
        }
391
    }
392
}
393