Passed
Push — master ( f2fe9c...c37bd3 )
by Philippe
02:30 queued 12s
created

Page::baseUrlSegment()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.1158

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 24
ccs 10
cts 12
cp 0.8333
rs 9.6111
cc 5
nc 5
nop 1
crap 5.1158
1
<?php
2
3
namespace Thinktomorrow\Chief\Pages;
4
5
use Illuminate\Support\Str;
6
use Illuminate\Support\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Thinktomorrow\Chief\Modules\Module;
9
use Thinktomorrow\Chief\Audit\AuditTrait;
10
use Thinktomorrow\Chief\Concerns\Featurable;
11
use Thinktomorrow\Chief\Menu\ActsAsMenuItem;
12
use Illuminate\Database\Eloquent\SoftDeletes;
13
use Thinktomorrow\Chief\Relations\ActsAsChild;
14
use Thinktomorrow\Chief\Snippets\WithSnippets;
15
use Thinktomorrow\Chief\Relations\ActsAsParent;
16
use Thinktomorrow\Chief\Urls\MemoizedUrlRecord;
17
use Thinktomorrow\Chief\Urls\UrlRecordNotFound;
18
use Thinktomorrow\Chief\Management\ManagedModel;
19
use Thinktomorrow\Chief\Relations\ActingAsChild;
20
use Thinktomorrow\AssetLibrary\AssetTrait;
21
use Thinktomorrow\Chief\Relations\ActingAsParent;
22
use Thinktomorrow\Chief\Concerns\Viewable\Viewable;
23
use Thinktomorrow\Chief\Concerns\Morphable\Morphable;
24
use Thinktomorrow\Chief\FlatReferences\FlatReference;
25
use Thinktomorrow\Chief\Urls\ProvidesUrl\ProvidesUrl;
26
use Thinktomorrow\Chief\Concerns\Archivable\Archivable;
27
use Thinktomorrow\Chief\Urls\ProvidesUrl\ResolvingRoute;
28
use Thinktomorrow\Chief\Concerns\Publishable\Publishable;
29
use Thinktomorrow\Chief\Concerns\Translatable\Translatable;
30
use Thinktomorrow\Chief\Concerns\Viewable\ViewableContract;
31
use Astrotomic\Translatable\Translatable as BaseTranslatable;
32
use Thinktomorrow\AssetLibrary\HasAsset;
33
use Thinktomorrow\Chief\Concerns\Morphable\MorphableContract;
34
use Thinktomorrow\Chief\Concerns\Translatable\TranslatableContract;
35
36
class Page extends Model implements ManagedModel, TranslatableContract, HasAsset, ActsAsParent, ActsAsChild, ActsAsMenuItem, MorphableContract, ViewableContract, ProvidesUrl
37
{
38
    use BaseTranslatable {
0 ignored issues
show
Bug introduced by
The trait Astrotomic\Translatable\Translatable requires the property $each which is not provided by Thinktomorrow\Chief\Pages\Page.
Loading history...
39
        getAttribute as getTranslatableAttribute;
40
    }
41
42
    use Morphable,
0 ignored issues
show
Bug introduced by
The trait Thinktomorrow\Chief\Concerns\Morphable\Morphable requires the property $morph_key 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\AssetTrait requires some properties which are not provided by Thinktomorrow\Chief\Pages\Page: $assetRelation, $pivot, $fallbackPath, $assetFallbackLocale, $each, $locale, $mediaConversionRegistrations, $useAssetFallbackLocale, $fallbackUrl, $media, $collection_name
Loading history...
43
        AssetTrait,
44
        Translatable,
45
        SoftDeletes,
46
        Publishable,
47
        Featurable,
48
        Archivable,
49
        AuditTrait,
50
        ActingAsParent,
51
        ActingAsChild,
52
        WithSnippets,
53
        ResolvingRoute,
54
        Viewable;
55
56
    // Explicitly mention the translation model so on inheritance the child class uses the proper default translation model
57
    protected $translationModel      = PageTranslation::class;
58
    protected $translationForeignKey = 'page_id';
59
    protected $translatedAttributes  = [
60
        'title', 'content', 'short', 'seo_title', 'seo_description', 'seo_keywords', 'seo_image'
61
    ];
62
63
    public $table          = "pages";
64
    protected $guarded     = [];
65
    protected $with        = ['translations'];
66
67
    protected $baseViewPath;
68
    protected static $baseUrlSegment = '/';
69
70
    protected static $cachedUrls = [];
71
72 488
    public static function clearCachedUrls()
73
    {
74 488
        static::$cachedUrls = null;
75 488
    }
76
77 220
    public function __construct(array $attributes = [])
78
    {
79 220
        $this->constructWithSnippets();
80
81 220
        if (!isset($this->baseViewPath)) {
82 220
            $this->baseViewPath = config('thinktomorrow.chief.base-view-paths.pages', 'pages');
83
        }
84
85 220
        parent::__construct($attributes);
86 220
    }
87
88 488
    public static function managedModelKey(): string
89
    {
90 488
        if (isset(static::$managedModelKey)) {
91 488
            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...
92
        }
93
94
        throw new \Exception('Missing required static property \'managedModelKey\' on ' . static::class. '.');
95
    }
96
97
    /**
98
     * Parse and render any found snippets in custom
99
     * or translatable attribute values.
100
     *
101
     * @param string $value
102
     * @return mixed|null|string|string[]
103
     */
104 194
    public function getAttribute($value)
105
    {
106 194
        $value = $this->getTranslatableAttribute($value);
107
108 194
        if ($this->shouldParseWithSnippets($value)) {
109 1
            $value = $this->parseWithSnippets($value);
110
        }
111
112 194
        return $value;
113
    }
114
115
    /**
116
     * Page specific modules. We exclude text modules since they are modules in pure
117
     * technical terms and not so much as behavioural elements for the admin.
118
     *
119
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
120
     */
121 2
    public function modules()
122
    {
123 2
        return $this->hasMany(Module::class, 'page_id')->where('morph_key', '<>', 'text');
124
    }
125
126 14
    public function flatReference(): FlatReference
127
    {
128 14
        return new FlatReference(static::class, $this->id);
129
    }
130
131 91
    public function flatReferenceLabel(): string
132
    {
133 91
        if ($this->exists) {
134 71
            $status = ! $this->isPublished() ? ' [' . $this->statusAsPlainLabel().']' : null;
135
136 71
            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...
137
        }
138
139 26
        return '';
140
    }
141
142 2
    public function flatReferenceGroup(): string
143
    {
144 2
        $classKey = get_class($this);
145 2
        if (property_exists($this, 'labelSingular')) {
146 1
            $labelSingular =  $this->labelSingular;
0 ignored issues
show
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...
147
        } else {
148 1
            $labelSingular = Str::singular($classKey);
149
        }
150
151 2
        return $labelSingular;
152
    }
153
154
    public function mediaUrls($type = null): Collection
155
    {
156
        $assets = $this->assets($type, app()->getLocale())->map->url();
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

156
        $assets = $this->assets($type, app()->/** @scrutinizer ignore-call */ getLocale())->map->url();
Loading history...
157
158
        if ($assets->first() == null) {
159
            $assets = $this->assets($type)->map->url();
160
        }
161
162
        return $assets;
163
    }
164
165
    public function mediaUrl($type = null): ?string
166
    {
167
        return $this->mediaUrls($type)->first();
168
    }
169
170
    public static function findPublished($id)
171
    {
172
        return static::published()->find($id);
173
    }
174
175 1
    public function scopeSortedByCreated($query)
176
    {
177 1
        $query->orderBy('created_at', 'DESC');
178 1
    }
179
180
    /** @inheritdoc */
181 65
    public function url(string $locale = null): string
182
    {
183 65
        if (!$locale) {
184 61
            $locale = app()->getLocale();
185
        }
186
        try {
187 65
            $memoizedKey = $this->getMorphClass().'-'.$this->id.'-'.$locale;
188
189 65
            if (isset(static::$cachedUrls[$memoizedKey])) {
190 36
                return static::$cachedUrls[$memoizedKey];
191
            }
192
193 65
            $slug = MemoizedUrlRecord::findByModel($this, $locale)->slug;
194
195 42
            return static::$cachedUrls[$memoizedKey] = $this->resolveUrl($locale, [$slug]);
196 26
        } catch (UrlRecordNotFound $e) {
197 26
            return '';
198
        }
199
    }
200
201 68
    public function resolveUrl(string $locale = null, $parameters = null): string
202
    {
203 68
        $routeName = config('thinktomorrow.chief.route.name');
204
205 68
        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

205
        return $this->resolveRoute($routeName, $parameters, /** @scrutinizer ignore-type */ $locale);
Loading history...
206
    }
207
208
    /** @inheritdoc */
209 69
    public function previewUrl(string $locale = null): string
210
    {
211 69
        return $this->url($locale).'?preview-mode';
212
    }
213
214
215
    /** @inheritdoc */
216 63
    public static function baseUrlSegment(string $locale = null): string
217
    {
218 63
        if (!isset(static::$baseUrlSegment)) {
219
            return '/';
220
        }
221
222 63
        if (!is_array(static::$baseUrlSegment)) {
0 ignored issues
show
introduced by
The condition is_array(static::baseUrlSegment) is always false.
Loading history...
223 54
            return static::$baseUrlSegment;
224
        }
225
226
        // When an array, we try to locate the expected segment by locale
227 9
        $key = $locale ?? app()->getlocale();
228
229 9
        if (isset(static::$baseUrlSegment[$key])) {
230 9
            return static::$baseUrlSegment[$key];
231
        }
232
233 1
        $fallback_locale = config('app.fallback_locale');
234 1
        if (isset(static::$baseUrlSegment[$fallback_locale])) {
235 1
            return static::$baseUrlSegment[$fallback_locale];
236
        }
237
238
        // Fall back to first entry in case no match is found
239
        return reset(static::$baseUrlSegment);
240
    }
241
242 7
    public function menuLabel(): string
243
    {
244 7
        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...
245
    }
246
247
    /**
248
     * We override the publishable trait defaults because Page needs
249
     * to be concerned with the archived state as well.
250
     *
251
     * TODO: IMPROVEMENT SHOULD BE TO MANAGE THE PAGE STATES IN ONE LOCATION. eg state machine
252
     */
253 79
    public function isPublished()
254
    {
255 79
        return (!!$this->published && is_null($this->archived_at));
256
    }
257
258 78
    public function isDraft()
259
    {
260 78
        return (!$this->published && is_null($this->archived_at));
261
    }
262
263 8
    public function publish()
264
    {
265 8
        $this->published = 1;
266 8
        $this->archived_at = null;
267
268 8
        $this->save();
269 8
    }
270
271 4
    public function draft()
272
    {
273 4
        $this->published = 0;
274 4
        $this->archived_at = null;
275
276 4
        $this->save();
277 4
    }
278
279
    public function statusAsLabel()
280
    {
281
        if ($this->isPublished()) {
282
            return '<a href="'.$this->url().'" target="_blank"><em>online</em></a>';
283
        }
284
285
        if ($this->isDraft()) {
286
            return '<a href="'.$this->previewUrl().'" target="_blank" class="text-error"><em>offline</em></a>';
287
        }
288
289
        if ($this->isArchived()) {
290
            return '<span><em>gearchiveerd</em></span>';
291
        }
292
293
        return '-';
294
    }
295
296 70
    public function statusAsPlainLabel()
297
    {
298 70
        if ($this->isPublished()) {
299
            return 'online';
300
        }
301
302 70
        if ($this->isDraft()) {
303 68
            return 'offline';
304
        }
305
306 4
        if ($this->isArchived()) {
307 4
            return 'gearchiveerd';
308
        }
309
310
        return '-';
311
    }
312
}
313