Passed
Pull Request — master (#198)
by Alex
04:50
created

createUpdateDeleteProcessInput()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 13
c 2
b 0
f 0
dl 0
loc 21
rs 9.2222
cc 6
nc 9
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(true);
62
63
        if (!(key_exists('id', $outData) && key_exists('status', $outData) && key_exists('errors', $outData))) {
64
            throw ODataException::createInternalServerError(
65
                'Controller response array missing at least one of id, status and/or errors fields.'
66
            );
67
        }
68
        return $outData;
69
    }
70
71
    /**
72
     * @param ResourceSet $sourceResourceSet
73
     * @param array       $data
74
     * @param string                     $verb
75
     * @param  Model|null                $source
76
     * @throws InvalidOperationException
77
     * @throws ODataException
78
     * @throws \Exception
79
     * @return Model|null
80
     */
81
    protected function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
82
    {
83
        $lastWord = 'update' == $verb ? 'updated' : 'created';
84
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
85
        if (!$this->getAuth()->canAuth($this->getVerbMap()[$verb], $class, $source)) {
86
            throw new ODataException('Access denied', 403);
87
        }
88
89
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
90
91
        $success = isset($payload['id']);
92
93
        if ($success) {
94
            try {
95
                return $class::findOrFail($payload['id']);
96
            } catch (\Exception $e) {
97
                throw new ODataException($e->getMessage(), 500);
98
            }
99
        }
100
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
101
    }
102
103
104
    /**
105
     * @param Model $sourceEntityInstance
106
     * @param array $data
107
     * @param string $class
108
     * @param string $verb
109
     *
110
     * @throws ODataException
111
     * @throws InvalidOperationException
112
     * @return array|mixed
113
     */
114
    protected function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
115
    {
116
        $raw = $this->getControllerContainer();
117
        $map = $raw->getMetadata();
118
119
        if (!array_key_exists($class, $map)) {
120
            throw new InvalidOperationException('Controller mapping missing for class ' . $class . '.');
121
        }
122
        $goal = $raw->getMapping($class, $verb);
123
        if (null == $goal) {
124
            throw new InvalidOperationException(
125
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
126
            );
127
        }
128
129
        $arrayData = $data;
130
        if (!is_array($arrayData)) {
0 ignored issues
show
introduced by
The condition is_array($arrayData) is always true.
Loading history...
131
            throw ODataException::createPreConditionFailedError(
132
                'Data not resolvable to key-value array.'
133
            );
134
        }
135
136
        $controlClass = $goal['controller'];
137
        $method = $goal['method'];
138
        $paramList = $goal['parameters'];
139
        $controller = App::make($controlClass);
140
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
141
        unset($data);
142
143
        $result = call_user_func_array(array($controller, $method), $parms);
144
145
        return $this->createUpdateDeleteProcessOutput($result);
146
    }
147
148
    /**
149
     * Delete resource from a resource set.
150
     *
151
     * @param ResourceSet $sourceResourceSet
152
     * @param object      $sourceEntityInstance
153
     *
154
     * @return bool true if resources successfully deleted, otherwise false
155
     * @throws \Exception
156
     */
157
    public function deleteResource(
158
        ResourceSet $sourceResourceSet,
159
        $sourceEntityInstance
160
    ) {
161
        $source = $this->unpackSourceEntity($sourceEntityInstance);
162
163
        $verb = 'delete';
164
        if (!($source instanceof Model)) {
165
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
166
        }
167
168
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
169
        $id = $source->getKey();
170
        $name = $source->getKeyName();
171
        $data = [$name => $id];
172
173
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
174
175
        $success = isset($data['id']);
176
        if ($success) {
177
            return true;
178
        }
179
        throw new ODataException('Target model not successfully deleted', 422);
180
    }
181
182
    /**
183
     * @param ResourceSet     $resourceSet          The entity set containing the entity to fetch
184
     * @param Model|Relation  $sourceEntityInstance The source entity instance
185
     * @param object          $data                 the New data for the entity instance
186
     *
187
     * @return Model|null                           returns the newly created model if successful,
188
     *                                              or null if model creation failed.
189
     * @throws \Exception
190
     */
191
    public function createResourceforResourceSet(
192
        ResourceSet $resourceSet,
193
        $sourceEntityInstance,
194
        $data
195
    ) {
196
        $verb = 'create';
197
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
198
    }
199
200
    /**
201
     * Updates a resource.
202
     *
203
     * @param ResourceSet       $sourceResourceSet    The entity set containing the source entity
204
     * @param Model|Relation    $sourceEntityInstance The source entity instance
205
     * @param KeyDescriptor     $keyDescriptor        The key identifying the entity to fetch
206
     * @param object            $data                 the New data for the entity instance
207
     * @param bool              $shouldUpdate         Should undefined values be updated or reset to default
208
     *
209
     * @return Model|null the new resource value if it is assignable or throw exception for null
210
     * @throws \Exception
211
     */
212
    public function updateResource(
213
        ResourceSet $sourceResourceSet,
214
        $sourceEntityInstance,
215
        KeyDescriptor $keyDescriptor,
216
        $data,
217
        $shouldUpdate = false
218
    ) {
219
        $verb = 'update';
220
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
221
    }
222
223
    /**
224
     * Puts an entity instance to entity set identified by a key.
225
     *
226
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
227
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
228
     * @param array $data
229
     *
230
     * @return bool|null Returns result of executing query
231
     */
232
    public function putResource(
233
        ResourceSet $resourceSet,
234
        KeyDescriptor $keyDescriptor,
235
        $data
236
    ) {
237
        // TODO: Implement putResource() method.
238
        return true;
239
    }
240
241
242
    /**
243
     * @param ResourceSet $resourceSet
244
     * @param Model|Relation|null $sourceEntityInstance
245
     * @param mixed $data
246
     * @param mixed $verb
247
     * @return Model|null
248
     * @throws InvalidOperationException
249
     * @throws ODataException
250
     */
251
    protected function createUpdateMainWrapper(ResourceSet $resourceSet, $sourceEntityInstance, $data, $verb)
252
    {
253
        /** @var Model|null $source */
254
        $source = $this->unpackSourceEntity($sourceEntityInstance);
255
256
        $result = $this->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
257
        if (null !== $result) {
258
            LaravelQuery::queueModel($result);
259
        }
260
        return $result;
261
    }
262
}
263