Completed
Push — master ( 68f1fa...1cb896 )
by Philip
06:06
created

AbstractData::getManyFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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