Skip to content

Commit

Permalink
Uploaders - Refactor and fixes (#5478)
Browse files Browse the repository at this point in the history
* add method to get ajax uploaders

* Apply fixes from StyleCI

[ci skip] [skip ci]

* use an abstract class

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* refactor uploaders

* Apply fixes from StyleCI

[ci skip] [skip ci]

* refactor rules

* Apply fixes from StyleCI

[ci skip] [skip ci]

* move ajax to PRO, cleanup

* Apply fixes from StyleCI

[ci skip] [skip ci]

* make attributes available for all subfields

* fix tests

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* upload multiple and upload properly working 🙏

* fixes

* Apply fixes from StyleCI

[ci skip] [skip ci]

* allow the configuration of valueWithoutPath call.

* fix valid upload inside repeatables

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix condition

* cleanup

* fix

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix use case for enabling validation after entry is created

* Apply fixes from StyleCI

[ci skip] [skip ci]

* dont save array keys

* fix ajax validation

* fix validation messages

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fixes ValidUpload

* Apply fixes from StyleCI

[ci skip] [skip ci]

* dont json encode if casted in the model

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix previous file identification in repeatable

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix getting values

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add fake fields support

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip add uploaders tests

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add pro columns

* fix test suite

* fix tests

* ffix tests

* remove unused test views

* add uploaders to test coverage

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add coverage folder to gitignore

* make tests run faster by not reloading db when not necessary

* add coverage to validation tests

* add fake tests to uploaders

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add more tests

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add more upload assets

* fixes

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix single file

* Apply fixes from StyleCI

[ci skip] [skip ci]

* add image column

* fix tests

* Apply fixes from StyleCI

[ci skip] [skip ci]

* remove hardcoded macro names

* Apply fixes from StyleCI

[ci skip] [skip ci]

* remove double loop, fix single file uploader

* Apply fixes from StyleCI

[ci skip] [skip ci]

* use a big increments and unsigned for primary key

* handle pivot file deletion

* Apply fixes from StyleCI

[ci skip] [skip ci]

* register events for relation models

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix typo

* Apply fixes from StyleCI

[ci skip] [skip ci]

* fix relationship uploaders

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* wip

* Apply fixes from StyleCI

[ci skip] [skip ci]

* update temporary time key

* save objects in the macro

---------

Co-authored-by: StyleCI Bot <[email protected]>
  • Loading branch information
pxpm and StyleCIBot committed Nov 21, 2024
1 parent 13f6fc4 commit 530d655
Show file tree
Hide file tree
Showing 61 changed files with 2,393 additions and 279 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ composer.lock
.phpunit.result.cache
src/public/packages/
/.phpunit.cache
coverage/

7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@
]
},
"scripts": {
"test": "vendor/bin/phpunit --testdox",
"test": [
"@putenv XDEBUG_MODE=off",
"vendor/bin/phpunit"
],
"test-failing": "vendor/bin/phpunit --order-by=defects --stop-on-failure",
"test-coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text"
"test-coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage"
},
"extra": {
"branch-alias": {
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<include>
<directory suffix=".php">./src/app/Library/CrudPanel/Traits/</directory>
<directory>./src/app/Library/Validation/</directory>
<directory>./src/app/Library/Uploaders/</directory>
<directory suffix=".php">./src/app/Library/CrudPanel/</directory>
<directory suffix=".php">./src/app/Models/Traits/</directory>
<file>./src/app/Library/Widget.php</file>
Expand All @@ -35,9 +36,11 @@
</source>
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
<env name="BCRYPT_ROUNDS" value="12"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_FOREIGN_KEYS" value="true"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
Expand Down
4 changes: 2 additions & 2 deletions src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field)
$subfield['name'] = Str::replace(' ', '', $subfield['name']);

$subfield['parentFieldName'] = $field['name'];
$subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name'];
$subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.');

if (! isset($field['model'])) {
// we're inside a simple 'repeatable' with no model/relationship, so
Expand All @@ -294,8 +296,6 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field)
$currentEntity = $subfield['baseEntity'] ?? $field['entity'];
$subfield['baseModel'] = $subfield['baseModel'] ?? $field['model'];
$subfield['baseEntity'] = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$currentEntity : $currentEntity;
$subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name'];
$subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.');
}

$field['subfields'][$key] = $this->makeSureFieldHasNecessaryAttributes($subfield);
Expand Down
28 changes: 25 additions & 3 deletions src/app/Library/Uploaders/MultipleFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,16 @@ public function uploadFiles(Model $entry, $value = null)
}
}

return isset($entry->getCasts()[$this->getName()]) ? $previousFiles : json_encode($previousFiles);
$previousFiles = array_values($previousFiles);

if (empty($previousFiles)) {
return null;
}

return isset($entry->getCasts()[$this->getName()]) || $this->isFake() ? $previousFiles : json_encode($previousFiles);
}

/** @codeCoverageIgnore */
public function uploadRepeatableFiles($files, $previousRepeatableValues, $entry = null)
{
$fileOrder = $this->getFileOrderFromRequest();
Expand All @@ -73,11 +80,26 @@ public function uploadRepeatableFiles($files, $previousRepeatableValues, $entry
}
}
}
// create a temporary variable that we can unset keys
// everytime one is found. That way we avoid iterating
// already handled keys (notice we do a deep array copy)
$tempFileOrder = array_map(function ($item) {
return $item;
}, $fileOrder);

foreach ($previousRepeatableValues as $previousRow => $previousFiles) {
foreach ($previousFiles ?? [] as $key => $file) {
$key = array_search($file, $fileOrder, true);
if ($key === false) {
$previousFileInArray = array_filter($tempFileOrder, function ($items, $key) use ($file, $tempFileOrder) {
$found = array_search($file, $items ?? [], true);
if ($found !== false) {
Arr::forget($tempFileOrder, $key.'.'.$found);

return true;
}

return false;
}, ARRAY_FILTER_USE_BOTH);
if ($file && ! $previousFileInArray) {
Storage::disk($this->getDisk())->delete($file);
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/Library/Uploaders/SingleBase64Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

/** @codeCoverageIgnore */
class SingleBase64Image extends Uploader
{
public function uploadFiles(Model $entry, $value = null)
Expand Down Expand Up @@ -51,7 +52,7 @@ public function uploadRepeatableFiles($values, $previousRepeatableValues, $entry
}
}

$imagesToDelete = array_diff($previousRepeatableValues, $values);
$imagesToDelete = array_diff(array_filter($previousRepeatableValues), $values);

foreach ($imagesToDelete as $image) {
Storage::disk($this->getDisk())->delete($image);
Expand All @@ -65,7 +66,7 @@ protected function shouldUploadFiles($value): bool
return $value && is_string($value) && Str::startsWith($value, 'data:image');
}

protected function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
{
return $entry->exists && is_string($entryValue) && ! Str::startsWith($entryValue, 'data:image');
}
Expand Down
13 changes: 9 additions & 4 deletions src/app/Library/Uploaders/SingleFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function uploadFiles(Model $entry, $value = null)
return $previousFile;
}

/** @codeCoverageIgnore */
public function uploadRepeatableFiles($values, $previousRepeatableValues, $entry = null)
{
$orderedFiles = $this->getFileOrderFromRequest();
Expand All @@ -53,9 +54,13 @@ public function uploadRepeatableFiles($values, $previousRepeatableValues, $entry
}

foreach ($previousRepeatableValues as $row => $file) {
if ($file && ! isset($orderedFiles[$row])) {
$orderedFiles[$row] = null;
Storage::disk($this->getDisk())->delete($file);
if ($file) {
if (! isset($orderedFiles[$row])) {
$orderedFiles[$row] = null;
}
if (! in_array($file, $orderedFiles)) {
Storage::disk($this->getDisk())->delete($file);
}
}
}

Expand All @@ -65,7 +70,7 @@ public function uploadRepeatableFiles($values, $previousRepeatableValues, $entry
/**
* Single file uploaders send no value when they are not dirty.
*/
protected function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
{
return is_string($entryValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public function repeats(string $repeatableContainerName): self;

public function relationship(bool $isRelation): self;

public function fake(bool|string $isFake): self;

/**
* Getters.
*/
Expand Down Expand Up @@ -60,4 +62,10 @@ public function canHandleMultipleFiles(): bool;
public function isRelationship(): bool;

public function getPreviousFiles(Model $entry): mixed;

public function getValueWithoutPath(?string $value = null): ?string;

public function isFake(): bool;

public function getFakeAttribute(): bool|string;
}
17 changes: 15 additions & 2 deletions src/app/Library/Uploaders/Support/RegisterUploadEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private function registerSubfieldEvent(array $subfield, bool $registerModelEvent
$uploader = $this->getUploader($subfield, $this->uploaderConfiguration);
$crudObject = $this->crudObject->getAttributes();
$uploader = $uploader->repeats($crudObject['name']);
$uploader = $uploader->fake((isset($crudObject['fake']) && $crudObject['fake']) ? ($crudObject['store_in'] ?? 'extras') : false);

// If this uploader is already registered bail out. We may endup here multiple times when doing modifications to the crud object.
// Changing `subfields` properties will call the macros again. We prevent duplicate entries by checking
Expand Down Expand Up @@ -139,6 +140,14 @@ private function setupModelEvents(string $model, UploaderInterface $uploader): v
$uploader->deleteUploadedFiles($entry);
});

// if the uploader is a relationship and handles repeatable files, we will also register the deleting event on the
// parent model. that way we can control the deletion of the files when the parent model is deleted.
if ($uploader->isRelationship() && $uploader->handleRepeatableFiles) {
app('crud')->model::deleting(function ($entry) use ($uploader) {
$uploader->deleteUploadedFiles($entry);
});
}

app('UploadersRepository')->markAsHandled($uploader->getIdentifier());
}

Expand All @@ -154,9 +163,13 @@ private function setupModelEvents(string $model, UploaderInterface $uploader): v
*/
private function getUploader(array $crudObject, array $uploaderConfiguration): UploaderInterface
{
$customUploader = isset($uploaderConfiguration['uploader']) && class_exists($uploaderConfiguration['uploader']);
$hasCustomUploader = isset($uploaderConfiguration['uploader']);

if ($hasCustomUploader && ! is_a($uploaderConfiguration['uploader'], UploaderInterface::class, true)) {
throw new Exception('Invalid uploader class provided for '.$this->crudObjectType.' type: '.$crudObject['type']);
}

if ($customUploader) {
if ($hasCustomUploader) {
return $uploaderConfiguration['uploader']::for($crudObject, $uploaderConfiguration);
}

Expand Down
Loading

0 comments on commit 530d655

Please sign in to comment.