Completed
Push — master ( 4b1496...c64940 )
by Maxime
04:19
created

ContentfulMapper::levelFallBack()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 5
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Distilleries\Contentful\Models\Base;
4
5
use Exception;
6
use Illuminate\Support\Carbon;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\DB;
9
use Distilleries\Contentful\Models\Locale;
10
use Illuminate\Support\Str;
11
12
abstract class ContentfulMapper
13
{
14
    /**
15
     * Map entry specific payload.
16
     *
17
     * @param  array $entry
18
     * @param  string $locale
19
     * @return array
20
     * @throws \Exception
21
     */
22
    abstract protected function map(array $entry, string $locale): array;
23
24
    /**
25
     * Map entry with common data + specific payload for each locales.
26
     *
27
     * @param  array $entry
28
     * @return array
29
     * @throws \Exception
30
     */
31
    public function toLocaleEntries(array $entry, Collection $locales): array
32
    {
33
        $entries = [];
34
        $common = [
35
            'contentful_id' => $entry['sys']['id'],
36
            'created_at' => new Carbon($entry['sys']['createdAt']),
37
            'updated_at' => new Carbon($entry['sys']['updatedAt']),
38
        ];
39
40
        foreach ($locales as $locale) {
41
            // Add specific fields
42
            $data = array_merge($common, $this->map($entry, $locale->code));
43
44
            $data['country'] = Locale::getCountry($locale->code);
45
            $data['locale'] = Locale::getLocale($locale->code);
46
47
            if (!isset($data['payload'])) {
48
                $data['payload'] = $this->mapPayload($entry, $locale->code);
49
            }
50
51
            if (!isset($data['relationships'])) {
52
                $data['relationships'] = $this->mapRelationships($data['payload']);
53
            }
54
55
            if (isset($data['slug']) && Str::contains($data['slug'], 'untitled-')) {
56
                $data['slug'] = null;
57
            }
58
59
60
            $entries[] = $data;
61
        }
62
63
        return $entries;
64
    }
65
66
    // --------------------------------------------------------------------------------
67
    // --------------------------------------------------------------------------------
68
    // --------------------------------------------------------------------------------
69
70
    /**
71
     * Return raw entry fields payload for given locale.
72
     *
73
     * @param  array $entry
74
     * @param  string $locale
75
     * @return array
76
     */
77
    protected function mapPayload(array $entry, string $locale): array
78
    {
79
        $payload = [];
80
81
        $fallbackLocale = Locale::fallback($locale);
82
        foreach ($entry['fields'] as $field => $localesData) {
83
            if (isset($localesData[$locale])) {
84
                $payload[$field] = $localesData[$locale];
85
            } else {
86
                // Fallback field...
87
                if (isset($localesData[$fallbackLocale]) && $this->levelFallBack($field) == 'all') {
88
                    $payload[$field] = $localesData[$fallbackLocale];
89
                } else {
90
                    $payload[$field] = null;
91
                }
92
            }
93
        }
94
95
        return $payload;
96
    }
97
98
99
    protected function levelFallBack($field)
100
    {
101
        $levelMaster = ['slug'];
102
        return in_array($field, $levelMaster) ? 'master' : 'all';
103
104
    }
105
106
    // --------------------------------------------------------------------------------
107
    // --------------------------------------------------------------------------------
108
    // --------------------------------------------------------------------------------
109
110
    /**
111
     * Map relationships in given payload.
112
     *
113
     * @param  array $payload
114
     * @return array
115
     * @throws \Exception
116
     */
117
    protected function mapRelationships($payload): array
118
    {
119
        $relationships = [];
120
121
        foreach ($payload as $field => $value) {
122
            if (is_array($value)) {
123
                if ($this->isLink($value)) {
124
                    $relationships[] = $this->relationshipSignature($value);
125
                } else {
126
                    foreach ($value as $entry) {
127
                        if ($this->isLink($entry)) {
128
                            $relationships[] = $this->relationshipSignature($entry);
129
                        }
130
                    }
131
                }
132
            } 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...
133
                // No relationship
134
            }
135
        }
136
137
        return $relationships;
138
    }
139
140
    /**
141
     * Return relationship signature for given "localized" field.
142
     *
143
     * @param  array $localeField
144
     * @return array|null
145
     * @throws \Exception
146
     */
147
    private function relationshipSignature($localeField): ?array
148
    {
149
        if ($localeField['sys']['linkType'] === 'Asset') {
150
            return ['id' => $localeField['sys']['id'], 'type' => 'asset'];
151
        } else if ($localeField['sys']['linkType'] === 'Entry') {
152
            if (app()->runningInConsole()) {
153
                // From SYNC
154
                return ['id' => $localeField['sys']['id'], 'type' => $this->contentTypeFromSyncEntries($localeField['sys']['id'])];
155
            } else {
156
                // From Webhook
157
                return ['id' => $localeField['sys']['id'], 'type' => $this->contentTypeFromEntryTypes($localeField['sys']['id'])];
158
            }
159
        }
160
161
        throw new Exception('Invalid field signature... ' . PHP_EOL . print_r($localeField, true));
162
    }
163
164
    /**
165
     * Return if field is a Link one.
166
     *
167
     * @param  mixed $localeField
168
     * @return bool
169
     */
170
    private function isLink($localeField): bool
171
    {
172
        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...
173
    }
174
175
    /**
176
     * Return contentful-type for given Contentful ID from `sync_entries` table.
177
     *
178
     * @param  string $contentfulId
179
     * @return string
180
     * @throws \Exception
181
     */
182
    private function contentTypeFromSyncEntries(string $contentfulId): string
183
    {
184
        $pivot = DB::table('sync_entries')
185
            ->select('contentful_type')
186
            ->where('contentful_id', '=', $contentfulId)
187
            ->first();
188
189
        if (empty($pivot)) {
190
            throw new Exception('Unknown content-type from synced entry: ' . $contentfulId);
191
        }
192
193
        return $pivot->contentful_type;
194
    }
195
196
    /**
197
     * Return content-type for given Contentful ID from `entry_types` table.
198
     *
199
     * @param  string $contentfulId
200
     * @return string
201
     * @throws \Exception
202
     */
203
    private function contentTypeFromEntryTypes(string $contentfulId): string
204
    {
205
        $pivot = DB::table('entry_types')
206
            ->select('contentful_type')
207
            ->where('contentful_id', '=', $contentfulId)
208
            ->first();
209
210
        if (empty($pivot)) {
211
            throw new Exception('Unknown content-type from webhook entry: ' . $contentfulId);
212
        }
213
214
        return $pivot->contentful_type;
215
    }
216
217
    // --------------------------------------------------------------------------------
218
    // --------------------------------------------------------------------------------
219
    // --------------------------------------------------------------------------------
220
221
    /**
222
     * Return all locales in entry payload.
223
     *
224
     * @param  array $entry
225
     * @return array
226
     */
227
    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...
228
    {
229
        $locales = [];
230
231
        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...
232
            $firstField = array_first($entry['fields']);
233
            $locales = array_keys($firstField);
234
        }
235
236
        return $locales;
237
    }
238
}
239