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

createUpdateDeleteProcessOutput()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 10
rs 10
cc 4
nc 2
nop 1
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, array $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
        $controlClass = $goal['controller'];
130
        $method = $goal['method'];
131
        $paramList = $goal['parameters'];
132
        $controller = App::make($controlClass);
133
        $parms = $this->createUpdateDeleteProcessInput($data, $paramList, $sourceEntityInstance);
134
        unset($data);
135
136
        $result = call_user_func_array(array($controller, $method), $parms);
137
138
        return $this->createUpdateDeleteProcessOutput($result);
139
    }
140
141
    /**
142
     * Delete resource from a resource set.
143
     *
144
     * @param ResourceSet $sourceResourceSet
145
     * @param object      $sourceEntityInstance
146
     *
147
     * @return bool true if resources successfully deleted, otherwise false
148
     * @throws \Exception
149
     */
150
    public function deleteResource(
151
        ResourceSet $sourceResourceSet,
152
        $sourceEntityInstance
153
    ) {
154
        $source = $this->unpackSourceEntity($sourceEntityInstance);
155
156
        $verb = 'delete';
157
        if (!($source instanceof Model)) {
158
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
159
        }
160
161
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
162
        $id = $source->getKey();
163
        $name = $source->getKeyName();
164
        $data = [$name => $id];
165
166
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
167
168
        $success = isset($data['id']);
169
        if ($success) {
170
            return true;
171
        }
172
        throw new ODataException('Target model not successfully deleted', 422);
173
    }
174
175
    /**
176
     * @param ResourceSet     $resourceSet          The entity set containing the entity to fetch
177
     * @param Model|Relation  $sourceEntityInstance The source entity instance
178
     * @param object          $data                 the New data for the entity instance
179
     *
180
     * @return Model|null                           returns the newly created model if successful,
181
     *                                              or null if model creation failed.
182
     * @throws \Exception
183
     */
184
    public function createResourceforResourceSet(
185
        ResourceSet $resourceSet,
186
        $sourceEntityInstance,
187
        $data
188
    ) {
189
        $verb = 'create';
190
        return $this->createUpdateMainWrapper($resourceSet, $sourceEntityInstance, $data, $verb);
191
    }
192
193
    /**
194
     * Updates a resource.
195
     *
196
     * @param ResourceSet       $sourceResourceSet    The entity set containing the source entity
197
     * @param Model|Relation    $sourceEntityInstance The source entity instance
198
     * @param KeyDescriptor     $keyDescriptor        The key identifying the entity to fetch
199
     * @param object            $data                 the New data for the entity instance
200
     * @param bool              $shouldUpdate         Should undefined values be updated or reset to default
201
     *
202
     * @return Model|null the new resource value if it is assignable or throw exception for null
203
     * @throws \Exception
204
     */
205
    public function updateResource(
206
        ResourceSet $sourceResourceSet,
207
        $sourceEntityInstance,
208
        KeyDescriptor $keyDescriptor,
209
        $data,
210
        $shouldUpdate = false
211
    ) {
212
        $verb = 'update';
213
        return $this->createUpdateMainWrapper($sourceResourceSet, $sourceEntityInstance, $data, $verb);
214
    }
215
216
    /**
217
     * Puts an entity instance to entity set identified by a key.
218
     *
219
     * @param ResourceSet   $resourceSet   The entity set containing the entity to update
220
     * @param KeyDescriptor $keyDescriptor The key identifying the entity to update
221
     * @param array $data
222
     *
223
     * @return bool|null Returns result of executing query
224
     */
225
    public function putResource(
226
        ResourceSet $resourceSet,
227
        KeyDescriptor $keyDescriptor,
228
        $data
229
    ) {
230
        // TODO: Implement putResource() method.
231
        return true;
232
    }
233
234
235
    /**
236
     * @param ResourceSet $resourceSet
237
     * @param Model|Relation|null $sourceEntityInstance
238
     * @param mixed $data
239
     * @param mixed $verb
240
     * @return Model|null
241
     * @throws InvalidOperationException
242
     * @throws ODataException
243
     */
244
    protected function createUpdateMainWrapper(ResourceSet $resourceSet, $sourceEntityInstance, $data, $verb)
245
    {
246
        /** @var Model|null $source */
247
        $source = $this->unpackSourceEntity($sourceEntityInstance);
248
249
        $result = $this->createUpdateCoreWrapper($resourceSet, $data, $verb, $source);
250
        if (null !== $result) {
251
            LaravelQuery::queueModel($result);
252
        }
253
        return $result;
254
    }
255
}
256