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 3 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();
}
}
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
118 changes: 118 additions & 0 deletions src/Model/Behavior/FileAssociationBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types = 1);

namespace Burzum\FileStorage\Model\Behavior;

use App\Storage\Identifiers;
use ArrayObject;
use Burzum\FileStorage\FileStorage\DataTransformer;
use Burzum\FileStorage\FileStorage\DataTransformerInterface;
use Cake\Core\Configure;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventDispatcherTrait;
use Cake\Event\EventInterface;
use Cake\ORM\Association\HasOne;
use Cake\ORM\Behavior;
use League\Flysystem\AdapterInterface;
use Phauthentic\Infrastructure\Storage\FileInterface;
use Phauthentic\Infrastructure\Storage\FileStorage;
use Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface;
use RuntimeException;
use Throwable;

/**
* File Association Behavior.
*
* @author Florian Krämer
* @copyright 2012 - 2020 Florian Krämer
* @license MIT
*/
class FileAssociationBehavior extends Behavior
{
/**
* @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 \App\Model\Entity\Event $entity
* @param \ArrayObject $options
*
* @return void
*/
public function beforeSave(
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->getError() === UPLOAD_ERR_OK) {
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);
}
}
}

/**
* @param \Cake\Event\EventInterface $event
* @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())
])
->first();

if ($result) {
$this->getTable()->{$association}->delete($result);
}
}
}
14 changes: 7 additions & 7 deletions src/Model/Behavior/FileStorageBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ class FileStorageBehavior extends Behavior
protected FileStorage $fileStorage;

/**
* @var \Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface
* @var \Phauthentic\Infrastructure\Storage\Processor\ProcessorInterface|null
*/
protected ?ProcessorInterface $imageProcessor;
protected $imageProcessor;

/**
* @var \Burzum\FileStorage\FileStorage\DataTransformerInterface
Expand Down Expand Up @@ -219,7 +219,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 @@ -336,14 +336,14 @@ public function processImages(FileInterface $file, EntityInterface $entity): Fil
{
$imageSizes = 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]);
$file = $this->getImageProcessor()->process($file);

return $file;
}
Expand Down
3 changes: 2 additions & 1 deletion tests/Fixture/FileStorageFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class FileStorageFixture extends TestFixture
'user_id' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 36],
'foreign_key' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 36],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We probably want to add another fixture for AIIDs.

Copy link
Owner

Choose a reason for hiding this comment

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

OK

'model' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 64],
'collection' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 128],
'filename' => ['type' => 'string', 'null' => false, 'default' => null],
'filesize' => ['type' => 'integer', 'null' => true, 'default' => null, 'length' => 16],
'mime_type' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 32],
'extension' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 32],
'hash' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 64],
'path' => ['type' => 'string', 'null' => true, 'default' => null],
'adapter' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 32, 'comment' => 'Gaufrette Storage Adapter Class'],
'adapter' => ['type' => 'string', 'null' => true, 'default' => null, 'length' => 32],
'variants' => ['type' => 'json', 'null' => true, 'default' => null],
'metadata' => ['type' => 'json', 'null' => true, 'default' => null],
'created' => ['type' => 'datetime', 'null' => true, 'default' => null],
Expand Down