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

Wn Backend Module Laravel Package

winter/wn-backend-module

Core backend module for Winter CMS, providing the admin interface, authentication, navigation, dashboards, settings, and widgets. It supplies the foundation for building and managing backend features and extensions.

View on GitHub
Deep Wiki
Context7

Getting Started

Don't composer require this. The package is a read-only split of modules/backend from the wintercms/winter monorepo. It has no standalone use — it depends on winter/storm, Twig, the plugin registry, and the rest of the Winter runtime. You get it for free by installing Winter CMS:

composer create-project wintercms/winter my-app
cd my-app
php artisan winter:install

If you already have a Winter site, the code lives at modules/backend/. Treat it as vendor code you read and extend, not as a package you require or modify.

Where to look first, in order:

  1. modules/backend/classes/Controller.php — the base class for every admin controller (Backend\Classes\Controller). Understand $this->addCss(), $this->addJs(), $this->pageTitle, $this->vars, and the AJAX handler dispatch via on* methods.
  2. modules/backend/behaviors/ — ListController, FormController, RelationController, ReorderController, ImportExportController. These are the workhorses: $implement = [...], $listConfig, $formConfig, $relationConfig and you have CRUD.
  3. modules/backend/widgets/ and modules/backend/formwidgets/ — Toolbar, Search, Filter, ReportContainer, Dashboard; field types like fileupload, mediafinder, richeditor, recordfinder, relation, repeater, taglist.
  4. modules/backend/models/BackendUser.php, modules/backend/auth/ — staff auth, sessions, and password handling.
  5. modules/backend/controllers/ — reference implementations of every pattern (Index, Settings, Users, Media, Dashboard). Copy shamelessly.

First use case: scaffold a plugin with an admin screen. In a Winter site:

php artisan create:plugin Acme.Blog
php artisan create:controller Acme.Blog Posts

The generator wires up controllers/posts/config_list.yaml and config_form.yaml, extends Backend\Classes\Controller, and implements ListController/FormController. That scaffold is the day-to-day shape of the work: extend Backend\Classes\Controller, implement behaviors, write Twig views, and hook into the backend menu via Plugin.php's registerNavigation().

Implementation Patterns

CRUD via behaviors — the 90% path. Every admin screen you'll write is a controller with $implement = ['@ListController', '@FormController'] (optionally @RelationController, @ReorderController, @ImportExportController). The config YAML files under controllers/<name>/config_*.yaml define columns, filters, scopes, form fields, and relation render modes. Prefer config over code — it survives upgrades and is the documented surface.

Custom list columns and filters. For anything the built-in column types don't cover, define a column type or a Backend\Classes\ListColumn subclass:

// plugins/acme/blog/columns/StatusColumn.php
class StatusColumn extends ListColumn
{
    public function evalIsStatus($record) { return $record->is_published ? 'Published' : 'Draft'; }
}

Register it in the plugin's Plugin.php via ListColumn::extend(...) or the registerListColumnTypes() hook. Same pattern for FilterWidget, FormWidget (field types), and ReportWidget (dashboard tiles).

Form field widgets. When a field needs custom UI, extend Backend\Classes\FormWidgetBase. The lifecycle is prepareVars(), render(), getSaveValue()/getLoadValue() — that's how you plug in rich editors, geo pickers, or bespoke pickers. Register with FormWidgetBase::extend(...) in the plugin boot.

Permissions. Register in the plugin's Plugin.php:

public function registerPermissions() {
    return ['acme.blog.manage_posts' => [
        'tab' => 'Acme', 'label' => 'Manage blog posts',
    ]];
}

Then guard with BackendAuth::user()->hasPermission('acme.blog.manage_posts') in controllers ($this->requiredPermissions) or Twig ({% if backend_user.hasPermission(...) %}). This is the day-to-day answer to "should this button show?"

AJAX handlers. Any public function onFoo() on a controller is callable from the admin via data-request="onFoo". Return arrays for partials (['#result' => $this->makePartial('result')]), or ['error' => ...] / ['redirect' => ...] for common outcomes. This is the standard pattern for modal + form + save without writing routes.

Settings pages. Use Backend\Models\Settings and the Settings controller with $settingsCode / $settingsFields — you get a form backed by a JSON settings model with no migration. Register via Plugin::registerSettings().

Dashboard widgets. Extend Backend\Classes\ReportWidgetBase, render a partial, register in Plugin.php's registerReportWidgets(). Ideal for KPI tiles and "recent orders" style boxes.

Navigation. registerNavigation() in Plugin.php returns menu entries with label, url, icon, and nested sideMenu. This is the extension point for adding plugin functionality into the admin chrome. Do not edit core navigation.

Media manager and file uploads. Use Backend\Models\MediaLibrary + the mediafinder/fileupload form widgets rather than reinventing storage. It respects Winter's configured storage disk and permissions, and gives you thumbnails/validation for free.

Localization. All UI strings should go through Lang::get('acme.blog::lang.…') with lang/en/lang.php in the plugin. Backend locale switching and RTL are handled by the module — don't fight it.

Gotchas and Tips

Never edit modules/backend/ in place. It's a read-only mirror of upstream. Edits will be lost on the next platform upgrade and won't survive composer update. If you truly need a core change, patch upstream or use composer-patches with a pinned version — and record it as ongoing maintenance.

Issue reporting is upstream. Don't file against this repo; the canonical source is wintercms/winter. Same for PRs.

Version coupling is real. Backend module versions track the Winter CMS release, which tracks a supported Laravel/PHP matrix. Bumping the admin module without bumping Winter (or vice versa) is unsupported. Pin the platform and rehearse upgrades on staging — treat backend versions as platform releases with plugin regression tests, not routine composer update churn.

Behavior config paths are relative and silent. If $listConfig = 'config_list.yaml' doesn't resolve, the behavior just won't wire up — no loud error. Path is relative to the controller class directory. Check modules/backend/behaviors/ListController.php's getConfigPath() if things go sideways.

Asset compilation surprises. The backend uses LESS + asset publishing. After changing a plugin's CSS/JS, run:

php artisan winter:version   # sanity
php artisan cache:clear
php artisan view:clear
php artisan plugin:refresh Acme.Blog

If styles disappear, it's almost always a stale asset cache or a addCss() path that's resolved from the plugin's assets/ dir, not from public/.

Twig scope ≠ Laravel Blade conventions. Admin views are Twig. {{ foo }} is escaped, {% partial 'name' %}, {{ form_field(...) }}, {{ 'acme.blog::lang.x'|trans }}. Don't reach for Blade helpers — they don't exist. The Backend controller base class is what injects the common Twig vars (backend_user, this.page, etc.).

Permission checks in Twig are easy to forget. Hiding a nav item in registerNavigation() doesn't protect the controller. Guard both — set $this->requiredPermissions on the controller and add permission keys to nav entries. Auth is your blast radius.

AJAX handler naming. Anything starting with on on a controller is publicly callable as an admin AJAX handler. Don't name internal helpers onSomethingInternal unless you've validated the request in a beforeX filter — every onFoo is an endpoint.

Modals and partials. The standard pattern is $this->makePartial('form') returned from the handler as ['#modalContent' => ...] combined with data-request and data-request-success="jQuery.modal('hide')". It's idiosyncratic but consistent — copy from core controllers rather than building your own.

Form save flow. FormController fires form.beforeSave / form.afterSave / form.beforeCreate etc. Use those model events rather than overriding onSave() — it keeps upgrade compatibility and composes with relation saves.

Frontend/backend auth confusion. Auth::user() is the frontend user; BackendAuth::user() is the staff user. Mixing them in an admin controller is a common source of "why is my permission check failing" — use BackendAuth.

Session and CSRF config. The backend uses its own session cookie (backend_session) and CSRF handling configured in config/backend.php. If admin logouts kick in unexpectedly, check that file's sessionLifetime and cookie domain, and check that APP_URL matches the admin host.

Debugging admin pages. APP_DEBUG=true + the Winter event logging in storage/logs/ shows AJAX failures and Twig errors with useful stack traces. For AJAX specifically, the browser devtools network tab shows a JSON payload with an error field even when the UI just silently does nothing — read the raw response before assuming a bug in your code.

Extension points to prefer over subclasses. Ordered by preference: behaviors → widgets/form widgets → Plugin.php registration methods → Twig partial overrides via theme/plugin partials/ → class extension via ::extend() → forking. Reach for the last one only with an owner and a patch list.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle