Passed
Push — master ( 00089d...cdf2c0 )
by Philippe
08:10 queued 12s
created

Page::findPublished()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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

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

215
        return $this->resolveRoute($routeName, $parameters, /** @scrutinizer ignore-type */ $locale);
Loading history...
216 63
    }
217
218 63
    /** @inheritdoc */
219
    public function baseUrlSegment(string $locale = null): string
220
    {
221
        if (!isset(static::$baseUrlSegment)) {
222 63
            return '/';
223 54
        }
224
225
        if (!is_array(static::$baseUrlSegment)) {
0 ignored issues
show
introduced by
The condition is_array(static::baseUrlSegment) is always false.
Loading history...
226
            return static::$baseUrlSegment;
227 9
        }
228
229 9
        // When an array, we try to locate the expected segment by locale
230 9
        $key = $locale ?? app()->getlocale();
231
232
        if (isset(static::$baseUrlSegment[$key])) {
233 1
            return static::$baseUrlSegment[$key];
234 1
        }
235 1
236
        $fallback_locale = config('app.fallback_locale');
237
        if (isset(static::$baseUrlSegment[$fallback_locale])) {
238
            return static::$baseUrlSegment[$fallback_locale];
239
        }
240
241
        // Fall back to first entry in case no match is found
242 7
        return reset(static::$baseUrlSegment);
243
    }
244 7
245
    public function menuLabel(): string
246
    {
247
        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...
248
    }
249
250
    public function statusAsLabel()
251
    {
252
        if ($this->isPublished()) {
253 79
            return '<a href="' . $this->url() . '" target="_blank"><em>online</em></a>';
254
        }
255 79
256
        if ($this->isDraft()) {
257
            return '<a href="' . $this->url() . '" target="_blank" class="text-error"><em>offline</em></a>';
258 78
        }
259
260 78
        if ($this->isArchived()) {
261
            return '<span><em>gearchiveerd</em></span>';
262
        }
263 8
264
        return '-';
265 8
    }
266 8
267
    public function statusAsPlainLabel()
268 8
    {
269 8
        if ($this->isPublished()) {
270
            return 'online';
271 4
        }
272
273 4
        if ($this->isDraft()) {
274 4
            return 'offline';
275
        }
276 4
277 4
        if ($this->isArchived()) {
278
            return 'gearchiveerd';
279
        }
280
281
        return '-';
282
    }
283
284
    public function stateOf($key): string
285
    {
286
        return $this->$key ?? PageState::DRAFT;
287
    }
288
289
    public function changeStateOf($key, $state)
290
    {
291
        // Ignore change to current state - it should not trigger events either
292
        if ($state === $this->stateOf($key)) {
293
            return;
294
        }
295
296 70
        PageState::assertNewState($this, $key, $state);
297
298 70
        $this->$key = $state;
299
    }
300
}
301