Issues (150)

src/Extension/BaseRelationsTrait.php (1 issue)

1
<?php
2
3
namespace SoliDry\Extension;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Http\Response;
7
use SoliDry\Blocks\FileManager;
8
use SoliDry\Helpers\Classes;
9
use SoliDry\Helpers\ConfigHelper;
10
use SoliDry\Helpers\Json;
11
use SoliDry\Helpers\MigrationsHelper;
12
use SoliDry\Types\DirsInterface;
13
use SoliDry\Types\ModelsInterface;
14
use SoliDry\Types\PhpInterface;
15
use SoliDry\Types\ApiInterface;
16
use SoliDry\Helpers\ConfigHelper as conf;
17
18
/**
19
 * Trait BaseRelationsTrait
20
 *
21
 * @package SoliDry\Extension
22
 *
23
 * @property Json json
24
 * @property \SoliDry\Containers\Response response
25
 */
26
trait BaseRelationsTrait
27
{
28
    use BaseModelTrait;
29
30
    /**
31
     * GET the relationships of this particular Entity
32
     *
33
     * @param Request $request
34
     * @param int|string $id
35
     * @param string $relation
36
     * @return Response
37
     */
38
    public function relations(Request $request, $id, string $relation): Response
39
    {
40
        $model = $this->getEntity($id);
41
        if (empty($model)) {
42
            return $this->response->getModelNotFoundError($this->entity, $id);
43
        }
44
45
        return $this->response->getRelations($model->$relation, $relation, $request);
46
    }
47
48
    /**
49
     * GET the fully represented relationships of this particular Entity
50
     *
51
     * @param Request $request
52
     * @param int|string $id
53
     * @param string $relation
54
     * @return Response
55
     */
56
    public function related(Request $request, $id, string $relation): Response
57
    {
58
        $data = ($request->input(ModelsInterface::PARAM_DATA) === NULL) ? ModelsInterface::DEFAULT_DATA
59
            : Json::decode(urldecode($request->input(ModelsInterface::PARAM_DATA)));
60
61
        $model = $this->getEntity($id);
62
        if (empty($model)) {
63
            return $this->response->getModelNotFoundError($this->entity, $id);
64
        }
65
66
        $relEntity = ucfirst($relation);
67
        $formRequestEntity = $this->getFormRequestEntity(conf::getModuleName(), $relEntity);
68
        $relFormRequest = new $formRequestEntity();
69
70
        $this->response->setFormRequest($relFormRequest);
71
        $this->response->setEntity($relEntity);
72
73
        return $this->response->getRelated($model->$relation, $data);
74
    }
75
76
    /**
77
     * POST relationships for specific entity id
78
     *
79
     * @param Request $request
80
     * @param int|string $id
81
     * @param string $relation
82
     * @return Response
83
     */
84
    public function createRelations(Request $request, $id, string $relation): Response
85
    {
86
        $model = $this->presetRelations($request, $id, $relation);
87
88
        return $this->response->get($model, []);
89
    }
90
91
    /**
92
     * PATCH relationships for specific entity id
93
     *
94
     * @param Request $request
95
     * @param int|string $id
96
     * @param string $relation
97
     * @return Response
98
     */
99
    public function updateRelations(Request $request, $id, string $relation): Response
100
    {
101
        $model = $this->presetRelations($request, $id, $relation);
102
103
        return $this->response->get($model, []);
104
    }
105
106
    /**
107
     * @param Request $request
108
     * @param int|string $id
109
     * @param string $relation
110
     * @return mixed
111
     */
112
    private function presetRelations(Request $request, $id, string $relation)
113
    {
114
        $json = Json::decode($request->getContent());
115
        $this->setRelationships($json, $id, true);
116
117
        // set include for relations
118
        $_GET['include'] = $relation;
119
        $model = $this->getEntity($id);
120
121
        if (empty($model)) {
122
            return $this->response->getModelNotFoundError($this->entity, $id);
123
        }
124
125
        return $model;
126
    }
127
128
    /**
129
     * DELETE relationships for specific entity id
130
     *
131
     * @param Request $request JSON API formatted string
132
     * @param int|string $id   int id of an entity
133
     * @param string $relation
134
     * @return Response
135
     */
136
    public function deleteRelations(Request $request, $id, string $relation): Response
137
    {
138
        $json = Json::decode($request->getContent());
139
        $jsonApiRels = Json::getData($json);
140
        if (empty($jsonApiRels) === false) {
141
            $lowEntity = strtolower($this->entity);
142
            foreach ($jsonApiRels as $index => $val) {
143
                $rId = $val[ApiInterface::RAML_ID];
144
                // if pivot file exists then save
145
                $ucEntity = ucfirst($relation);
146
                $file = DirsInterface::MODULES_DIR . PhpInterface::SLASH
147
                    . ConfigHelper::getModuleName() . PhpInterface::SLASH .
148
                    DirsInterface::ENTITIES_DIR . PhpInterface::SLASH .
149
                    $this->entity . $ucEntity . PhpInterface::PHP_EXT;
150
                if (file_exists(PhpInterface::SYSTEM_UPDIR . $file)) { // ManyToMany rel
151
                    $pivotEntity = Classes::getModelEntity($this->entity . $ucEntity);
152
                    // clean up old links
153
                    $this->getModelEntities(
154
                        $pivotEntity,
155
                        [
156
                            [
157
                                $lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID => $id,
158
                                $relation . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID  => $rId,
159
                            ],
160
                        ]
161
                    )->delete();
162
                } else { // OneToOne/Many - note this is always updates one row related to entity e.g.:
163
                    // find article by id and update tag_id or topic_id
164
                    $entity = Classes::getModelEntity($this->entity);
165
166
                    /** @var \Illuminate\Database\Eloquent\Builder $model */
167
                    $model = $this->getModelEntities(
168
                        $entity, [
169
                            ApiInterface::RAML_ID,
170
                            $id,
171
                        ]
172
                    );
173
174
                    $model->update([$relation . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID => 0]);
175
                }
176
            }
177
        }
178
179
        return $this->response->getDeleteRelations();
180
    }
181
182
    /**
183
     * @param array $json
184
     * @param int|string $eId
185
     * @param bool $isRemovable
186
     */
187
    protected function setRelationships(array $json, $eId, bool $isRemovable = false): void
188
    {
189
        $jsonApiRels = Json::getRelationships($json);
190
        if (empty($jsonApiRels) === false) {
191
            foreach ($jsonApiRels as $entity => $value) {
192
                if (empty($value[ApiInterface::RAML_DATA][ApiInterface::RAML_ID]) === false) {
193
                    // if there is only one relationship
194
                    $rId = $value[ApiInterface::RAML_DATA][ApiInterface::RAML_ID];
195
                    $this->saveRelationship($entity, $eId, $rId, $isRemovable);
196
                } else {
197
                    // if there is an array of relationships
198
                    foreach ($value[ApiInterface::RAML_DATA] as $index => $val) {
199
                        $rId = $val[ApiInterface::RAML_ID];
200
                        $this->saveRelationship($entity, $eId, $rId, $isRemovable);
201
                    }
202
                }
203
            }
204
        }
205
    }
206
207
    /**
208
     * @param      $entity
209
     * @param int|string $eId
210
     * @param int|string $rId
211
     * @param bool $isRemovable
212
     */
213
    private function saveRelationship($entity, $eId, $rId, bool $isRemovable = false): void
214
    {
215
        $ucEntity = Classes::getClassName($entity);
216
        $lowEntity = MigrationsHelper::getTableName($this->entity);
217
        // if pivot file exists then save
218
        $filePivot = FileManager::getPivotFile($this->entity, $ucEntity);
219
        $filePivotInverse = FileManager::getPivotFile($ucEntity, $this->entity);
220
        $pivotExists = file_exists(PhpInterface::SYSTEM_UPDIR . $filePivot);
221
        $pivotInverseExists = file_exists(PhpInterface::SYSTEM_UPDIR . $filePivotInverse);
222
        if ($pivotExists === true || $pivotInverseExists === true) { // ManyToMany rel
223
            $pivotEntity = NULL;
224
225
            if ($pivotExists) {
226
                $pivotEntity = Classes::getModelEntity($this->entity . $ucEntity);
227
            } else {
228
                if ($pivotInverseExists) {
229
                    $pivotEntity = Classes::getModelEntity($ucEntity . $this->entity);
230
                }
231
            }
232
233
            if ($isRemovable === true) {
234
                $this->clearPivotBeforeSave($pivotEntity, $lowEntity, $eId);
235
            }
236
            $this->savePivot($pivotEntity, $lowEntity, $entity, $eId, $rId);
237
        } else { // OneToOne
238
            $this->saveModel($ucEntity, $lowEntity, $eId, $rId);
239
        }
240
    }
241
242
    /**
243
     * @param string $pivotEntity
244
     * @param string $lowEntity
245
     * @param int|string $eId
246
     */
247
    private function clearPivotBeforeSave(string $pivotEntity, string $lowEntity, $eId): void
248
    {
249
        if ($this->relsRemoved === false) {
250
            // clean up old links
251
            $this->getModelEntities(
252
                $pivotEntity,
253
                [$lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID, $eId]
254
            )->delete();
255
            $this->relsRemoved = true;
0 ignored issues
show
Bug Best Practice introduced by
The property relsRemoved does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
256
        }
257
    }
258
259
    /**
260
     * @param string $pivotEntity
261
     * @param string $lowEntity
262
     * @param string $entity
263
     * @param int|string $eId
264
     * @param int|string $rId
265
     */
266
    private function savePivot(string $pivotEntity, string $lowEntity, string $entity, $eId, $rId): void
267
    {
268
        $pivot = new $pivotEntity();
269
        $pivot->{$entity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID} = $rId;
270
        $pivot->{$lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID} = $eId;
271
        $pivot->save();
272
    }
273
274
    /**
275
     * Saves model with related id from linked table full duplex
276
     *
277
     * @param string $ucEntity
278
     * @param string $lowEntity
279
     * @param int|string $eId
280
     * @param int|string $rId
281
     */
282
    private function saveModel(string $ucEntity, string $lowEntity, $eId, $rId): void
283
    {
284
        $relEntity = Classes::getModelEntity($ucEntity);
285
        $model = $this->getModelEntity($relEntity, $rId);
286
287
        // swap table and field trying to find rels with inverse
288
        if (!property_exists($model, $lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID)) {
289
            $ucTmp = $ucEntity;
290
            $ucEntity = ucfirst($lowEntity);
291
            $relEntity = Classes::getModelEntity($ucEntity);
292
            $model = $this->getModelEntity($relEntity, $eId);
293
            $lowEntity = strtolower($ucTmp);
294
295
            $model->{$lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID} = $rId;
296
            $model->save();
297
298
            return;
299
        }
300
301
        $model->{$lowEntity . PhpInterface::UNDERSCORE . ApiInterface::RAML_ID} = $eId;
302
        $model->save();
303
    }
304
}