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

System Bundle Laravel Package

unifik/system-bundle

A Symfony bundle that provides a reusable system layer for Unifik applications, bundling common services, entities, and configuration to speed up development and keep projects consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

unifik/system-bundle is a CMS-flavoured Symfony bundle built around a Page → Zone → Block → Widget composition model. It is not a framework you adopt wholesale; it is a set of abstract entities, traits and Doctrine wiring you inherit from to build a site. Treat it as scaffolding, not a dependency you can install and forget.

Minimal setup:

composer require unifik/system-bundle
// config/bundles.php
Unifik\SystemBundle\UnifikSystemBundle::class => ['all' => true],

Then map the bundle's abstract classes into your own src/Entity namespace. The normal pattern is one concrete class per abstract:

  • Page — routable content node; owns Zones.
  • Zone — a named slot inside a Page (header, main, sidebar…).
  • Block — an instance of a widget placed in a Zone, with ordering.
  • Widget — the reusable unit of content/rendering.

Look first at, in this order:

  1. Entity/ — the abstract base classes and their interfaces (PageInterface, ZoneInterface, BlockInterface, WidgetInterface). This is the real API surface.
  2. Resources/config/doctrine/ — the mapping. Read it before writing your own, because inheritance strategy and table names are decided here.
  3. Resources/config/services.xml (or .yaml) — what is auto-registered and under which aliases.
  4. DependencyInjection/Configuration.php — the actual bundle configuration tree.

First use case: create a single Page subclass with two Zones (head, body), attach one Block to body whose Widget renders a hardcoded string, and confirm the page resolves via the bundle's routing. Do this before touching the admin, SEO or media features — the Page/Zone/Block relationship is the part everything else assumes, and if your Doctrine inheritance is wrong you will find out here rather than three days later.

Be realistic about support: 6 stars and 0 dependents means you are the first person to hit most edge cases. Budget time to read the source rather than search for documentation. Pin the exact version in composer.json.

Implementation Patterns

Inheritance over composition for entities. The bundle is designed for extends AbstractPage style extension, not for embedding traits into your own unrelated entities. Follow the class hierarchy exactly; the Doctrine mappings and repository assumptions depend on it. If you deviate, you own the mapping and the repository overrides.

Trait composition for cross-cutting behaviour. The bundles in this family ship traits (timestampable, sluggable, sortable, publishable) that you use inside your concrete entities. This is the intended extension point for adding fields — add a trait, add its mapping, done. Do not edit the vendor mappings.

Zone/Block as the unit of reuse. The idiomatic workflow is: define a Widget subclass for each content type (rich text, image gallery, form, menu), then place Block rows referencing them into Zones per page. Rendering a page means iterating zones, then blocks in sortOrder, dispatching to each widget's render method. Keep widget render methods side-effect free and cacheable; do the data loading inside the widget, not in the controller.

Dynamic routing via the Page entity. Rather than declaring a route per page, the bundle resolves the request path against Page rows (typically via a slug field). Integration tip: put your catch-all route last in config/routes.yaml, and make sure your static routes are excluded from page resolution, otherwise the page resolver will shadow your login/admin endpoints.

Admin integration. If you use Sonata or EasyAdmin, write admin classes against your concrete entities but configure forms using the bundle's form types where they exist. The bundle's form types already know about the block-collection shape; reimplementing the collection widget by hand is a common and avoidable time sink.

Multi-site. Where the bundle exposes a site/locale dimension on Page, set the current site early (a kernel request listener) so that repository queries are scoped. Do not rely on the default scope being correct — it usually is not once you add a second site.

Migrations. Generate a baseline migration from the bundle's mapping before you add your own fields, then layer your changes on top. This makes the vendor-vs-yours boundary explicit in the migration history, which matters a lot when you eventually need to patch or fork.

Gotchas and Tips

Doctrine inheritance is the main trap. These bundles typically use single-table or class-table inheritance with a discriminator. Two failure modes dominate: (a) adding a field to a subclass without re-running the schema diff, so it silently maps to the parent table, and (b) JOINED vs SINGLE_TABLE mismatches between the bundle mapping and your override, which produce rows that load as the wrong class. Verify with doctrine:mapping:info and dump the actual table DDL after every entity change.

Trait method collisions. Multiple traits on one entity will collide on getId, setId, or timestamp accessors. PHP reports this only when the class is loaded, so you get a fatal at container warm-up with a confusing message. Resolve with use TraitA, TraitB { TraitA::getId insteadof TraitB; } and keep a single source of truth for identity.

Cache invalidation for blocks and widgets. Page composition is the classic stale-cache offender: editing a Block inside a Zone changes a page you cached under a different key. Tag your cache entries with the page id and every block id rendered into it, or simply invalidate on Block/Widget postUpdate/postPersist. Do not assume the bundle handles this.

Slug changes break links. If the Page slug drives routing, renaming a page 404s every existing URL. Keep a slug-history table (old slug → page id) and redirect on miss; the bundle generally does not do this for you.

Overriding templates. Override via templates/bundles/UnifikSystemBundle/... rather than editing vendor files. Check the bundle's Resources/views for the exact path — the directory name must match the bundle's name exactly, including vendor prefix rules, or the override is silently ignored.

Configuration quirks. The config tree is small and validation is loose, so typos in config keys are often ignored rather than rejected. After changing bundle config, run debug:config unifik_system (adjust the alias to whatever Configuration.php declares) to confirm the value actually landed.

Symfony version drift. With low maintenance activity, expect the bundle to target an older Symfony. Check composer.json constraints before upgrading your app; if the constraints are stale, composer why-not will be your first debugging step. Isolate the bundle behind your own service/facade layer so a future fork or replacement touches one namespace, not the whole app.

Debugging rendering. When a page renders empty, the failure is almost always ordering of the three lookups: page resolution → zone collection → block collection filtered by active and sort order. Log each step's result count; a zero at step one is a routing problem, a zero at step three is a publish/active flag problem.

Extension points worth knowing. Doctrine event subscribers registered by the bundle (slug generation, timestamping), the widget renderer service, and the page resolver are the three seams where behaviour is meant to be overridden. Override by decorating the service in your app's services.yaml rather than subclassing — it survives bundle updates.

Testing. Boot the kernel in tests and assert on resolved page composition rather than on raw entity state; the composition path is where the bundle's assumptions live and where your regressions will appear.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package