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

moonshine/crud

MoonShine CRUD is a Laravel package that accelerates admin panel development by generating CRUD resources for Eloquent models. It provides forms, tables, filters, and actions, cutting boilerplate so developers can create and manage data screens quickly within the MoonShine ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

MoonShine is a Laravel admin panel/framework packaged as moonshine/moonshine (monorepo splits out the CRUD layer as moonshine/crud, but you install the meta-package). Start by requiring the package and running the installer:

composer require moonshine/moonshine
php artisan moonshine:install
php artisan moonshine:user   # optionally create the first admin

First things to look at after install:

  • app/MoonShine/Resources/ — generated ModelResource classes (one per model).
  • app/Providers/MoonShineServiceProvider.php — where resources, pages, and menu items are registered. Check MoonShineServiceProvider (or the auto-discovered MoonShine\Providers\MoonShineApplicationServiceProvider) to see how the menu() and resources() callbacks wire up.
  • config/moonshine.php — middleware stack (auth, web), guard, route prefix, and disk for uploads.

Your first use case: scaffold a resource for an existing model (php artisan moonshine:resource Post), open /admin/resource/post/index, and tweak the generated fields(), columns(), filters(), and rules(). Everything routes through MoonShine\Laravel\Resources\ModelResource (aka the "CRUD resource") — the same class is the entry point for index, create, edit, show, delete, and mass actions.

Implementation Patterns

ModelResource as the unit of work. Each resource overrides fields() (form inputs), columns() (table display), filters(), search(), rules(), and pages(). Treat the resource as the single source of truth for how a model is exposed to admins; resist the urge to put logic in controllers (MoonShine owns its controllers internally).

Fields are composable and chainable. Text::make('Title')->required(), Select::make('status')->options([...]), File::make('cover')->disk('public')->dir('posts'). For relationships, use BelongsTo::make('author', resource: UserResource::class) and HasMany::make('comments', resource: CommentResource::class) — the relation field handles the select box, but you must pass the target resource for it to render options correctly in 4.x.

Column/form separation matters. Fields drive the create/edit form; Columns drive the table and show page. A field you forget to add to columns() won't display in the index, but the DB is still updated — a common source of "data saved but not visible" confusion.

Use Resource subclasses plus Pages for non-CRUD screens. MoonShine\Laravel\Pages\Page with a components() method is how you bolt on dashboards, custom reports, or settings screens without writing blades. Register pages in MoonShineServiceProvider::pages().

Actions for row-level and bulk operations. Built-in Action types (Edit, Delete, MassDelete, Export) live in MoonShine\Laravel\Actions\*. For custom operations, extend Action and implement handle() — it receives the model (or mass-selected models) and is the idiomatic hook for "send welcome email on click", "regenerate slug", etc.

Policies still apply. MoonShine checks Laravel authorization policies/gates on resources by default (canViewAny, canCreate, etc.). If a menu item is visible but 403s, the policy method name is the usual culprit. Use Resource::can() / ->setCan() overrides only when a policy is genuinely inappropriate.

Queries and eager loading live in the resource. Override query() (or use $this->resource->setQuery()) to scope what admins see. Add ->with([...]) there rather than in a global scope to keep admin queries contained.

Reuse fields across resources. Extract a protected function baseFields(): Fields and merge into fields() — MoonShine's Fields is a collection-like object supporting ->add()/->prepend(), so composing form field sets is straightforward.

Testing. Use $this->actingAs($admin) + $this->get(PostResource::getUrl('index')) in Pest/PHPUnit feature tests. Resource URL helpers (PostResource::getUrl('create')) keep tests resilient to route-prefix changes.

Gotchas and Tips

  • Middleware and guard are the top install problem. If /admin bounces to login but your auth guard is api (or the session driver is misconfigured for the admin area), the default moonshine guard in config/moonshine.php needs to reference a working web guard. Verify config('moonshine.auth.guard').
  • Upgrades are not semver-obvious. The 4.18.2 → 4.19.6 jump here spans several patch/minor releases across the monorepo (CRUD, UI, Core split packages). Always read the monorepo changelog for the intermediate tags, not just the target one, and re-publish assets (php artisan moonshine:publish or vendor:publish --tag=moonshine-assets) after upgrade — stale JS/CSS is a frequent "nothing renders" symptom. Clear bootstrap/cache/*.php and run php artisan optimize:clear.
  • The split package trap. moonshine/crud is a sub-package. If you only require moonshine/crud in a Laravel app you'll miss the bootstrapping. Install moonshine/moonshine and let it pull the split packages; don't composer require the sub-packages directly unless you're extending the framework.
  • Field column() vs table Column::make(). In newer 4.x the naming collides conceptually — Field::showOnIndex()/showOnForm()/showOnDetail() toggles are the escape hatch when you want one field but a different table column shape.
  • File/Image uploads silently fail when the storage disk isn't public-symlinked (php artisan storage:link) or the disk isn't listed in config/filesystems.php. The uploader writes to the disk you name — no fallback.
  • Mass actions and chunking. Large deletions via MassDelete run row-by-row; for big tables, override the action or use a queued job rather than relying on the default synchronous handler.
  • Customization extension points to know: MoonShine\Laravel\Providers\MoonShineServiceProvider (register resources/pages/menu), config/moonshine.php (routes, guard, disk, theme), Blade components under moonshine::components.*, and the Action/Field/Column base classes for domain-specific extensions. Prefer these over editing vendor files — the monorepo releases frequently and vendor patches will be overwritten.
  • Debugging queries: enable Laravel Telescope or DB::listen() in a local admin session; MoonShine's resource query() chain runs on every index load, so N+1s surface fast in the table. Use ->with() in the resource query rather than per-column callbacks for performance.
  • Theming/UI updates move between minor versions. Don't pin UI behavior in tests (CSS class assertions, DOM structure); assert on field names and rendered text instead, since the split UI package evolves independently of CRUD.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console