Skip to content
Merged
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
14 changes: 11 additions & 3 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,14 @@ Basic example
<?php echo $this->Form->input('photo', ['type' => 'file']); ?>
<?php echo $this->Form->end(); ?>

Using the above setup, uploaded files cannot be deleted. To do so, a
field must be added to store the directory of the file as follows:
Deleting files
--------------

Using the setup from the previous example, uploaded files can only be deleted as long as the path is configured to use
static tokens. As soon as dynamic tokens are incorporated, like for example ``{day}``, the generated path will change
over time, and files cannot be deleted anymore at a later point.

In order to prevent such situations, a field must be added to store the directory of the file as follows:

.. code:: sql

Expand Down Expand Up @@ -132,9 +138,11 @@ field must be added to store the directory of the file as follows:
<?php echo $this->Form->create($user, ['type' => 'file']); ?>
<?php echo $this->Form->input('username'); ?>
<?php echo $this->Form->input('photo', ['type' => 'file']); ?>
<?php echo $this->Form->input('photo_dir', ['type' => 'hidden']); ?>
<?php echo $this->Form->end(); ?>

Using such a setup, the behavior will use the stored path value instead of generating the path dynamically when deleting
files.

Advanced example
----------------

Expand Down
9 changes: 7 additions & 2 deletions src/Model/Behavior/UploadBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,18 @@ public function afterDelete(Event $event, Entity $entity, ArrayObject $options)
continue;
}

$path = $this->getPathProcessor($entity, $entity->{$field}, $field, $settings)->basepath();
$dirField = Hash::get($settings, 'fields.dir', 'dir');
if ($entity->has($dirField)) {
$path = $entity->get($dirField);
} else {
$path = $this->getPathProcessor($entity, $entity->get($field), $field, $settings)->basepath();
}

$callback = Hash::get($settings, 'deleteCallback', null);
if ($callback && is_callable($callback)) {
$files = $callback($path, $entity, $field, $settings);
} else {
$files = [$path . $entity->{$field}];
$files = [$path . $entity->get($field)];
}

$writer = $this->getWriter($entity, [], $field, $settings);
Expand Down
75 changes: 66 additions & 9 deletions tests/TestCase/Model/Behavior/UploadBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,34 +415,91 @@ public function testAfterDeleteSkip()

public function testAfterDeleteUsesPathProcessorToDetectPathToTheFile()
{
$this->entity->field = rand(1000, 9999);
$path = rand(1000, 9999) . DIRECTORY_SEPARATOR;
$dir = '/some/path/';
$field = 'file.txt';

$methods = array_diff($this->behaviorMethods, ['afterDelete', 'config', 'setConfig', 'getConfig']);
$behavior = $this->getMockBuilder('Josegonzalez\Upload\Model\Behavior\UploadBehavior')
->setMethods($methods)
->setConstructorArgs([$this->table, $this->dataOk])
->getMock();
$behavior->config($this->dataOk);

$this->entity->expects($this->at(0))
->method('has')
->with('dir')
->will($this->returnValue(false));
$this->entity->expects($this->at(1))
->method('get')
->with('field')
->will($this->returnValue($field));
$this->entity->expects($this->at(2))
->method('get')
->with('field')
->will($this->returnValue($field));

// expecting getPathProcessor to be called with right arguments for dataOk
$behavior->expects($this->once())->method('getPathProcessor')
->with($this->entity, $this->entity->field, 'field', $this->dataOk['field'])
$behavior->expects($this->once())
->method('getPathProcessor')
->with($this->entity, $field, 'field', $this->dataOk['field'])
->willReturn($this->processor);
// basepath of processor should return our fake path
$this->processor->expects($this->once())->method('basepath')
->willReturn($path);
$this->processor->expects($this->once())
->method('basepath')
->willReturn($dir);
// expecting getWriter to be called with right arguments for dataOk
$behavior->expects($this->once())->method('getWriter')
$behavior->expects($this->once())
->method('getWriter')
->with($this->entity, [], 'field', $this->dataOk['field'])
->willReturn($this->writer);
// and here we check that file with right path will be deleted
$this->writer->expects($this->once())->method('delete')
->with([$path . $this->entity->field])
$this->writer->expects($this->once())
->method('delete')
->with([$dir . $field])
->willReturn([true]);

$behavior->afterDelete(new Event('fake.event'), $this->entity, new ArrayObject);
}

public function testAfterDeletePrefersStoredPathOverPathProcessor()
{
$dir = '/some/path/';
$field = 'file.txt';

$methods = array_diff($this->behaviorMethods, ['afterDelete', 'config', 'setConfig', 'getConfig']);
$behavior = $this->getMockBuilder('Josegonzalez\Upload\Model\Behavior\UploadBehavior')
->setMethods($methods)
->setConstructorArgs([$this->table, $this->dataOk])
->getMock();
$behavior->config($this->dataOk);

$this->entity->expects($this->at(0))
->method('has')
->with('dir')
->will($this->returnValue(true));
$this->entity->expects($this->at(1))
->method('get')
->with('dir')
->will($this->returnValue($dir));
$this->entity->expects($this->at(2))
->method('get')
->with('field')
->will($this->returnValue($field));

$behavior->expects($this->never())
->method('getPathProcessor');
$behavior->expects($this->once())
->method('getWriter')
->will($this->returnValue($this->writer));

$this->writer->expects($this->once())
->method('delete')
->with([$dir . $field])
->will($this->returnValue([true]));

$this->assertNull($behavior->afterDelete(new Event('fake.event'), $this->entity, new ArrayObject));
}

public function testAfterDeleteNoDeleteCallback()
{
$this->entity->field = rand(1000, 9999);
Expand Down