This will guide you to upgrade from Backpack 4.1 to v5. The steps are color-coded by the probability that you will need it for your application: High, Medium and Low.
Please make sure your project respects the requirements below, before you start the upgrade process. You can check with php artisan backpack:version
:
If you're running Backpack version 3.x or 4.0, please follow ALL other upgrade guides first, to incrementally get to use Backpack 4.1. Test that your app works well with each version, after each upgrade. Only afterwards can you follow this guide, to upgrade from 4.1 to v5. Previous upgrade guides:
The upgrade guide might seem long or intimidating, but really, it's an easy upgrade. Most projects will only be affected by a few of the breaking changes outlined below. And when there are changes needed, they're pretty small.
Please go thorough all steps, to ensure a smooth upgrade process. The steps are color-coded by how likely we think that step is needed for your project: High, Medium and Low. At the very least, read what's in bold.
Step 0. Upgrade to Laravel 8 if you don't use it yet, then test to confirm your app is working fine. If you also want to upgrade to Laravel 9, we recommend you do that after you've upgraded Backpack to v5. It'll be easier that way.
Step 1. Update your composer.json
file to require:
"backpack/crud": "^5.0.0",
"backpack/pro": "^1.0.0",
If the spit between these two packages is news to you, please read the open-core section of the release notes, to understand how this affects you.
These two packages together will help you have all the features in v4.1 and more. But since backpack/pro
is a closed-source package, to download it, you need to generate your token and password here. If no button is there for you, it means you don't have free access to backpack/pro
, so you'll need to purchase it. Follow the 2-step process called "Instructions" in your token, if you haven't already done that on this project.
Step 2. If you have other backpack addons installed (eg. Backpack\PermissionManager), most of them don't need a version bump, but you should take into consideration that some might need (eg: MenuCRUD). However, if you have third-party Backpack add-ons installed, you might want to bump their versions - please check each addon's page.
Step 3. Run composer update
in the command line.
If you get any conflicts with backpack first party addons most of the time is just moving one version up, eg:
backpack/menucrud: ^2.0
tobackpack/menucrud: ^3.0
.
Step 4. The repeatable
field has been completely rewritten, in order to work with values as a nested PHP array, not a JSON. This has HUGE benefits, but it does present some small breaking changes. If you've used the repeatable
field in your CRUDs
array
or json
(eg. add protected $casts = ['testimonials' => 'array'];
to your model);repeatable
you used a subfield with multiple values (select_multiple
or select2_multiple
etc.), it would not store 'categories': [1, 2]
like you'd expect, but 'categories[]': [1, 2]
; those brackets were not intentional, but we could not fix them without telling you about it; so, when upgrading to v5:
Step 5. For repeatable
fields, you no longer have to create custom validation logic. You can now use Laravel's nested array validation. If you've used a repeatable
in your CRUDs and validated it in your FormRequest:
json_decode()
the value in the input; if you've used our validation example, that means instead of $fieldGroups = json_decode($value);
you should do $fieldGroups = is_array($value) ? $value : [];
;'testimonial.*.name' => 'required|min:5|max:256'
to validate all name
subfields inside a repeatable testimonial
;No changes needed.
Step 6. Security improvement: Starting with v5, Backpack will throttle password request attempts, so that a malicious actor cannot use the "reset password" functionality to send unsolicited emails. We recommend you add this configuration entry to your config/backpack/base.php
(before "Authentication"). See #3862 for details.
/*
|--------------------------------------------------------------------------
| Security
|--------------------------------------------------------------------------
*/
// Backpack will prevent visitors from requesting password recovery too many times
// for a certain email, to make sure they cannot be spammed that way.
// How many seconds should a visitor wait, after they've requested a
// password reset, before they can try again for the same email?
'password_recovery_throttle_notifications' => 600, // time in seconds
// Backpack will prevent an IP from trying to reset the password too many times,
// so that a malicious actor cannot try too many emails, too see if they have
// accounts or to increase the AWS/SendGrid/etc bill.
//
// How many times in any given time period should the user be allowed to
// attempt a password reset? Take into account that user might wrongly
// type an email at first, so at least allow one more try.
// Defaults to 3,10 - 3 times in 10 minutes.
'password_recovery_throttle_access' => '3,10',
Step 7. Operation configurations have been moved from config/backpack/crud.php
, each to its own file. That way, if an operation config value isn't present, it will fall back to Backpack's default value. To upgrade, please:
php artisan vendor:publish --provider="Backpack\CRUD\BackpackServiceProvider" --tag=config
- this will publish a new config file for each operation (eg. config/backpack/operations/create.php
, update.php
etc.);config/backpack/crud.php
, then make the same changes inside the config files you've published above;config/backpack/crud.php
delete the operations
array entirely;Step 8. Inside config/backpack/crud.php
, you were previously able to change what inputs are stripped from the request before saving, by configuring saveAllInputsExcept
for the Create and Update operations. We have moved the configuration to config/backpack/operations/create.php
& config/backpack/operations/update.php
, then:
strippedRequest
;invokable
class, acting like a closure, that backpack will run to strip the request.strippedRequest
is undefined or false, Backpack will strip all inputs that don't have fields, which we consider is the safest approach;If in your config/backpack/operations/create.php
or config/backpack/operations/update.php
you've copied saveAllInputsExcept
as null
or false
, you don't have to do anything.
However, if you have an array for your saveAllInputsExcept
, you can now achieve the same thing by stripping the request yourself in an invokable class. Basically you first create the invokable class and then add it into the config:
<?php
namespace App\Http\Requests;
use Illuminate\Http\Request;
class StripBackpackRequest
{
public function __invoke(Request $request)
{
return $request->except('_token', '_method', '_http_referrer', '_current_tab', '_save_action');
}
}
- 'saveAllInputsExcept' => ['_token', '_method', 'http_referrer', 'current_tab', 'save_action'],
+ 'strippedRequest' => '\App\Http\Requests\StripBackpackRequest',
+ }),
But you can also do a lot more, because you have the $request
in that class. You can see an example here.
In addition, please notice that all hidden parameters are now prefixed by an underscore. Starting with v5, if it starts with an underscore, you know it's not an actual database column.
Step 9. The Show operation will now show created_at
, updated_at
columns by default, if they exist. In most cases, this is what you want - show as many things as possible inside the Show operation - and created_at
and updated_at
provide some useful information about the entry. So it should NOT negatively affect you. But if you don't want to show those columns, you can turn off this new default behavior in your config/backpack/operations/show.php
, by defining 'timestamps' => false,
. You can also do 'softDeletes' => true,
if you want to show that column, by the way.
Step 10. We've improved the guessing of column types, by also taking into consideration your Model casts (PR here). If you have places in your CrudControllers where you have NOT defined a column type (and just assumed it'll probably be text
), but that item is cast as date
, json
, array
etc... Backpack will now try to show a more appropriate column type for it, instead of text
. This is unlikely to affect you negatively, but... it's not a terrible idea to go through all your ListOperation and ShowOperation views, to make sure you're happy with what's displayed.
Step 11. There have been some changes in how the repeatable
field works by default:
'min_rows' => 1
;fields
attribute to subfields
for more clarity;text
field;category
(for example), and your main entity happened to have a category
relationship on it, then the repeatable field would show a relationship field, which is probably not what you wanted;Step 12. We have removed the simplemde
field, since that JS library hasn't received any updates since 2016. However, there is a drop-in replacement called easymde
which is well maintained. If you're using the simplemde
field type anywhere in your project, please use easymde
instead. A simple find & replace in your Controllers should do.
Step 13. If you're using the Reorder operation and have overridden some of its functionality in your CrudController, please take note that the information is now passed as JSON. Replicate the small changes here in your custom code too.
Step 14. When setting up your Show operation in Backpack 4.1, after your setupShowOperation()
was run, Backpack would try to add more columns. That made it very difficult to delete auto-added columns. You might have used a workaround for this.
Starting with Backpack v5, the operation will no longer do any "automatic setup" inside setupShowOperation()
... nor after it. Instead, if you want Backpack to guess column names, you have to manually call a new method, $this->autoSetupShowOperation()
. To upgrade:
setupShowOperation()
in your CrudControllers, you are not affected by this - don't worry;setupShowOperation()
method in your CrudControllers:
$this->autoSetupShowOperation();
wherever you want inside your setupShowOperation()
; add it to the end of your setupShowOperation()
to keep the same behaviour as before;setupShowOperation()
), call $this->autoSetupShowOperation();
at the start of your setupShowOperation()
call and eliminate your workarounds, it should now "just work";Step 15. The Create and Update operations no longer save the request()
, they save your ProductFormRequest
(the one that contains the validation). If you have NOT modified the request()
or CRUD::getRequest()
in any of your CrudControllers, you will not be affected by this, move on.
However, if you have modified the request (most likely to add or remove inputs), those changes will never reach the database now. To give you an example, if you've ever overridden the store()
or update()
methods to add an input, it might look something like this:
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation { store as traitStore; }
public function store()
{
$this->crud->setOperationSetting('saveAllInputsExcept', ['save_action', 'token', 'http_referrer']);
$this->crud->getRequest()->request->add(['updated_by' => backpack_user()->id]);
return $this->traitStore();
}
That will no longer work, because Backpack won't save the request()
or $this->crud->getRequest()
any more, but your ProductRequest
. But there are now more ways than ever to achieve the same thing. Please read all solutions below and decide which one is best for you:
Solution (A)
That actually means it's easier to change the request now inside a CrudController, but you need to do it inside the strippedRequest
closure:
public function store()
{
$this->crud->setOperationSetting('strippedRequest', function($request) {
$request->request->add([ 'updated_by' => backpack_user()->id ]);
return $request->except(['_save_action', '_token', '_http_referrer']);
});
return $this->traitStore();
}
Solution (B)
If you feel overriding the store()
or update()
methods was messy... you're not alone. We have good news for you, you can now move that logic to your ProductRequest
, for example inside the lesser-known prepareForValidation()
method:
// app/Http/Requests/ProductRequest.php
protected function prepareForValidation()
{
\CRUD::setOperationSetting('strippedRequest', function ($request) {
$input = $request->only(\CRUD::getAllFieldNames());
$input['updated_by'] = backpack_user()->id;
return $input;
});
}
Solution (C)
Alternatively... if you absolutely hate this new behaviour and want your previous code to continue working, you can now easily tell Backpack to save the CRUD::getRequest()
, for all CRUDs, in your config/backpack/operations/create.php
and config/backpack/operations/update.php
. That way, you can keep your old CrudController overrides:
'strippedRequest' => (function ($request) {
return CRUD::getRequest()->request->except('_token', '_method', '_http_referrer', '_current_tab', '_save_action');
}),
Step 16. If you've customized the saving process of the Create or Update operations (read: you've overridden the store()
or update()
methods), please take into consideration that starting with Backpack v5, when a select multiple is emptied, it will still be part of the request, as null
. Whereas previously (if emptied) it was missing entirely. This applies to all select
and select2
fields when used as multiple
. You might need to change your saving logic accordingly, instead of expecting them to be missing, to expect them to be null
.
Step 17. The page_or_link
field has been moved from backpack/crud
to backpack/menucrud
because it made little sense outside it. If you've used the page_or_link
field anywhere in your CrudControllers:
MenuCRUD
installed:
composer.json
("backpack/menucrud": "^3.0.0"
)page_or_link
field, make sure to specify its view_namespace ('view_namespace' => 'menucrud::fields'
);MenuCRUD
installed;
resources/views/vendor/backpack/fields/page_or_link.blade.php
and paste the content from here;Step 18. We've removed the custom CSS & JS files that Backpack provided for each operation (eg. list.css
and create.js
- see why here).
public/packages/backpack/crud/css
and public/packages/backpack/crud/js
, copy them to a different location (we suggest public/assets/admin/css
and public/assets/admin/js
). Then use the brand-new script
and style
widgets to load them only where you need them. See the updated docs section for more information. This is only needed if YOU have added any custom code there. The Backpack CSS that was there is now included in the bundle.css
and bundle.js
files.public/packages/backpack/crud/css
and public/packages/backpack/crud/js
directories - those files are no longer loaded.Step 19. We've updated a lot of CSS & JS dependencies to their latest versions - with one notable exception - Bootstrap. Since not all dependencies support Bootstrap 5 yet, we'll still be using Bootstrap 4 for a while. But as soon as that changes, we'll release a new version for that upgrade alone. This will also make it easier to upgrade - no worries that the interface breaks now, since we haven't upgraded Bootstrap. And thanks to our new business model - you'll most likely have access to the next Backpack version too.
There are two ways to publish the latest styles and scripts for these dependencies:
public/packages
folder, or placed anything custom inside it:
public/packages
directory and all its contents;php artisan vendor:publish --provider="Backpack\CRUD\BackpackServiceProvider" --tag=public
resources/views/vendor/elfinder
and run php artisan backpack:filemanager:install
php artisan vendor:publish --provider="Backpack\CRUD\BackpackServiceProvider" --tag=public --force
. Please note this will overwrite anything that's already there. This B solution has a downside: unused files are not removed. A few files Backpack no longer uses will still be in your public/packages
folder, even though they're no longer used.Step 20. Have you developed any custom fields or columns? Rephrased: do you have anything inside your resources/views/vendor/backpack/crud/fields
or resources/views/vendor/backpack/crud/columns
? If so, and those fields or columns load any external CSS or JS, we recommended you load them using @loadOnce('path/to/file.css')
and @loadOnce('path/to/file.js')
instead of <link href="path/to/file.css">
and <script src="path/to/file.js"></script>
. This will make sure that piece of JS/CSS/code is only loaded once per pageload. You can find more info about it here (and why it's more than @once
). Your custom fields should still work without this change, but it's such an easy one.
Step 21. If you've overwritten any of the default operations in any way (blade files or PHP classes), take note that we've renamed the system GET/POST parameters (aka hidden inputs) - they're all prefixed by underscore now, to differentiate them from actual database columns. Please replace http_referrer
, locale
, current_tab
with _http_referrer
, _locale
, _current_tab
, respectively. Take a look at the PR to see the affected files. In 99% of all cases you won't be affected by this, there's little reason to overwrite the default operations. This also applies if you've overridden the SaveActions
or form_content
.
Step 22. If you've overwritten resources/views/crud/form_content.blade.php
you may need to update it.
JS Fields API is now imported on that file, the easiest way is to add that include directly near the end of the file.
In most of the cases you won't be affected by this, but if you have this file in your project source, please make sure it includes the following line.
...
</script>
+ @include('crud::inc.form_fields_script')
@endsection
Step 23. By default, all columns now echo using {{ }}
instead of {!! !!}
. That means they "escape the output", assuming they contain strings, not HTML. This was done to increase default security, to protect the admin from any malicious strings that might have been stored in the database. There are two exceptions to this, two columns that are not escaped
by default: custom_html
and markdown
, where Backpack assumes you store HTML. To upgrade:
array
, array_count
, closure
, model_function
, model_function_attribute
, relationship_count
or textarea
columns, you can use 'escaped' => false
on those columns to go back to the previous behaviour. But please read more about this, it might be a good idea to sanitize your input/output if you've forgotten to do so.markdown
or custom_html
columns, please note that they still DO NOT escape the output by default (since they most likely store HTML); make sure you've properly sanitized your input or output - it's super-easy using an HTML Purifier package (you can do that by casting the attribute to CleanHtmlOutput::class
in your Model or manually);Step 24. Clear your app's cache:
php artisan config:clear
php artisan cache:clear
php artisan view:clear
If the table view still looks wonky (search bar out of place, big + instead of ellipsis), then do a hard-reload in your browser (Cmd+Shift+R or Ctrl+Shift+F5) to purge the browser cache too.
You're done! Good job. Thank you for taking the time to upgrade. Now you can:
Then you'll love our premium add-ons - productivity tools and tons of new features.