AbstractFile::upload()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 11
cp 0
rs 9.7666
c 0
b 0
f 0
cc 3
nc 4
nop 0
crap 12
1
<?php
2
3
namespace SumoCoders\FrameworkCoreBundle\ValueObject;
4
5
use Doctrine\ORM\Mapping as ORM;
6
use Gedmo\Mapping\Annotation\Slug;
7
use Gedmo\Mapping\Annotation\SlugHandler;
8
use Gedmo\Sluggable\Sluggable;
9
use Gedmo\Sluggable\SluggableListener;
10
use Gedmo\Sluggable\Util\Urlizer;
11
use Symfony\Component\HttpFoundation\File\UploadedFile;
12
13
/**
14
 * The following things are mandatory to use this class.
15
 *
16
 * You need to implement the method getUploadDir.
17
 * When using this class in an entity certain life cycle callbacks should be called
18
 * prepareToUpload for PrePersist() and PreUpdate()
19
 * upload for PostPersist() and PostUpdate()
20
 * remove for PostRemove()
21
 */
22
abstract class AbstractFile
23
{
24
    /**
25
     * @var string
26
     * Column(type="string", length=255, nullable=true)
27
     */
28
    protected $fileName = null;
29
30
    /**
31
     * @var UploadedFile
32
     */
33
    protected $file;
34
35
    /**
36
     * @var string
37
     */
38
    protected $oldFileName;
39
40
    /**
41
     * @var string
42
     */
43
    protected $namePrefix;
44
45
    protected function __construct(?string $fileName)
46
    {
47
        $this->fileName = $fileName;
48
    }
49
50
    public function getFileName(): ?string
51
    {
52
        return $this->fileName;
53
    }
54
55
    public function getAbsolutePath(): ?string
56
    {
57
        return $this->fileName === null ? null : $this->getUploadRootDir() . '/' . $this->fileName;
58
    }
59
60
    public function getWebPath(): string
61
    {
62
        $file = $this->getAbsolutePath();
63
        if (is_file($file) && file_exists($file)) {
64
            return '/files/' . $this->getUploadDir() . '/' . $this->fileName;
65
        }
66
67
        return '';
68
    }
69
70
    protected function getUploadRootDir(): string
71
    {
72
        // the absolute directory path where uploaded documents should be saved
73
        return __DIR__ . '/../../../../web/files/' . $this->getTrimmedUploadDir();
74
    }
75
76
    protected function getTrimmedUploadDir(): string
77
    {
78
        return trim($this->getUploadDir(), '/\\');
79
    }
80
81
    /**
82
     * The dir in the web folder where the file needs to be uploaded.
83
     * The base directory is always the web/files directory
84
     *
85
     * @return string
86
     */
87
    abstract protected function getUploadDir(): string;
88
89
    public function setFile(UploadedFile $file = null): self
90
    {
91
        if ($file === null) {
92
            return $this;
93
        }
94
95
        $this->file = $file;
96
        // check if we have an old file path
97
        if ($this->fileName === null) {
98
            return $this;
99
        }
100
101
        // store the old name to delete after the update
102
        $this->oldFileName = $this->fileName;
103
        $this->fileName = null;
104
105
        return clone $this;
106
    }
107
108
    /**
109
     * @param UploadedFile|null $uploadedFile
110
     * @param string|null $namePrefix If set this will be prepended to the generated filename
111
     *
112
     * @return static
113
     */
114
    public static function fromUploadedFile(UploadedFile $uploadedFile = null, string $namePrefix = null)
115
    {
116
        $file = new static(null);
117
        $file->setFile($uploadedFile);
118
        if ($namePrefix !== null) {
119
            $file->setNamePrefix($namePrefix);
120
        }
121
122
        return $file;
123
    }
124
125
    public function getFile(): ?UploadedFile
126
    {
127
        return $this->file;
128
    }
129
130
    final public function hasFile(): bool
131
    {
132
        return $this->file instanceof UploadedFile;
133
    }
134
135
    /**
136
     * This function should be called for the life cycle events PrePersist() and PreUpdate()
137
     */
138
    public function prepareToUpload(): void
139
    {
140
        if ($this->getFile() === null) {
141
            return;
142
        }
143
144
        // do whatever you want to generate a unique name
145
        $filename = sha1(uniqid(mt_rand(), true));
146
        if ($this->namePrefix !== null) {
147
            $filename = Urlizer::urlize($this->namePrefix) . '_' . $filename;
148
        }
149
        $this->fileName = $filename . '.' . $this->getFile()->guessExtension();
150
    }
151
152
    /**
153
     * This function should be called for the life cycle events PostPersist() and PostUpdate()
154
     */
155
    public function upload(): void
156
    {
157
        // check if we have an old image
158
        if ($this->oldFileName !== null) {
159
            $this->removeOldFile();
160
        }
161
162
        if (!$this->hasFile()) {
163
            return;
164
        }
165
166
        $this->writeFileToDisk();
167
168
        $this->file = null;
169
    }
170
171
172
173
    /**
174
     * This will remove the old file, can be extended to add extra functionality
175
     */
176
    protected function removeOldFile(): void
177
    {
178
        // delete the old file
179
        $oldFile = $this->getUploadRootDir() . '/' . $this->oldFileName;
180
        if (is_file($oldFile) && file_exists($oldFile)) {
181
            unlink($oldFile);
182
        }
183
184
        $this->oldFileName = null;
185
    }
186
187
    /**
188
     * if there is an error when moving the file, an exception will
189
     * be automatically thrown by move(). This will properly prevent
190
     * the entity from being persisted to the database on error
191
     */
192
    protected function writeFileToDisk(): void
193
    {
194
        $this->getFile()->move($this->getUploadRootDir(), $this->fileName);
195
    }
196
197
    /**
198
     * This function should be called for the life cycle event PostRemove()
199
     */
200
    public function remove(): void
201
    {
202
        $file = $this->getAbsolutePath();
203
        if (!is_file($file) || !file_exists($file)) {
204
            return;
205
        }
206
207
        unlink($file);
208
    }
209
210
    public function __toString(): string
211
    {
212
        return (string) $this->fileName;
213
    }
214
215
    public static function fromString(?string $fileName): ?self
216
    {
217
        return $fileName !== null ? new static($fileName) : null;
218
    }
219
220
    /**
221
     * The next time doctrine saves this to the database the file will be removed
222
     */
223
    public function markForDeletion(): void
224
    {
225
        $this->oldFileName = $this->fileName;
226
        $this->fileName = null;
227
    }
228
229
    /**
230
     * @param string $namePrefix If set this will be prepended to the generated filename
231
     *
232
     * @return self
233
     */
234
    public function setNamePrefix(string $namePrefix): self
235
    {
236
        $this->namePrefix = $namePrefix;
237
238
        return $this;
239
    }
240
241
    /**
242
     * @internal Used by the form types
243
     *
244
     * @param bool $isPendingDeletion
245
     */
246
    public function setPendingDeletion($isPendingDeletion): void
247
    {
248
        if ($isPendingDeletion) {
249
            $this->markForDeletion();
250
        }
251
    }
252
253
    /**
254
     * @internal Used by the form types
255
     *
256
     * @return bool
257
     */
258
    public function isPendingDeletion(): bool
259
    {
260
        return \strlen($this->oldFileName) > 0 && $this->fileName === null;
261
    }
262
263
    public function jsonSerialize(): string
264
    {
265
        return $this->getWebPath();
266
    }
267
}
268