Passed
Push — 0.3 ( bea7df...6843d6 )
by Ben
12:45 queued 02:54
created

Page   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Test Coverage

Coverage 78.89%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 43
eloc 93
dl 0
loc 257
ccs 71
cts 90
cp 0.7889
rs 8.96
c 7
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A flatReference() 0 3 1
A managedModelKey() 0 7 2
A getAttribute() 0 9 2
A flatReferenceGroup() 0 6 2
A modules() 0 3 1
A flatReferenceLabel() 0 9 4
A findPublished() 0 3 1
A mediaUrl() 0 3 1
A menuLabel() 0 3 1
A scopeSortedByCreated() 0 3 1
A url() 0 12 3
A isPublished() 0 3 2
A mediaUrls() 0 12 2
A isDraft() 0 3 2
A publish() 0 6 1
A draft() 0 6 1
A baseUrlSegment() 0 19 4
A resolveUrl() 0 5 1
A statusAsPlainLabel() 0 15 4
A statusAsLabel() 0 15 4
A previewUrl() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Page 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.

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 Page, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Thinktomorrow\Chief\Pages;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Database\Eloquent\Model;
7
use Thinktomorrow\Chief\Management\ManagedModel;
8
use Thinktomorrow\Chief\Urls\MemoizedUrlRecord;
9
use Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl;
10
use Thinktomorrow\Chief\Urls\ProvidesUrl\ResolvingRoute;
11
use Thinktomorrow\Chief\Concerns\Viewable\Viewable;
12
use Thinktomorrow\Chief\Concerns\Viewable\ViewableContract;
13
use Thinktomorrow\Chief\Modules\Module;
14
use Thinktomorrow\Chief\Audit\AuditTrait;
15
use Spatie\MediaLibrary\HasMedia\HasMedia;
16
use Thinktomorrow\Chief\Concerns\Featurable;
17
use Thinktomorrow\Chief\Menu\ActsAsMenuItem;
18
use Illuminate\Database\Eloquent\SoftDeletes;
19
use Thinktomorrow\Chief\Relations\ActsAsChild;
20
use Thinktomorrow\Chief\Snippets\WithSnippets;
21
use Thinktomorrow\Chief\Relations\ActsAsParent;
22
use Thinktomorrow\Chief\Relations\ActingAsChild;
23
use Thinktomorrow\AssetLibrary\Traits\AssetTrait;
24
use Thinktomorrow\Chief\Relations\ActingAsParent;
25
use Thinktomorrow\Chief\Concerns\Morphable\Morphable;
26
use Thinktomorrow\Chief\FlatReferences\FlatReference;
27
use Thinktomorrow\Chief\Concerns\Archivable\Archivable;
28
use Dimsav\Translatable\Translatable as BaseTranslatable;
29
use Thinktomorrow\Chief\Concerns\Publishable\Publishable;
30
use Thinktomorrow\Chief\Concerns\Translatable\Translatable;
31
use Thinktomorrow\Chief\Concerns\Morphable\MorphableContract;
32
use Thinktomorrow\Chief\Concerns\Translatable\TranslatableContract;
33
use Thinktomorrow\Chief\Urls\UrlRecordNotFound;
34
35
class Page extends Model implements ManagedModel, TranslatableContract, HasMedia, ActsAsParent, ActsAsChild, ActsAsMenuItem, MorphableContract, ViewableContract, ProvidesUrl
36
{
37
    use BaseTranslatable {
0 ignored issues
show
introduced by
The trait Dimsav\Translatable\Translatable requires some properties which are not provided by Thinktomorrow\Chief\Pages\Page: $translations, $useTranslationFallback, $localeKey
Loading history...
38
        getAttribute as getTranslatableAttribute;
39
    }
40
41
    use Morphable,
0 ignored issues
show
introduced by
The trait Thinktomorrow\Chief\Concerns\Morphable\Morphable requires some properties which are not provided by Thinktomorrow\Chief\Pages\Page: $translations, $morph_key
Loading history...
Bug introduced by
The trait Thinktomorrow\Chief\Conc...anslatable\Translatable requires the property $translations which is not provided by Thinktomorrow\Chief\Pages\Page.
Loading history...
introduced by
The trait Thinktomorrow\Chief\Concerns\Viewable\Viewable requires some properties which are not provided by Thinktomorrow\Chief\Pages\Page: $content, $viewKey
Loading history...
introduced by
The trait Thinktomorrow\AssetLibrary\Traits\AssetTrait requires some properties which are not provided by Thinktomorrow\Chief\Pages\Page: $assets, $pivot, $each, $locale, $mediaConversionRegistrations, $media, $type, $collection_name
Loading history...
42
        AssetTrait,
43
        Translatable,
44
        SoftDeletes,
45
        Publishable,
46
        Featurable,
47
        Archivable,
48
        AuditTrait,
49
        ActingAsParent,
50
        ActingAsChild,
51
        WithSnippets,
52
        ResolvingRoute,
53
        Viewable;
54
55
    // Explicitly mention the translation model so on inheritance the child class uses the proper default translation model
56
    protected $translationModel      = PageTranslation::class;
57
    protected $translationForeignKey = 'page_id';
58
    protected $translatedAttributes  = [
59
        'title', 'content', 'short', 'seo_title', 'seo_description', 'seo_keywords', 'seo_image'
60
    ];
61
62
    public $table          = "pages";
63
    protected $guarded     = [];
64
    protected $with        = ['translations'];
65
66
    protected $baseViewPath;
67
    protected static $baseUrlSegment = '/';
68
69 183
    public function __construct(array $attributes = [])
70
    {
71 183
        $this->constructWithSnippets();
72
73 183
        if (!isset($this->baseViewPath)) {
74 183
            $this->baseViewPath = config('thinktomorrow.chief.base-view-paths.pages', 'pages');
75
        }
76
77 183
        parent::__construct($attributes);
78 183
    }
79
80 446
    public static function managedModelKey(): string
81
    {
82 446
        if (isset(static::$managedModelKey)) {
83 446
            return static::$managedModelKey;
0 ignored issues
show
Bug introduced by
The property managedModelKey does not seem to exist on Thinktomorrow\Chief\Pages\Page. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
84
        }
85
86
        throw new \Exception('Missing required static property \'managedModelKey\' on ' . static::class. '.');
87
    }
88
89
    /**
90
     * Parse and render any found snippets in custom
91
     * or translatable attribute values.
92
     *
93
     * @param string $value
94
     * @return mixed|null|string|string[]
95
     */
96 158
    public function getAttribute($value)
97
    {
98 158
        $value = $this->getTranslatableAttribute($value);
99
100 158
        if ($this->shouldParseWithSnippets($value)) {
101 1
            $value = $this->parseWithSnippets($value);
102
        }
103
104 158
        return $value;
105
    }
106
107
    /**
108
     * Page specific modules. We exclude text modules since they are modules in pure
109
     * technical terms and not so much as behavioural elements for the admin.
110
     *
111
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
112
     */
113 2
    public function modules()
114
    {
115 2
        return $this->hasMany(Module::class, 'page_id')->where('morph_key', '<>', 'text');
116
    }
117
118 9
    public function flatReference(): FlatReference
119
    {
120 9
        return new FlatReference(static::class, $this->id);
121
    }
122
123 74
    public function flatReferenceLabel(): string
124
    {
125 74
        if ($this->exists) {
126 57
            $status = ! $this->isPublished() ? ' [' . $this->statusAsPlainLabel().']' : null;
127
128 57
            return $this->title ? $this->title . $status : '';
0 ignored issues
show
Bug introduced by
The property title does not seem to exist on Thinktomorrow\Chief\Pages\Page. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
129
        }
130
131 22
        return '';
132
    }
133
134 4
    public function flatReferenceGroup(): string
135
    {
136 4
        $classKey = get_class($this);
137 4
        $labelSingular = property_exists($this, 'labelSingular') ? $this->labelSingular : str_singular($classKey);
0 ignored issues
show
Deprecated Code introduced by
The function str_singular() has been deprecated: Str::singular() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

137
        $labelSingular = property_exists($this, 'labelSingular') ? $this->labelSingular : /** @scrutinizer ignore-deprecated */ str_singular($classKey);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Bug introduced by
The property labelSingular does not seem to exist on Thinktomorrow\Chief\Pages\Page. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
138
139 4
        return $labelSingular;
140
    }
141
142
    public function mediaUrls($type = null): Collection
143
    {
144
        // TODO getallfiles should actually get all files...
145
        // What was the creator of the assetlibrary package thinking. It sure wasn't me... I promise...
146
        $assets = $this->getAllFiles($type, app()->getLocale())->map->getFileUrl();
0 ignored issues
show
introduced by
The method getLocale() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

146
        $assets = $this->getAllFiles($type, app()->/** @scrutinizer ignore-call */ getLocale())->map->getFileUrl();
Loading history...
147
148
        if($assets->first() == null)
149
        {
150
            $assets = $this->getAllFiles($type)->map->getFileUrl();
151
        }
152
153
        return $assets;
154
    }
155
156
    public function mediaUrl($type = null): ?string
157 1
    {
158
        return $this->mediaUrls($type)->first();
159 1
    }
160 1
161
    public static function findPublished($id)
162
    {
163 50
        return static::published()->find($id);
164
    }
165 50
166 46
    public function scopeSortedByCreated($query)
167
    {
168
        $query->orderBy('created_at', 'DESC');
169
    }
170 50
171
    /** @inheritdoc */
172 31
    public function url(string $locale = null): string
173 21
    {
174 21
        if (!$locale) {
175
            $locale = app()->getLocale();
176
        }
177
178 57
        try {
179
            $slug = MemoizedUrlRecord::findByModel($this, $locale)->slug;
180 57
181
            return $this->resolveUrl($locale, [$slug]);
182 57
        } catch (UrlRecordNotFound $e) {
183
            return '';
184
        }
185
    }
186 55
187
    public function resolveUrl(string $locale = null, $parameters = null): string
188 55
    {
189
        $routeName = config('thinktomorrow.chief.route.name');
190
191
        return $this->resolveRoute($routeName, $parameters, $locale);
0 ignored issues
show
Bug introduced by
It seems like $locale can also be of type string; however, parameter $locale of Thinktomorrow\Chief\Pages\Page::resolveRoute() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
        return $this->resolveRoute($routeName, $parameters, /** @scrutinizer ignore-type */ $locale);
Loading history...
192
    }
193 52
194
    /** @inheritdoc */
195 52
    public function previewUrl(string $locale = null): string
196
    {
197
        return $this->url($locale).'?preview-mode';
198
    }
199 52
200 44
201
    /** @inheritdoc */
202
    public static function baseUrlSegment(string $locale = null): string
203
    {
204 8
        if (!isset(static::$baseUrlSegment)) {
205
            return '/';
206 8
        }
207 8
208
        if (!is_array(static::$baseUrlSegment)) {
0 ignored issues
show
introduced by
The condition is_array(static::baseUrlSegment) is always false.
Loading history...
209
            return static::$baseUrlSegment;
210
        }
211
212
        // When an array, we try to locate the expected segment by locale
213
        $key = $locale ?? app()->getlocale();
214 7
215
        if (isset(static::$baseUrlSegment[$key])) {
216 7
            return static::$baseUrlSegment[$key];
217
        }
218
219
        // Fall back to first entry in case no match is found
220
        return reset(static::$baseUrlSegment);
221
    }
222
223
    public function menuLabel(): string
224
    {
225 65
        return $this->title ?? '';
0 ignored issues
show
Bug introduced by
The property title does not seem to exist on Thinktomorrow\Chief\Pages\Page. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
226
    }
227 65
228
    /**
229
     * We override the publishable trait defaults because Page needs
230 64
     * to be concerned with the archived state as well.
231
     *
232 64
     * TODO: IMPROVEMENT SHOULD BE TO MANAGE THE PAGE STATES IN ONE LOCATION. eg state machine
233
     */
234
    public function isPublished()
235 8
    {
236
        return (!!$this->published && is_null($this->archived_at));
237 8
    }
238 8
239
    public function isDraft()
240 8
    {
241 8
        return (!$this->published && is_null($this->archived_at));
242
    }
243 4
244
    public function publish()
245 4
    {
246 4
        $this->published = 1;
247
        $this->archived_at = null;
248 4
249 4
        $this->save();
250
    }
251
252
    public function draft()
253
    {
254
        $this->published = 0;
255
        $this->archived_at = null;
256
257
        $this->save();
258
    }
259
260
    public function statusAsLabel()
261
    {
262
        if ($this->isPublished()) {
263
            return '<a href="'.$this->url().'" target="_blank"><em>online</em></a>';
264
        }
265
266
        if ($this->isDraft()) {
267
            return '<a href="'.$this->previewUrl().'" target="_blank" class="text-error"><em>offline</em></a>';
268 56
        }
269
270 56
        if ($this->isArchived()) {
271
            return '<span><em>gearchiveerd</em></span>';
272
        }
273
274 56
        return '-';
275 54
    }
276
277
    public function statusAsPlainLabel()
278 4
    {
279 4
        if ($this->isPublished()) {
280
            return 'online';
281
        }
282
283
        if ($this->isDraft()) {
284
            return 'offline';
285
        }
286
287
        if ($this->isArchived()) {
288
            return 'gearchiveerd';
289
        }
290
291
        return '-';
292
    }
293
}
294