Passed
Pull Request — master (#198)
by Alex
05:55
created

LaravelWriteQuery::putResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 3
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: alex
5
 * Date: 29/09/19
6
 * Time: 6:08 PM
7
 */
8
9
namespace AlgoWeb\PODataLaravel\Query;
10
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Database\Eloquent\Relations\Relation;
13
use Illuminate\Http\JsonResponse;
14
use Illuminate\Http\Request;
15
use Illuminate\Support\Facades\App;
16
use POData\Common\InvalidOperationException;
17
use POData\Common\ODataException;
18
use POData\Providers\Metadata\ResourceSet;
19
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
20
use Symfony\Component\Process\Exception\InvalidArgumentException;
21
22
class LaravelWriteQuery extends LaravelBaseQuery
23
{
24
    /**
25
     * @param array $data
26
     * @param array $paramList
27
     * @param Model $sourceEntityInstance
28
     * @return array
29
     */
30
    protected function createUpdateDeleteProcessInput($data, $paramList, Model $sourceEntityInstance)
31
    {
32
        $parms = [];
33
34
        foreach ($paramList as $spec) {
35
            $varType = isset($spec['type']) ? $spec['type'] : null;
36
            $varName = $spec['name'];
37
            if (null == $varType) {
38
                $parms[] = ('id' == $varName) ? $sourceEntityInstance->getKey() : $sourceEntityInstance->$varName;
39
                continue;
40
            }
41
            // TODO: Give this smarts and actively pick up instantiation details
42
            /** @var Request $var */
43
            $var = new $varType();
44
            if ($spec['isRequest']) {
45
                $var->setMethod('POST');
46
                $var->request = new \Symfony\Component\HttpFoundation\ParameterBag($data);
47
            }
48
            $parms[] = $var;
49
        }
50
        return $parms;
51
    }
52
53
54
    /**
55
     * @param JsonResponse $result
56
     * @throws ODataException
57
     * @return array|mixed
58
     */
59
    protected function createUpdateDeleteProcessOutput(JsonResponse $result)
60
    {
61
        $outData = $result->getData();
62
        if (is_object($outData)) {
63
            $outData = (array) $outData;
64
        }
65
66
        if (!is_array($outData)) {
67
            throw ODataException::createInternalServerError('Controller response does not have an array.');
68
        }
69
        if (!(key_exists('id', $outData) && key_exists('status', $outData) && key_exists('errors', $outData))) {
70
            throw ODataException::createInternalServerError(
71
                'Controller response array missing at least one of id, status and/or errors fields.'
72
            );
73
        }
74
        return $outData;
75
    }
76
77
    /**
78
     * @param ResourceSet $sourceResourceSet
79
     * @param array       $data
80
     * @param string                     $verb
81
     * @param  Model|null                $source
82
     * @throws InvalidOperationException
83
     * @throws ODataException
84
     * @throws \Exception
85
     * @return Model|null
86
     */
87
    protected function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
88
    {
89
        $lastWord = 'update' == $verb ? 'updated' : 'created';
90
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
91
        if (!$this->getAuth()->canAuth($this->getVerbMap()[$verb], $class, $source)) {
92
            throw new ODataException('Access denied', 403);
93
        }
94
95
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
96
97
        $success = isset($payload['id']);
98
99
        if ($success) {
100
            try {
101
                return $class::findOrFail($payload['id']);
102
            } catch (\Exception $e) {
103
                throw new ODataException($e->getMessage(), 500);
104
            }
105
        }
106
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
107
    }
108
109
110
    /**
111
     * @param Model $sourceEntityInstance
112
     * @param array|object $data
113
     * @param string $class
114
     * @param string $verb
115
     *
116
     * @throws ODataException
117
     * @throws InvalidOperationException
118
     * @return array|mixed
119
     */
120
    protected function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
121
    {
122
        $raw = $this->getControllerContainer();
123
        $map = $raw->getMetadata();
124
125
        if (!array_key_exists($class, $map)) {
126
            throw new InvalidOperationException('Controller mapping missing for class ' . $class . '.');
127
        }
128
        $goal = $raw->getMapping($class, $verb);
129
        if (null == $goal) {
130
            throw new InvalidOperationException(
131
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
132
            );
133
        }
134
135
        $arrayData = is_object($data) ? (array)$data : $data;
136
        if (!is_array($arrayData)) {
0 ignored issues
show
introduced by
The condition is_array($arrayData) is always true.
Loading history...
137
            throw ODataException::createPreConditionFailedError(
138
                'Data not resolvable to key-value array.'
139
            );
140
        }
141
142
        $controlClass = $goal['controller'];
143
        $method = $goal['method'];
144
        $paramList = $goal['parameters'];
145
        $controller = App::make($controlClass);
146
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
147
        unset($data);
148
149
        $result = call_user_func_array(array($controller, $method), $parms);
150
151
        return $this->createUpdateDeleteProcessOutput($result);
152
    }
153
154
    /**
155
     * Delete resource from a resource set.
156
     *
157
     * @param ResourceSet $sourceResourceSet
158
     * @param object      $sourceEntityInstance
159
     *
160
     * @return bool true if resources sucessfully deteled, otherwise false
161
     * @throws \Exception
162
     */
163
    public function deleteResource(
164
        ResourceSet $sourceResourceSet,
165
        $sourceEntityInstance
166
    ) {
167
        $source = $this->unpackSourceEntity($sourceEntityInstance);
168
169
        $verb = 'delete';
170
        if (!($source instanceof Model)) {
171
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
172
        }
173
174
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
175
        $id = $source->getKey();
176
        $name = $source->getKeyName();
177
        $data = [$name => $id];
178
179
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
180
181
        $success = isset($data['id']);
182
        if ($success) {
183
            return true;
184
        }
185
        throw new ODataException('Target model not successfully deleted', 422);
186
    }
187
188
    /**
189
     * @param ResourceSet     $resourceSet          The entity set containing the entity to fetch
190
     * @param Model|Relation  $sourceEntityInstance The source entity instance
191
     * @param object          $data                 the New data for the entity instance
192
     *
193
     * @return Model|null                           returns the newly created model if successful,
194
     *                                              or null if model creation failed.
195
     * @throws \Exception
196
     */
197
    public function createResourceforResourceSet(
198
        ResourceSet $resourceSet,
199
        $sourceEntityInstance,
200
        $data
201
    ) {
202
        $verb = 'create';
203
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
204
    }
205
206
    /**
207
     * Updates a resource.
208
     *
209
     * @param ResourceSet       $sourceResourceSet    The entity set containing the source entity
210
     * @param Model|Relation    $sourceEntityInstance The source entity instance
211
     * @param KeyDescriptor     $keyDescriptor        The key identifying the entity to fetch
212
     * @param object            $data                 the New data for the entity instance
213
     * @param bool              $shouldUpdate         Should undefined values be updated or reset to default
214
     *
215
     * @return Model|null the new resource value if it is assignable or throw exception for null
216
     * @throws \Exception
217
     */
218
    public function updateResource(
219
        ResourceSet $sourceResourceSet,
220
        $sourceEntityInstance,
221
        KeyDescriptor $keyDescriptor,
222
        $data,
223
        $shouldUpdate = false
224
    ) {
225
        $verb = 'update';
226
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
227
    }
228
229
    /**
230
     * Puts an entity instance to entity set identified by a key.
231
     *
232
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
233
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
234
     * @param array $data
235
     *
236
     * @return bool|null Returns result of executing query
237
     */
238
    public function putResource(
239
        ResourceSet $resourceSet,
240
        KeyDescriptor $keyDescriptor,
241
        $data
242
    ) {
243
        // TODO: Implement putResource() method.
244
        return true;
245
    }
246
247
248
    /**
249
     * @param ResourceSet $resourceSet
250
     * @param Model|Relation|null $sourceEntityInstance
251
     * @param mixed $data
252
     * @param mixed $verb
253
     * @return Model|null
254
     * @throws InvalidOperationException
255
     * @throws ODataException
256
     */
257
    protected function createUpdateMainWrapper(ResourceSet $resourceSet, $sourceEntityInstance, $data, $verb)
258
    {
259
        /** @var Model|null $source */
260
        $source = $this->unpackSourceEntity($sourceEntityInstance);
261
262
        $result = $this->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
263
        if (null !== $result) {
264
            LaravelQuery::queueModel($result);
265
        }
266
        return $result;
267
    }
268
}
269