From 6a89dc922c42e863bbee3fadf7b490ca8d4b8dd1 Mon Sep 17 00:00:00 2001 From: Cristian Tabacitu Date: Mon, 25 Nov 2024 14:13:23 +0200 Subject: [PATCH 1/9] reorganize uploader docs --- 7.x-dev/crud-uploaders.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 2b49ef32..14d4f903 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -5,10 +5,10 @@ ## About -Uploading and managing files is a common task in Admin Panels. Starting with Backpack v6, you can fully setup your upload fields in your field definition, using purpose-built classes we call Uploaders. No more need to create mutators, manual validation of input or custom code to handle the files - though you can still do that, if you want. +Uploading and managing files is a common task in Admin Panels. In Backpack v7, your field definition can include uploading logic, thanks to some classes we call Uploaders. You don't need to create mutators, manual validation of input or custom code to handle file upload - though you can still do that, if you want. - -## How it works + +## How to Use Uploaders When adding an upload field (`upload`, `upload_multiple`, `image` or `dropzone`) to your operation, tell Backpack that you want to use the appropriate Uploader, by using `withFiles()`: @@ -23,8 +23,8 @@ That's it. Backpack will now handle the upload, storage and deletion of the file > - (*) If you want your files to be deleted when the entry is deleted, please [Configure File Deletion](#deleting-files-when-entry-is-deleted) - -## Configuring the Uploaders + +## How to Configure Uploaders The `withFiles()` method accepts an array of options that you can use to customize the upload. @@ -56,6 +56,25 @@ This allows you to overwrite or set the uploader class for this field. You can u - **`fileNamer`** - default: **null** It accepts a `FileNameGeneratorInterface` instance or a closure. As the name implies, this will be used to generate the file name. Read more about in the [Naming uploaded files](#upload-name-files) section. + +## Available Uploaders + +We've already created Uploaders for the most common scenarios: +- CRUD comes with `SingleFile`, `MultipleFiles`, `SingleBas64Image` +- PRO comes with `AjaxUploader`, `DropzoneUploader`, `EasyMDEUploader` +- if you want to use spatie/medialibrary you can just install [medialibrary-uploaders](https://github.com/Laravel-Backpack/medialibrary-uploaders) to get `MediaAjaxUploader`, `MediaMultipleFiles`, `MediaSingleBase64Image`, `MediaSingleFile` + + + +## How to Create Uploaders + +Do you want to create your own Uploader class, for your custom field? Here's how you can do that, and how Uploader classes work behind the scenes. + +// TODO + + +## FAQ about Uploaders + ### Handling uploads in relationship fields From 42dd2e7b40224b77f1422cb0c5639f0206e733ed Mon Sep 17 00:00:00 2001 From: pxpm Date: Wed, 27 Nov 2024 12:46:25 +0000 Subject: [PATCH 2/9] add docs on how to create an uploader --- 7.x-dev/crud-uploaders.md | 127 +++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 14d4f903..6e70c85f 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -70,7 +70,132 @@ We've already created Uploaders for the most common scenarios: Do you want to create your own Uploader class, for your custom field? Here's how you can do that, and how Uploader classes work behind the scenes. -// TODO +First thing you need to decide if you need "Ajax" or "Non-Ajax" upload. The big difference is that the "non-ajax" uploaders process the file upload when you submit your form, while the ajax upload process the file using a javascript ajax request, so it can be uploaded before you submit the form. + +First let's see how to create a non-ajax uploader, for that we will create a `CustomUploader` class that extends the abstract class `Uploader`. + +```php +namespace App\Uploaders\CustomUploader; + +use Backpack\CRUD\app\Library\Uploaders\Uploader; + +class CustomUploader extends Uploader +{ + // the function we need to implement + public function uploadFiles(Model $entry, $values) + { + // $entry is the model instance we are working with + // $values is the sent files from request. + + // do your upload logic here + + return $valueToBeStoredInTheDatabaseEntry; + } + + // in case you want to use your uploader inside repeatable fields + protected function uploadRepeatableFiles($values, $previousValues, $entry = null) + { + } +} +``` + +You can now use this uploader in your field definition: + +```php +CRUD::field('avatar')->type('upload')->withFiles([ + 'uploader' => \App\Uploaders\CustomUploader::class, +]); +``` + +But most likely, you have a `custom_upload` field that you'd like to use this uploader without having to specify it every time. You can do that by adding it to the `UploadersRepository` in your Service Provider `boot()` method: + +```php +// in your App\Providers\AppServiceProvider.php + +protected function boot() +{ + app('UploadersRepository')->addUploaderClasses(['custom_upload' => \App\Uploaders\CustomUploader::class], 'withFiles'); +} +``` + +You can now use `CRUD::field('avatar')->type('custom_upload')->withFiles();` and it will use your custom uploader. What happen behind the scenes is that Backpack will register your uploader to be ran in 3 different model events: `saving`, `retrieved` and `deleting`. + +The `Uploader` class has 3 "entry points" for the mentioned events: **`storeUploadedFiles()`**, **`retrieveUploadedFiles()`** and **`deleteUploadedFiles()`**. You can overwrite these methods in your custom uploader to add your custom logic but for most uploaders you will not need to overwrite them as they are "setup" methods for the action that will be performed, and after setup they call the relevant methods that each uploader will implement, like ```uploadFiles()``` or ```uploadRepeatableFiles()```. + +The base uploader class has most of the functionality implemented and use **"strategy methods"** to configure the underlying behavior. + +**`shouldUploadFiles`** - a method that returns a boolean to determine if the files should be uploaded. By default it returns true, but you can overwrite it to add your custom logic. + +**`shouldKeepPreviousValuesUnchanged`** - a method that returns a boolean to determine if the previous values should be kept unchanged and not perform the upload. + +**`hasDeletedFiles`** - a method that returns a boolean to determine if the files were deleted from the field. + +This is the implementation of those methods in `SingleFile` uploader: +```php +protected function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool +{ + // if a string is sent as the value, it means the file was not changed so we should keep + // previous value unchanged + return is_string($entryValue); +} + +protected function hasDeletedFiles($entryValue): bool +{ + // if the value is null, it means the file was deleted from the field + return $entryValue === null; +} + +protected function shouldUploadFiles($value): bool +{ + // when the value is an instance of UploadedFile, it means the file was uploaded and we should upload it + return is_a($value, 'Illuminate\Http\UploadedFile', true); +} +``` + +For the ajax uploaders, the process is similar, but the `CustomUploader` class should extend `BackpackAjaxUploader` **(requires backpack/pro)** instead of `Uploader`. + +```php + +namespace App\Uploaders\CustomUploader; + +use Backpack\Pro\Uploaders\BackpackAjaxUploader; + +class CustomUploader extends BackpackAjaxUploader +{ + // this is called on `saving` event of the main entry, at this point you already performed the upload + // of the files in the ajax endpoint. By default they are in a temp folder, so here is the place + // where you should move them to the final disk and path and setup what will be saved in the database. + public function uploadFiles(Model $entry, $values) + { + return $valueToBeStoredInTheDatabaseEntry; + } + + // in case you want to use your uploader inside repeatable fields + protected function uploadRepeatableFiles($values, $previousValues, $entry = null) + { + } +} +``` + +The process to register the uploader in the `UploadersRepositoy` is the same as the non-ajax uploader. `app('UploadersRepository')->addUploaderClasses(['custom_upload' => \App\Uploaders\CustomUploader::class], 'withFiles');` in the boot method of your provider. + +In addition to the field configuration, ajax uploaders require that you use the `AjaxUploadOperation` trait in your controller. The operation is responsible to register the ajax route where your files will be sent and the upload process will be handled and the delete route from where you can delete **temporary files**. + +Similar to model events, there are two "setup" methods for those endpoints: **`processAjaxEndpointUploads()`** and **`deleteAjaxEndpointUpload()`**. You can overwrite them to add your custom logic but most of the time you will not need to do that and just implement the `uploadFiles()` and `uploadRepeatableFiles()` methods. + +The ajax uploader also has the same "strategy methods" as the non-ajax uploader (see above), but adds a few more: +**`ajaxEndpointSuccessResponse($files = null)`** - this should return a `JsonResponse` with the needed information when the upload is successful. By default it returns a json response with the file path. + +**`ajaxEndpointErrorResponse($message)`** - use this method to change the endpoint response in case the upload failed. Similar to the success it should return a `JsonResponse`. + +**`getAjaxEndpointDisk()`** - by default a `temporaryDisk` is used to store the files before they are moved to the final disk. (when uploadFiles() is called). You can overwrite this method to change the disk used. + +**`getAjaxEndpointPath()`** - by default the path is `/temp` but you can overwrite this method to change the path used. + +**`getDefaultAjaxEndpointValidation()`** - Should return the default validation rules (in the format of `BackpackCustomRule`) for the ajax endpoint. By default it returns a `ValidGenericAjaxEndpoint` rule. + + +For any other customization you would like to perform, please check the source code of the `Uploader` and `BackpackAjaxUploader` classes. ## FAQ about Uploaders From 000b574e7e6d2674c5af4b269c4cb2b5bea86d45 Mon Sep 17 00:00:00 2001 From: pxpm Date: Wed, 27 Nov 2024 13:06:40 +0000 Subject: [PATCH 3/9] add getUploadedFilesFromRequest strategy method --- 7.x-dev/crud-uploaders.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 6e70c85f..91d70739 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -130,6 +130,8 @@ The base uploader class has most of the functionality implemented and use **"str **`hasDeletedFiles`** - a method that returns a boolean to determine if the files were deleted from the field. +**`getUploadedFilesFromRequest`** - this is the method that will be called to get the values sent in the request. Some uploaders require you get the `->files()` others the `->input()`. By default it returns the `->files()`. + This is the implementation of those methods in `SingleFile` uploader: ```php protected function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool From 208046b7598ffe8b04a47268fa954392bc8d398a Mon Sep 17 00:00:00 2001 From: pxpm Date: Mon, 2 Dec 2024 13:47:03 +0000 Subject: [PATCH 4/9] update docs for temporary file deletion --- 7.x-dev/crud-uploaders.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 91d70739..7b78ac1e 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -330,6 +330,22 @@ class SomeModel extends Model } ``` + +## Deleting temporary files + +When using ajax uploaders, the files are uploaded to a temporary disk and path before being moved to the final disk and path. If by some reason the user does not finish the operation, those files may lay around in your server temporary folder. +To delete them, we have created a `backpack:purge-temporary-folder` command that you can schedule to run every day, or in the time frame that better suits your needs. + +```php +// in your routes/console +use Illuminate\Console\Scheduling\Schedule; + +Schedule::command('backpack:purge-temporary-folder')->daily(); + +``` + +For additional configuration check the `config/backpack/operations/ajax-uploads.php` file. Those configurations can also be passed on a "per-command" basis, eg: `backpack:purge-temporary-folder --disk=public --path=temp --older-than=5`. + ### Configuring uploaders in custom fields From ea3aee810c1ea13c6fd98e891369eeaf807f31b0 Mon Sep 17 00:00:00 2001 From: pxpm Date: Tue, 3 Dec 2024 10:45:03 +0000 Subject: [PATCH 5/9] add docs in field about uploader --- 7.x-dev/crud-fields.md | 3 +++ 7.x-dev/crud-uploaders.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/7.x-dev/crud-fields.md b/7.x-dev/crud-fields.md index fed013a9..b0310059 100644 --- a/7.x-dev/crud-fields.md +++ b/7.x-dev/crud-fields.md @@ -872,6 +872,9 @@ CRUD::field([ > NOTE: Summernote does NOT sanitize the input. If you do not trust the users of this field, you should sanitize the input or output using something like HTML Purifier. Personally we like to use install [mewebstudio/Purifier](https://github.com/mewebstudio/Purifier) and add an [accessor or mutator](https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators) on the Model, so that wherever the model is created from (admin panel or app), the output will always be clean. [Example here](https://github.com/Laravel-Backpack/demo/commit/7342cffb418bb568b9e4ee279859685ddc0456c1). +#### Uploading files with summernote + +Summernote saves images as base64 encoded strings in the database. If you want to save them as files on the server, you can use the [Summernote File Upload](https://backpackforlaravel.com/docs/7.x/crud-uploaders) . Please note that the Summernote Uploader is part of the `backpack/pro` package. Input preview: ![CRUD Field - summernote](https://backpackforlaravel.com/uploads/docs-4-2/fields/summernote.png) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 7b78ac1e..dad7b7dc 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -10,7 +10,7 @@ Uploading and managing files is a common task in Admin Panels. In Backpack v7, y ## How to Use Uploaders -When adding an upload field (`upload`, `upload_multiple`, `image` or `dropzone`) to your operation, tell Backpack that you want to use the appropriate Uploader, by using `withFiles()`: +When adding an upload field (`upload`, `upload_multiple`, `image`, `dropzone`, `easymde`, `summernote`) to your operation, tell Backpack that you want to use the appropriate Uploader, by using `withFiles()`: ```php CRUD::field('avatar')->type('upload')->withFiles(); @@ -61,7 +61,7 @@ It accepts a `FileNameGeneratorInterface` instance or a closure. As the name imp We've already created Uploaders for the most common scenarios: - CRUD comes with `SingleFile`, `MultipleFiles`, `SingleBas64Image` -- PRO comes with `AjaxUploader`, `DropzoneUploader`, `EasyMDEUploader` +- PRO comes with `DropzoneUploader`, `EasyMDEUploader`, `SummernoteUploader` - if you want to use spatie/medialibrary you can just install [medialibrary-uploaders](https://github.com/Laravel-Backpack/medialibrary-uploaders) to get `MediaAjaxUploader`, `MediaMultipleFiles`, `MediaSingleBase64Image`, `MediaSingleFile` From d847a9c0fe6eed741d339799ec31ca157dba795f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20T=C4=83b=C4=83citu?= Date: Thu, 5 Dec 2024 12:22:51 +0200 Subject: [PATCH 6/9] Apply suggestions from code review --- 7.x-dev/crud-fields.md | 2 +- 7.x-dev/crud-uploaders.md | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/7.x-dev/crud-fields.md b/7.x-dev/crud-fields.md index b0310059..78c62af9 100644 --- a/7.x-dev/crud-fields.md +++ b/7.x-dev/crud-fields.md @@ -874,7 +874,7 @@ CRUD::field([ #### Uploading files with summernote -Summernote saves images as base64 encoded strings in the database. If you want to save them as files on the server, you can use the [Summernote File Upload](https://backpackforlaravel.com/docs/7.x/crud-uploaders) . Please note that the Summernote Uploader is part of the `backpack/pro` package. +Summernote saves images as base64 encoded strings in the database. If you want to save them as files on the server, you can use the [Summernote Uploader](https://backpackforlaravel.com/docs/7.x/crud-uploaders). Please note that the Summernote Uploader is part of the `backpack/pro` package. Input preview: ![CRUD Field - summernote](https://backpackforlaravel.com/uploads/docs-4-2/fields/summernote.png) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index dad7b7dc..a2b11e65 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -70,7 +70,12 @@ We've already created Uploaders for the most common scenarios: Do you want to create your own Uploader class, for your custom field? Here's how you can do that, and how Uploader classes work behind the scenes. -First thing you need to decide if you need "Ajax" or "Non-Ajax" upload. The big difference is that the "non-ajax" uploaders process the file upload when you submit your form, while the ajax upload process the file using a javascript ajax request, so it can be uploaded before you submit the form. +First thing you need to decide if you are creating a _non-ajax_ or _ajax_ uploader: +- _non-ajax_ uploaders process the file upload when you submit your form; +- _ajax_ uploaders process the file upload before the form is submitted, by submitting an AJAX request using Javascript; + + +## How to Create a Custom Non-Ajax Uploader First let's see how to create a non-ajax uploader, for that we will create a `CustomUploader` class that extends the abstract class `Uploader`. @@ -107,7 +112,7 @@ CRUD::field('avatar')->type('upload')->withFiles([ ]); ``` -But most likely, you have a `custom_upload` field that you'd like to use this uploader without having to specify it every time. You can do that by adding it to the `UploadersRepository` in your Service Provider `boot()` method: +If you custom uploader was created to work for a custom field (say it's called `custom_upload`), you can tell Backpack to always use this uploader for that field type - that way you don't have to specify it every time you use the field. You can do that in your Service Provider `boot()` method, by adding it to the `UploadersRepository`: ```php // in your App\Providers\AppServiceProvider.php @@ -118,11 +123,11 @@ protected function boot() } ``` -You can now use `CRUD::field('avatar')->type('custom_upload')->withFiles();` and it will use your custom uploader. What happen behind the scenes is that Backpack will register your uploader to be ran in 3 different model events: `saving`, `retrieved` and `deleting`. +You can now use `CRUD::field('avatar')->type('custom_upload')->withFiles();` and it will use your custom uploader. What happens behind the scenes is that Backpack will register your uploader to run on 3 different model events: `saving`, `retrieved` and `deleting`. The `Uploader` class has 3 "entry points" for the mentioned events: **`storeUploadedFiles()`**, **`retrieveUploadedFiles()`** and **`deleteUploadedFiles()`**. You can overwrite these methods in your custom uploader to add your custom logic but for most uploaders you will not need to overwrite them as they are "setup" methods for the action that will be performed, and after setup they call the relevant methods that each uploader will implement, like ```uploadFiles()``` or ```uploadRepeatableFiles()```. -The base uploader class has most of the functionality implemented and use **"strategy methods"** to configure the underlying behavior. +Notice this custom class you're creating is extending `Backpack\CRUD\app\Library\Uploaders\Uploader`. That base uploader class has most of the functionality implemented and uses **"strategy methods"** to configure the underlying behavior. **`shouldUploadFiles`** - a method that returns a boolean to determine if the files should be uploaded. By default it returns true, but you can overwrite it to add your custom logic. @@ -152,9 +157,11 @@ protected function shouldUploadFiles($value): bool // when the value is an instance of UploadedFile, it means the file was uploaded and we should upload it return is_a($value, 'Illuminate\Http\UploadedFile', true); } -``` -For the ajax uploaders, the process is similar, but the `CustomUploader` class should extend `BackpackAjaxUploader` **(requires backpack/pro)** instead of `Uploader`. + +## How to Create a Custom Ajax Uploader + +For the ajax uploaders, the process is similar, but your custom uploader class should extend `BackpackAjaxUploader` instead of `Uploader` (**note that this requires backpack/pro**). ```php @@ -186,15 +193,11 @@ In addition to the field configuration, ajax uploaders require that you use the Similar to model events, there are two "setup" methods for those endpoints: **`processAjaxEndpointUploads()`** and **`deleteAjaxEndpointUpload()`**. You can overwrite them to add your custom logic but most of the time you will not need to do that and just implement the `uploadFiles()` and `uploadRepeatableFiles()` methods. The ajax uploader also has the same "strategy methods" as the non-ajax uploader (see above), but adds a few more: -**`ajaxEndpointSuccessResponse($files = null)`** - this should return a `JsonResponse` with the needed information when the upload is successful. By default it returns a json response with the file path. - -**`ajaxEndpointErrorResponse($message)`** - use this method to change the endpoint response in case the upload failed. Similar to the success it should return a `JsonResponse`. - -**`getAjaxEndpointDisk()`** - by default a `temporaryDisk` is used to store the files before they are moved to the final disk. (when uploadFiles() is called). You can overwrite this method to change the disk used. - -**`getAjaxEndpointPath()`** - by default the path is `/temp` but you can overwrite this method to change the path used. - -**`getDefaultAjaxEndpointValidation()`** - Should return the default validation rules (in the format of `BackpackCustomRule`) for the ajax endpoint. By default it returns a `ValidGenericAjaxEndpoint` rule. +- **`ajaxEndpointSuccessResponse($files = null)`** - This should return a `JsonResponse` with the needed information when the upload is successful. By default it returns a json response with the file path. +- **`ajaxEndpointErrorResponse($message)`** - Use this method to change the endpoint response in case the upload failed. Similar to the success it should return a `JsonResponse`. +- **`getAjaxEndpointDisk()`** - By default a `temporaryDisk` is used to store the files before they are moved to the final disk (when uploadFiles() is called). You can overwrite this method to change the disk used. +- **`getAjaxEndpointPath()`** - By default the path is `/temp` but you can override this method to change the path used. +- **`getDefaultAjaxEndpointValidation()`** - Should return the default validation rules (in the format of `BackpackCustomRule`) for the ajax endpoint. By default it returns a `ValidGenericAjaxEndpoint` rule. For any other customization you would like to perform, please check the source code of the `Uploader` and `BackpackAjaxUploader` classes. From 311d7f66709b9d9aa66898fa970e0d9eeb41497d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20T=C4=83b=C4=83citu?= Date: Thu, 5 Dec 2024 12:23:39 +0200 Subject: [PATCH 7/9] Apply suggestions from code review --- 7.x-dev/crud-uploaders.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index a2b11e65..44bb4cf1 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -75,7 +75,7 @@ First thing you need to decide if you are creating a _non-ajax_ or _ajax_ upload - _ajax_ uploaders process the file upload before the form is submitted, by submitting an AJAX request using Javascript; -## How to Create a Custom Non-Ajax Uploader +### How to Create a Custom Non-Ajax Uploader First let's see how to create a non-ajax uploader, for that we will create a `CustomUploader` class that extends the abstract class `Uploader`. @@ -159,7 +159,7 @@ protected function shouldUploadFiles($value): bool } -## How to Create a Custom Ajax Uploader +### How to Create a Custom Ajax Uploader For the ajax uploaders, the process is similar, but your custom uploader class should extend `BackpackAjaxUploader` instead of `Uploader` (**note that this requires backpack/pro**). From 0a299e61d35302b74f39e3f1769a700c6fe5e6de Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Thu, 5 Dec 2024 10:52:33 +0000 Subject: [PATCH 8/9] Update 7.x-dev/crud-uploaders.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cristian Tăbăcitu --- 7.x-dev/crud-uploaders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index 44bb4cf1..a56beaf3 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -125,7 +125,7 @@ protected function boot() You can now use `CRUD::field('avatar')->type('custom_upload')->withFiles();` and it will use your custom uploader. What happens behind the scenes is that Backpack will register your uploader to run on 3 different model events: `saving`, `retrieved` and `deleting`. -The `Uploader` class has 3 "entry points" for the mentioned events: **`storeUploadedFiles()`**, **`retrieveUploadedFiles()`** and **`deleteUploadedFiles()`**. You can overwrite these methods in your custom uploader to add your custom logic but for most uploaders you will not need to overwrite them as they are "setup" methods for the action that will be performed, and after setup they call the relevant methods that each uploader will implement, like ```uploadFiles()``` or ```uploadRepeatableFiles()```. +The `Uploader` class has 3 "entry points" for the mentioned events: **`storeUploadedFiles()`**, **`retrieveUploadedFiles()`** and **`deleteUploadedFiles()`**. You can override these methods in your custom uploader, but typically you will not need to do that. The methods already delegate what will happen to the relevant methods (eg. if it's not a repeatable, call ```uploadFiles()```, othewise call ```uploadRepeatableFiles()```). Notice this custom class you're creating is extending `Backpack\CRUD\app\Library\Uploaders\Uploader`. That base uploader class has most of the functionality implemented and uses **"strategy methods"** to configure the underlying behavior. From 7873d5a5fde5901935e74a4ece7f13ca9a150a1b Mon Sep 17 00:00:00 2001 From: pxpm Date: Thu, 5 Dec 2024 12:26:42 +0000 Subject: [PATCH 9/9] add repeatable uploader docs --- 7.x-dev/crud-uploaders.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/7.x-dev/crud-uploaders.md b/7.x-dev/crud-uploaders.md index a56beaf3..e5089291 100644 --- a/7.x-dev/crud-uploaders.md +++ b/7.x-dev/crud-uploaders.md @@ -97,9 +97,20 @@ class CustomUploader extends Uploader return $valueToBeStoredInTheDatabaseEntry; } - // in case you want to use your uploader inside repeatable fields - protected function uploadRepeatableFiles($values, $previousValues, $entry = null) + // this is called when your uploader field is a subfield of a repeatable field. In here you receive + // the sent values in the current request and the previous repeatable values (only the uploads values). + protected function uploadRepeatableFiles($values, $previousValues) { + // you should return an array of arrays (each sub array is a repeatable row) where the array key is the field name. + // backpack will merge this values along the other repeatable fields and save them in the database. + return [ + [ + 'custom_upload' => 'path/file.jpg' + ], + [ + 'custom_upload' => 'path/file.jpg' + ] + ]; } } ``` @@ -179,9 +190,20 @@ class CustomUploader extends BackpackAjaxUploader return $valueToBeStoredInTheDatabaseEntry; } - // in case you want to use your uploader inside repeatable fields - protected function uploadRepeatableFiles($values, $previousValues, $entry = null) + // this is called when your uploader field is a subfield of a repeatable field. In here you receive + // the sent values in the current request and the previous repeatable values (only the uploads values). + protected function uploadRepeatableFiles($values, $previousValues) { + // you should return an array of arrays (each sub array is a repeatable row) where the array key is the field name. + // backpack will merge this values along the other repeatable fields and save them in the database. + return [ + [ + 'custom_upload' => 'path/file.jpg' + ], + [ + 'custom_upload' => 'path/file.jpg' + ] + ]; } } ```