Skip to content
This repository was archived by the owner on Mar 5, 2022. It is now read-only.

Fix upload for edit #213

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions config/Migrations/20201110234846_AddCollectionColumn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);

use Migrations\AbstractMigration;

class AddCollectionColumn extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
* @return void
*/
public function change()
{
$this->table('file_storage')
->addColumn('collection', 'string', ['length' => 128, 'null' => true, 'default' => null])
->update();
}
}
4 changes: 2 additions & 2 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
<properties>
<property name="newlinesCountBetweenOpenTagAndDeclare" value="2" />
<property name="newlinesCountBetweenOpenTagAndDeclare" value="2"/>
</properties>
</rule>

<rule ref="Spryker">
<exclude name="Spryker.Formatting.MethodSignatureParametersLineBreakMethod" />
<exclude name="Spryker.Formatting.MethodSignatureParametersLineBreakMethod"/>
</rule>

</ruleset>
4 changes: 3 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
parameters:
level: 7
level: 8
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
bootstrapFiles:
- tests/bootstrap.php
earlyTerminatingMethodCalls:
Cake\Console\Shell:
- abort
ignoreErrors:
- '#Cannot cast array\<string\>\|string to string#'
2 changes: 1 addition & 1 deletion src/FileStorage/DataTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function entityToFileObject(EntityInterface $entity): FileInterface
public function fileObjectToEntity(FileInterface $file, ?EntityInterface $entity): EntityInterface
{
$data = [
'id' => $file->uuid(),
'id' => $file->uuid(), //FIXME
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to set the id for primary key records?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The File object doesn't care, because the whole lib doesn't care about persistance (its not their job) but if you want to persist it and your file path relies on it, then you clearly should set it. The File objects only purpose and reason that it features certain things is that it might need them for generation pathes or doing certain operations in processors.

The File object is like a DTO between persistence that the user has to take core of in his app and the library that only knows about what the file object provides.

'model' => $file->model(),
'foreign_key' => $file->modelId(),
'filesize' => $file->filesize(),
Expand Down
126 changes: 126 additions & 0 deletions src/Model/Behavior/FileAssociationBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

declare(strict_types = 1);

namespace Burzum\FileStorage\Model\Behavior;

use ArrayObject;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventInterface;
use Cake\ORM\Association\HasOne;
use Cake\ORM\Behavior;
use Laminas\Diactoros\UploadedFile;

/**
* File Association Behavior.
*
* @author Florian Krämer
* @copyright 2012 - 2020 Florian Krämer
* @license MIT
*/
class FileAssociationBehavior extends Behavior
{
/**
* @var array
* @inheritDoc
*/
protected $_defaultConfig = [
'associations' => [],
];

/**
* @inheritDoc
*/
public function initialize(array $config): void
{
parent::initialize($config);

$class = get_class($this->getTable());
foreach ($config['associations'] as $association => $assocConfig) {
$associationObject = $this->getTable()->getAssociation($association);

$defaults = [
'replace' => $associationObject instanceof HasOne,
'model' => substr($class, strrpos($class, '\\') + 1, -5),
'property' => $this->getTable()->getAssociation($association)->getProperty(),
];

$config['associations'][$association] = $assocConfig + $defaults;
}

$this->setConfig('associations', $config['associations']);
}

/**
* @param \Cake\Event\EventInterface $event
* @param \Cake\Datasource\EntityInterface $entity
* @param \ArrayObject $options
*
* @return void
*/
public function afterSave(
EventInterface $event,
EntityInterface $entity,
ArrayObject $options
): void {
$associations = $this->getConfig('associations');

foreach ($associations as $association => $assocConfig) {
$property = $assocConfig['property'];
if ($entity->{$property} === null) {
continue;
}

if ($entity->id && $entity->{$property} && $entity->{$property}->file) {
$file = $entity->{$property}->file;

$ok = false;
if (is_array($file) && $file['error'] === UPLOAD_ERR_OK) {
$ok = true;
} elseif ($file instanceof UploadedFile && $file->getError() === UPLOAD_ERR_OK) {
$ok = true;
}

if (!$ok) {
continue;
}

if ($assocConfig['replace'] === true) {
$this->findAndRemovePreviousFile($entity, $association, $assocConfig);
}

$entity->{$property}->set('collection', $assocConfig['collection']);
$entity->{$property}->set('model', $assocConfig['model']);
$entity->{$property}->set('foreign_key', $entity->id);

$this->getTable()->{$association}->saveOrFail($entity->{$property});
}
}
}

/**
* @param \Cake\Datasource\EntityInterface $entity
* @param string $association
* @param array $assocConfig
*
* @return void
*/
protected function findAndRemovePreviousFile(
EntityInterface $entity,
string $association,
array $assocConfig
): void {
$result = $this->getTable()->{$association}->find()
->where([
'collection' => $assocConfig['collection'],
'model' => $assocConfig['model'],
'foreign_key' => $entity->get((string)$this->getTable()->getPrimaryKey()),
'id !=' => $entity->get($assocConfig['property'])->get((string)$this->getTable()->{$association}->getPrimaryKey()),
])
->first();

if ($result) {
$this->getTable()->{$association}->delete($result);
}
}
}
57 changes: 27 additions & 30 deletions src/Model/Behavior/FileStorageBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ class FileStorageBehavior extends Behavior
/**
* @var \Phauthentic\Infrastructure\Storage\FileStorage
*/
protected FileStorage $fileStorage;
protected $fileStorage;

/**
* @var \Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface
* @var \Burzum\FileStorage\FileStorage\DataTransformerInterface
*/
protected ?ProcessorInterface $imageProcessor;
protected $transformer;

/**
* @var \Burzum\FileStorage\FileStorage\DataTransformerInterface
* @var \Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface
*/
protected DataTransformerInterface $transformer;
protected $processor;

/**
* Default config
Expand All @@ -55,14 +55,9 @@ class FileStorageBehavior extends Behavior
'ignoreEmptyFile' => true,
'fileField' => 'file',
'fileStorage' => null,
'imageProcessor' => null,
'fileProcessor' => null,
];

/**
* @var array
*/
protected array $processors = [];

/**
* @inheritDoc
*
Expand All @@ -85,6 +80,8 @@ public function initialize(array $config): void
$this->getTable()
);
}

//$this->processors = (array)$this->getConfig('processors');
}

/**
Expand Down Expand Up @@ -184,11 +181,12 @@ public function afterSave(EventInterface $event, EntityInterface $entity, ArrayO
try {
$file = $this->entityToFileObject($entity);
$file = $this->fileStorage->store($file);

// TODO: move into stack processing
$file = $this->processImages($file, $entity);

foreach ($this->processors as $processor) {
$file = $processor->process($file);
}
$processor = $this->getFileProcessor();
$file = $processor->process($file);

$entity = $this->fileObjectToEntity($file, $entity);
$this->getTable()->save(
Expand Down Expand Up @@ -219,7 +217,7 @@ protected function checkEntityBeforeSave(EntityInterface $entity)
{
if ($entity->isNew()) {
if (!$entity->has('model')) {
$entity->set('model', $this->getTable()->getTable());
$entity->set('model', $this->getTable()->getAlias());
}

if (!$entity->has('adapter')) {
Expand Down Expand Up @@ -266,7 +264,7 @@ protected function getFileInfoFromUpload(&$upload, $field = 'file')
if (!is_array($uploadedFile)) {
$upload['filesize'] = $uploadedFile->getSize();
$upload['mime_type'] = $uploadedFile->getClientMediaType();
$upload['extension'] = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
$upload['extension'] = pathinfo((string)$uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
$upload['filename'] = $uploadedFile->getClientFilename();
} else {
$upload['filesize'] = $uploadedFile['size'];
Expand All @@ -285,7 +283,7 @@ protected function getFileInfoFromUpload(&$upload, $field = 'file')
*
* @return int Number of deleted records / files
*/
public function deleteAllFiles($conditions)
public function deleteAllFiles(array $conditions)
{
$table = $this->getTable();

Expand Down Expand Up @@ -334,16 +332,15 @@ public function fileObjectToEntity(FileInterface $file, ?EntityInterface $entity
*/
public function processImages(FileInterface $file, EntityInterface $entity): FileInterface
{
$imageSizes = Configure::read('FileStorage.imageVariants');
$imageSizes = (array)Configure::read('FileStorage.imageVariants');
$model = $file->model();
$identifier = $entity->get('identifier');
$collection = $entity->get('collection');

if (!isset($imageSizes[$model][$identifier])) {
if (!isset($imageSizes[$model][$collection])) {
return $file;
}

$file = $file->withVariants($imageSizes[$model][$identifier]);
$file = $this->imageProcessor->process($file);
$file = $file->withVariants($imageSizes[$model][$collection]);

return $file;
}
Expand All @@ -353,20 +350,20 @@ public function processImages(FileInterface $file, EntityInterface $entity): Fil
*
* @return \Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface
*/
protected function getImageProcessor(): ProcessorInterface
protected function getFileProcessor(): ProcessorInterface
{
if ($this->imageProcessor !== null) {
return $this->imageProcessor;
if ($this->processor !== null) {
return $this->processor;
}

if ($this->getConfig('imageProcessor') instanceof ProcessorInterface) {
$this->imageProcessor = $this->getConfig('imageProcessor');
if ($this->getConfig('fileProcessor') instanceof ProcessorInterface) {
$this->processor = $this->getConfig('fileProcessor');
}

if ($this->imageProcessor === null) {
throw new RuntimeException('No image processor found');
if ($this->processor === null) {
throw new RuntimeException('No processor found');
}

return $this->imageProcessor;
return $this->processor;
}
}
18 changes: 18 additions & 0 deletions src/Model/Entity/FileStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@
* @author Florian Krämer
* @copyright 2012 - 2020 Florian Krämer
* @license MIT
*
* @property array $variants
* @property array $metadata
* @property int $id
* @property int|null $user_id
* @property int|null $foreign_key
* @property string|null $model
* @property string|null $filename
* @property int|null $filesize
* @property string|null $mime_type
* @property string|null $extension
* @property string|null $hash
* @property string|null $path
* @property string|null $adapter
* @property \Cake\I18n\FrozenTime $created
* @property \Cake\I18n\FrozenTime $modified
* @property string|null $collection
* @property array $variant_urls
*/
class FileStorage extends Entity implements FileStorageEntityInterface
{
Expand Down
16 changes: 16 additions & 0 deletions src/Model/Table/FileStorageTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@
* @author Florian Krämer
* @copyright 2012 - 2020 Florian Krämer
* @license MIT
*
* @method \Burzum\FileStorage\Model\Entity\FileStorage newEmptyEntity()
* @method \Burzum\FileStorage\Model\Entity\FileStorage newEntity(array $data, array $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[] newEntities(array $data, array $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage get($primaryKey, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage findOrCreate($search, ?callable $callback = null, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[] patchEntities(iterable $entities, array $data, array $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage|false save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[]|\Cake\Datasource\ResultSetInterface|false saveMany(iterable $entities, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[]|\Cake\Datasource\ResultSetInterface saveManyOrFail(iterable $entities, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[]|\Cake\Datasource\ResultSetInterface|false deleteMany(iterable $entities, $options = [])
* @method \Burzum\FileStorage\Model\Entity\FileStorage[]|\Cake\Datasource\ResultSetInterface deleteManyOrFail(iterable $entities, $options = [])
* @mixin \Cake\ORM\Behavior\TimestampBehavior
* @mixin \Burzum\FileStorage\Model\Behavior\FileStorageBehavior
*/
class FileStorageTable extends Table
{
Expand Down
5 changes: 4 additions & 1 deletion src/Shell/ImageVersionShell.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ImageVersionShell extends Shell
/**
* Storage Table Object
*
* @var \Cake\ORM\Table|null
* @var \Cake\ORM\Table
*/
public $Table;

Expand Down Expand Up @@ -200,6 +200,9 @@ public function regenerate(): void

foreach ($operations as $version => $operation) {
try {
if ($this->command === null) {
$this->abort('No command given');
}
$this->_loop($this->command, $this->args[0], [$version => $operation], $options);
} catch (Exception $e) {
$this->abort($e->getMessage());
Expand Down
Loading