Passed
Push — master ( 1b8711...f8bf6d )
by Mario
04:02
created

WorksWithFileUploads::getDefaultRelativePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace RafflesArgentina\ResourceController\Traits;
4
5
use Lang;
6
use Storage;
7
8
use Symfony\Component\HttpFoundation\File\UploadedFile;
9
use Symfony\Component\HttpFoundation\File\Exception\UploadException;
10
11
use Illuminate\Http\Request;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Database\Eloquent\Relations\Relation;
14
use Illuminate\Database\Eloquent\Relations\{HasOne, MorphOne, BelongsTo};
15
use Illuminate\Database\Eloquent\Relations\{HasMany, MorphMany, MorphToMany, BelongsToMany};
16
17
trait WorksWithFileUploads
18
{
19
    /**
20
     * Upload request files and update or create relations handling array type request data.
21
     *
22
     * @param Request     $request      The Request object.
23
     * @param Model       $model        The eloquent model.
24
     * @param string|null $relativePath The file uploads relative path.
25
     *
26
     * @return void
27
     */
28
    public function uploadFiles(Request $request, Model $model, $relativePath = null)
29
    {
30
        if (!$relativePath) {
31
            $relativePath = $this->getDefaultRelativePath();
32
        }
33
34
        $files = $request->files;
35
        foreach ($files as $relationName => $uploadedFiles) {
36
            $this->_checkFileRelationExists($model, $relationName);
37
            $relation = $model->{$relationName}();
38
39
            foreach ($uploadedFiles as $id => $uploadedFile) {
40
                if (!$uploadedFile->isValid()) {
41
                    throw new UploadException($uploadedFile->getError());
42
                }
43
44
                $location = $this->getUploadedFileLocation($uploadedFile, $relativePath);
45
                $this->handleFileRelations($model, $relation, $location, $id);
46
            }
47
        }
48
    }
49
50
    /**
51
     * Get storage path for the configured driver.
52
     *
53
     * @param string $relativePath The relative path.
54
     *
55
     * @return string
56
     */
57
    protected function getStoragePath($relativePath)
58
    {
59
        return Storage::disk()->getDriver()->getAdapter()->getPathPrefix().$relativePath;
60
    }
61
62
    /**
63
     * Get default relative path.
64
     *
65
     * @return string
66
     */
67
    protected function getDefaultRelativePath()
68
    {
69
        return 'uploads/';
70
    }
71
72
    /**
73
     * Get location column.
74
     *
75
     * @return string
76
     */
77
    protected function getLocationColumn()
78
    {
79
        return 'location';
80
    }
81
82
    /**
83
     * Handle file relations.
84
     *
85
     * @param Model    $model    The eloquent model.
86
     * @param Relation $relation The eloquent relation.
87
     * @param string   $location The uploaded file location.
88
     * @param string   $id       The related model id.
89
     *
90
     * @return void
91
     */
92
    protected function handleFileRelations(Model $model, Relation $relation, $location, $id)
93
    {
94
        switch (true) {
95
        case $relation instanceof HasOne || $relation instanceof MorphOne:
96
            $this->updateOrCreateFileHasOne($model, $relation, $location, $id);
97
            break;
98
        case $relation instanceof BelongsTo:
99
            $this->updateOrCreateFileBelongsToOne($model, $relation, $location, $id);
100
            break;
101
        case $relation instanceof HasMany || $relation instanceof MorphMany:
102
            $this->updateOrCreateFileHasMany($model, $relation, $location, $id);
103
            break;
104
        case $relation instanceof BelongsToMany || $relation instanceof MorphToMany:
105
            $this->updateOrCreateFileBelongsToMany($model, $relation, $location, $id);
106
            break;
107
        }
108
    }
109
110
    /**
111
     * Get the uploaded file location from the UploadedFile object.
112
     *
113
     * @param UploadedFile $uploadedFile The UploadedFile object.
114
     * @param string       $relativePath The uploaded file relative path.
115
     *
116
     * @return string
117
     */
118
    protected function getUploadedFileLocation(UploadedFile $uploadedFile, $relativePath)
119
    {
120
        $extension = $uploadedFile->guessExtension();
121
        $filename = str_random().'.'.$extension;
122
123
        $storagePath = $this->getStoragePath($relativePath);
124
125
        $uploadedFile->move($storagePath, $filename);
126
127
        $location = $relativePath.$filename;
128
129
        return $location; 
130
    }
131
132
    /**
133
     * HasOne file relation updateOrCreate logic.
134
     *
135
     * @param Model       $model    The eloquent model.
136
     * @param Relation    $relation The eloquent relation.
137
     * @param string      $location The uploaded file location.
138
     * @param string|null $id       The related model id.
139
     *
140
     * @return void
141
     */
142
    protected function updateOrCreateFileHasOne(Model $model, Relation $relation, $location, $id = null)
143
    {
144
        $column = $this->getLocationColumn();
145
146
        if (!$relation->first()) {
147
            $relation->create([$column => $location]);
148
        } else {
149
            $relation->update([$column => $location]);
150
        }
151
    }
152
153
    /**
154
     * BelongsToOne file relation updateOrCreate logic.
155
     *
156
     * @param Model       $model    The eloquent model.
157
     * @param Relation    $relation The eloquent relation.
158
     * @param string      $location The uploaded file location.
159
     * @param string|null $id       The related model id.
160
     *
161
     * @return void
162
     */
163
    protected function updateOrCreateFileBelongsToOne(Model $model, Relation $relation, $location, $id = null)
164
    {
165
        $related = $relation->getRelated();
166
167
        $column = $this->getLocationColumn();
168
169
        if (!$relation->first()) {
170
            $relation->associate($related->create([$column => $location]));
171
            $model->save();
172
        } else {
173
            $relation->update([$column => $location]);
174
        }
175
    }
176
177
    /**
178
     * HasMany file relation updateOrCreate logic.
179
     *
180
     * @param Model       $model    The eloquent model.
181
     * @param Relation    $relation The eloquent relation.
182
     * @param string      $location The uploaded file location.
183
     * @param string|null $id       The related model id.
184
     *
185
     * @return void
186
     */
187
    protected function updateOrCreateFileHasMany(Model $model, Relation $relation, $location, $id = null)
188
    {
189
        $column = $this->getLocationColumn();
190
191
        $relation->updateOrCreate(['id' => $id], [$column => $location]);
192
    }
193
194
    /**
195
     * BelongsToMany file relation updateOrCreate logic.
196
     *
197
     * @param Model       $model    The eloquent model.
198
     * @param Relation    $relation The eloquent relation.
199
     * @param string      $location The uploaded file location.
200
     * @param string|null $id       The related model id.
201
     *
202
     * @return void
203
     */
204
    protected function updateOrCreateFileBelongsToMany(Model $model, Relation $relation, $location, $id = null)
205
    {
206
        $related = $relation->getRelated();
207
208
        $column = $this->getLocationColumn();
209
210
        $related->updateOrCreate(['id' => $id], [$column => $location]);
211
        $relation->syncWithoutDetaching($id, [$column => $location]);
212
    }
213
214
    /**
215
     * Throw an exception if request file is not named after an existent relation.
216
     *
217
     * @param Model  $model        The eloquent model.
218
     * @param string $relationName The eloquent relation name.
219
     *
220
     * @throws UploadException
221
     *
222
     * @return void
223
     */
224
    private function _checkFileRelationExists(Model $model, $relationName)
225
    {
226
        if (!method_exists($model, $relationName) || !$model->{$relationName}() instanceof Relation) {
227
            if (Lang::has('resource-controller.filerelationinexistent')) {
228
                $message = trans('resource-controller.filerelationinexistent', ['relationName' => $relationName]);
229
            } else {
230
                $message = "Request file '{$relationName}' is not named after an existent relation.";
231
            }
232
233
            throw new UploadException($message);
0 ignored issues
show
Bug introduced by
It seems like $message can also be of type array; however, parameter $message of Symfony\Component\HttpFo...xception::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

233
            throw new UploadException(/** @scrutinizer ignore-type */ $message);
Loading history...
234
        }
235
    }
236
}
237