Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Crud Laravel Package

backpack/crud

Backpack for Laravel is a powerful CRUD package for building admin panels quickly. Create, read, update, and delete resources with minimal code, customize fields, filters, and widgets, and manage users and permissions through a clean, flexible interface.

View on GitHub
Deep Wiki
Context7

Getting Started

A developer should treat Backpack CRUD as a framework for building admin panels, not a pre-built admin. The first hour should be spent proving one simple model end-to-end: list, create, update, delete, validation, and access control.

Minimal setup:

composer require backpack/crud
php artisan backpack:install
php artisan migrate
php artisan storage:link

Then create an admin user if the installer did not already do so, and log in at the configured admin path, usually /admin.

Where to look first:

  • config/backpack/base.php — route prefix, admin guard, middleware, branding.
  • config/backpack/crud.php — default CRUD behaviour, pagination, field/column defaults.
  • routes/backpack/custom.php — where CRUD routes are registered.
  • app/Http/Controllers/Admin/ — generated CRUD controllers.
  • app/Http/Requests/ — generated form requests for create/update validation.
  • Published views under resources/views/vendor/backpack/ — only if you need to override UI.

First use case: a simple internal admin CRUD for a Product model.

php artisan backpack:crud Product

Wire the route:

// routes/backpack/custom.php
Route::crud('product', \App\Http\Controllers\Admin\ProductCrudController::class);

The generated controller usually looks like this:

class ProductCrudController extends CrudController
{
    use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
    use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation;
    use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
    use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
    use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;

    public function setup()
    {
        CRUD::setModel(\App\Models\Product::class);
        CRUD::setRoute(config('backpack.base.route_prefix').'/product');
        CRUD::setEntityName('product');
    }

    protected function setupListOperation()
    {
        CRUD::column('name');
        CRUD::column('price');
        CRUD::column('created_at');
    }

    protected function setupCreateOperation()
    {
        CRUD::field('name')->type('text');
        CRUD::field('price')->type('number');
        CRUD::field('description')->type('textarea');
    }

    protected function setupUpdateOperation()
    {
        $this->setupCreateOperation();
    }
}

Once that works, add the next layer deliberately: a filter, a relationship field, a file upload, a permission rule, and a custom column. That sequence exposes most of Backpack’s day-to-day surface area without starting with a complex domain.

Implementation Patterns

One CRUD controller per entity, configured through setup methods

Use setup() for shared model/route/entity configuration, and keep setupListOperation(), setupCreateOperation(), setupUpdateOperation(), and setupShowOperation() focused on UI configuration. Avoid putting business logic in these methods. Validation belongs in FormRequests; domain logic belongs in services, actions, or model methods.

A common pattern is to define the create form once and reuse it for update:

protected function setupCreateOperation()
{
    CRUD::field('name')->type('text');
    CRUD::field('slug')->type('text')->hint('Leave blank to auto-generate');
    CRUD::field('price')->type('number');
    CRUD::field('status')->type('select')->values([
        'draft' => 'Draft',
        'published' => 'Published',
    ]);
}

protected function setupUpdateOperation()
{
    $this->setupCreateOperation();
}

Override individual fields only where the update form genuinely differs.

Treat list screens as query-building surfaces

List screens are where Backpack saves the most time: columns, search, filters, pagination, and row buttons. Use column types instead of raw HTML where possible:

CRUD::column('name');
CRUD::column('price')->type('number')->prefix('$');
CRUD::column('status')->type('select')->values([
    'draft' => 'Draft',
    'published' => 'Published',
]);
CRUD::column('created_at')->type('datetime');

For computed values, use a closure:

CRUD::column('display_name')
    ->label('Name')
    ->type('text')
    ->value(fn ($entry) => $entry->first_name.' '.$entry->last_name);

Add filters for the fields support teams actually search by:

CRUD::filter('status')
    ->type('select')
    ->values(['draft' => 'Draft', 'published' => 'Published'])
    ->whenActive(function ($value) {
        $this->crud->addClause('where', 'status', $value);
    });

Eager-load relationships used in list columns to avoid N+1 queries:

$this->crud->with(['category', 'author']);

Expose only the columns needed for triage. A 30-column list is slower and harder to use than a focused list plus a details page.

Use relationship fields intentionally

For belongsTo, a select field is usually enough:

CRUD::field('category_id')
    ->type('select')
    ->entity('category')
    ->attribute('name')
    ->model(\App\Models\Category::class);

For belongsToMany, use select2_multiple:

CRUD::field('tags')
    ->type('select2_multiple')
    ->entity('tags')
    ->attribute('name')
    ->model(\App\Models\Tag::class)
    ->pivot(true);

For large relationship sets, use AJAX-backed selects or FetchOperation endpoints rather than loading every record into the page. For hasMany, use relationship fields or inline create flows only when the child record is genuinely simple; otherwise link to a separate CRUD.

Handle uploads through Backpack’s upload fields

Use upload or upload_multiple, not a plain file input, and validate through the FormRequest:

CRUD::field('image')->type('upload')->label('Image')->disk('public');
CRUD::field('documents')->type('upload_multiple')->disk('public');

In the request, call withFiles() so Backpack processes the upload correctly:

protected function withFiles()
{
    return ['image', 'documents'];
}

Display uploads with the appropriate column type:

CRUD::column('image')->type('image');

Keep disk configuration, file size limits, MIME validation, and cleanup rules explicit. Uploads are one of the most common sources of silent admin-panel bugs.

Use custom operations and buttons for non-CRUD workflows

When the action is not create/read/update/delete, add a custom operation or button rather than hiding logic inside a list closure. Typical examples: publish, approve, export, sync, or send invoice. Backpack’s operation classes, routes, and button configuration keep these actions discoverable and testable.

Use FetchOperation for AJAX data endpoints, especially dependent selects and large typeaheads. Use save actions for cross-cutting form behaviour such as slug generation, audit logging, or recalculation.

Wire authorization early

Backpack does not automatically give you a secure admin panel. Choose one approach and apply it consistently:

  • Laravel policies and gates.
  • Backpack PermissionManager with roles and permissions.
  • Custom allowAccess() / denyAccess() logic in the controller.

For example:

public function setup()
{
    parent::setup();

    if (! backpack_user()->can('manage products')) {
        $this->crud->denyAccess(['list', 'create', 'update', 'delete']);
    }
}

Also hide fields and buttons the user cannot use. Hiding a button is not authorization; the route and request must still be protected.

Generate and run tests for critical CRUD flows

Use Backpack’s test generators where available:

php artisan backpack:tests

At minimum, test:

  • Admin can list records.
  • Admin can create a record with valid data.
  • Validation rejects invalid data.
  • Admin can update a record.
  • Unauthorized users cannot access the CRUD.
  • File uploads persist and validate.

Use model factories and avoid relying on production-like seed data.

Gotchas and Tips

  • Backpack is not turnkey. It gives you the CRUD plumbing and UI, but you still own access control, validation, relationships, uploads, UX, and performance. Budget for that.
  • Authorization is opt-in. Configure the admin guard, middleware, and permission layer early. A working CRUD route without authorization is a security incident waiting to happen.
  • Names must match reality. Field and column names must correspond to database columns, accessors, or relationship methods. A column named category will not automatically work if the relationship method is categoryRelation or the foreign key is category_id.
  • Relationship fields need the right name, entity, attribute, and model. The most common mistake is mixing the foreign key column with the relationship method. For belongsTo, the field name is often category_id, while entity is category.
  • Update validation is different from create validation. Unique rules must ignore the current record ID. File fields often need nullable or sometimes. Always use a FormRequest rather than inline validation.
  • Do not trust hidden or disabled fields. Validate everything server-side, especially foreign keys, status values, prices, and ownership.
  • Watch list-query performance. Eager-load relationships, avoid per-row queries inside column closures, paginate sensibly, and avoid showing large text or file columns in the list.
  • Uploads fail quietly if configuration is wrong. Check the storage disk, storage:link, withFiles(), MIME/size validation, and whether the column expects a path or URL. Test uploads in the same environment where they will run.
  • Do not edit vendor files. Override views, publish only the config you need, and put custom fields, columns, operations, and widgets in application code.
  • Version and PRO assumptions matter. Backpack major versions change APIs, and several attractive features are paid addons: editable columns, data form modal, DevTools, report operation, calendar operation, and some test generators. Confirm which packages and license keys are installed before designing around them.
  • Debug CRUD routes with Laravel tools first. Use php artisan route:list, inspect routes/backpack/custom.php, clear config/view caches, and check the admin guard. If assets or menus are missing, rerun the installer.
  • Inspect $this->crud while developing. Temporarily dump the CRUD configuration in a setup method to understand which fields, columns, and operations are active. Remove the dump before committing.
  • Configuration quirks are usually route-prefix or guard issues. config/backpack/base.php controls the admin route prefix and guard. If login works but CRUD routes redirect or 403, start there.
  • Use extension points instead of hacks. Custom operations, custom field/column types, save actions, widgets, FetchOperation endpoints, model observers, and published views are the intended extension surfaces. Prefer them over editing generated controllers into unmaintainable one-offs.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core