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

Cockpit Laravel Package

mpociot/cockpit

Cockpit is a Laravel package for quickly adding an admin cockpit to your application. It gives you a foundation for managing resources, organizing backend tools, and building custom dashboard interfaces without starting from scratch.

View on GitHub
Deep Wiki
Context7

Getting Started

Run the compatibility check before the install, not after. Zero dependents and 39 stars means nobody is exercising this against a modern Laravel/PHP pair. In a throwaway branch:

composer require mpociot/cockpit --dry-run
composer why-not mpociot/cockpit

Read the constraint block in its composer.json against your lockfile. If it caps below your framework version, the spike is over — writing two thin CRUD screens with Laravel primitives will be cheaper than pinning or backporting.

First hour, in order:

  1. composer require mpociot/cockpit
  2. php artisan vendor:publish — run interactively and record every tag it offers (config, views, assets, possibly migrations). The README almost certainly won't match what the package actually registers, so keep that list.
  3. Open vendor/mpociot/cockpit/src/ and read the service provider end to end. It's small. This is the highest-yield thing you can do all day: it tells you the real route prefix, middleware stack, view namespace, config keys, and registration order — no guessing.
  4. php artisan route:list --path=admin and read the middleware column on every row.
  5. php artisan config:clear && php artisan view:clear before the first request.

First use case: one low-risk model, one vertical slice. Don't start with the dashboard or the navigation tree. Register a single resource against a lookup table, a settings record, or a Category — something where a bad write is annoying rather than catastrophic. Confirm you get: a paginated listing, a working search box, an edit form that persists, and a delete that respects your authorization. That one slice validates config discovery, field rendering, relation resolution, persistence, and auth in a single pass.

Mount it behind a guard you own. Don't inherit whatever middleware the package registers. Wrap its routes in your own group and keep the resource definitions in a directory you control (app/Cockpit/ or similar) so the exit path is rm -rf app/Cockpit plus one provider line:

Route::middleware(['web', 'auth:admin'])
    ->prefix('admin')
    ->group(base_path('routes/cockpit.php'));

Implementation Patterns

Treat resource definitions as presentation config, not domain logic. The day-to-day loop is: pick a model, declare which columns appear in the listing, which fields appear in the form, which relations render as selects or nested displays. The moment you catch yourself writing a closure in a resource definition that branches on model state, stop. Push it onto the model as an accessor, a scope, or a policy. Config files are where business logic goes to die unowned and unreviewed.

Field types should mirror your validation, not replace it. Cockpit's generated forms are a UI over a write path — they are not your validation layer. Any model editable in the panel needs a FormRequest or model-level rules behind it, because the generated form is effectively an unlogged API endpoint with a nice font. If the panel is the only write path to a table, that table has no validation at all.

Authorization: find out how it decides, then verify. The package claims pluggable authorization callbacks. Before you trust that, trace one request: does the callback run per resource, per action, per field? Two outcomes, in descending order of defensibility:

  • Gate/Policy-driven — you Gate::define() abilities and check them in middleware or the callback. Reviewable, testable, survives a rewrite.
  • Config-boolean-driven — visibility is a flag in an array. Not defensible for a high-privilege surface. Wrap every panel route in can: middleware yourself and treat the package's own check as cosmetic.

Either way, add a CI assertion that enumerates admin routes and asserts each one carries your guard and your ability check. A single route registered outside your group is a data-exposure incident, not a bug.

Relations: check the eager-load behaviour on day one. Listing screens that render $row->owner->name are the canonical N+1. If the resource definition exposes an eager-load or "with" hook, use it on every relation column. If it doesn't, override the query in your own controller or resource and load explicitly. Verify with DB::listen() or Telescope rather than by eye — a 30-row page hiding 30 queries looks identical to a fast one.

Widgets are the sanctioned extension point. The dashboard composes cards. For each one, extend the package's base widget class, keep the render method thin, and push the query into a named method, a repository, or a model scope:

class OpenTicketsWidget extends BaseWidget
{
    public function render()
    {
        return view('cockpit::widgets.open-tickets', [
            'tickets' => $this->openTickets(),
        ]);
    }

    protected function openTickets()
    {
        return Ticket::whereNull('resolved_at')->latest()->limit(5)->get();
    }
}

A widget doing one latest()->take(5) is fine. Three of those against multi-million-row tables with no index is the page that times out at 9am Monday.

Override views for branding, never for logic. Publishing and swapping Blade templates is the supported way to match your app's look. It is not the way to add a CSV export button or a bulk action — that goes in a widget, a route you own, or a small controller. Every line of forked Blade is a line you re-diff at the next Laravel major.

Reuse the JSON API deliberately. The Vue frontend talks to a JSON API the package exposes. If you're building a custom frontend or an ops CLI against the same data, hit those endpoints rather than duplicating query logic — but document the response shape in your own repo, because the package won't.

Register your resources and widgets from your own provider. App\Providers\CockpitServiceProvider (or your AppServiceProvider) calling the package's registration API is the pattern that survives composer update. Editing vendor files does not — the next install undoes it and you won't remember why the panel broke.

Audit trail via observers, not package hooks. For an admin panel you need an audit trail. Don't go looking for package-provided hooks; attach a LogsAdminChanges observer to every model the panel touches. That keeps the trail in your codebase and intact if you ever migrate off Cockpit.

Two tests that pay for themselves immediately — then run them across every panel route via a data provider:

it('redirects anonymous users away from the panel', function () {
    $this->get('/admin')->assertRedirect();
});

it('serves the panel to an authorized operator', function () {
    $this->actingAs($operator, 'admin')->get('/admin')->assertOk();
});

Gotchas and Tips

You are the maintainer now. Pin the exact version, commit the lockfile, and if the package ships assets, publish them and commit the compiled output instead of building at deploy. Determinism beats freshness when there's no upstream cadence to rely on.

Diff the route surface after every composer update. php artisan route:list --path=admin > routes-before.txt and compare. An admin package silently registering a route outside your middleware group is the single most likely failure mode here, and it's invisible until someone finds the URL.

Config caching will bite you. If panel and resource definitions live in PHP config files, php artisan config:cache freezes them — a deploy that adds a resource but skips config:clear ships a dashboard that's quietly missing a section, and nobody will connect the two events. If definitions are registered in a service provider instead, they're cache-safe but you lose the caching benefit. Pick one, write it in the deploy runbook, and don't mix.

Unbounded listings. Check that pagination is actually on, that COUNT(*) isn't running on every keystroke, and that whatever column the search box hits has an index. Admin panels are low-QPS, but operators will paste a wildcard into search and hit a sequential scan on a table you forgot existed.

No optimistic locking. Two operators editing the same row will silently clobber each other. Fine for settings rows; not fine for records with money or state machines attached. Add an updated_at check in your own save path, or write down that you accepted the risk.

Debugging checklist for blank pages and 404s:

  • php artisan route:list — does the route exist at the path you think it does?
  • Is your middleware group actually applied, or did the package register its own routes first?
  • php artisan view:clear — stale compiled Blade outlives a vendor:publish
  • Does the view namespace you're referencing (cockpit:: or whatever the provider registers) match what you published?
  • Log::info() inside the package's boot() to confirm registration order relative to your providers.

Never edit vendor. When you need a behaviour the package doesn't expose — and you will — the fix is usually 5–10 lines in a fork. That's the fork burden the star count implied. Budget for it explicitly rather than discovering it mid-sprint.

Draw the scope line early. The failure mode of any config-driven panel is that "quick admin screen" quietly becomes the home for business logic nobody code-reviews. After your second resource, write down the criteria for what earns a Cockpit screen versus a real feature — trusted internal users, non-destructive operations, no customer data. Everything else gets built properly.

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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect