Api::error()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Vinelab\Api;
4
5
/*
6
 * @author Mahmoud Zalt <[email protected]>
7
 * @author Abed Halawi <[email protected]>
8
 */
9
10
use Illuminate\Config\Repository;
11
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
12
use Illuminate\Contracts\Pagination\Paginator;
13
use Illuminate\Support\Collection;
14
use Illuminate\Support\Facades\App;
15
use Input;
16
17
/**
18
 * Class Api.
19
 */
20
class Api
21
{
22
    /**
23
     * @var int
24
     */
25
    protected $limit;
26
27
    /**
28
     * @var string
29
     */
30
    private $mappers_base_namespace;
31
32
    /**
33
     * @var \Vinelab\Api\ResponseHandler
34
     */
35
    private $response_handler;
36
37
    /**
38
     * @var \Vinelab\Api\ErrorHandler
39
     */
40
    private $error;
41
42
    /**
43
     * @var \Illuminate\Config\Repository
44
     */
45
    private $config_reader;
46
47
    /**
48
     * @param \Vinelab\Api\ResponseHandler  $response_handler
49
     * @param \Vinelab\Api\ErrorHandler     $error_handler
50
     * @param \Illuminate\Config\Repository $config_reader
51
     */
52
    public function __construct(
53
        ResponseHandler $response_handler,
54
        ErrorHandler $error_handler,
55
        Repository $config_reader
56
    ) {
57
        $this->response_handler = $response_handler;
58
        $this->error = $error_handler;
59
        $this->config_reader = $config_reader;
60
        // get config file values and store them in attributes
61
        $this->readConfigFile();
62
    }
63
64
    /**
65
     * get config file values and store them in attributes.
66
     */
67
    private function readConfigFile()
68
    {
69
        // reading the config file to be stored in the 'configurations' variable below
70
        $configurations = $this->config_reader->get('api');
71
        $this->mappers_base_namespace = $configurations['mappers'];
72
        $this->limit = $configurations['limit'];
73
    }
74
75
    /**
76
     * Map and respond.
77
     *
78
     * @param string|mixed $mapper
79
     * @param mixed        $data
80
     *
81
     * @throws ApiException
82
     *
83
     * @return Illuminate\Http\JsonResponse
84
     */
85
    public function respond($mapper, $data)
86
    {
87
        $arguments = [];
88
        // if data is instance of Paginator then get the values of the total and the page from the paginator,
89
        // and add them to the arguments array (total, page)
90
        if ($this->isPaginatorInstance($data)) {
91
            $arguments[0] = $data->total();
92
            $arguments[1] = $data->currentPage();
93
            $arguments[2] = $data->perPage();
94
        }
95
        // skip the first 2 arguments and save the rest to the 'arguments array':
96
        // > in case data is instance of Paginator then this will append all the arguments to the 'arguments array'
97
        // starting by the third arguments which should be the 'status'.
98
        // > in case data is is not instance of Paginator (means total and page and per_page are added manually as
99
        // arguments) then this will add all the arguments to the 'arguments array' starting by the third arguments
100
        // which should be the 'page'.
101
        foreach (array_slice(func_get_args(), 2) as $arg) {
102
            $arguments[count($arguments)] = $arg;
103
        }
104
        $result[] = $this->data($mapper, $data);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
105
106
        return call_user_func_array([$this->response_handler, 'respond'], array_merge($result, $arguments));
107
    }
108
109
    /**
110
     * check if $data is instance of Laravel Paginator.
111
     *
112
     * @param $data
113
     *
114
     * @return bool
115
     */
116
    public function isPaginatorInstance($data)
117
    {
118
        return ($data instanceof Paginator || $data instanceof LengthAwarePaginator) ? true : false;
119
    }
120
121
    /**
122
     * Get the formatted data out of the given mapper and data.
123
     *
124
     * @param string|mixed $mapper
125
     * @param mixed        $data
126
     *
127
     * @return array
128
     */
129
    public function data($mapper, $data)
130
    {
131
        // we won't deal with empty data
132
        if (is_array($data) && empty($data)
133
            || (($this->isPaginatorInstance($data)) && $data->isEmpty())
134
            || ($data instanceof Collection && $data->isEmpty())
135
        ) {
136
            return [];
137
        }
138
        // First we check whether we've been passed an array in which case we consider the array to be ['mapper',
139
        // 'method'] where 'mapper' can either be the mapper class name as a string or the actual instance.
140
        $method = 'map';
141
        if (is_array($mapper) && count($mapper) == 2) {
142
            $instance = $mapper[0];
143
            $method = $mapper[1];
144
            $mapper = $instance;
145
        }
146
        // check whether $mapper is an actual mapper instance, otherwise
147
        // resolve the mapper class name into a mapper instance.
148
        if (!is_object($mapper)) {
149
            $mapper = $this->resolveMapperClassName($mapper);
150
        }
151
        // In the case of a Collection or Paginator all we need is the data as a
152
        // Traversable so that we iterate and map each item.
153
        if ($data instanceof Collection) {
154
            $data = $data->all();
155
        } elseif ($this->isPaginatorInstance($data)) {
156
            $data = $data->items();
157
        }
158
159
        // call the map function of the mapper for each data in the $data array
160
        return (is_array($data) && !$this->isAssocArray($data)) ?
161
            array_map([$mapper, $method], $data) : $mapper->$method($data);
162
    }
163
164
    /**
165
     * Resolve a class name of a mapper into the actual instance.
166
     *
167
     * @param string $classname
168
     *
169
     * @return mixed
170
     */
171
    private function resolveMapperClassName($classname)
172
    {
173
        return App::make($this->getMapperNamespace().$classname);
174
    }
175
176
    /**
177
     * get the value of mappers_base_namespace from the config file.
178
     *
179
     * @return string
180
     */
181
    public function getMapperNamespace()
182
    {
183
        return $this->mappers_base_namespace;
184
    }
185
186
    /**
187
     * Check whether the given array is an associative array (key-value).
188
     *
189
     * @param array $array
190
     *
191
     * @return bool
192
     */
193
    public function isAssocArray($array)
194
    {
195
        return is_array($array) && array_keys($array) !== range(0, count($array) - 1);
196
    }
197
198
    /**
199
     * Get the content only from the response.
200
     *
201
     * @param mixed $mapper
202
     * @param mixed $data
203
     *
204
     * @return array
205
     */
206
    public function content($mapper, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $mapper 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...
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...
207
    {
208
        $response = call_user_func_array([$this, 'respond'], func_get_args());
209
210
        return $response->getData(true);
211
    }
212
213
    /**
214
     * An error occurred, respond accordingly.
215
     *
216
     * @return Illuminate\Http\JsonResponse
217
     */
218
    public function error()
219
    {
220
        return call_user_func_array([$this->error, 'handle'], func_get_args());
221
    }
222
223
    /**
224
     * this function will be accessed as facade from anywhere to get the limit number of data for the endpoint call.
225
     *
226
     * @return int
227
     */
228
    public function limit()
229
    {
230
        // get limit from config file
231
        $limit = $this->getMaximumLimit();
232
        // get the limit from the request if available else get the the default predefined limit from the config file
233
        if (Input::get('limit') && is_numeric(Input::get('limit'))) {
234
            $limit = Input::get('limit');
235
        }
236
237
        // validate the limit does not exceed the allowed value
238
        return $this->validateRequestedLimitValue($limit);
239
    }
240
241
    /**
242
     * get the limit value from the config file which represents the default and the maximum limit.
243
     *
244
     * @return int
245
     */
246
    public function getMaximumLimit()
247
    {
248
        // get the default limit value of results per request from the config file
249
        return (int) $this->limit;
250
    }
251
252
    /**
253
     * validate the requested limit doesn't exceed the default predefined limit from the config file
254
     * the user is allowed to override the default limit (form the config file) if and only if it is
255
     * less then the default limit.
256
     *
257
     * @param $limit
258
     *
259
     * @return int
260
     */
261
    private function validateRequestedLimitValue($request_limit)
262
    {
263
        // get limit from config file
264
        $limit = $this->getMaximumLimit();
265
266
        // check if limit value exceed the allowed predefined default limit value
267
        return ($request_limit <= $limit) ? (int) $request_limit : (int) $limit;
268
    }
269
270
    /**
271
     * override the limit of the config file.
272
     *
273
     * @param $limit
274
     */
275
    public function setLimit($limit)
276
    {
277
        $this->limit = (int) $limit;
278
    }
279
280
    /**
281
     * Set the base namespace from which to resolve mappers.
282
     *
283
     * @param string $namespace
284
     */
285
    public function setMapperNamespace($namespace)
286
    {
287
        $this->mappers_base_namespace = $namespace;
288
    }
289
}
290