Completed
Push — master ( fcab66...68f1fa )
by Philip
05:57
created

AbstractData::doUpdate()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
ccs 0
cts 0
cp 0
c 0
b 0
f 0
nc 1
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
14
use League\Flysystem\FilesystemInterface;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\Response;
17
use Symfony\Component\HttpFoundation\StreamedResponse;
18
19
/**
20
 * The abstract class for reading and writing data.
21
 */
22
abstract class AbstractData {
23
24
    /**
25
     * Return value on successful deletion.
26
     */
27
    const DELETION_SUCCESS = 0;
28
29
    /**
30
     * Return value on failed deletion due to existing references.
31
     */
32
    const DELETION_FAILED_STILL_REFERENCED = 1;
33
34
    /**
35
     * Return value on failed deletion due to a failed before delete event.
36
     */
37
    const DELETION_FAILED_EVENT = 2;
38
39
    /**
40
     * Holds the {@see EntityDefinition} entity definition.
41
     */
42
    protected $definition;
43
44
    /**
45
     * Holds the filesystem.
46
     * @var FilesystemInterface
47
     */
48
    protected $filesystem;
49
50
    /**
51
     * Holds the events.
52
     */
53
    protected $events;
54
55
    /**
56
     * Performs the actual deletion.
57
     *
58
     * @param Entity $entity
59
     * the id of the entry to delete
60
     * @param boolean $deleteCascade
61
     * whether to delete children and subchildren
62
     *
63
     * @return integer
64
     * true on successful deletion
65
     */
66
    abstract protected function doDelete(Entity $entity, $deleteCascade);
67
68
    /**
69
     * Creates an {@see Entity} from the raw data array with the field name
70
     * as keys and field values as values.
71
     *
72
     * @param array $row
73
     * the array with the raw data
74
     *
75
     * @return Entity
76
     * the entity containing the array data then
77
     */
78 34
    protected function hydrate(array $row) {
79 34
        $fieldNames = $this->definition->getFieldNames(true);
80 34
        $entity     = new Entity($this->definition);
81 34
        foreach ($fieldNames as $fieldName) {
82 34
            $entity->set($fieldName, $row[$fieldName]);
83
        }
84 34
        return $entity;
85
    }
86
87
    /**
88
     * Executes the event chain of an entity.
89
     *
90
     * @param Entity $entity
91
     * the entity having the event chain to execute
92
     * @param string $moment
93
     * the "moment" of the event, can be either "before" or "after"
94
     * @param string $action
95
     * the "action" of the event, can be either "create", "update" or "delete"
96
     *
97
     * @return boolean
98
     * true on successful execution of the full chain or false if it broke at
99
     * any point (and stopped the execution)
100
     */
101 34
    protected function shouldExecuteEvents(Entity $entity, $moment, $action) {
102 34
        if (!isset($this->events[$moment.'.'.$action])) {
103 33
            return true;
104
        }
105 7
        foreach ($this->events[$moment.'.'.$action] as $event) {
106 7
            $result = $event($entity);
107 7
            if (!$result) {
108 7
                return false;
109
            }
110
        }
111 6
        return true;
112
    }
113
114
    /**
115
     * Executes a function for each file field of this entity.
116
     *
117
     * @param Entity $entity
118
     * the just created entity
119
     * @param string $entityName
120
     * the name of the entity as this class here is not aware of it
121
     * @param \Closure $function
122
     * the function to perform, takes $entity, $entityName and $field as parameter
123
     */
124 8
    protected function performOnFiles(Entity $entity, $entityName, $function) {
125 8
        $fields = $this->definition->getEditableFieldNames();
126 8
        foreach ($fields as $field) {
127 8
            if ($this->definition->getType($field) == 'file') {
128 8
                $function($entity, $entityName, $field);
129
            }
130
        }
131 8
    }
132
133
134
    /**
135
     * Enriches an entity with metadata:
136
     * id, version, created_at, updated_at
137
     *
138
     * @param mixed $id
139
     * the id of the entity to enrich
140
     * @param Entity $entity
141
     * the entity to enrich
142
     */
143 34
    protected function enrichEntityWithMetaData($id, Entity $entity) {
144 34
        $entity->set('id', $id);
145 34
        $createdEntity = $this->get($entity->get('id'));
146 34
        $entity->set('version', $createdEntity->get('version'));
147 34
        $entity->set('created_at', $createdEntity->get('created_at'));
148 34
        $entity->set('updated_at', $createdEntity->get('updated_at'));
149 34
    }
150
151
    /**
152
     * Gets the many-to-many fields.
153
     *
154
     * @return array|\string[]
155
     * the many-to-many fields
156
     */
157 35
    protected function getManyFields() {
158 35
        $fields = $this->definition->getFieldNames(true);
159
        return array_filter($fields, function($field) {
160 35
            return $this->definition->getType($field) === 'many';
161 35
        });
162
    }
163
164
    /**
165
     * Gets all form fields including the many-to-many-ones.
166
     *
167
     * @return array
168
     * all form fields
169
     */
170 34
    protected function getFormFields() {
171 34
        $manyFields = $this->getManyFields();
172 34
        $formFields = [];
173 34
        foreach ($this->definition->getEditableFieldNames() as $field) {
174 34
            if (!in_array($field, $manyFields)) {
175 34
                $formFields[] = $field;
176
            }
177
        }
178 34
        return $formFields;
179
    }
180
181
    /**
182
     * Performs the cascading children deletion.
183
     *
184
     * @param integer $id
185
     * the current entities id
186
     * @param boolean $deleteCascade
187
     * whether to delete children and sub children
188
     */
189 3
    protected function deleteChildren($id, $deleteCascade) {
190 3
        foreach ($this->definition->getChildren() as $childArray) {
191 3
            $childData = $this->definition->getServiceProvider()->getData($childArray[2]);
192 3
            $children  = $childData->listEntries([$childArray[1] => $id]);
193 3
            foreach ($children as $child) {
194 2
                $result = $this->shouldExecuteEvents($child, 'before', 'delete');
195 2
                if (!$result) {
196
                    return;
197
                }
198 2
                $childData->doDelete($child, $deleteCascade);
199 3
                $this->shouldExecuteEvents($child, 'after', 'delete');
200
            }
201
        }
202 3
    }
203
204
    /**
205
     * Gets an array of reference ids for the given entities.
206
     *
207
     * @param array $entities
208
     * the entities to extract the ids
209
     * @param string $field
210
     * the reference field
211
     *
212
     * @return array
213
     * the extracted ids
214
     */
215 21
    protected function getReferenceIds(array $entities, $field) {
216
        $ids = array_map(function(Entity $entity) use ($field) {
217 21
            $id = $entity->get($field);
218 21
            return is_array($id) ? $id['id'] : $id;
219 21
        }, $entities);
220 21
        return $ids;
221
    }
222
223
    /**
224
     * Constructs a file system path for the given parameters for storing the
225
     * file of the file field.
226
     *
227
     * @param string $entityName
228
     * the entity name
229
     * @param Entity $entity
230
     * the entity
231
     * @param string $field
232
     * the file field in the entity
233
     *
234
     * @return string
235
     * the constructed path for storing the file of the file field
236
     */
237 7
    protected function getPath($entityName, Entity $entity, $field) {
238 7
        return $this->definition->getField($field, 'path').'/'.$entityName.'/'.$entity->get('id').'/'.$field;
239
    }
240
241
    /**
242
     * Writes the uploaded files.
243
     *
244
     * @param Request $request
245
     * the HTTP request containing the file data
246
     * @param Entity $entity
247
     * the just manipulated entity
248
     * @param string $entityName
249
     * the name of the entity as this class here is not aware of it
250
     * @param string $action
251
     * the name of the performed action
252
     *
253
     * @return boolean
254
     * true if all before events passed
255
     */
256 6
    protected function writeFile(Request $request, Entity $entity, $entityName, $action) {
0 ignored issues
show
Coding Style introduced by
function writeFile() 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...
257 6
        $result = $this->shouldExecuteEvents($entity, 'before', $action);
258 6
        if (!$result) {
259 2
            return false;
260
        }
261 6
        $filesystem = $this->filesystem;
262
        $this->performOnFiles($entity, $entityName, function($entity, $entityName, $field) use ($filesystem, $request) {
263 6
            $file = $request->files->get($field);
264 6
            if ($file->isValid()) {
265 6
                $path     = $this->getPath($entityName, $entity, $field);
266 6
                $filename = $path.'/'.$file->getClientOriginalName();
267 6
                if ($filesystem->has($filename)) {
268 3
                    $filesystem->delete($filename);
269
                }
270 6
                $stream = fopen($file->getRealPath(), 'r+');
271 6
                $filesystem->writeStream($filename, $stream);
272 6
                fclose($stream);
273
            }
274 6
        });
275 6
        $this->shouldExecuteEvents($entity, 'after', $action);
276 6
        return true;
277
    }
278
279
    /**
280
     * Performs the persistence of the given entity as new entry in the datasource.
281
     *
282
     * @param Entity $entity
283
     * the entity to persist
284
     *
285
     * @return boolean
286
     * true on successful creation
287
     */
288
    abstract protected function doCreate(Entity $entity);
0 ignored issues
show
Coding Style introduced by
function doCreate() 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...
289
290
    /**
291
     * Performs the updates of an existing entry in the datasource having the same id.
292
     *
293
     * @param Entity $entity
294
     * the entity with the new data
295
     *
296
     * @return boolean
297
     * true on successful update
298
     */
299
    abstract protected function doUpdate(Entity $entity);
0 ignored issues
show
Coding Style introduced by
function doUpdate() 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...
300
301
    /**
302
     * Adds an event to fire for the given parameters. The event function must
303
     * have this signature:
304
     * function (Entity $entity)
305
     * and has to return true or false.
306
     * The events are executed one after another in the added order as long as
307
     * they return "true". The first event returning "false" will stop the
308
     * process.
309
     *
310
     * @param string $moment
311
     * the "moment" of the event, can be either "before" or "after"
312
     * @param string $action
313
     * the "action" of the event, can be either "create", "update" or "delete"
314
     * @param \Closure $function
315
     * the event function to be called if set
316
     */
317 8
    public function pushEvent($moment, $action, \Closure $function) {
318 8
        $events                            = isset($this->events[$moment.'.'.$action]) ? $this->events[$moment.'.'.$action] : [];
319 8
        $events[]                          = $function;
320 8
        $this->events[$moment.'.'.$action] = $events;
321 8
    }
322
323
324
    /**
325
     * Removes and returns the latest event for the given parameters.
326
     *
327
     * @param string $moment
328
     * the "moment" of the event, can be either "before" or "after"
329
     * @param string $action
330
     * the "action" of the event, can be either "create", "update" or "delete"
331
     *
332
     * @return \Closure|null
333
     * the popped event or null if no event was available.
334
     */
335 8
    public function popEvent($moment, $action) {
336 8
        if (array_key_exists($moment.'.'.$action, $this->events)) {
337 8
            return array_pop($this->events[$moment.'.'.$action]);
338
        }
339 1
        return null;
340
    }
341
342
343
    /**
344
     * Gets the entity with the given id.
345
     *
346
     * @param string $id
347
     * the id
348
     *
349
     * @return Entity
350
     * the entity belonging to the id or null if not existant
351
     *
352
     * @return void
353
     */
354
    abstract public function get($id);
355
356
    /**
357
     * Gets a list of entities fullfilling the given filter or all if no
358
     * selection was given.
359
     *
360
     * @param array $filter
361
     * the filter all resulting entities must fulfill, the keys as field names
362
     * @param array $filterOperators
363
     * the operators of the filter like "=" defining the full condition of the field
364
     * @param integer|null $skip
365
     * if given and not null, it specifies the amount of rows to skip
366
     * @param integer|null $amount
367
     * if given and not null, it specifies the maximum amount of rows to retrieve
368
     * @param string|null $sortField
369
     * if given and not null, it specifies the field to sort the entries
370
     * @param boolean|null $sortAscending
371
     * if given and not null, it specifies that the sort order is ascending,
372
     * descending else
373
     *
374
     * @return Entity[]
375
     * the entities fulfilling the filter or all if no filter was given
376
     */
377
    abstract public function listEntries(array $filter = [], array $filterOperators = [], $skip = null, $amount = null, $sortField = null, $sortAscending = null);
378
379
    /**
380
     * Persists the given entity as new entry in the datasource.
381
     *
382
     * @param Entity $entity
383
     * the entity to persist
384
     *
385
     * @return boolean
386
     * true on successful creation
387
     */
388 34 View Code Duplication
    public function create(Entity $entity) {
0 ignored issues
show
Coding Style introduced by
function create() 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...
389 34
        $result = $this->shouldExecuteEvents($entity, 'before', 'create');
390 34
        if (!$result) {
391 2
            return false;
392
        }
393 34
        $result = $this->doCreate($entity);
394 34
        $this->shouldExecuteEvents($entity, 'after', 'create');
395 34
        return $result;
396
    }
397
398
    /**
399
     * Updates an existing entry in the datasource having the same id.
400
     *
401
     * @param Entity $entity
402
     * the entity with the new data
403
     *
404
     * @return boolean
405
     * true on successful update
406
     */
407 13 View Code Duplication
    public function update(Entity $entity) {
0 ignored issues
show
Coding Style introduced by
function update() 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...
408 13
        if (!$this->shouldExecuteEvents($entity, 'before', 'update')) {
409 2
            return false;
410
        }
411 13
        $result = $this->doUpdate($entity);
412 13
        $this->shouldExecuteEvents($entity, 'after', 'update');
413 13
        return $result;
414
    }
415
416
    /**
417
     * Deletes an entry from the datasource.
418
     *
419
     * @param Entity $entity
420
     * the entity to delete
421
     *
422
     * @return integer
423
     * returns one of:
424
     * - AbstractData::DELETION_SUCCESS -> successful deletion
425
     * - AbstractData::DELETION_FAILED_STILL_REFERENCED -> failed deletion due to existing references
426
     * - AbstractData::DELETION_FAILED_EVENT -> failed deletion due to a failed before delete event
427
     */
428 4
    public function delete($entity) {
429 4
        $result = $this->shouldExecuteEvents($entity, 'before', 'delete');
430 4
        if (!$result) {
431 2
            return static::DELETION_FAILED_EVENT;
432
        }
433 4
        $result = $this->doDelete($entity, $this->definition->isDeleteCascade());
434 4
        $this->shouldExecuteEvents($entity, 'after', 'delete');
435 4
        return $result;
436
    }
437
438
    /**
439
     * Gets ids and names of a table. Used for building up the dropdown box of
440
     * reference type fields for example.
441
     *
442
     * @param string $entity
443
     * the entity
444
     * @param string $nameField
445
     * the field defining the name of the rows
446
     *
447
     * @return array
448
     * an array with the ids as key and the names as values
449
     */
450
    abstract public function getIdToNameMap($entity, $nameField);
451
452
    /**
453
     * Retrieves the amount of entities in the datasource fulfilling the given
454
     * parameters.
455
     *
456
     * @param string $table
457
     * the table to count in
458
     * @param array $params
459
     * an array with the field names as keys and field values as values
460
     * @param array $paramsOperators
461
     * the operators of the parameters like "=" defining the full condition of the field
462
     * @param boolean $excludeDeleted
463
     * false, if soft deleted entries in the datasource should be counted, too
464
     *
465
     * @return integer
466
     * the count fulfilling the given parameters
467
     */
468
    abstract public function countBy($table, array $params, array $paramsOperators, $excludeDeleted);
469
470
    /**
471
     * Checks whether a given set of ids is assigned to any entity exactly
472
     * like it is given (no subset, no superset).
473
     *
474
     * @param string $field
475
     * the many field
476
     * @param array $thatIds
477
     * the id set to check
478
     * @param string|null $excludeId
479
     * one optional own id to exclude from the check
480
     *
481
     * @return boolean
482
     * true if the set of ids exists for an entity
483
     */
484
    abstract public function hasManySet($field, array $thatIds, $excludeId = null);
485
486
    /**
487
     * Gets the {@see EntityDefinition} instance.
488
     *
489
     * @return EntityDefinition
490
     * the definition instance
491
     */
492 74
    public function getDefinition() {
493 74
        return $this->definition;
494
    }
495
496
    /**
497
     * Creates a new, empty entity instance having all fields prefilled with
498
     * null or the defined value in case of fixed fields.
499
     *
500
     * @return Entity
501
     * the newly created entity
502
     */
503 37
    public function createEmpty() {
504 37
        $entity = new Entity($this->definition);
505 37
        $fields = $this->definition->getEditableFieldNames();
506 37
        foreach ($fields as $field) {
507 37
            $value = null;
508 37
            if ($this->definition->getType($field) == 'fixed') {
509 37
                $value = $this->definition->getField($field, 'value');
510
            }
511 37
            $entity->set($field, $value);
512
        }
513 37
        $entity->set('id', null);
514 37
        return $entity;
515
    }
516
517
    /**
518
     * Creates the uploaded files of a newly created entity.
519
     *
520
     * @param Request $request
521
     * the HTTP request containing the file data
522
     * @param Entity $entity
523
     * the just created entity
524
     * @param string $entityName
525
     * the name of the entity as this class here is not aware of it
526
     *
527
     * @return boolean
528
     * true if all before events passed
529
     */
530 4
    public function createFiles(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...
531 4
        return $this->writeFile($request, $entity, $entityName, 'createFiles');
532
    }
533
534
    /**
535
     * Updates the uploaded files of an updated entity.
536
     *
537
     * @param Request $request
538
     * the HTTP request containing the file data
539
     * @param Entity $entity
540
     * the updated entity
541
     * @param string $entityName
542
     * the name of the entity as this class here is not aware of it
543
     *
544
     * @return boolean
545
     * true on successful update
546
     */
547 2
    public function updateFiles(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...
548
        // With optional soft deletion, the file should be deleted first.
549 2
        return $this->writeFile($request, $entity, $entityName, 'updateFiles');
550
    }
551
552
    /**
553
     * Deletes a specific file from an existing entity.
554
     *
555
     * @param Entity $entity
556
     * the entity to delete the file from
557
     * @param string $entityName
558
     * the name of the entity as this class here is not aware of it
559
     * @param string $field
560
     * the field of the entity containing the file to be deleted
561
     *
562
     * @return boolean
563
     * true on successful deletion
564
     */
565 2 View Code Duplication
    public function deleteFile(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...
566 2
        $result = $this->shouldExecuteEvents($entity, 'before', 'deleteFile');
567 2
        if (!$result) {
568 1
            return false;
569
        }
570
        // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
571 2
        $this->shouldExecuteEvents($entity, 'after', 'deleteFile');
572 2
        return true;
573
    }
574
575
    /**
576
     * Deletes all files of an existing entity.
577
     *
578
     * @param Entity $entity
579
     * the entity to delete the files from
580
     * @param string $entityName
581
     * the name of the entity as this class here is not aware of it
582
     *
583
     * @return boolean
584
     * true on successful deletion
585
     */
586 2 View Code Duplication
    public function deleteFiles(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...
587 2
        $result = $this->shouldExecuteEvents($entity, 'before', 'deleteFiles');
588 2
        if (!$result) {
589 1
            return false;
590
        }
591
        $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...
592
            // For now, we are defensive and don't delete ever. As soon as soft deletion is optional, files will get deleted.
593 2
        });
594 2
        $this->shouldExecuteEvents($entity, 'after', 'deleteFiles');
595 2
        return true;
596
    }
597
598
    /**
599
     * Renders (outputs) a file of an entity. This includes setting headers
600
     * like the file size, mimetype and name, too.
601
     *
602
     * @param Entity $entity
603
     * the entity to render the file from
604
     * @param string $entityName
605
     * the name of the entity as this class here is not aware of it
606
     * @param string $field
607
     * the field of the entity containing the file to be rendered
608
     *
609
     * @return Response
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use StreamedResponse.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
610
     * the HTTP streamed response
611
     */
612 2
    public function renderFile(Entity $entity, $entityName, $field) {
613 2
        $targetPath = $this->getPath($entityName, $entity, $field);
614 2
        $fileName   = $entity->get($field);
615 2
        $file       = $targetPath.'/'.$fileName;
616 2
        $mimeType   = $this->filesystem->getMimetype($file);
617 2
        $size       = $this->filesystem->getSize($file);
618 2
        $stream     = $this->filesystem->readStream($file);
619 2
        $response   = new StreamedResponse(function() use ($stream) {
620 1
            while ($data = fread($stream, 1024)) {
621 1
                echo $data;
622 1
                flush();
623
            }
624 1
            fclose($stream);
625 2
        }, 200, [
626 2
            'Cache-Control' => 'public, max-age=86400',
627 2
            'Content-length' => $size,
628 2
            'Content-Type' => $mimeType,
629 2
            'Content-Disposition' => 'attachment; filename="'.$fileName.'"'
630
        ]);
631 2
        return $response;
632
    }
633
634
}
635