Completed
Push — master ( 747ecc...c2e4f8 )
by Philip
05:53
created

FileHandler::updateFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 4
1
<?php
2
3
/*
4
 * This file is part of the CRUDlex package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CRUDlex;
13
use League\Flysystem\FilesystemInterface;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\StreamedResponse;
16
17
/**
18
 * Handles the files.
19
 */
20
class FileHandler {
21
22
    /**
23
     * @var FilesystemInterface
24
     */
25
    protected $filesystem;
26
27
    /**
28
     * @var EntityDefinition
29
     */
30
    protected $entityDefinition;
31
32
    /**
33
     * Constructs a file system path for the given parameters for storing the
34
     * file of the file field.
35
     *
36
     * @param string $entityName
37
     * the entity name
38
     * @param Entity $entity
39
     * the entity
40
     * @param string $field
41
     * the file field in the entity
42
     *
43
     * @return string
44
     * the constructed path for storing the file of the file field
45
     */
46
    protected function getPath($entityName, Entity $entity, $field) {
47
        return $this->entityDefinition->getField($field, 'path').'/'.$entityName.'/'.$entity->get('id').'/'.$field;
48
    }
49
50
    /**
51
     * Executes a function for each file field of this entity.
52
     *
53
     * @param Entity $entity
54
     * the just created entity
55
     * @param string $entityName
56
     * the name of the entity as this class here is not aware of it
57
     * @param \Closure $function
58
     * the function to perform, takes $entity, $entityName and $field as parameter
59
     */
60
    protected function performOnFiles(Entity $entity, $entityName, $function) {
61
        $fields = $this->entityDefinition->getEditableFieldNames();
62
        foreach ($fields as $field) {
63
            if ($this->entityDefinition->getType($field) == 'file') {
64
                $function($entity, $entityName, $field);
65
            }
66
        }
67
    }
68
69
    /**
70
     * Writes the uploaded files.
71
     *
72
     * @param AbstractData $data
73
     * the AbstractData instance who should receive the events
74
     * @param Request $request
75
     * the HTTP request containing the file data
76
     * @param Entity $entity
77
     * the just manipulated entity
78
     * @param string $entityName
79
     * the name of the entity as this class here is not aware of it
80
     * @param string $action
81
     * the name of the performed action
82
     *
83
     * @return boolean
84
     * true if all before events passed
85
     */
86
    protected function shouldWriteFile(AbstractData $data, Request $request, Entity $entity, $entityName, $action) {
87
        $result = $data->shouldExecuteEvents($entity, 'before', $action);
88
        if (!$result) {
89
            return false;
90
        }
91
        $filesystem = $this->filesystem;
92
        $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) use ($filesystem, $request) {
93
            $file = $request->files->get($field);
94
            if ($file->isValid()) {
95
                $path     = $this->getPath($entityName, $entity, $field);
96
                $filename = $path.'/'.$file->getClientOriginalName();
97
                if ($filesystem->has($filename)) {
98
                    $filesystem->delete($filename);
99
                }
100
                $stream = fopen($file->getRealPath(), 'r+');
101
                $filesystem->writeStream($filename, $stream);
102
                fclose($stream);
103
            }
104
        });
105
        $data->shouldExecuteEvents($entity, 'after', $action);
106
        return true;
107
    }
108
109
    /**
110
     * FileHandler constructor.
111
     * @param FilesystemInterface $filesystem
112
     * the filesystem to use
113
     */
114
    public function __construct(FilesystemInterface $filesystem, EntityDefinition $entityDefinition) {
115
        $this->filesystem       = $filesystem;
116
        $this->entityDefinition = $entityDefinition;
117
    }
118
119
120
    /**
121
     * Renders (outputs) a file of an entity. This includes setting headers
122
     * like the file size, mimetype and name, too.
123
     *
124
     * @param Entity $entity
125
     * the entity to render the file from
126
     * @param string $entityName
127
     * the name of the entity as this class here is not aware of it
128
     * @param string $field
129
     * the field of the entity containing the file to be rendered
130
     *
131
     * @return StreamedResponse
132
     * the HTTP streamed response
133
     */
134
    public function renderFile(Entity $entity, $entityName, $field) {
135
        $targetPath = $this->getPath($entityName, $entity, $field);
136
        $fileName   = $entity->get($field);
137
        $file       = $targetPath.'/'.$fileName;
138
        $mimeType   = $this->filesystem->getMimetype($file);
139
        $size       = $this->filesystem->getSize($file);
140
        $stream     = $this->filesystem->readStream($file);
141
        $response   = new StreamedResponse(function() use ($stream) {
142
            while ($data = fread($stream, 1024)) {
143
                echo $data;
144
                flush();
145
            }
146
            fclose($stream);
147
        }, 200, [
148
            'Cache-Control' => 'public, max-age=86400',
149
            'Content-length' => $size,
150
            'Content-Type' => $mimeType,
151
            'Content-Disposition' => 'attachment; filename="'.$fileName.'"'
152
        ]);
153
        return $response;
154
    }
155
156
157
    /**
158
     * Deletes all files of an existing entity.
159
     *
160
     * @param AbstractData $data
161
     * the AbstractData instance who should receive the events
162
     * @param Entity $entity
163
     * the entity to delete the files from
164
     * @param string $entityName
165
     * the name of the entity as this class here is not aware of it
166
     *
167
     * @return boolean
168
     * true on successful deletion
169
     */
170 View Code Duplication
    public function deleteFiles(AbstractData $data, Entity $entity, $entityName) {
0 ignored issues
show
Coding Style introduced by
function deleteFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
171
        $result = $data->shouldExecuteEvents($entity, 'before', 'deleteFiles');
172
        if (!$result) {
173
            return false;
174
        }
175
        $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) {
0 ignored issues
show
Unused Code introduced by
The parameter $entity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $entityName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
176
            // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
177
        });
178
        $data->shouldExecuteEvents($entity, 'after', 'deleteFiles');
179
        return true;
180
    }
181
182
    /**
183
     * Deletes a specific file from an existing entity.
184
     *
185
     * @param AbstractData $data
186
     * the AbstractData instance who should receive the events
187
     * @param Entity $entity
188
     * the entity to delete the file from
189
     * @param string $entityName
190
     * the name of the entity as this class here is not aware of it
191
     * @param string $field
192
     * the field of the entity containing the file to be deleted
193
     * @return bool true on successful deletion
194
     * true on successful deletion
195
     */
196 View Code Duplication
    public function deleteFile(AbstractData $data, Entity $entity, $entityName, $field) {
0 ignored issues
show
Coding Style introduced by
function deleteFile() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Unused Code introduced by
The parameter $entityName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $field is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
        $result = $data->shouldExecuteEvents($entity, 'before', 'deleteFile');
198
        if (!$result) {
199
            return false;
200
        }
201
        // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
202
        $data->shouldExecuteEvents($entity, 'after', 'deleteFile');
203
        return true;
204
    }
205
206
    /**
207
     * Creates the uploaded files of a newly created entity.
208
     *
209
     * @param AbstractData $data
210
     * the AbstractData instance who should receive the events
211
     * @param Request $request
212
     * the HTTP request containing the file data
213
     * @param Entity $entity
214
     * the just created entity
215
     * @param string $entityName
216
     * the name of the entity as this class here is not aware of it
217
     *
218
     * @return boolean
219
     * true if all before events passed
220
     */
221
    public function createFiles(AbstractData $data, Request $request, Entity $entity, $entityName) {
0 ignored issues
show
Coding Style introduced by
function createFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
222
        return $this->shouldWriteFile($data, $request, $entity, $entityName, 'createFiles');
223
    }
224
225
    /**
226
     * Updates the uploaded files of an updated entity.
227
     *
228
     * @param AbstractData $data
229
     * the AbstractData instance who should receive the events
230
     * @param Request $request
231
     * the HTTP request containing the file data
232
     * @param Entity $entity
233
     * the updated entity
234
     * @param string $entityName
235
     * the name of the entity as this class here is not aware of it
236
     *
237
     * @return boolean
238
     * true on successful update
239
     */
240
    public function updateFiles(AbstractData $data, Request $request, Entity $entity, $entityName) {
0 ignored issues
show
Coding Style introduced by
function updateFiles() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
241
        // With optional soft deletion, the file should be deleted first.
242
        return $this->shouldWriteFile($data, $request, $entity, $entityName, 'updateFiles');
243
    }
244
245
}