Completed
Push — master ( f19ce1...569ad9 )
by Maxime
03:06
created

ContentfulMapper::map()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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