Completed
Push — master ( 096673...3f861c )
by Stéphane
11:15
created

Entity::getField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php namespace Rocket\Entities;
2
3
use Illuminate\Support\Collection;
4
use InvalidArgumentException;
5
use RuntimeException;
6
use Illuminate\Support\Facades\DB;
7
8
/**
9
 * Entity manager
10
 *
11
 * @property int $id The content ID
12
 * @property int $language_id The language in which this entity is
13
 * @property-read \DateTime $created_at
14
 * @property-read \DateTime $updated_at
15
 */
16
abstract class Entity
17
{
18
    public static $types;
19
20
    /**
21
     * The content represented by this entity
22
     *
23
     * @var Content
24
     */
25
    protected $content; //id, created_at
26
27
    /**
28
     * The revision represented by this entity
29
     *
30
     * @var Revision
31
     */
32
    protected $revision; //language_id, updated_at
33
34
    /**
35
     * The fields in this entity
36
     *
37
     * @var array<FieldCollection>
38
     */
39
    protected $data;
40
41
    /**
42
     * Entity constructor.
43
     *
44
     * @param int $language_id The language this specific entity is in
45
     * @param array $data The data for this entity
46
     */
47 45
    public function __construct($language_id, $data = [])
48
    {
49 45
        if (!is_int($language_id) || $language_id == 0) {
50 3
            throw new InvalidArgumentException("You must set a valid 'language_id'.");
51
        }
52
53 42
        $fields = $this->getFields();
54
55 42
        $this->initialize($fields);
56
57 36
        $this->revision->language_id = $language_id;
58
59 36
        if ($data !== null) {
60 36
            $this->hydrate($data);
61 36
        }
62 36
    }
63
64 42
    protected function initialize(array $fields)
65
    {
66 42
        $this->content = new Content;
67 42
        $this->revision = new Revision;
68
69 42
        foreach ($fields as $field => $settings) {
70 42
            $this->data[$field] = $this->initializeField($field, $settings);
71 36
        }
72 36
    }
73
74 42
    protected function initializeField($field, $settings)
75
    {
76 42
        if ($this->isContentField($field) || $this->isRevisionField($field)) {
77 3
            throw new InvalidArgumentException(
78 3
                "The field '$field' cannot be used in '" . get_class($this) . "' as it is a reserved name"
79 3
            );
80
        }
81
82 39
        $type = $settings['type'];
83
84 39
        if (!array_key_exists($type, self::$types)) {
85 3
            throw new RuntimeException("Unkown type '$type' in '" . get_class($this) . "'");
86
        }
87
88 36
        $settings['type'] = self::$types[$settings['type']];
89
90 36
        return FieldCollection::initField($settings);
91
    }
92
93 36
    protected function hydrate($data)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
    {
95
        //TODO :: populate data
96 36
    }
97
98
    abstract public function getFields();
99
100
    /**
101
     * Get the database friendly content type
102
     *
103
     * @return string
104
     */
105 6
    public static function getContentType()
106
    {
107 6
        return str_replace('\\', '', snake_case((new \ReflectionClass(get_called_class()))->getShortName()));
108
    }
109
110
    /**
111
     * Create a new revision based on the same content ID but without the content.
112
     * Very useful if you want to add a new language
113
     *
114
     * @param int $language_id
115
     * @return static
116
     */
117 3
    public function newRevision($language_id = null)
118
    {
119 3
        $created = new static($language_id ?: $this->language_id);
120 3
        $created->content = $this->content;
121
122 3
        return $created;
123
    }
124
125
    /**
126
     * Check if the field is related to the content
127
     *
128
     * @param string $field
129
     * @return bool
130
     */
131 42
    protected function isContentField($field)
132
    {
133 42
        return in_array($field, ['id', 'created_at']);
134
    }
135
136
    /**
137
     * Check if the field exists on the entity
138
     *
139
     * @param string $field
140
     * @return bool
141
     */
142 33
    public function hasField($field)
143
    {
144 33
        return array_key_exists($field, $this->data);
145
    }
146
147
    /**
148
     * @param string $field
149
     * @return FieldCollection
150
     */
151 27
    public function getField($field)
152
    {
153 27
        return $this->data[$field];
154
    }
155
156
    /**
157
     * Check if the field is related to the revision
158
     *
159
     * @param string $field
160
     * @return bool
161
     */
162 39
    protected function isRevisionField($field)
163
    {
164 39
        return in_array($field, ['language_id', 'updated_at']);
165
    }
166
167
    /**
168
     * Dynamically retrieve attributes on the model.
169
     *
170
     * @param string $key
171
     * @throws RuntimeException
172
     * @return $this|bool|\Carbon\Carbon|\DateTime|mixed|static
173
     */
174 33
    public function __get($key)
175
    {
176 33
        if ($this->isContentField($key)) {
177 3
            return $this->content->getAttribute($key);
178
        }
179
180 33
        if ($this->isRevisionField($key)) {
181 6
            return $this->revision->getAttribute($key);
182
        }
183
184 30
        if ($this->hasField($key)) {
185 27
            return $this->getField($key);
186
        }
187
188 3
        throw new RuntimeException("Field '$key' doesn't exist in '" . get_class($this) . "'");
189
    }
190
191
    /**
192
     * Dynamically set attributes on the model.
193
     *
194
     * @param string $key
195
     * @param mixed $value
196
     * @throws RuntimeException
197
     */
198 15
    public function __set($key, $value)
199
    {
200 15
        if ($this->isContentField($key)) {
201 3
            $this->content->setAttribute($key, $value);
202
203 3
            return;
204
        }
205
206 15
        if ($this->isRevisionField($key)) {
207
            $this->revision->setAttribute($key, $value);
208
209
            return;
210
        }
211
212 15
        if ($this->hasField($key)) {
213 12
            if ($value == []) {
214 9
                $this->getField($key)->clear();
215
216 9
                return;
217
            }
218
219 3
            $this->getField($key)->offsetSet(0, $value);
220
221 3
            return;
222
        }
223
224 3
        throw new RuntimeException("Field '$key' doesn't exist in '" . get_class($this) . "'");
225
    }
226
227
    /**
228
     * Find the latest valid revision for this entity
229
     *
230
     * @param int $id
231
     * @param int $language_id
232
     * @return static
233
     */
234
    public static function find($id, $language_id)
235
    {
236
        $instance = new static($language_id);
237
238
        $instance->content = Content::findOrFail($id);
239
240
        // TODO :: logic to get valid revision, this will retrieve only one revision
241
        $instance->revision = Revision::where('content_id', $id)->where('language_id', $language_id)->firstOrFail();
242
243
        (new Collection($instance->getFields()))
244
            ->map(function($options) {
245
                return $options['type'];
246
            })
247
            ->values()
248
            ->unique()
249
            ->map(function($type) {
250
                return self::$types[$type];
251
            })
252
            ->each(function($type) use ($instance) {
253
                $type::where('revision_id', $instance->revision->id)->get()->each(function(Field $value) use ($instance) {
254
                    $instance->data[$value->name][$value->weight] = $value;
255
                });
256
            });
257
258
        return $instance;
259
    }
260
261
    /**
262
     * Save a revision
263
     */
264 9
    public function save()
265
    {
266
267 9
        DB::transaction(
268
            function () use ($newRevision) {
0 ignored issues
show
Bug introduced by
The variable $newRevision does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
269
                // Save content
270
                $this->content->save();
271
272
                // Create revision ID
273
                $this->revision->content_id = $this->content->id;
274
                $this->revision->save();
275
276
                // Prepare and save fields
277
                foreach (array_keys($this->data) as $fieldName) {
278
                    $field = $this->data[$fieldName];
279
280
                    //TODO :: remove deleted fields
281
282
                    $field->each(function (Field $value, $key) use ($fieldName) {
283
                        $value->weight = $key;
284
                        $value->revision_id = $this->revision->id;
285
                        $value->name = $fieldName;
286
287
                        $value->save();
288
                    });
289
                }
290
            }
291
        );
292
    }
293
294
    /**
295
     * Convert the Entity to an array.
296
     *
297
     * @return array
298
     */
299 3
    public function toArray()
300
    {
301
        $content = [
302 3
            '_content' => $this->content->toArray(),
303 3
            '_revision' => $this->revision->toArray(),
304 3
        ];
305
306 3
        foreach ($this->data as $field => $data) {
307 3
            $content[$field] = $data->toArray();
308 3
        }
309
310 3
        return $content;
311
    }
312
}
313