Completed
Push — master ( 62a47c...f8e5cb )
by
unknown
03:19
created

ContentfulModel::defaultCasts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
ccs 0
cts 6
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Distilleries\Contentful\Models\Base;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Database\Eloquent\Model;
7
use Distilleries\Contentful\Models\Asset;
8
use Distilleries\Contentful\Models\Traits\Localable;
9
use Distilleries\Contentful\Models\EntryRelationship;
10
11
abstract class ContentfulModel extends Model
12
{
13
    use Localable;
14
15
    /**
16
     * {@inheritdoc}
17
     */
18
    protected $primaryKey = 'contentful_id';
19
20
    /**
21
     * The content-type ID.
22
     *
23
     * @var string
24
     */
25
    protected $contentType = null;
26
27
    /**
28
     * {@inheritdoc}
29
     */
30
    protected $keyType = 'string';
31
32
    /**
33
     * {@inheritdoc}
34
     */
35
    public $incrementing = false;
36
37
    /**
38
     * ContentfulModel constructor.
39
     *
40
     * @param  array  $attributes
41
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
42
     */
43
    public function __construct(array $attributes = [])
44
    {
45
        // Override fillable
46
        foreach ($this->defaultFillable() as $defaultFillable) {
47
            if (! in_array($defaultFillable, $this->fillable)) {
48
                $this->fillable[] = $defaultFillable;
49
            }
50
        }
51
52
        // Override casts
53
        foreach ($this->defaultCasts() as $field => $type) {
54
            if (! isset($this->casts[$field])) {
55
                $this->casts[$field] = $type;
56
            }
57
        }
58
59
        $this->initContentType();
60
61
        parent::__construct($attributes);
62
    }
63
64
    /**
65
     * Init model content-type if needed.
66
     *
67
     * @return void
68
     */
69
    protected function initContentType()
70
    {
71
        if (empty($this->contentType)) {
72
            $this->contentType = lcfirst(class_basename(get_class($this)));
73
        }
74
    }
75
76
    /**
77
     * Return default fillable fields.
78
     *
79
     * @return array
80
     */
81
    public function defaultFillable(): array
82
    {
83
        return [
84
            'contentful_id',
85
            'country',
86
            'locale',
87
            'payload',
88
            'created_at',
89
            'updated_at',
90
        ];
91
    }
92
93
    /**
94
     * Return default casted fields.
95
     *
96
     * @return array
97
     */
98
    public function defaultCasts(): array
99
    {
100
        return [
101
            'payload' => 'array',
102
        ];
103
    }
104
105
    // --------------------------------------------------------------------------------
106
    // --------------------------------------------------------------------------------
107
    // --------------------------------------------------------------------------------
108
109
    /**
110
     * Return Contentful Asset for given link (sys or ID).
111
     *
112
     * @param  array|string|null  $link
113
     * @return \Distilleries\Contentful\Models\Asset|null
114
     */
115
    protected function contentfulAsset($link): ?Asset
116
    {
117
        $assetId = $this->contentfulLinkId($link);
118
119
        if (empty($assetId)) {
120
            return null;
121
        }
122
123
        $asset = (new Asset)->query()
124
            ->where('contentful_id', '=', $assetId)
125
            ->where('locale', '=', $this->locale)
126
            ->where('country', '=', $this->country)
127
            ->first();
128
129
        return ! empty($asset) ? $asset : null;
130
    }
131
132
    /**
133
     * Return payload of related Contentful entries.
134
     *
135
     * @param  string  $payload
136
     * @param  array  $links
137
     * @param  mixed  $query
138
     * @return \Illuminate\Support\Collection
139
     */
140
    protected function getAndSetPayloadContentfulEntries(string $payload, array $links, $query = null): Collection
141
    {
142
        if (empty($this->attributes[$payload])) {
143
            return collect();
144
        }
145
146
        if (! is_object($this->attributes[$payload])) {
147
            $this->attributes[$payload] = $this->contentfulEntries($links, $query);
148
        }
149
150
        return $this->attributes[$payload];
151
    }
152
153
    /**
154
     * Return payload of related Contentful entry.
155
     *
156
     * @param  string  $payload
157
     * @param  array  $links
158
     * @param  mixed  $query
159
     * @return \Distilleries\Contentful\Models\Base\ContentfulModel|null
160
     */
161
    protected function getAndSetPayloadContentfulEntry(string $payload, array $links, $query = null): ?ContentfulModel
162
    {
163
        if (empty($this->attributes[$payload])) {
164
            return null;
165
        }
166
167
        if (! is_object($this->attributes[$payload])) {
168
            $this->attributes[$payload] = $this->contentfulEntry($links, $query);
169
        }
170
171
        return $this->attributes[$payload];
172
    }
173
174
    /**
175
     * Return Contentful Entry for given link (sys or ID).
176
     *
177
     * @param  array|string|null  $link
178
     * @param  mixed  $query
179
     * @return \Distilleries\Contentful\Models\Base\ContentfulModel|null
180
     */
181
    protected function contentfulEntry($link, $query = null): ?ContentfulModel
182
    {
183
        $entryId = $this->contentfulLinkId($link);
184
185
        if (empty($entryId)) {
186
            return null;
187
        }
188
189
        $entries = $this->contentfulEntries([$entryId], $query);
190
191
        return $entries->isNotEmpty() ? $entries->first(): null;
192
    }
193
194
    /**
195
     * Return Contentful Entries for given ID.
196
     *
197
     * @param  array  $links
198
     * @param  mixed  $query
199
     * @return \Illuminate\Support\Collection
200
     */
201
    protected function contentfulEntries(array $links, $query = null): Collection
202
    {
203
        $entries = [];
204
205
        $entryIds = [];
206
        foreach ($links as $link) {
207
            $entryId = $this->contentfulLinkId($link);
208
            if (! empty($entryId)) {
209
                $entryIds[] = $entryId;
210
            }
211
        }
212
213
        if (! empty($entryIds)) {
214
            $relationships = EntryRelationship::query()
0 ignored issues
show
Bug introduced by
The method select() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean createSelectWithConstraint()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
215
                ->select('related_contentful_id', 'related_contentful_type', 'order')
216
                ->distinct()
217
                ->locale($this->locale, $this->country)
218
                ->where('source_contentful_id', '=', $this->contentful_id)
219
                ->whereIn('related_contentful_id', $entryIds)
220
                ->orderBy('order', 'asc')
221
                ->get();
222
223
            foreach ($relationships as $relationship) {
224
                if ($relationship->related_contentful_type === 'asset') {
225
                    $model = new Asset;
226
                } else {
227
                    $modelClass = config('contentful.namespace.model') . '\\' . studly_case($relationship->related_contentful_type);
228
                    $model = new $modelClass;
229
                }
230
231
                $instance = $model->query()
232
                    ->where('country', '=', $this->country)
233
                    ->where('locale', '=', $this->locale)
234
                    ->where('contentful_id', '=', $relationship->related_contentful_id);
235
236
                if (! empty($query)) {
237
                    $instance = call_user_func($query, $instance);
238
                }
239
240
                $instance = $instance->first();
241
242
                if (! empty($instance)) {
243
                    $entries[] = $instance;
244
                }
245
            }
246
        }
247
248
        return collect($entries);
249
    }
250
251
    /**
252
     * Return a collection of related models for base Contentful ID.
253
     *
254
     * @param  string  $contentfulId
255
     * @param  string  $contentfulType
256
     * @return \Illuminate\Support\Collection
257
     */
258
    protected function contentfulRelatedEntries(string $contentfulId, string $contentfulType = ''): Collection
259
    {
260
        $entries = [];
261
262
        $query = EntryRelationship::query()
0 ignored issues
show
Bug introduced by
The method select() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean createSelectWithConstraint()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
263
            ->select('source_contentful_id', 'source_contentful_type', '')
264
            ->locale($this->locale, $this->country)
265
            ->where('related_contentful_id', '=', $contentfulId);
266
267
        if (! empty($contentfulType)) {
268
            $query = $query->where('source_contentful_type', '=', $contentfulType);
269
        }
270
271
        $relationships = $query->orderBy('order', 'asc')->get();
272
        foreach ($relationships as $relationship) {
273
            if ($relationship->source_contentful_type === 'asset') {
274
                $model = new Asset;
275
            } else {
276
                $modelClass = rtrim(config('contentful.namespace.model'), '\\') . '\\' . studly_case($relationship->source_contentful_type);
277
                $model = new $modelClass;
278
            }
279
280
            $instance = $model->query()
281
                ->locale($this->locale, $this->country)
282
                ->where('contentful_id', '=', $relationship->source_contentful_id)
283
                ->first();
284
285
            if (! empty($instance)) {
286
                $entries[] = $instance;
287
            }
288
        }
289
290
        return collect($entries);
291
    }
292
293
    /**
294
     * Return Contentful link ID.
295
     *
296
     * @param  mixed  $link
297
     * @return string|null
298
     */
299
    protected function contentfulLinkId($link): ?string
300
    {
301
        if (empty($link)) {
302
            return null;
303
        }
304
305
        if (is_string($link)) {
306
            return $link;
307
        }
308
309
        if (is_array($link) and isset($link['sys']) and isset($link['sys']['id'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
310
            return $link['sys']['id'];
311
        }
312
313
        return null;
314
    }
315
316
    /**
317
     * Return model Contentful content-type.
318
     *
319
     * @return string
320
     */
321
    public function getContentType(): string
322
    {
323
        return $this->contentType;
324
    }
325
326
    /**
327
     * Return ID attribute.
328
     *
329
     * @return mixed
330
     */
331
    public function getIdAttribute()
332
    {
333
        return $this->getKey();
334
    }
335
336
    /**
337
     * Magical extended toArray().
338
     *
339
     * @return array
340
     */
341
    public function toArray()
342
    {
343
        $array = parent::toArray();
344
345
        foreach ($this->getMutatedAttributes() as $key) {
346
            if (! array_key_exists($key, $array)) {
347
                $array[$key] = $this->{$key};
348
            }
349
        }
350
351
        return $array;
352
    }
353
}
354