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

LaravelWriteQuery::createUpdateCoreWrapper()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 20
rs 9.5555
cc 5
nc 8
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 Symfony\Component\Process\Exception\InvalidArgumentException;
18
19
class LaravelWriteQuery extends LaravelBaseQuery
20
{
21
22
23
    /**
24
     * @param $data
25
     * @param $paramList
26
     * @param Model|null $sourceEntityInstance
27
     * @return array
28
     */
29
    public function createUpdateDeleteProcessInput($data, $paramList, Model $sourceEntityInstance)
30
    {
31
        $parms = [];
32
33
        foreach ($paramList as $spec) {
34
            $varType = isset($spec['type']) ? $spec['type'] : null;
35
            $varName = $spec['name'];
36
            if (null == $varType) {
37
                $parms[] = ('id' == $varName) ? $sourceEntityInstance->getKey() : $sourceEntityInstance->$varName;
38
                continue;
39
            }
40
            // TODO: Give this smarts and actively pick up instantiation details
41
            $var = new $varType();
42
            if ($spec['isRequest']) {
43
                $var->setMethod('POST');
44
                $var->request = new \Symfony\Component\HttpFoundation\ParameterBag($data);
45
            }
46
            $parms[] = $var;
47
        }
48
        return $parms;
49
    }
50
51
52
    /**
53
     * @param $result
54
     * @throws ODataException
55
     * @return array|mixed
56
     */
57
    public function createUpdateDeleteProcessOutput($result)
58
    {
59
        if (!($result instanceof \Illuminate\Http\JsonResponse)) {
60
            throw ODataException::createInternalServerError('Controller response not well-formed json.');
61
        }
62
        $outData = $result->getData();
63
        if (is_object($outData)) {
64
            $outData = (array) $outData;
65
        }
66
67
        if (!is_array($outData)) {
68
            throw ODataException::createInternalServerError('Controller response does not have an array.');
69
        }
70
        if (!(key_exists('id', $outData) && key_exists('status', $outData) && key_exists('errors', $outData))) {
71
            throw ODataException::createInternalServerError(
72
                'Controller response array missing at least one of id, status and/or errors fields.'
73
            );
74
        }
75
        return $outData;
76
    }
77
78
    /**
79
     * @param ResourceSet $sourceResourceSet
80
     * @param $data
81
     * @param                            $verb
82
     * @param  Model|null                $source
83
     * @throws InvalidOperationException
84
     * @throws ODataException
85
     * @throws \Exception
86
     * @return Model|null
87
     */
88
    public function createUpdateCoreWrapper(ResourceSet $sourceResourceSet, $data, $verb, Model $source = null)
89
    {
90
        $lastWord = 'update' == $verb ? 'updated' : 'created';
91
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
92
        if (!$this->getAuth()->canAuth($this->getVerbMap()[$verb], $class, $source)) {
93
            throw new ODataException('Access denied', 403);
94
        }
95
96
        $payload = $this->createUpdateDeleteCore($source, $data, $class, $verb);
97
98
        $success = isset($payload['id']);
99
100
        if ($success) {
101
            try {
102
                return $class::findOrFail($payload['id']);
103
            } catch (\Exception $e) {
104
                throw new ODataException($e->getMessage(), 500);
105
            }
106
        }
107
        throw new ODataException('Target model not successfully ' . $lastWord, 422);
108
    }
109
110
111
    /**
112
     * @param $sourceEntityInstance
113
     * @param $data
114
     * @param $class
115
     * @param string $verb
116
     *
117
     * @throws ODataException
118
     * @throws InvalidOperationException
119
     * @return array|mixed
120
     */
121
    public function createUpdateDeleteCore($sourceEntityInstance, $data, $class, $verb)
122
    {
123
        $raw = $this->getControllerContainer();
124
        $map = $raw->getMetadata();
125
126
        if (!array_key_exists($class, $map)) {
127
            throw new InvalidOperationException('Controller mapping missing for class ' . $class . '.');
128
        }
129
        $goal = $raw->getMapping($class, $verb);
130
        if (null == $goal) {
131
            throw new InvalidOperationException(
132
                'Controller mapping missing for ' . $verb . ' verb on class ' . $class . '.'
133
            );
134
        }
135
136
        if (null === $data) {
137
            $msg = 'Data must not be null';
138
            throw new InvalidOperationException($msg);
139
        }
140
        if (is_object($data)) {
141
            $arrayData = (array) $data;
142
        } else {
143
            $arrayData = $data;
144
        }
145
        if (!is_array($arrayData)) {
146
            throw ODataException::createPreConditionFailedError(
147
                'Data not resolvable to key-value array.'
148
            );
149
        }
150
151
        $controlClass = $goal['controller'];
152
        $method = $goal['method'];
153
        $paramList = $goal['parameters'];
154
        $controller = App::make($controlClass);
155
        $parms = $this->createUpdateDeleteProcessInput($arrayData, $paramList, $sourceEntityInstance);
156
        unset($data);
157
158
        $result = call_user_func_array(array($controller, $method), $parms);
159
160
        return $this->createUpdateDeleteProcessOutput($result);
161
    }
162
163
    /**
164
     * Delete resource from a resource set.
165
     *
166
     * @param ResourceSet $sourceResourceSet
167
     * @param object      $sourceEntityInstance
168
     *
169
     * @return bool true if resources sucessfully deteled, otherwise false
170
     * @throws \Exception
171
     */
172
    public function deleteResource(
173
        ResourceSet $sourceResourceSet,
174
        $sourceEntityInstance
175
    ) {
176
        $source = $this->unpackSourceEntity($sourceEntityInstance);
177
178
        $verb = 'delete';
179
        if (!($source instanceof Model)) {
180
            throw new InvalidArgumentException('Source entity must be an Eloquent model.');
181
        }
182
183
        $class = $sourceResourceSet->getResourceType()->getInstanceType()->getName();
184
        $id = $source->getKey();
185
        $name = $source->getKeyName();
186
        $data = [$name => $id];
187
188
        $data = $this->createUpdateDeleteCore($source, $data, $class, $verb);
189
190
        $success = isset($data['id']);
191
        if ($success) {
192
            return true;
193
        }
194
        throw new ODataException('Target model not successfully deleted', 422);
195
    }
196
}
197