Completed
Push — master ( 6ab7c2...784de3 )
by Maxime
05:14
created

ContentfulModel   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 338
ccs 0
cts 209
cp 0
rs 8.8
c 0
b 0
f 0
wmc 45
lcom 2
cbo 6

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 24 5
A initContentType() 0 7 2
A defaultFillable() 0 11 1
A defaultCasts() 0 6 1
A contentfulAsset() 0 17 3
A getAndSetPayloadContentfulEntries() 0 14 3
A getAndSetPayloadContentfulEntry() 0 14 3
A contentfulEntry() 0 13 3
B contentfulEntries() 0 58 8
B contentfulRelatedEntries() 0 39 5
A contentfulLinkId() 0 19 6
A getContentType() 0 4 1
A getIdAttribute() 0 4 1
A toArray() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like ContentfulModel 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

1
<?php
2
3
namespace Distilleries\Contentful\Models\Base;
4
5
use Distilleries\Contentful\Models\EntryRelationship;
6
use Distilleries\Contentful\Models\Traits\Localable;
7
use Illuminate\Support\Collection;
8
use Illuminate\Database\Eloquent\Model;
9
use Distilleries\Contentful\Models\Asset;
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
     * @var string
23
     */
24
    protected $contentType = null;
25
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
        {
48
            if (!in_array($defaultFillable, $this->fillable))
49
            {
50
                $this->fillable[] = $defaultFillable;
51
            }
52
        }
53
54
        // Override casts
55
        foreach ($this->defaultCasts() as $field => $type)
56
        {
57
            if (!isset($this->casts[$field]))
58
            {
59
                $this->casts[$field] = $type;
60
            }
61
        }
62
63
        $this->initContentType();
64
65
        parent::__construct($attributes);
66
    }
67
68
    protected function initContentType()
69
    {
70
        if (empty($this->contentType))
71
        {
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
    /**
111
     * Return Contentful Asset for given link (sys or ID).
112
     *
113
     * @param  array|string|null $link
114
     * @return \Distilleries\Contentful\Models\Asset|null
115
     */
116
    protected function contentfulAsset($link): ?Asset
117
    {
118
        $assetId = $this->contentfulLinkId($link);
119
120
        if (empty($assetId))
121
        {
122
            return null;
123
        }
124
125
        $asset = (new Asset)->query()
126
            ->where('contentful_id', '=', $assetId)
127
            ->where('locale', '=', $this->locale)
128
            ->where('country', '=', $this->country)
129
            ->first();
130
131
        return !empty($asset) ? $asset : null;
132
    }
133
134
    protected function getAndSetPayloadContentfulEntries(string $payload, array $links, $query = null): Collection
135
    {
136
        if (empty($this->payload[$payload]))
137
        {
138
            return collect();
139
        }
140
141
        if (!is_object($this->payload[$payload]))
142
        {
143
            $this->payload[$payload] = $this->contentfulEntries($links, $query);
144
        }
145
146
        return $this->payload[$payload];
147
    }
148
149
    protected function getAndSetPayloadContentfulEntry(string $payload, array $links, $query = null): ?ContentfulModel
150
    {
151
        if (empty($this->payload[$payload]))
152
        {
153
            return null;
154
        }
155
156
        if (!is_object($this->payload[$payload]))
157
        {
158
            $this->payload[$payload] = $this->contentfulEntry($links, $query);
159
        }
160
161
        return $this->payload[$payload];
162
    }
163
164
    /**
165
     * Return Contentful Entry for given link (sys or ID).
166
     *
167
     * @param  array|string|null $link
168
     * @param  callback|null $query
169
     * @return \Distilleries\Contentful\Models\Base\ContentfulModel|null
170
     */
171
    protected function contentfulEntry($link, $query = null): ?ContentfulModel
172
    {
173
        $entryId = $this->contentfulLinkId($link);
174
175
        if (empty($entryId))
176
        {
177
            return null;
178
        }
179
180
        $entries = $this->contentfulEntries([$entryId], $query);
181
182
        return $entries->isNotEmpty() ? $entries->first() : null;
183
    }
184
185
    /**
186
     * Return Contentful Entries for given ID.
187
     *
188
     * @param  array $links
189
     * @param  callback|null $query
190
     * @return \Illuminate\Support\Collection
191
     */
192
    protected function contentfulEntries(array $links, $query = null): Collection
193
    {
194
        $entries = [];
195
196
        $entryIds = [];
197
        foreach ($links as $link)
198
        {
199
            $entryId = $this->contentfulLinkId($link);
200
            if (!empty($entryId))
201
            {
202
                $entryIds[] = $entryId;
203
            }
204
        }
205
206
        if (!empty($entryIds))
207
        {
208
            $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...
209
                ->select('related_contentful_id', 'related_contentful_type', 'order')
210
                ->distinct()
211
                ->locale($this->locale, $this->country)
212
                ->where('source_contentful_id', '=', $this->contentful_id)
213
                ->whereIn('related_contentful_id', $entryIds)
214
                ->orderBy('order', 'asc')
215
                ->get();
216
217
            foreach ($relationships as $relationship)
218
            {
219
                if ($relationship->related_contentful_type === 'asset')
220
                {
221
                    $model = new Asset;
222
                } else
223
                {
224
                    $modelClass = config('contentful.namespace.model') . '\\' . studly_case($relationship->related_contentful_type);
225
                    $model = new $modelClass;
226
                }
227
228
                $instance = $model->query()
229
                    ->where('country', '=', $this->country)
230
                    ->where('locale', '=', $this->locale)
231
                    ->where('contentful_id', '=', $relationship->related_contentful_id);
232
233
234
                if (!empty($query))
235
                {
236
                    $instance = call_user_func($query, $instance);
237
                }
238
239
                $instance = $instance->first();
240
241
                if (!empty($instance))
242
                {
243
                    $entries[] = $instance;
244
                }
245
            }
246
        }
247
248
        return collect($entries);
249
    }
250
251
252
    /**
253
     * Return a collection of related models for base Contentful ID.
254
     *
255
     * @param  string $contentfulId
256
     * @param  string $contentfulType
257
     * @return \Illuminate\Support\Collection
258
     */
259
    protected function contentfulRelatedEntries(string $contentfulId, string $contentfulType = ''): Collection
260
    {
261
        $entries = [];
262
263
        $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...
264
            ->select('source_contentful_id', 'source_contentful_type', '')
265
            ->locale($this->locale, $this->country)
266
            ->where('related_contentful_id', '=', $contentfulId);
267
268
        if (!empty($contentfulType))
269
        {
270
            $query = $query->where('source_contentful_type', '=', $contentfulType);
271
        }
272
273
        $relationships = $query->orderBy('order', 'asc')->get();
274
        foreach ($relationships as $relationship)
275
        {
276
            if ($relationship->source_contentful_type === 'asset')
277
            {
278
                $model = new Asset;
279
            } else
280
            {
281
                $modelClass = rtrim(config('contentful.namespace.model'), '\\') . '\\' . studly_case($relationship->source_contentful_type);
282
                $model = new $modelClass;
283
            }
284
285
            $instance = $model->query()
286
                ->locale($this->locale, $this->country)
287
                ->where('contentful_id', '=', $relationship->source_contentful_id)
288
                ->first();
289
290
            if (!empty($instance))
291
            {
292
                $entries[] = $instance;
293
            }
294
        }
295
296
        return collect($entries);
297
    }
298
299
    /**
300
     * Return Contentful link ID.
301
     *
302
     * @param  mixed $link
303
     * @return string|null
304
     */
305
    protected function contentfulLinkId($link): ?string
306
    {
307
        if (empty($link))
308
        {
309
            return null;
310
        }
311
312
        if (is_string($link))
313
        {
314
            return $link;
315
        }
316
317
        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...
318
        {
319
            return $link['sys']['id'];
320
        }
321
322
        return null;
323
    }
324
325
    public function getContentType(): string
326
    {
327
        return $this->contentType;
328
    }
329
330
    public function getIdAttribute()
331
    {
332
        return $this->getKey();
333
    }
334
335
    public function toArray()
336
    {
337
        $array = parent::toArray();
338
        foreach ($this->getMutatedAttributes() as $key)
339
        {
340
            if (!array_key_exists($key, $array))
341
            {
342
                $array[$key] = $this->{$key};
343
            }
344
        }
345
        return $array;
346
    }
347
348
}
349