BaseResource::setAllAttributes()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
/**
3
 * Base Resource
4
 */
5
namespace Twigger\UnionCloud\API\Resource;
6
7
use Carbon\Carbon;
8
use Twigger\UnionCloud\API\Exception\Resource\ResourceNotFoundException;
9
use Twigger\UnionCloud\API\ResourceCollection;
10
use Twigger\UnionCloud\API\Traits\CastsAttributes;
11
use Twigger\UnionCloud\API\Traits\ParsesAttributes;
12
13
/**
14
 * Contains methods to deal with a Resource
15
 *
16
 * Class BaseResource
17
 *
18
 * @package Twigger\UnionCloud\API\Core\Resources
19
 */
20
class BaseResource
21
{
22
    use ParsesAttributes, CastsAttributes;
23
24
    /**
25
     * Holds an array representing the resource
26
     *
27
     * Should store attributes in snake case
28
     *
29
     * @var mixed $modelParameters
30
     */
31
    public $attributes = [];
32
33
    /**
34
     * Enable casting variables.
35
     *
36
     * The key of the array should be the attribute in camelCase
37
     * The value of the array should be one of:
38
     *      date -> will parse to a Carbon instance
39
     *      properCase -> will parse names
40
     *      Class::class -> will pass the individual attributes into the class constructor.
41
     *
42
     * @var array
43
     */
44
    protected $casts = [];
45
46
    /**
47
     * Enable advanced casting for multiple return variables
48
     *
49
     * The key of the array should contain the new attribute reference in snake case,
50
     * and a string representing the class. i.e.
51
     * usergroup|UserGroup::class.
52
     *
53
     * The value of the array should be an array to specify the transformation from
54
     * the first attribute to the second attribute. I.e. if the initial request contains
55
     * a key called ug_id, but the UserGroup class has an attribute id, we put
56
     *  ['ug_id' => 'id']
57
     *
58
     * @var array
59
     */
60
    protected $customCasts = [];
61
62
63
64
    /*
65
    |--------------------------------------------------------------------------
66
    | Getting and Setting attributes
67
    |--------------------------------------------------------------------------
68
    |
69
    | Save the attributes returned from the JSON response into a more
70
    | useful form.
71
    |
72
    */
73
74
    /**
75
     * BaseResource constructor.
76
     *
77
     * Set all the attributes to make up the model
78
     *
79
     * @throws ResourceNotFoundException
80
     *
81
     * @param $attributes
82
     */
83
    public function __construct($attributes = [])
84
    {
85
        $this->setAllAttributes($attributes);
86
    }
87
88
89
    /**
90
     * Set all attributes given a response from UnionCloud
91
     *
92
     * This will overwrite any previously saved attributes
93
     *
94
     * @throws ResourceNotFoundException
95
     *
96
     * @param $attributes array of snake case attribute keys and values
97
     */
98
    protected function setAllAttributes($attributes)
99
    {
100
        foreach($attributes as $attributeKey => $value)
101
        {
102
            $value = $this->castAttribute($attributeKey, $value);
103
            if($value !== null) // Will occur for customCasts, since we don't want the attribute to exist in this class but in the child resource
104
            {
105
                $this->attributes[$attributeKey] = $value;
106
            }
107
        }
108
    }
109
110
111
    /**
112
     * Allow getting model attributes
113
     *
114
     * If the attribute doesn't exist, this will return false.
115
     * Ensure the result is exactly false (=== false).
116
     *
117
     * @param string $attributeKey in camelCase
118
     *
119
     * @throws ResourceNotFoundException
120
     *
121
     * @return mixed|bool
122
     */
123
    public function __get($attributeKey)
124
    {
125
        if($this->doesAttributeExist($attributeKey)) {
126
            $attribute = $this->attributes[$attributeKey];
127
            return $attribute;
128
            //return $this->castAttribute($attributeKey, $attribute);
129
        }
130
        return false;
131
    }
132
133
    /**
134
     * Allow setting model attributes
135
     *
136
     * @param string $attributeKey camelCase
137
     * @param mixed $value
138
     *
139
     * @throws ResourceNotFoundException
140
     *
141
     * @return $this
142
     */
143
    public function __set($attributeKey, $value) {
144
        $snakeAttributeKey = $this->fromCamelToSnake($attributeKey);
145
        if (!$this->doesAttributeExist($attributeKey)) {
146
            $this->attributes[$snakeAttributeKey] = '';
147
        }
148
        $value = $this->castAttribute($attributeKey, $value);
149
        $this->attributes[$snakeAttributeKey] = $value;
150
        return $this;
151
    }
152
153
154
155
156
157
158
159
160
161
162
163
164
    /*
165
    |--------------------------------------------------------------------------
166
    | Casts
167
    |--------------------------------------------------------------------------
168
    |
169
    | Allow for casting of attributes.
170
    |
171
    */
172
173
174
175
176
177
    /**
178
     * Cast an attribute from the JSON response to a more useful form
179
     *
180
     * @param string $attributeKey snake of the key
181
     * @param mixed $attributeValue Value of the attribute
182
     *
183
     * @throws ResourceNotFoundException
184
     *
185
     * @return mixed Casted value of the attribute value
186
     */
187
    private function castAttribute($attributeKey, $attributeValue)
188
    {
189
        if(($castTo = $this->getAttributeCastSetting($attributeKey)) !== false)
190
        {
191
            # Cast is one of date, properName etc
192
            return $this->castAttributeFromCode($castTo, $attributeValue);
193
        } elseif(($castToSettings = $this->getAttributeCustomCastSettings($attributeKey)) !== false)
194
        {
195
            # Cast is custom
196
197
            // The attribute under which the new resource is accessible
198
            $newAttribute = $castToSettings['new_attribute'];
199
            // The new resource to use
200
            $handler = $castToSettings['handler'];
201
            // The attribute to be set in the new handler
202
            $newHandlerAttribute = $castToSettings['new_handler_attribute'];
203
204
            if(!$this->doesAttributeExist($newAttribute))
205
            {
206
                $this->$newAttribute = new $handler([]);
207
            }
208
209
            $this->$newAttribute->$newHandlerAttribute = $attributeValue;
210
211
            // If it doesn't already exist, make it so it does
212
            // Populate the correct attribute in the class
213
            return null;
214
        }
215
        return $attributeValue;
216
    }
217
218
    /**
219
     * Actually casts the attribute
220
     *
221
     * @param string $castTo
222
     * @param mixed $attributeValue
223
     *
224
     * @return mixed
225
     *
226
     * @throws ResourceNotFoundException
227
     */
228
229
    private function castAttributeFromCode($castTo, $attributeValue)
230
    {
231
        switch ($castTo)
232
        {
233
            case 'date':
234
                return $this->parseDate($attributeValue);
235
            case 'properCase':
236
                return $this->parseProperCase($attributeValue);
237
            default:
238
                return $this->parseCustomResource($attributeValue, $castTo);
239
        }
240
241
        // TODO throw error to do with casting
242
    }
243
    /**
244
     * Return the setting for the cast from the user
245
     *
246
     * Will return false if $casts doesn't have an index associated
247
     * with the attributeKey. Otherwise, returns the value of the
248
     * cast array, i.e. the setting
249
     *
250
     * @param string $attributeKey snake case of attribute key
251
     *
252
     * @return string|false
253
     */
254
255
    private function getAttributeCastSetting($attributeKey)
256
    {
257
        if(property_exists($this, 'casts'))
258
        {
259
            if(array_key_exists($camelCase = $this->fromSnakeToCamel($attributeKey), $this->casts))
260
            {
261
                return $this->casts[$camelCase];
262
            }
263
        }
264
265
        return false;
266
    }
267
268
    /**
269
     * Used to create more custom casting
270
     *
271
     * Given an API response contains several parameters, i.e. ug_id and ug_name, and
272
     * both have to be put into a usergroup attribute, containing a usergroup, this
273
     * method is used. The received attributeKey will be found in the customCasts array,
274
     * and set up correctly.
275
     *
276
     * @param string $attributeKey
277
     *
278
     * @return array|bool
279
     */
280
    private function getAttributeCustomCastSettings($attributeKey)
281
    {
282
        if(property_exists($this, 'customCasts') && is_array($this->customCasts))
283
        {
284
            foreach($this->customCasts as $classSettings=>$customCast)
285
            {
286
                if(array_key_exists($camelCase = $this->fromSnakeToCamel($attributeKey), $customCast))
287
                {
288
                    $castSettings = explode('|', $classSettings);
289
                    return [
290
                        'new_attribute' => $castSettings[0],
291
                        'handler' => $castSettings[1],
292
                        'current_attribute' => $attributeKey,
293
                        'new_handler_attribute' => $customCast[$camelCase]
294
                    ];
295
                }
296
            }
297
        }
298
299
        return false;
300
    }
301
302
303
304
305
306
307
308
309
    /*
310
    |--------------------------------------------------------------------------
311
    | Helper Functions
312
    |--------------------------------------------------------------------------
313
    |
314
    | Helper functions for using resources.
315
    |
316
    */
317
318
319
320
321
322
323
324
    /**
325
     * Determines if an attribute is present in a resource
326
     *
327
     * @param string $attributeKey camelCase attribute key
328
     * @param string $type camel|snake, type of string passed in $attributeKey.
329
     *
330
     * @return bool
331
     */
332
    private function doesAttributeExist($attributeKey, $type='camel')
333
    {
334
        if($type === 'camel')
335
        {
336
            $attributeKey = $this->fromCamelToSnake($attributeKey);
337
        }
338
        if (property_exists($this, 'attributes'))
339
        {
340
            if (array_key_exists($attributeKey, $this->attributes)) {
341
                // TODO Check the attribute key isn't blank
342
                if($this->attributes[$attributeKey])
343
                {
344
                    return true;
345
                }
346
            }
347
        }
348
        return false;
349
    }
350
351
352
    /**
353
     * Return an array of the resource attributes
354
     *
355
     * @return mixed
356
     */
357
    public function getAttributes()
358
    {
359
        return $this->attributes;
360
    }
361
362
}