Passed
Pull Request — master (#182)
by Alex
06:05
created

LaravelWriteQuery::createUpdateMainWrapper()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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