Completed
Push — master ( 7b9015...c5b311 )
by Vladimir
26s queued 11s
created

PageManager   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 356
Duplicated Lines 7.58 %

Coupling/Cohesion

Components 2
Dependencies 23

Test Coverage

Coverage 83.46%

Importance

Changes 0
Metric Value
dl 27
loc 356
ccs 111
cts 133
cp 0.8346
rs 9.6
c 0
b 0
f 0
wmc 35
lcom 2
cbo 23

15 Methods

Rating   Name   Duplication   Size   Complexity  
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 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
A trackNewPageView() 0 28 4
A __construct() 0 22 1

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 20
            BasePageView::STATIC_TYPE => [],
65 20
            BasePageView::DYNAMIC_TYPE => [],
66 20
            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
        switch ($namespace)
202
        {
203 19
            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 7
            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 3
            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->declareTrackingNamespace($namespace);
223 18
        $this->addObjectToTracker($pageView, $namespace);
224 18
    }
225
226
    /**
227
     * Add a new ContentItem to the respective parent PageView of the ContentItem.
228
     *
229
     * @param ContentItem $contentItem
230
     *
231
     * @since 0.1.0
232
     */
233 1
    public function trackNewContentItem(&$contentItem)
234
    {
235 1
        $collection = $contentItem->getNamespace();
236 1
        $this->trackedItems[BasePageView::DYNAMIC_TYPE][$collection]->addCollectableItem($contentItem);
237 1
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242 18
    protected function &handleTrackableItem(File $filePath, array $options = [])
243
    {
244 18
        $pageView = BasePageView::create($filePath, [
245 18
            'site' => $this->configuration->getConfiguration(),
246
        ]);
247
248 18
        $this->trackNewPageView($pageView);
249
250 17
        return $pageView;
251
    }
252
253
    /**
254
     * Handle special behavior and treatment for static PageViews while we're iterating through them.
255
     *
256
     * @param StaticPageView $pageView
257
     *
258
     * @since 0.1.0
259
     */
260 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...
261
    {
262 12
        $pageView->evaluateFrontMatter([], [
263 12
            'site' => $this->configuration->getConfiguration(),
264
        ]);
265
266 12
        if (empty($pageView['title']))
267
        {
268 12
            return;
269
        }
270
271 2
        $this->staticPages[$pageView['title']] = &$pageView;
272 2
    }
273
274
    /**
275
     * Handle special behavior and treatment for dynamic PageViews while we're iterating through them.
276
     *
277
     * @param DynamicPageView $pageView
278
     *
279
     * @since 0.1.0
280
     *
281
     * @throws \LogicException             An invalid PageView has been given as a Dynamic PageView
282
     * @throws CollectionNotFoundException When a collection or dataset specified in a Dynamic PageView doesn't exist
283
     * @throws \Exception                  When the permalink for the given PageView hasn't been set
284
     */
285 4
    private function handleTrackableDynamicPageView(&$pageView)
286
    {
287 4
        $frontMatter = $pageView->getRawFrontMatter();
288 4
        $dataSource = null;
289 4
        $namespace = null;
290
291 4
        if (isset($frontMatter['collection']))
292
        {
293 4
            $dataSource = &$this->collectionManager->getCollections();
294 4
            $namespace = 'collection';
295
        }
296
        elseif (isset($frontMatter['dataset']))
297
        {
298
            $dataSource = &$this->dataManager->getDataItems();
299
            $namespace = 'dataset';
300
        }
301
302 4
        if ($dataSource === null)
303
        {
304
            throw new \LogicException('Invalid Dynamic PageView defined');
305
        }
306
307 4
        $collection = $frontMatter[$namespace];
308
309 4
        if (!isset($dataSource[$collection]))
310
        {
311 1
            throw new CollectionNotFoundException("The '$collection' $namespace is not defined");
312
        }
313
314
        /** @var ContentItem|DataItem $item */
315 3
        foreach ($dataSource[$collection] as &$item)
316
        {
317 3
            $item->evaluateFrontMatter($frontMatter, [
318 3
                'site' => $this->configuration->getConfiguration(),
319
            ]);
320 3
            $item->saveParentPageView($pageView);
321 3
            $item->buildPermalink(true);
322
323 3
            if ($item instanceof ContentItem)
324
            {
325 3
                $this->registerExplicitAssets($item);
326
            }
327
328 3
            $event = new CollectionItemFinalized($item);
329 3
            $this->eventDispatcher->dispatch(CollectionItemFinalized::NAME, $event);
330
331 3
            $pageView->addCollectableItem($item);
332
        }
333 3
    }
334
335
    /**
336
     * Handle special behavior and treatment for repeater PageViews while we're iterating through them.
337
     *
338
     * @param RepeaterPageView $pageView
339
     *
340
     * @since 0.2.0
341
     */
342 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...
343
    {
344 3
        $pageView->evaluateFrontMatter([], [
345 3
            'site' => $this->configuration->getConfiguration(),
346
        ]);
347 3
        $pageView->configurePermalinks();
348
349 3
        if (empty($pageView['title']))
350
        {
351 3
            return;
352
        }
353
354
        $this->repeaterPages[$pageView['title']] = &$pageView;
355
    }
356
357 3
    private function registerExplicitAssets(ContentItem $contentItem)
358
    {
359 3
        $assets = $contentItem['assets'];
360
361 3
        if (!is_array($assets) && $assets !== null)
362
        {
363
            $this->logger->warning('The "assets" directive in FrontMatter must be null or an array of strings.');
364
            return;
365
        }
366
367 3
        if ($assets === null || count($assets) === 0)
368
        {
369 3
            return;
370
        }
371
372
        $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...
373
        $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...
374
375
        foreach ($assets as $i => $asset)
376
        {
377
            if (!is_string($asset))
378
            {
379
                $this->logger->warning('{path}: Item #{index} of the "assets" array is not a string', [
380
                    'path' => $contentItem->getRelativeFilePath(),
381
                    'index' => $i,
382
                ]);
383
384
                continue;
385
            }
386
387
            $assetFile = new File($sourceFolder->generatePath($asset));
388
            $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...
389
390
            $this->assetManager->addExplicitAsset($assetPermalink, $assetFile);
391
        }
392
    }
393
}
394