Completed
Push — master ( f8e5cb...66cdb5 )
by Maxime
02:52
created

ContentfulMapper::relationshipSignature()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 15
ccs 0
cts 14
cp 0
crap 12
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
namespace Distilleries\Contentful\Models\Base;
4
5
use Exception;
6
use Illuminate\Support\Str;
7
use Illuminate\Support\Carbon;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Facades\DB;
10
use Distilleries\Contentful\Models\Locale;
11
use Distilleries\Contentful\Api\DeliveryApi;
12
use Distilleries\Contentful\Repositories\Traits\EntryType;
13
14
abstract class ContentfulMapper
15
{
16
    use EntryType;
17
18
    /**
19
     * Map entry specific payload.
20
     *
21
     * @param  array $entry
22
     * @param  string $locale
23
     * @return array
24
     * @throws \Exception
25
     */
26
    abstract protected function map(array $entry, string $locale): array;
27
28
    /**
29
     * Map entry with common data + specific payload for each locales.
30
     *
31
     * @param  array $entry
32
     * @param  \Illuminate\Support\Collection $locales
33
     * @return array
34
     * @throws \Exception
35
     */
36
    public function toLocaleEntries(array $entry, Collection $locales): array
37
    {
38
        $entries = [];
39
40
        $common = [
41
            'contentful_id' => $entry['sys']['id'],
42
            'created_at' => new Carbon($entry['sys']['createdAt']),
43
            'updated_at' => new Carbon($entry['sys']['updatedAt']),
44
        ];
45
46
        foreach ($locales as $locale) {
47
            // Add specific fields
48
            $data = array_merge($common, $this->map($entry, $locale->code));
49
50
            $data['country'] = Locale::getCountry($locale->code);
51
            $data['locale'] = Locale::getLocale($locale->code);
52
53
            if (!isset($data['payload'])) {
54
                $data['payload'] = $this->mapPayload($entry, $locale->code);
55
            }
56
57
            if (!isset($data['relationships'])) {
58
                $data['relationships'] = $this->mapRelationships($data['payload']);
59
            }
60
61
            if (isset($data['slug']) && Str::contains($data['slug'], 'untitled-')) {
62
                $data['slug'] = null;
63
            }
64
65
            $entries[] = $data;
66
        }
67
68
        return $entries;
69
    }
70
71
    // --------------------------------------------------------------------------------
72
    // --------------------------------------------------------------------------------
73
    // --------------------------------------------------------------------------------
74
75
    /**
76
     * Return raw entry fields payload for given locale.
77
     *
78
     * @param  array $entry
79
     * @param  string $locale
80
     * @return array
81
     */
82
    protected function mapPayload(array $entry, string $locale): array
83
    {
84
        $payload = [];
85
        $dontFallback = config('contentful.payload_fields_not_fallback', []);
86
87
        $fallbackLocale = Locale::fallback($locale);
88
        foreach ($entry['fields'] as $field => $localesData) {
89
            if (isset($localesData[$locale])) {
90
                $payload[$field] = $localesData[$locale];
91
            } else {
92
                if (!in_array($field,
93
                        $dontFallback) && isset($localesData[$fallbackLocale]) && ($this->levelFallBack($field) === 'all')) {
94
                    $payload[$field] = $localesData[$fallbackLocale];
95
                } else {
96
                    $payload[$field] = null;
97
                }
98
            }
99
        }
100
101
        return $payload;
102
    }
103
104
    /**
105
     * Level fallback.
106
     *
107
     * @param  string $field
108
     * @return string
109
     */
110
    protected function levelFallBack($field): string
111
    {
112
        $levelMaster = ['slug'];
113
114
        return in_array($field, $levelMaster) ? 'master' : 'all';
115
    }
116
117
    // --------------------------------------------------------------------------------
118
    // --------------------------------------------------------------------------------
119
    // --------------------------------------------------------------------------------
120
121
    /**
122
     * Map relationships in given payload.
123
     *
124
     * @param  array $payload
125
     * @return array
126
     * @throws \Exception
127
     */
128
    protected function mapRelationships($payload): array
129
    {
130
        $relationships = [];
131
132
        foreach ($payload as $field => $value) {
133
            if (is_array($value)) {
134
                if ($this->isLink($value)) {
135
                    try {
136
                        $relationships[] = $this->relationshipSignature($value);
137
                    } catch (Exception $e) {
138
                        //
139
                    }
140
                } else {
141
                    foreach ($value as $entry) {
142
                        if ($this->isLink($entry)) {
143
                            try {
144
                                $relationships[] = $this->relationshipSignature($entry);
145
                            } catch (Exception $e) {
146
                                //
147
                            }
148
                        }
149
                    }
150
                }
151
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
152
                // No relationship
153
            }
154
        }
155
156
        return $relationships;
157
    }
158
159
    /**
160
     * Return relationship signature for given "localized" field.
161
     *
162
     * @param  array $localeField
163
     * @return array|null
164
     * @throws \Exception
165
     */
166
    private function relationshipSignature($localeField): ?array
167
    {
168
        if ($localeField['sys']['linkType'] === 'Asset') {
169
            return ['id' => $localeField['sys']['id'], 'type' => 'asset'];
170
        } else {
171
            if ($localeField['sys']['linkType'] === 'Entry') {
172
                return [
173
                    'id' => $localeField['sys']['id'],
174
                    'type' => $this->contentTypeFromEntryTypes($localeField['sys']['id'])
175
                ];
176
            }
177
        }
178
179
        throw new Exception('Invalid field signature... ' . PHP_EOL . print_r($localeField, true));
180
    }
181
182
    /**
183
     * Return if field is a Link one.
184
     *
185
     * @param  mixed $localeField
186
     * @return boolean
187
     */
188
    private function isLink($localeField): bool
189
    {
190
        return isset($localeField['sys']) and isset($localeField['sys']['type']) and ($localeField['sys']['type'] === 'Link');
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...
191
    }
192
193
    /**
194
     * Return contentful-type for given Contentful ID from `sync_entries` table.
195
     *
196
     * @param  string $contentfulId
197
     * @return string
198
     * @throws \Exception
199
     */
200
    public function contentTypeFromEntryTypes(string $contentfulId): string
201
    {
202
        $pivot = DB::table('sync_entries')
203
            ->select('contentful_type')
204
            ->where('contentful_id', '=', $contentfulId)
205
            ->first();
206
207
        if (empty($pivot)) {
208
            try {
209
                $entry = app(DeliveryApi::class)->entries([
210
                    'id' => $contentfulId,
211
                    'locale' => '*',
212
                    'content_type' => 'single_entry',
213
                ]);
214
215
                if (!empty($entry) and !empty($entry['sys']['contentType']) and !empty($entry['sys']['contentType']['sys'])) {
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...
216
                    $this->upsertEntryType($entry, $entry['sys']['contentType']['sys']['id']);
217
218
                    return $entry['sys']['contentType']['sys']['id'];
219
                }
220
            } catch (Exception $e) {
221
                throw new Exception('Unknown content-type from synced entry: ' . $contentfulId);
222
            }
223
        }
224
225
        return $pivot->contentful_type;
226
    }
227
228
    // --------------------------------------------------------------------------------
229
    // --------------------------------------------------------------------------------
230
    // --------------------------------------------------------------------------------
231
232
    /**
233
     * Return all locales in entry payload.
234
     *
235
     * @param  array $entry
236
     * @return array
237
     */
238
    private function entryLocales(array $entry): array
1 ignored issue
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
239
    {
240
        $locales = [];
241
242
        if (isset($entry['fields']) and !empty($entry['fields'])) {
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...
243
            $firstField = array_first($entry['fields']);
244
            $locales = array_keys($firstField);
245
        }
246
247
        return $locales;
248
    }
249
}
250