Completed
Push — master ( 039121...10fe91 )
by Timur
02:19
created

ApiResource::initializeResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Laravel\Forge;
4
5
use ArrayAccess;
6
use InvalidArgumentException;
7
use GuzzleHttp\ClientInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use GuzzleHttp\Exception\RequestException;
10
use Laravel\Forge\Traits\ArrayAccessTrait;
11
use Laravel\Forge\Contracts\ResourceContract;
12
use Laravel\Forge\Exceptions\Resources\DeleteResourceException;
13
use Laravel\Forge\Exceptions\Resources\UpdateResourceException;
14
15
abstract class ApiResource implements ArrayAccess, ResourceContract
16
{
17
    use ArrayAccessTrait;
18
19
    /**
20
     * @var \Laravel\Forge\ApiProvider
21
     */
22
    protected $api;
23
24
    /**
25
     * @var array
26
     */
27
    protected $data = [];
28
29
    /**
30
     * @var \Laravel\Forge\ApiResource
31
     */
32
    protected $owner;
33
34
    /**
35
     * Create new resource instance.
36
     *
37
     * @param \Laravel\Forge\ApiProvider $api   = null
38
     * @param array                      $data  = []
39
     * @param \Laravel\Forge\ApiResource $owner
40
     */
41
    public function __construct(ApiProvider $api = null, array $data = [], ApiResource $owner = null)
42
    {
43
        $this->api = $api;
44
        $this->data = $data;
45
        $this->owner = $owner;
46
47
        $this->initializeResource();
0 ignored issues
show
Unused Code introduced by
The call to the method Laravel\Forge\ApiResource::initializeResource() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
48
    }
49
50
    /**
51
     * Resource type.
52
     *
53
     * @return string
54
     */
55
    abstract public static function resourceType();
56
57
    /**
58
     * Resource path (relative to owner or API root).
59
     *
60
     * @return string
61
     */
62
    abstract public function resourcePath();
63
64
    /**
65
     * Initialize new resource.
66
     *
67
     * @return mixed
68
     */
69
    protected function initializeResource()
70
    {
71
        return $this;
72
    }
73
74
    /**
75
     * Create new Resource instance from HTTP response.
76
     *
77
     * @param \Psr\Http\Message\ResponseInterface $response
78
     * @param \Laravel\Forge\ApiProvider          $api
79
     * @param \Laravel\Forge\ApiResource          $owner    = null
80
     */
81
    public static function createFromResponse(ResponseInterface $response, ApiProvider $api, ApiResource $owner = null)
82
    {
83
        $json = json_decode((string) $response->getBody(), true);
84
        $resourceType = static::resourceType();
85
86
        if (empty($json[$resourceType])) {
87
            static::throwNotFoundException();
88
        }
89
90
        return new static($api, $json[$resourceType], $owner);
91
    }
92
93
    /**
94
     * Throw HTTP Not Found exception.
95
     *
96
     * @throws \Exception
97
     */
98
    protected static function throwNotFoundException()
99
    {
100
        throw new InvalidArgumentException('Given response is not a '.static::resourceType().' response.');
101
    }
102
103
    /**
104
     * Determines if current resource has an owner.
105
     *
106
     * @return bool
107
     */
108
    public function hasResourceOwner(): bool
109
    {
110
        return !is_null($this->owner);
111
    }
112
113
    /**
114
     * Get current resource owner.
115
     *
116
     * @return \Laravel\Forge\ApiResource|null
117
     */
118
    public function resourceOwner()
119
    {
120
        return $this->owner;
121
    }
122
123
    /**
124
     * Get API provider.
125
     *
126
     * @return \Laravel\Forge\ApiProvider
127
     */
128
    public function getApi(): ApiProvider
129
    {
130
        return $this->api;
131
    }
132
133
    /**
134
     * Get underlying API provider's HTTP client.
135
     *
136
     * @return \GuzzleHttp\ClientInterface
137
     */
138
    public function getHttpClient(): ClientInterface
139
    {
140
        return $this->api->getClient();
141
    }
142
143
    /**
144
     * Get resource data.
145
     *
146
     * @param string|int $key
147
     * @param mixed      $default = null
148
     *
149
     * @return mixed|null
150
     */
151
    public function getData($key, $default = null)
152
    {
153
        return $this->data[$key] ?? $default;
154
    }
155
156
    /**
157
     * Resource API URL.
158
     *
159
     * @param string $path            = ''
160
     * @param bool   $withPropagation = true
161
     *
162
     * @return string
163
     */
164
    public function apiUrl(string $path = '', bool $withPropagation = true): string
165
    {
166
        $path = ($path ? '/'.ltrim($path, '/') : '');
167
        $resourcePath = rtrim($this->resourcePath(), '/').'/'.$this->id().$path;
168
169
        if (!$this->hasResourceOwner() || !$withPropagation) {
170
            return $resourcePath;
171
        }
172
173
        return $this->resourceOwner()->apiUrl($resourcePath);
174
    }
175
176
    /**
177
     * Resource ID.
178
     *
179
     * @return int
180
     */
181
    public function id(): int
182
    {
183
        return intval($this->getData('id', 0));
184
    }
185
186
    /**
187
     * Resource name.
188
     *
189
     * @return string|null
190
     */
191
    public function name()
192
    {
193
        return $this->getData('name');
194
    }
195
196
    /**
197
     * Resource status.
198
     *
199
     * @return string|null
200
     */
201
    public function status()
202
    {
203
        return $this->getData('status');
204
    }
205
206
    /**
207
     * Get resource creation date.
208
     *
209
     * @return string|null
210
     */
211
    public function createdAt()
212
    {
213
        return $this->getData('created_at');
214
    }
215
216
    /**
217
     * Update resource data.
218
     *
219
     * @throws UpdateResourceException
220
     *
221
     * @return bool
222
     */
223
    public function update(array $payload): bool
224
    {
225
        $resourceType = static::resourceType();
226
227
        try {
228
            $response = $this->getHttpClient()->request('PUT', $this->apiUrl(), [
229
                'form_params' => $payload,
230
            ]);
231
        } catch (RequestException $e) {
232
            $this->throwResourceException($e->getResponse(), 'update', UpdateResourceException::class);
0 ignored issues
show
Bug introduced by
It seems like $e->getResponse() can be null; however, throwResourceException() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
233
        }
234
235
        $json = json_decode((string) $response->getBody(), true);
236
237
        if (empty($json[$resourceType])) {
238
            return false;
239
        }
240
241
        $this->data = $json[$resourceType];
242
243
        return true;
244
    }
245
246
    /**
247
     * Delete current resource.
248
     *
249
     * @throws \Laravel\Forge\Exceptions\Resources\DeleteResourceException
250
     *
251
     * @return bool
252
     */
253
    public function delete()
254
    {
255
        try {
256
            $this->getHttpClient()->request('DELETE', $this->apiUrl());
257
        } catch (RequestException $e) {
258
            $this->throwResourceException($e->getResponse(), 'delete', DeleteResourceException::class);
0 ignored issues
show
Bug introduced by
It seems like $e->getResponse() can be null; however, throwResourceException() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
259
        }
260
261
        return true;
262
    }
263
264
    /**
265
     * @throws \Exception
266
     */
267
    protected function throwResourceException(ResponseInterface $response, string $action, string $className)
268
    {
269
        $message = 'Unable to '.$action.' resource (type: '.static::resourceType().', ID: '.$this->id().'). ';
270
        $message .= 'Server response: "'.((string) $response->getBody()).'".';
271
272
        throw new $className($message, $response->getStatusCode());
273
    }
274
}
275