Test Failed
Push — master ( dc68d1...1f5199 )
by Mathieu
02:31
created

AbstractNews   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 449
Duplicated Lines 15.59 %

Coupling/Cohesion

Components 4
Dependencies 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
lcom 4
cbo 9
dl 70
loc 449
rs 8.439
c 1
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 8 8 2
A dateTimeDate() 0 6 1
A verifyDates() 0 10 3
A adminDateFilter() 0 4 1
A setTitle() 0 6 1
A setSubtitle() 0 6 1
A setSummary() 0 6 1
A setContent() 0 6 1
A setImage() 0 6 1
A setInfoUrl() 0 6 1
B setNewsDate() 19 19 5
A setKeywords() 0 6 1
A title() 0 4 1
A subtitle() 0 4 1
A summary() 0 4 1
A infoUrl() 0 4 1
A newsDate() 0 4 1
A content() 0 4 1
A image() 0 4 1
A canonicalUrl() 0 4 1
A defaultMetaTitle() 0 4 1
A defaultMetaDescription() 14 14 3
A defaultMetaImage() 0 4 1
A keywords() 0 4 1
C parseAsMultiple() 29 29 8
A preSave() 0 8 1
A preUpdate() 0 8 1
A postSave() 0 7 1
A postUpdate() 0 7 1
A isActiveRoute() 0 7 2

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AbstractNews often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractNews, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Charcoal\Cms;
4
5
use DateTime;
6
use DateTimeInterface;
7
use InvalidArgumentException;
8
9
// From PSR-7
10
use Psr\Http\Message\RequestInterface;
11
use Psr\Http\Message\ResponseInterface;
12
13
// From 'charcoal-object'
14
use Charcoal\Object\Content;
15
use Charcoal\Object\CategorizableInterface;
16
use Charcoal\Object\CategorizableTrait;
17
use Charcoal\Object\PublishableInterface;
18
use Charcoal\Object\PublishableTrait;
19
use Charcoal\Object\RoutableInterface;
20
use Charcoal\Object\RoutableTrait;
21
22
// From 'charcoal-translator'
23
use Charcoal\Translator\Translation;
24
25
// From 'charcoal-cms'
26
use Charcoal\Cms\MetatagInterface;
27
use Charcoal\Cms\NewsInterface;
28
use Charcoal\Cms\SearchableInterface;
29
use Charcoal\Cms\SearchableTrait;
30
use Charcoal\Cms\TemplateableInterface;
31
32
// Local dependencies
33
use Charcoal\Cms\Support\Helpers\DateHelper;
34
35
// Pimple dependencies
36
use Pimple\Container;
37
38
/**
39
 * News
40
 */
41
abstract class AbstractNews extends Content implements
42
    CategorizableInterface,
43
    MetatagInterface,
44
    NewsInterface,
45
    PublishableInterface,
46
    RoutableInterface,
47
    SearchableInterface,
48
    TemplateableInterface
49
{
50
    use CategorizableTrait;
51
    use PublishableTrait;
52
    use MetatagTrait;
53
    use RoutableTrait;
54
    use SearchableTrait;
55
    use TemplateableTrait;
56
57
    /**
58
     * @var Translation|string|null
59
     */
60
    private $title;
61
62
    /**
63
     * @var Translation|string|null
64
     */
65
    private $subtitle;
66
67
    /**
68
     * @var Translation|string|null
69
     */
70
    private $summary;
71
72
    /**
73
     * @var Translation|string|null
74
     */
75
    private $content;
76
77
    /**
78
     * @var Translation|string|null
79
     */
80
    private $image;
81
82
    /**
83
     * @var DateTimeInterface|null
84
     */
85
    private $newsDate;
86
87
    /**
88
     * @var Translation|string|null
89
     */
90
    private $infoUrl;
91
92
    /**
93
     * @var array
94
     */
95
    protected $keywords;
96
97
    // ==========================================================================
98
    // INIT
99
    // ==========================================================================
100
101
    /**
102
     * Section constructor.
103
     * @param array $data The data.
104
     */
105 View Code Duplication
    public function __construct(array $data = null)
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...
106
    {
107
        parent::__construct($data);
108
109
        if (is_callable([ $this, 'defaultData' ])) {
110
            $this->setData($this->defaultData());
111
        }
112
    }
113
114
    // ==========================================================================
115
    // FUNCTIONS
116
    // ==========================================================================
117
118
    /**
119
     * In the datetime attribute of the <time> tag
120
     * @return string The datetime attribute formatted.
121
     */
122
    public function dateTimeDate()
123
    {
124
        $newsDate = $this->newsDate();
125
126
        return $newsDate->format('Y-m-d H:i:s');
127
    }
128
129
    /**
130
     * Some dates cannot be null
131
     * @return void
132
     */
133
    public function verifyDates()
134
    {
135
        if (!$this->newsDate()) {
136
            $this->setNewsDate('now');
137
        }
138
139
        if (!$this->publishDate()) {
140
            $this->setPublishDate('now');
141
        }
142
    }
143
144
    /**
145
     * @return string The date filtered for admin dual select input and others.
146
     */
147
    public function adminDateFilter()
148
    {
149
        return $this->newsDate()->format('Y-m-d');
150
    }
151
152
    // ==========================================================================
153
    // SETTERS
154
    // ==========================================================================
155
156
    /**
157
     * @param mixed $title The news title (localized).
158
     * @return self
159
     */
160
    public function setTitle($title)
161
    {
162
        $this->title = $this->translator()->translation($title);
163
164
        return $this;
165
    }
166
167
    /**
168
     * @param  mixed $subtitle The news subtitle (localized).
169
     * @return self
170
     */
171
    public function setSubtitle($subtitle)
172
    {
173
        $this->subtitle = $this->translator()->translation($subtitle);
174
175
        return $this;
176
    }
177
178
    /**
179
     * @param mixed $summary The news summary (localized).
180
     * @return self
181
     */
182
    public function setSummary($summary)
183
    {
184
        $this->summary = $this->translator()->translation($summary);
185
186
        return $this;
187
    }
188
189
    /**
190
     * @param mixed $content The news content (localized).
191
     * @return self
192
     */
193
    public function setContent($content)
194
    {
195
        $this->content = $this->translator()->translation($content);
196
197
        return $this;
198
    }
199
200
    /**
201
     * @param mixed $image The section main image (localized).
202
     * @return self
203
     */
204
    public function setImage($image)
205
    {
206
        $this->image = $this->translator()->translation($image);
207
208
        return $this;
209
    }
210
211
    /**
212
     * @param mixed $url The info URL (news source or where to find more information; localized).
213
     * @return self
214
     */
215
    public function setInfoUrl($url)
216
    {
217
        $this->infoUrl = $this->translator()->translation($url);
218
219
        return $this;
220
    }
221
222
    /**
223
     * @param  string|DateTimeInterface $newsDate The news date.
224
     * @throws InvalidArgumentException If the timestamp is invalid.
225
     * @return self
226
     */
227 View Code Duplication
    public function setNewsDate($newsDate)
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...
228
    {
229
        if ($newsDate === null || $newsDate === '') {
230
            $this->newsDate = null;
231
232
            return $this;
233
        }
234
        if (is_string($newsDate)) {
235
            $newsDate = new DateTime($newsDate);
236
        }
237
        if (!($newsDate instanceof DateTimeInterface)) {
238
            throw new InvalidArgumentException(
239
                'Invalid "Revision Date" value. Must be a date/time string or a DateTimeInterface object.'
240
            );
241
        }
242
        $this->newsDate = $newsDate;
243
244
        return $this;
245
    }
246
247
    /**
248
     * Set the object's keywords.
249
     *
250
     * @param  string|string[] $keywords One or more entries.
251
     * @return self
252
     */
253
    public function setKeywords($keywords)
254
    {
255
        $this->keywords = $this->parseAsMultiple($keywords);
256
257
        return $this;
258
    }
259
260
    // ==========================================================================
261
    // GETTERS
262
    // ==========================================================================
263
264
    /**
265
     * @return Translation|string|null
266
     */
267
    public function title()
268
    {
269
        return $this->title;
270
    }
271
272
    /**
273
     * @return Translation|string|null
274
     */
275
    public function subtitle()
276
    {
277
        return $this->subtitle;
278
    }
279
280
    /**
281
     * @return Translation|string|null
282
     */
283
    public function summary()
284
    {
285
        return $this->summary;
286
    }
287
288
    /**
289
     * @return Translation|string|null
290
     */
291
    public function infoUrl()
292
    {
293
        return $this->infoUrl;
294
    }
295
296
    /**
297
     * @return DateTimeInterface|null
298
     */
299
    public function newsDate()
300
    {
301
        return $this->newsDate;
302
    }
303
304
    /**
305
     * @return Translation|string|null
306
     */
307
    public function content()
308
    {
309
        return $this->content;
310
    }
311
312
    /**
313
     * @return Translation|string|null
314
     */
315
    public function image()
316
    {
317
        return $this->image;
318
    }
319
320
    // ==========================================================================
321
    // META TAGS
322
    // ==========================================================================
323
324
    /**
325
     * MetatagTrait > canonical_url
326
     *
327
     * @return string
328
     * @todo
329
     */
330
    public function canonicalUrl()
331
    {
332
        return '';
333
    }
334
335
    /**
336
     * @return Translation|string|null
337
     */
338
    public function defaultMetaTitle()
339
    {
340
        return $this->title();
341
    }
342
343
    /**
344
     * @return Translation|string|null
345
     */
346 View Code Duplication
    public function defaultMetaDescription()
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...
347
    {
348
        $content = $this->translator()->translation($this->content());
349
        if ($content instanceof Translation) {
350
            $desc = [];
351
            foreach ($content->data() as $lang => $text) {
352
                $desc[$lang] = strip_tags($text);
353
            }
354
355
            return $this->translator()->translation($desc);
356
        }
357
358
        return null;
359
    }
360
361
    /**
362
     * @return Translation|string|null
363
     */
364
    public function defaultMetaImage()
365
    {
366
        return $this->image();
367
    }
368
369
    /**
370
     * Retrieve the object's keywords.
371
     *
372
     * @return string[]
373
     */
374
    public function keywords()
375
    {
376
        return $this->keywords;
377
    }
378
379
    // ==========================================================================
380
    // Utils
381
    // ==========================================================================
382
383
    /**
384
     * Parse the property value as a "multiple" value type.
385
     *
386
     * @param  mixed                    $value     The value being converted to an array.
387
     * @param  string|PropertyInterface $separator The boundary string.
388
     * @return array
389
     */
390 View Code Duplication
    public function parseAsMultiple($value, $separator = ',')
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...
391
    {
392
        if (!isset($value) ||
393
            (is_string($value) && !strlen(trim($value))) ||
394
            (is_array($value) && !count(array_filter($value, 'strlen')))
395
        ) {
396
            return [];
397
        }
398
399
        /**
400
         * This property is marked as "multiple".
401
         * Manually handling the resolution to array
402
         * until the property itself manages this.
403
         */
404
        if (is_string($value)) {
405
            return explode($separator, $value);
406
        }
407
408
        /**
409
         * If the parameter isn't an array yet,
410
         * means we might be dealing with an integer,
411
         * an empty string, or an object.
412
         */
413
        if (!is_array($value)) {
414
            return [ $value ];
415
        }
416
417
        return $value;
418
    }
419
420
    // ==========================================================================
421
    // EVENTS
422
    // ==========================================================================
423
424
    /**
425
     * {@inheritdoc}
426
     *
427
     * @return boolean
428
     */
429
    public function preSave()
430
    {
431
        $this->verifyDates();
432
        $this->setSlug($this->generateSlug());
433
        $this->generateDefaultMetaTags();
434
435
        return parent::preSave();
436
    }
437
438
    /**
439
     * {@inheritdoc}
440
     *
441
     * @param array $properties Optional properties to update.
442
     * @return boolean
443
     */
444
    public function preUpdate(array $properties = null)
445
    {
446
        $this->verifyDates();
447
        $this->setSlug($this->generateSlug());
448
        $this->generateDefaultMetaTags();
449
450
        return parent::preUpdate($properties);
451
    }
452
453
    /**
454
     * @return boolean Parent postSave().
455
     */
456
    public function postSave()
457
    {
458
        // RoutableTrait
459
        $this->generateObjectRoute($this->slug());
460
461
        return parent::postSave();
462
    }
463
464
    /**
465
     * @param array|null $properties Properties.
466
     * @return boolean
467
     */
468
    public function postUpdate(array $properties = null)
469
    {
470
        // RoutableTrait
471
        $this->generateObjectRoute($this->slug());
472
473
        return parent::postUpdate($properties);
0 ignored issues
show
Bug introduced by
It seems like $properties defined by parameter $properties on line 468 can also be of type array; however, Charcoal\Source\StorableTrait::postUpdate() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
474
    }
475
476
    /**
477
     * GenericRoute checks if the route is active.
478
     * Default in RoutableTrait.
479
     *
480
     * @return boolean
481
     */
482
    public function isActiveRoute()
483
    {
484
        return (
485
            $this->active() &&
486
            $this->isPublished()
487
        );
488
    }
489
}
490