Completed
Push — master ( 640e1f...c0b408 )
by Mario
05:09
created

WorksWithFileUploads   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 28
dl 0
loc 226
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A updateOrCreateFileHasOne() 0 8 2
A getLocationColumn() 0 3 1
A getDefaultRelativePath() 0 3 1
A moveUploadedFile() 0 3 1
A getStoragePath() 0 3 1
A updateOrCreateFileBelongsToOne() 0 10 2
B handleFileRelations() 0 15 8
B uploadFiles() 0 23 5
A getFilename() 0 6 1
A updateOrCreateFileBelongsToMany() 0 7 1
A updateOrCreateFileHasMany() 0 4 1
A _checkFileRelationExists() 0 10 4
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
                $filename = $this->getFilename($uploadedFile);
45
46
                $destination = $this->getStoragePath($relativePath);
47
                $this->moveUploadedFile($uploadedFile, $filename, $destination);
48
49
                $location = $relativePath.$filename;
50
                $this->handleFileRelations($model, $relation, $location, $id);
51
            }
52
        }
53
    }
54
55
    /**
56
     * Get the name of the file.
57
     *
58
     * @param UploadedFile $uploadedFile The UploadedFile object.
59
     *
60
     * @return string
61
     */
62
    protected function getFilename(UploadedFile $uploadedFile)
63
    {
64
        $extension = $uploadedFile->guessExtension();
65
        $filename = str_random().'.'.$extension;
66
67
        return $filename;
68
    }
69
70
    /**
71
     * Get storage path for the configured driver.
72
     *
73
     * @param string $relativePath The relative path.
74
     *
75
     * @return string
76
     */
77
    protected function getStoragePath($relativePath)
78
    {
79
        return Storage::disk()->getDriver()->getAdapter()->getPathPrefix().$relativePath;
80
    }
81
82
    /**
83
     * Move the uploaded file to specified filename and destination.
84
     *
85
     * @param UploadedFile $uploadedFile The UploadedFile object.
86
     * @param string       $filename     The name of the file.
87
     * @param string       $destination  The file destination.
88
     *
89
     * @return \Symfony\Component\HttpFoundation\File\File
90
     */
91
    protected function moveUploadedFile($uploadedFile, $filename, $destination)
92
    {
93
        return $uploadedFile->move($destination, $filename);
94
    }
95
96
    /**
97
     * Get location column.
98
     *
99
     * @return string
100
     */
101
    protected function getLocationColumn()
102
    {
103
        return 'location';
104
    }
105
106
    /**
107
     * Get default relative path.
108
     *
109
     * @return string
110
     */
111
    protected function getDefaultRelativePath()
112
    {
113
        return 'uploads/';
114
    }
115
116
    /**
117
     * Handle file relations.
118
     *
119
     * @param Model    $model    The eloquent model.
120
     * @param Relation $relation The eloquent relation.
121
     * @param string   $location The uploaded file location.
122
     * @param string   $id       The related model id.
123
     *
124
     * @return void
125
     */
126
    protected function handleFileRelations(Model $model, Relation $relation, $location, $id)
127
    {
128
        switch (true) {
129
        case $relation instanceof HasOne || $relation instanceof MorphOne:
130
            $this->updateOrCreateFileHasOne($model, $relation, $location, $id);
131
            break;
132
        case $relation instanceof BelongsTo:
133
            $this->updateOrCreateFileBelongsToOne($model, $relation, $location, $id);
134
            break;
135
        case $relation instanceof HasMany || $relation instanceof MorphMany:
136
            $this->updateOrCreateFileHasMany($model, $relation, $location, $id);
137
            break;
138
        case $relation instanceof BelongsToMany || $relation instanceof MorphToMany:
139
            $this->updateOrCreateFileBelongsToMany($model, $relation, $location, $id);
140
            break;
141
        }
142
    }
143
144
    /**
145
     * HasOne file relation updateOrCreate logic.
146
     *
147
     * @param Model       $model    The eloquent model.
148
     * @param Relation    $relation The eloquent relation.
149
     * @param string      $location The uploaded file location.
150
     * @param string|null $id       The related model id.
151
     *
152
     * @return void
153
     */
154
    protected function updateOrCreateFileHasOne(Model $model, Relation $relation, $location, $id = null)
155
    {
156
        $column = $this->getLocationColumn();
157
158
        if (!$relation->first()) {
159
            $relation->create([$column => $location]);
160
        } else {
161
            $relation->update([$column => $location]);
162
        }
163
    }
164
165
    /**
166
     * BelongsToOne file relation updateOrCreate logic.
167
     *
168
     * @param Model       $model    The eloquent model.
169
     * @param Relation    $relation The eloquent relation.
170
     * @param string      $location The uploaded file location.
171
     * @param string|null $id       The related model id.
172
     *
173
     * @return void
174
     */
175
    protected function updateOrCreateFileBelongsToOne(Model $model, Relation $relation, $location, $id = null)
176
    {
177
        $column = $this->getLocationColumn();
178
        $related = $relation->getRelated();
179
180
        if (!$relation->first()) {
181
            $relation->associate($related->create([$column => $location]));
182
            $model->save();
183
        } else {
184
            $relation->update([$column => $location]);
185
        }
186
    }
187
188
    /**
189
     * HasMany file relation updateOrCreate logic.
190
     *
191
     * @param Model       $model    The eloquent model.
192
     * @param Relation    $relation The eloquent relation.
193
     * @param string      $location The uploaded file location.
194
     * @param string|null $id       The related model id.
195
     *
196
     * @return void
197
     */
198
    protected function updateOrCreateFileHasMany(Model $model, Relation $relation, $location, $id = null)
199
    {
200
        $column = $this->getLocationColumn();
201
        $relation->updateOrCreate(['id' => $id], [$column => $location]);
202
    }
203
204
    /**
205
     * BelongsToMany file relation updateOrCreate logic.
206
     *
207
     * @param Model       $model    The eloquent model.
208
     * @param Relation    $relation The eloquent relation.
209
     * @param string      $location The uploaded file location.
210
     * @param string|null $id       The related model id.
211
     *
212
     * @return void
213
     */
214
    protected function updateOrCreateFileBelongsToMany(Model $model, Relation $relation, $location, $id = null)
215
    {
216
        $column = $this->getLocationColumn();
217
        $related = $relation->getRelated();
218
219
        $related->updateOrCreate(['id' => $id], [$column => $location]);
220
        $relation->syncWithoutDetaching($id, [$column => $location]);
221
    }
222
223
    /**
224
     * Throw an exception if request file is not named after an existent relation.
225
     *
226
     * @param Model  $model        The eloquent model.
227
     * @param string $relationName The eloquent relation name.
228
     *
229
     * @throws UploadException
230
     *
231
     * @return void
232
     */
233
    private function _checkFileRelationExists(Model $model, $relationName)
234
    {
235
        if (!method_exists($model, $relationName) || !$model->{$relationName}() instanceof Relation) {
236
            if (Lang::has('resource-controller.filerelationinexistent')) {
237
                $message = trans('resource-controller.filerelationinexistent', ['relationName' => $relationName]);
238
            } else {
239
                $message = "Request file '{$relationName}' is not named after an existent relation.";
240
            }
241
242
            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

242
            throw new UploadException(/** @scrutinizer ignore-type */ $message);
Loading history...
243
        }
244
    }
245
}
246