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

Base Laravel Package

winter/base

Winter/base is the core foundation package of Winter CMS, a free, open-source Laravel-based content management system. It provides the essential modules, helpers, and framework glue that power the platform, giving developers a solid base for building fast, extensible websites and applications.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install once, then live in modules/ and plugins/ — composer create-project wintercms/winter my-site (or clone the repo for core work), run php artisan winter:install, then php artisan winter:up. Everything you extend lives in plugins/<author>/<name>/; everything you inherit lives in modules/{system,backend,cms} and vendor/winter/storm.
  • Read the layer below before writing anything. The dependency chain is Winter core → Winter Storm → Laravel → Symfony. Before hand-rolling a helper, check vendor/winter/storm/src/<Module>/README.md — each module catalogues its public API. grep -rl 'function <thing>' vendor/winter/storm/src/ vendor/laravel/framework/src/ vendor/symfony/ is a ten-second answer.
  • First use case: build a plugin that registers a backend controller and a frontend component. Scaffold with php artisan create:plugin Acme.Blog and php artisan create:controller Acme.Blog Posts, then php artisan create:component Acme.Blog PostList. This exercises the three things you'll do daily: plugin registration (Plugin.php), backend CRUD via Backend\Classes\Controller + config_form.yaml/config_list.yaml, and CMS components with components/PostList.php + default.htm.
  • Memorise the path helpers immediately — themes_path(), plugins_path(), media_path(), uploads_path(), temp_path() are autoloaded by Storm. Use them instead of base_path('themes'). They're the single most common source of "works locally, breaks in a different layout" bugs.
  • Set up your local loop before writing features: php artisan winter:mirror public --relative after adding any plugin (assets 404 otherwise), and vendor/bin/phpcs -nq --report=full --extensions=php <changed files> before every push — CI's PHP style job fails the whole PR in ~15s and is trivially reproducible locally.

Implementation Patterns

  • Backend CRUD is configuration-first. A controller's config_list.yaml and config_form.yaml drive list columns, filters, form fields, and relations. Reach for $widget->addTabFields() / getListColumns() overrides only when YAML can't express it; the widget system will fight you if you try to render forms by hand.
  • Model behaviour: Winter\Storm\Argon\Database\Model with behaviors declared in $implement — Backend\Behaviors\FormController, ListController, RelationController, Winter\Storm\Database\Behaviors\Purgeable, Sortable, Sluggable. Validation lives in $rules, attribute transformation in getAttribute()/setAttribute() mutators or $jsonable/$dates casts.
  • Components over page templates. Register a component on a page or layout ([postList] in the template's config block), and let it own its AJAX handlers via onRun()/onHandle(). Prefer $this->page['posts'] = ... and a partial include over embedding query logic in Twig.
  • Plugin registration is a single Plugin.php — registerComponents(), registerNavigation(), registerMarkupTags(), registerSchedule(), boot(). Keep boot() cheap; it runs on every request. Defer heavy work into service providers registered through register() or into Event::listen closures.
  • Use the framework's primitives instead of PHP's. PathResolver::resolve()/within()/join()/standardize() for path math (boundary-safe, unlike str_starts_with), Str::/Arr::/collect() for data munging, Symfony\Component\Finder\Finder::create()->files()->name('*.less')->in($dir) for directory walks, Symfony\Component\Filesystem\Filesystem::dumpFile() for atomic writes, Symfony\Component\Process\Process for shelling out, Http::get() for outbound HTTP.
  • Asset pipelines differ by plugin and you should pick one deliberately. The legacy pipeline (assets/css, assets/js + $this->addCss()), Mix, and Vite (vite:install/vite:compile <Plugin>). For new plugins use Vite; for a two-line CSS tweak in a legacy backend, edit the source and re-mirror.
  • Extending core without forking: Event::listen on the backend's documented events, Backend\Widgets\* extend/extendFormFields closures in a plugin's boot(), ListController::extendListColumns, FormController::extendFormFields, MarkupTag registration for Twig helpers, and System\Classes\PluginManager for cross-plugin discovery. Never edit modules/ in a deployed site.
  • Background work goes through queues (Queue::push, php artisan queue:work) or the scheduler in Plugin::registerSchedule() — not synchronous loops in a controller. Winter installs default to the sync driver, which will silently make a "background" job block the request; switch to database or redis early.
  • Tests: extend System\Tests\Bootstrap\PluginTestCase when the test needs plugins and auth, System\Tests\Bootstrap\TestCase when it only needs the framework. Temp files that flow through the asset combiner must live under base_path() — use base_path('storage/framework/cache/<unique>') and File::deleteDirectory() in tearDown().

Gotchas and Tips

  • Autoloading is not PSR-4. Winter\Storm\Support\ClassLoader lowercases every namespace directory segment while the filename keeps PascalCase: System\Twig\Node\Foo → modules/system/twig/node/Foo.php. A capitalized directory works on macOS/Windows and fails on Linux CI with Class not found. When Windows CI is green and every Ubuntu job is red, suspect directory case before anything else.
  • Symlinked plugins break in four specific places. (1) Assets 404 until php artisan winter:mirror public --relative — and a 404'd JS module served as text/html gets ORB-blocked, so the UI renders blank with no console error; check the network tab first. (2) vite:compile resolves the plugin's real path, so Node can't find the project's node_modules — symlink them in. (3) node_modules/.vite caching means a source edit can produce identical output; rm -rf it. (4) Prefer PathResolver::within()/resolve() over realpath() + str_starts_with when touching asset resolution — the within check is separator-boundary safe.
  • vite:watch writes an unreachable hot-file URL on IPv6 hosts — laravel-vite-plugin writes http://[::]:5173 into assets/dist/hot, which browsers can't reach, and the backend loads unstyled. Fix with echo -n 'http://localhost:5173' > <plugin>/assets/dist/hot, reload once, and make sure the file is removed when you stop the watcher or the site keeps pointing at a dead server.
  • Template phpcs bites on alternative syntax. <?php if ($x): ?>attr="…"<?php endif ?> on one line fails "Newline required after opening brace". Put if:, the body, and endif on their own lines. Run phpcs on changed files before pushing — it's the fastest failing CI job.
  • vendor/ drift causes irreproducible test failures. A test that fails locally but passes on a fresh clone is usually a stale vendor/winter/storm; composer update in Storm (or reinstall) before blaming the environment. Same for symlinked dev setups — verify vendor/winter/storm points where you think it does.
  • Layering discipline is the extension point. CMS/theme/plugin/system policy belongs in modules/; generic filesystem, path, parser, and network primitives belong in Storm. If Storm needs CMS-specific policy (e.g. which directories count as theme asset roots), add a public setter on the Storm class and let the module supply it — never reach into module constants from Storm. This is what keeps forks mergable.
  • Config caching hides YAML/env changes. php artisan config:cache during deploy will freeze config/app.php and any env-derived plugin settings; remember config:clear when a config change "doesn't take". Similarly, plugin Plugin.php registration is cached in the plugin registry — php artisan plugin:refresh <Author.Plugin> when a newly added component or navigation item doesn't appear.
  • Default queue driver is sync. Anything you push to a queue runs inline until you change QUEUE_CONNECTION. This turns "background" image processing into a request timeout that only shows up under load.
  • winter:up after pulling. Migrations, asset mirrors, and version bumps are not applied by git pull or composer update alone. A site that loads a half-migrated schema is a confusing place to debug.
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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager