Passed
Push — develop ( c023ce...9576d7 )
by Daniel
06:40
created

FileUploader   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 78
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 78
ccs 0
cts 40
cp 0
rs 10
c 0
b 0
f 0
wmc 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A upload() 0 21 2
A getRealPath() 0 3 1
A validateNewFile() 0 6 2
A unlinkFile() 0 7 2
A getNewFilename() 0 13 2
A __construct() 0 10 1
1
<?php
2
3
namespace Silverback\ApiComponentBundle\Uploader;
4
5
use ApiPlatform\Core\Validator\ValidatorInterface;
6
use Doctrine\ORM\EntityManagerInterface;
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\HttpFoundation\File\File;
11
use Symfony\Component\HttpFoundation\File\UploadedFile;
12
use Symfony\Component\PropertyAccess\PropertyAccess;
13
14
class FileUploader
15
{
16
    private $rootPath;
17
    private $validator;
18
    private $propertyAccessor;
19
    private $em;
20
21
    public function __construct(
22
        ValidatorInterface $validator,
23
        EntityManagerInterface $em,
24
        array $rootPaths = []
25
    )
26
    {
27
        $this->validator = $validator;
28
        $this->rootPath = $rootPaths[0];
29
        $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
30
        $this->em = $em;
31
    }
32
33
    private function getRealPath ($filename): string
34
    {
35
        return rtrim($this->rootPath, '/') . '/' . $filename;
36
    }
37
38
    private function getNewFilename(UploadedFile $file): string
39
    {
40
        $fs = new Filesystem();
41
42
        $ext = $file->guessExtension();
43
        $basename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
44
        $filename = $basename.'.'.$ext;
45
        $i=0;
46
        while($fs->exists($this->getRealPath($filename))) {
47
            $i++;
48
            $filename = $basename.".$i.$ext";
49
        }
50
        return $filename;
51
    }
52
53
    private function validateNewFile($entity, $field, UploadedFile $file): void
54
    {
55
        $this->propertyAccessor->setValue($entity, $field, $file);
56
        $errors = $this->validator->validate($entity);
57
        if (\count($errors)) {
58
            throw new InvalidArgumentException((string) $errors);
59
        }
60
    }
61
62
    private function unlinkFile(File $currentFile): void
63
    {
64
        $oldFilePath = $currentFile->getRealPath();
65
        if (!is_writable($oldFilePath)) {
66
            throw new RuntimeException('The existing file cannot be deleted. File upload aborted');
67
        }
68
        unlink($currentFile->getRealPath());
69
    }
70
71
    public function upload($entity, string $field, UploadedFile $file)
72
    {
73
        /** @var File|null|string $currentFile */
74
        $currentFile = $this->propertyAccessor->getValue($entity, $field);
75
76
        // Set to the new file and validate it before we upload and persist any changes
77
        $this->validateNewFile($entity, $field, $file);
78
79
        // Validation passed, remove old file first (in case we don't have permission to do it)
80
        if ($currentFile instanceof File) {
81
            $this->unlinkFile($currentFile);
82
        }
83
84
        // Old file removed, let's update!
85
        $filename = $this->getNewFilename($file);
86
        $file->move($this->rootPath, $filename);
87
        // We may need this, but perhaps not so we will try to persist the UploadedFile entity first as it extends File anyway
88
        // $movedFile = new File($this->getRealPath($filename));
89
        $this->propertyAccessor->setValue($entity, $field, $file);
90
        $this->em->flush();
91
        return $entity;
92
    }
93
}
94