You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 7.x-dev/crud-uploaders.md
+126-1Lines changed: 126 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,7 +70,132 @@ We've already created Uploaders for the most common scenarios:
70
70
71
71
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.
72
72
73
-
// TODO
73
+
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.
74
+
75
+
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`.
76
+
77
+
```php
78
+
namespace App\Uploaders\CustomUploader;
79
+
80
+
use Backpack\CRUD\app\Library\Uploaders\Uploader;
81
+
82
+
class CustomUploader extends Uploader
83
+
{
84
+
// the function we need to implement
85
+
public function uploadFiles(Model $entry, $values)
86
+
{
87
+
// $entry is the model instance we are working with
88
+
// $values is the sent files from request.
89
+
90
+
// do your upload logic here
91
+
92
+
return $valueToBeStoredInTheDatabaseEntry;
93
+
}
94
+
95
+
// in case you want to use your uploader inside repeatable fields
96
+
protected function uploadRepeatableFiles($values, $previousValues, $entry = null)
97
+
{
98
+
}
99
+
}
100
+
```
101
+
102
+
You can now use this uploader in your field definition:
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:
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`.
122
+
123
+
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()```.
124
+
125
+
The base uploader class has most of the functionality implemented and use **"strategy methods"** to configure the underlying behavior.
126
+
127
+
**`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.
128
+
129
+
**`shouldKeepPreviousValuesUnchanged`** - a method that returns a boolean to determine if the previous values should be kept unchanged and not perform the upload.
130
+
131
+
**`hasDeletedFiles`** - a method that returns a boolean to determine if the files were deleted from the field.
132
+
133
+
This is the implementation of those methods in `SingleFile` uploader:
134
+
```php
135
+
protected function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
136
+
{
137
+
// if a string is sent as the value, it means the file was not changed so we should keep
138
+
// previous value unchanged
139
+
return is_string($entryValue);
140
+
}
141
+
142
+
protected function hasDeletedFiles($entryValue): bool
143
+
{
144
+
// if the value is null, it means the file was deleted from the field
145
+
return $entryValue === null;
146
+
}
147
+
148
+
protected function shouldUploadFiles($value): bool
149
+
{
150
+
// when the value is an instance of UploadedFile, it means the file was uploaded and we should upload it
For the ajax uploaders, the process is similar, but the `CustomUploader` class should extend `BackpackAjaxUploader`**(requires backpack/pro)** instead of `Uploader`.
156
+
157
+
```php
158
+
159
+
namespace App\Uploaders\CustomUploader;
160
+
161
+
use Backpack\Pro\Uploaders\BackpackAjaxUploader;
162
+
163
+
class CustomUploader extends BackpackAjaxUploader
164
+
{
165
+
// this is called on `saving` event of the main entry, at this point you already performed the upload
166
+
// of the files in the ajax endpoint. By default they are in a temp folder, so here is the place
167
+
// where you should move them to the final disk and path and setup what will be saved in the database.
168
+
public function uploadFiles(Model $entry, $values)
169
+
{
170
+
return $valueToBeStoredInTheDatabaseEntry;
171
+
}
172
+
173
+
// in case you want to use your uploader inside repeatable fields
174
+
protected function uploadRepeatableFiles($values, $previousValues, $entry = null)
175
+
{
176
+
}
177
+
}
178
+
```
179
+
180
+
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.
181
+
182
+
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**.
183
+
184
+
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.
185
+
186
+
The ajax uploader also has the same "strategy methods" as the non-ajax uploader (see above), but adds a few more:
187
+
**`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.
188
+
189
+
**`ajaxEndpointErrorResponse($message)`** - use this method to change the endpoint response in case the upload failed. Similar to the success it should return a `JsonResponse`.
190
+
191
+
**`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.
192
+
193
+
**`getAjaxEndpointPath()`** - by default the path is `/temp` but you can overwrite this method to change the path used.
194
+
195
+
**`getDefaultAjaxEndpointValidation()`** - Should return the default validation rules (in the format of `BackpackCustomRule`) for the ajax endpoint. By default it returns a `ValidGenericAjaxEndpoint` rule.
196
+
197
+
198
+
For any other customization you would like to perform, please check the source code of the `Uploader` and `BackpackAjaxUploader` classes.
0 commit comments