Passed
Push — master ( 82a00b...207c37 )
by Arthur
03:41
created

Json::outputErrors()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 13
c 0
b 0
f 0
rs 10
cc 4
nc 4
nop 2
1
<?php
2
3
namespace SoliDry\Helpers;
4
5
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Http\Request;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SoliDry\Helpers\Request. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use League\Fractal\Manager;
9
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
10
use League\Fractal\Resource\Collection;
11
use League\Fractal\Resource\Item;
12
use League\Fractal\Resource\ResourceInterface;
13
use League\Fractal\Serializer\JsonApiSerializer;
14
use SoliDry\Types\ModelsInterface;
15
use SoliDry\Types\PhpInterface;
16
use SoliDry\Types\ApiInterface;
17
use SoliDry\Extension\BaseFormRequest;
18
use SoliDry\Extension\JSONApiInterface;
19
use SoliDry\Transformers\DefaultTransformer;
20
use SoliDry\Helpers\Request as Req;
21
22
/**
23
 * Class Json
24
 * @package SoliDry\Helpers
25
 */
26
class Json
27
{
28
    /**
29
     * @param array $jsonApiArr
30
     *
31
     * @return array
32
     */
33
    public static function getAttributes(array $jsonApiArr) : array
34
    {
35
        return empty($jsonApiArr[ApiInterface::RAML_DATA][ApiInterface::RAML_ATTRS]) ? [] : $jsonApiArr[ApiInterface::RAML_DATA][ApiInterface::RAML_ATTRS];
36
    }
37
38
    /**
39
     * Returns an array of bulk attributes for each element
40
     *
41
     * @param array $jsonApiArr
42
     * @return array
43
     */
44
    public static function getBulkAttributes(array $jsonApiArr) : array
45
    {
46
        return empty($jsonApiArr[ApiInterface::RAML_DATA]) ? [] : $jsonApiArr[ApiInterface::RAML_DATA];
47
    }
48
49
    /**
50
     * @param array $jsonApiArr
51
     *
52
     * @return array
53
     */
54
    public static function getRelationships(array $jsonApiArr) : array
55
    {
56
        return empty($jsonApiArr[ApiInterface::RAML_DATA][ApiInterface::RAML_RELATIONSHIPS]) ? [] : $jsonApiArr[ApiInterface::RAML_DATA][ApiInterface::RAML_RELATIONSHIPS];
57
    }
58
59
    /**
60
     * @param array $jsonApiArr
61
     *
62
     * @return array
63
     */
64
    public static function getData(array $jsonApiArr) : array
65
    {
66
        return empty($jsonApiArr[ApiInterface::RAML_DATA]) ? [] : $jsonApiArr[ApiInterface::RAML_DATA];
67
    }
68
69
    /**
70
     * @param $relations      \Illuminate\Database\Eloquent\Collection
71
     * @param string $entity
72
     * @return array JSON API rels compatible array
73
     */
74
    public static function getRelations($relations, string $entity) : array
75
    {
76
        $jsonArr = [];
77
        if ($relations instanceof \Illuminate\Database\Eloquent\Collection) {
78
            $cnt = \count($relations);
79
            if ($cnt > 1) {
80
                foreach ($relations as $v) {
81
                    $attrs     = $v->getAttributes();
82
                    $jsonArr[] = [ApiInterface::RAML_TYPE => $entity,
83
                                  ApiInterface::RAML_ID   => $attrs[ApiInterface::RAML_ID]];
84
                }
85
            } else {
86
                foreach ($relations as $v) {
87
                    $attrs   = $v->getAttributes();
88
                    $jsonArr = [ApiInterface::RAML_TYPE => $entity,
89
                                ApiInterface::RAML_ID   => $attrs[ApiInterface::RAML_ID]];
90
                }
91
            }
92
        } elseif ($relations instanceof Model) {
93
            $attrs   = $relations->getAttributes();
94
            $jsonArr = [ApiInterface::RAML_TYPE => $entity,
95
                        ApiInterface::RAML_ID   => $attrs[ApiInterface::RAML_ID]];
96
        }
97
98
        return $jsonArr;
99
    }
100
101
    /**
102
     * Output errors in JSON API compatible format
103
     * @param array $errors
104
     * @param bool $return
105
     * @return string
106
     */
107
    public static function outputErrors(array $errors, bool $return = false)
108
    {
109
        $arr[JSONApiInterface::CONTENT_ERRORS] = [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$arr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $arr = array(); before regardless.
Loading history...
110
        if (empty($errors) === false) {
111
            $arr[JSONApiInterface::CONTENT_ERRORS] = $errors;
112
        }
113
        // errors and codes must be clear with readable json
114
        $encoded = self::encode($arr, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
115
        if (false === $return && env('APP_ENV') !== 'dev') {
116
            echo $encoded;
117
            exit(JSONApiInterface::EXIT_STATUS_ERROR);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
118
        }
119
        return $encoded;
120
    }
121
122
    /**
123
     *
124
     * @param array $errors
125
     * @return string
126
     */
127
    public function getErrors(array $errors) : string
128
    {
129
        $arr[JSONApiInterface::CONTENT_ERRORS] = [];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$arr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $arr = array(); before regardless.
Loading history...
130
        if (empty($errors) === false) {
131
            $arr[JSONApiInterface::CONTENT_ERRORS] = $errors;
132
        }
133
134
        return self::encode($arr, JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
135
    }
136
137
    /**
138
     * Returns composition of relations
139
     *
140
     * @param Request $request
141
     * @param array $data
142
     * @return string
143
     */
144
    public static function prepareSerializedRelations(Request $request, array $data) : string
145
    {
146
        $arr[JSONApiInterface::CONTENT_LINKS] = [
0 ignored issues
show
Comprehensibility Best Practice introduced by
$arr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $arr = array(); before regardless.
Loading history...
147
            JSONApiInterface::CONTENT_SELF => $request->getUri(),
148
        ];
149
150
        $arr[JSONApiInterface::CONTENT_DATA] = $data;
151
152
        return self::encode($arr);
153
    }
154
155
    /**
156
     * @param BaseFormRequest $formRequest
157
     * @param                 $model
158
     * @param string $entity
159
     * @param bool $isCollection
160
     *
161
     * @param array $meta
162
     * @return Collection|Item
163
     */
164
    public static function getResource(BaseFormRequest $formRequest, $model, string $entity, bool $isCollection = false, array $meta = [])
165
    {
166
        $transformer = new DefaultTransformer($formRequest);
167
        if ($isCollection === true) {
168
            $collection = new Collection($model, $transformer, strtolower($entity));
169
            if (empty($meta) === false) {
170
                $collection->setMeta($meta);
171
            }
172
173
            if ($model instanceof LengthAwarePaginator) { // only for paginator
174
                $collection->setPaginator(new IlluminatePaginatorAdapter($model));
175
            }
176
177
            return $collection;
178
        }
179
180
        $item = new Item($model, $transformer, strtolower($entity));
181
        $item->setMeta($meta);
182
183
        return $item;
184
    }
185
186
    /**
187
     * Prepares data to output in json-api format
188
     *
189
     * @param ResourceInterface $resource
190
     * @param array $data
191
     * @return string
192
     */
193
    public static function prepareSerializedData(ResourceInterface $resource, $data = ModelsInterface::DEFAULT_DATA) : string
194
    {
195
        if (empty($resource->getData())) { // preventing 3d party libs (League etc) from crash on empty data
196
            return self::encode([
197
                ModelsInterface::PARAM_DATA => []
198
            ]);
199
        }
200
201
        $manager = new Manager();
202
        if (isset($_GET['include'])) {
203
            $manager->parseIncludes($_GET['include']);
204
        }
205
206
        $manager->setSerializer(new JsonApiSerializer((new Req())->getBasePath()));
207
        return self::getSelectedData($manager->createData($resource)->toJson(), $data);
208
    }
209
210
    /**
211
     * Encoder array -> json
212
     *
213
     * @param array $array
214
     * @param int $opts
215
     * @return string
216
     */
217
    public static function encode(array $array, int $opts = 0)
218
    {
219
        return json_encode($array, $opts);
220
    }
221
222
    /**
223
     * Decoder json -> array
224
     *
225
     * @param mixed $json
226
     * @return mixed
227
     */
228
    public static function decode(string $json): array
229
    {
230
        return json_decode($json, true);
231
    }
232
233
    /**
234
     * This method is wrapper over decode to let polymorfism happen between raml/json parsers
235
     *
236
     * @param string $json
237
     * @return array
238
     */
239
    public static function parse(string $json): array
240
    {
241
        return self::decode($json);
242
    }
243
244
    /**
245
     * Gets data only with those elements of object/array that should be provided as output
246
     *
247
     * @param string $json
248
     * @param array $data
249
     * @return string
250
     */
251
    private static function getSelectedData(string $json, array $data) : string
252
    {
253
        if (current($data) === PhpInterface::ASTERISK) {// do nothing - grab all fields
254
            return $json;
255
        }
256
257
        $jsonArr = self::decode($json);
258
        $current = current($jsonArr[ApiInterface::RAML_DATA]);
259
260
        if (empty($current[JSONApiInterface::CONTENT_ATTRIBUTES]) === false) {// this is an array of values
261
            self::unsetArray($jsonArr, $data);
262
        } else {// this is just one element
263
            self::unsetObject($jsonArr, $data);
264
        }
265
266
        return self::encode($jsonArr);
267
    }
268
269
    /**
270
     * Unsets objects from array that shouldn't be provided as output
271
     *
272
     * @param array &$json
273
     * @param array $data
274
     */
275
    private static function unsetArray(array &$json, array $data) : void
276
    {
277
        foreach ($json as &$jsonObject) {
278
            foreach ($jsonObject as &$v) {
279
                if (empty($v[JSONApiInterface::CONTENT_ATTRIBUTES]) === false) { // can be any of meta/link
280
                    foreach ($v[JSONApiInterface::CONTENT_ATTRIBUTES] as $key => $attr) {
281
                        if (\in_array($key, $data, true) === false) {
282
                            unset($v[JSONApiInterface::CONTENT_ATTRIBUTES][$key]);
283
                        }
284
                    }
285
                }
286
            }
287
        }
288
    }
289
290
    /**
291
     * Unsets objects that shouldn't be provided as output
292
     *
293
     * @param array $json
294
     * @param array $data
295
     */
296
    private static function unsetObject(array &$json, array $data) : void
297
    {
298
        if (empty($json[JSONApiInterface::CONTENT_DATA]) === false
299
            && empty($json[JSONApiInterface::CONTENT_DATA][JSONApiInterface::CONTENT_ATTRIBUTES]) === false) {
300
301
            foreach ($json[JSONApiInterface::CONTENT_DATA][JSONApiInterface::CONTENT_ATTRIBUTES] as $k => $v) {
302
                if (\in_array($k, $data, true) === false) {
303
                    unset($json[JSONApiInterface::CONTENT_DATA][JSONApiInterface::CONTENT_ATTRIBUTES][$k]);
304
                }
305
            }
306
        }
307
    }
308
}