Test Failed
Push — ft/states ( 7e5f34...a5a3f4 )
by Ben
06:21
created

Page::changeState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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

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

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