1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Backpack\CRUD\app\Library\Uploaders; |
4
|
|
|
|
5
|
|
|
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD; |
6
|
|
|
use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface; |
7
|
|
|
use Illuminate\Database\Eloquent\Model; |
8
|
|
|
use Illuminate\Support\Arr; |
9
|
|
|
use Illuminate\Support\Facades\Storage; |
10
|
|
|
|
11
|
|
|
class MultipleFiles extends Uploader |
12
|
|
|
{ |
13
|
|
|
public static function for(array $field, $configuration): UploaderInterface |
14
|
|
|
{ |
15
|
|
|
return (new self($field, $configuration))->multiple(); |
16
|
|
|
} |
17
|
|
|
|
18
|
|
|
public function uploadFiles(Model $entry, $value = null) |
19
|
|
|
{ |
20
|
|
|
if ($value && isset($value[0]) && is_null($value[0])) { |
21
|
|
|
$value = false; |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
$filesToDelete = $this->getFilesToDeleteFromRequest(); |
25
|
|
|
$value = $value ?? collect($value)->flatten()->toArray(); |
26
|
|
|
$previousFiles = $this->getPreviousFiles($entry) ?? []; |
27
|
|
|
|
28
|
|
|
if (is_array($previousFiles) && empty($previousFiles[0] ?? [])) { |
29
|
|
|
$previousFiles = []; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
if (! is_array($previousFiles) && is_string($previousFiles)) { |
33
|
|
|
$previousFiles = json_decode($previousFiles, true); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
if ($filesToDelete) { |
|
|
|
|
37
|
|
|
foreach ($previousFiles as $previousFile) { |
38
|
|
|
if (in_array($previousFile, $filesToDelete)) { |
39
|
|
|
Storage::disk($this->getDisk())->delete($previousFile); |
40
|
|
|
|
41
|
|
|
$previousFiles = Arr::where($previousFiles, function ($value, $key) use ($previousFile) { |
|
|
|
|
42
|
|
|
return $value != $previousFile; |
43
|
|
|
}); |
44
|
|
|
} |
45
|
|
|
} |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
if (! is_array($value)) { |
49
|
|
|
$value = []; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
foreach ($value as $file) { |
53
|
|
|
if ($file && is_file($file)) { |
54
|
|
|
$fileName = $this->getFileName($file); |
55
|
|
|
$file->storeAs($this->getPath(), $fileName, $this->getDisk()); |
56
|
|
|
$previousFiles[] = $this->getPath().$fileName; |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$previousFiles = array_values($previousFiles); |
61
|
|
|
|
62
|
|
|
if (empty($previousFiles)) { |
63
|
|
|
return null; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
return isset($entry->getCasts()[$this->getName()]) || $this->isFake() ? $previousFiles : json_encode($previousFiles); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** @codeCoverageIgnore */ |
70
|
|
|
public function uploadRepeatableFiles($files, $previousRepeatableValues, $entry = null) |
|
|
|
|
71
|
|
|
{ |
72
|
|
|
$fileOrder = $this->getFileOrderFromRequest(); |
73
|
|
|
|
74
|
|
|
foreach ($files as $row => $files) { |
|
|
|
|
75
|
|
|
foreach ($files ?? [] as $file) { |
76
|
|
|
if ($file && is_file($file)) { |
77
|
|
|
$fileName = $this->getFileName($file); |
78
|
|
|
$file->storeAs($this->getPath(), $fileName, $this->getDisk()); |
79
|
|
|
$fileOrder[$row][] = $this->getPath().$fileName; |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
} |
83
|
|
|
// create a temporary variable that we can unset keys |
84
|
|
|
// everytime one is found. That way we avoid iterating |
85
|
|
|
// already handled keys (notice we do a deep array copy) |
86
|
|
|
$tempFileOrder = array_map(function ($item) { |
87
|
|
|
return $item; |
88
|
|
|
}, $fileOrder); |
89
|
|
|
|
90
|
|
|
foreach ($previousRepeatableValues as $previousRow => $previousFiles) { |
91
|
|
|
foreach ($previousFiles ?? [] as $key => $file) { |
92
|
|
|
$previousFileInArray = array_filter($tempFileOrder, function ($items, $key) use ($file, $tempFileOrder) { |
93
|
|
|
$found = array_search($file, $items ?? [], true); |
94
|
|
|
if ($found !== false) { |
95
|
|
|
Arr::forget($tempFileOrder, $key.'.'.$found); |
96
|
|
|
|
97
|
|
|
return true; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
return false; |
101
|
|
|
}, ARRAY_FILTER_USE_BOTH); |
102
|
|
|
if ($file && ! $previousFileInArray) { |
|
|
|
|
103
|
|
|
Storage::disk($this->getDisk())->delete($file); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
return $fileOrder; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
public function hasDeletedFiles($value): bool |
112
|
|
|
{ |
113
|
|
|
return empty($this->getFilesToDeleteFromRequest()) ? false : true; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
public function getEntryAttributeValue(Model $entry) |
117
|
|
|
{ |
118
|
|
|
$value = $entry->{$this->getAttributeName()}; |
119
|
|
|
|
120
|
|
|
return isset($entry->getCasts()[$this->getName()]) ? $value : json_encode($value); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
private function getFilesToDeleteFromRequest(): array |
124
|
|
|
{ |
125
|
|
|
return collect(CRUD::getRequest()->get('clear_'.$this->getNameForRequest()))->flatten()->toArray(); |
|
|
|
|
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.