Completed
Push — master ( 847696...f19ce1 )
by Maxime
05:34
created

ContentfulModel::contentfulLinkId()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 1
dl 0
loc 19
ccs 0
cts 16
cp 0
crap 42
rs 9.0111
c 0
b 0
f 0
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
    /**
135
     * Return Contentful Entry for given link (sys or ID).
136
     *
137
     * @param  array|string|null $link
138
     * @param  callback|null $query
139
     * @return \Distilleries\Contentful\Models\Base\ContentfulModel|null
140
     */
141
    protected function contentfulEntry($link, $query = null): ?ContentfulModel
142
    {
143
        $entryId = $this->contentfulLinkId($link);
144
145
        if (empty($entryId))
146
        {
147
            return null;
148
        }
149
150
        $entries = $this->contentfulEntries([$entryId], $query);
151
152
        return $entries->isNotEmpty() ? $entries->first() : null;
153
    }
154
155
    /**
156
     * Return Contentful Entries for given ID.
157
     *
158
     * @param  array $links
159
     * @param  callback|null $query
160
     * @return \Illuminate\Support\Collection
161
     */
162
    protected function contentfulEntries(array $links, $query = null): Collection
163
    {
164
        $entries = [];
165
166
        $entryIds = [];
167
        foreach ($links as $link)
168
        {
169
            $entryId = $this->contentfulLinkId($link);
170
            if (!empty($entryId))
171
            {
172
                $entryIds[] = $entryId;
173
            }
174
        }
175
176
        if (!empty($entryIds))
177
        {
178
            $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...
179
                ->select('related_contentful_id', 'related_contentful_type', 'order')
180
                ->distinct()
181
                ->locale($this->locale, $this->country)
182
                ->where('source_contentful_id', '=', $this->contentful_id)
183
                ->whereIn('related_contentful_id', $entryIds)
184
                ->orderBy('order', 'asc')
185
                ->get();
186
187
            foreach ($relationships as $relationship)
188
            {
189
                if ($relationship->related_contentful_type === 'asset')
190
                {
191
                    $model = new Asset;
192
                } else
193
                {
194
                    $modelClass = config('contentful.namespace.model') . '\\' . studly_case($relationship->related_contentful_type);
195
                    $model = new $modelClass;
196
                }
197
198
                $instance = $model->query()
199
                    ->where('country', '=', $this->country)
200
                    ->where('locale', '=', $this->locale)
201
                    ->where('contentful_id', '=', $relationship->related_contentful_id);
202
203
204
                if (!empty($query))
205
                {
206
                    $instance = call_user_func($query, $instance);
207
                }
208
209
                $instance = $instance->first();
210
211
                if (!empty($instance))
212
                {
213
                    $entries[] = $instance;
214
                }
215
            }
216
        }
217
218
        return collect($entries);
219
    }
220
221
    /**
222
     * Return a collection of related models for base Contentful ID.
223
     *
224
     * @param  string $contentfulId
225
     * @param  string $contentfulType
226
     * @return \Illuminate\Support\Collection
227
     */
228
    protected function contentfulRelatedEntries(string $contentfulId, string $contentfulType = ''): Collection
229
    {
230
        $entries = [];
231
232
        $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...
233
            ->select('source_contentful_id', 'source_contentful_type', '')
234
            ->locale($this->locale, $this->country)
235
            ->where('related_contentful_id', '=', $contentfulId);
236
237
        if (!empty($contentfulType))
238
        {
239
            $query = $query->where('source_contentful_type', '=', $contentfulType);
240
        }
241
242
        $relationships = $query->orderBy('order', 'asc')->get();
243
        foreach ($relationships as $relationship)
244
        {
245
            if ($relationship->source_contentful_type === 'asset')
246
            {
247
                $model = new Asset;
248
            } else
249
            {
250
                $modelClass = rtrim(config('contentful.namespace.model'), '\\') . '\\' . studly_case($relationship->source_contentful_type);
251
                $model = new $modelClass;
252
            }
253
254
            $instance = $model->query()
255
                ->locale($this->locale, $this->country)
256
                ->where('contentful_id', '=', $relationship->source_contentful_id)
257
                ->first();
258
259
            if (!empty($instance))
260
            {
261
                $entries[] = $instance;
262
            }
263
        }
264
265
        return collect($entries);
266
    }
267
268
    /**
269
     * Return Contentful link ID.
270
     *
271
     * @param  mixed $link
272
     * @return string|null
273
     */
274
    protected function contentfulLinkId($link): ?string
275
    {
276
        if (empty($link))
277
        {
278
            return null;
279
        }
280
281
        if (is_string($link))
282
        {
283
            return $link;
284
        }
285
286
        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...
287
        {
288
            return $link['sys']['id'];
289
        }
290
291
        return null;
292
    }
293
294
    public function getContentType(): string
295
    {
296
        return $this->contentType;
297
    }
298
299
    public function getIdAttribute()
300
    {
301
        return $this->getKey();
302
    }
303
304
    public function toArray()
305
    {
306
        $array = parent::toArray();
307
        foreach ($this->getMutatedAttributes() as $key)
308
        {
309
            if (!array_key_exists($key, $array))
310
            {
311
                $array[$key] = $this->{$key};
312
            }
313
        }
314
        return $array;
315
    }
316
317
}
318