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

Widget Bundle Laravel Package

evgeniy-it/widget-bundle

Widget Bundle brings MODX-style shortcodes to your CMS, parsing nested [[widget? &option=value]] tags through a Twig filter. Includes simple template-based widgets, Doctrine repository widgets, array settings, and nesting.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal steps

  1. Install: composer require evgeniy-it/widget-bundle. Expect to hit platform conflicts — the last release is from January 2017, so its composer.json will almost certainly pin Symfony 2.x/3.x and PHP 5.x/7.0. You will likely need --ignore-platform-reqs or a fork with loosened constraints before anything autoloads.
  2. Register the bundle. On classic Symfony, add it to AppKernel::registerBundles(). On Symfony 4+/Flex there is no AppKernel, so you must manually add it in config/bundles.php and verify the bundle's service definitions still load (they are probably XML/YAML under Resources/config, not the modern services: layout).
  3. Confirm the Twig extension is registered. The whole public API is a single filter, widget, so if {{ 'x'|widget }} throws "Unknown filter", the bundle did not boot — that is your first diagnostic.

Where to look first

  • The bundle class (WidgetBundle.php) and its DI extension, to see which services/tags are registered.
  • The Twig extension class behind the widget filter — this is the entry point for every render.
  • The parser/renderer service that turns [[name? &opt=val]] into a widget class, plus the interface a widget must implement.

First use case

Take a CMS body field containing:

{{ '[[simple? &content=`Hello world`]]'|widget }}

and confirm you get Hello world. Then move to a custom-template widget and a nested widget before touching anything custom. That single call proves install, DI, Twig wiring, parsing, and rendering all at once.

Implementation Patterns

Render at output, not at save. Store the shortcode string in the DB untouched and run it through |widget when composing the response. This keeps content editable and lets widget behaviour change without a data migration. Add |raw after it ({{ page.body|widget|raw }}) or Twig will escape the HTML the widget produced.

Use the filter as a view-layer boundary. Widgets are essentially parameterised template includes. The clean pattern is: content editors write [[widget_name? &setting=…]], developers own the widget templates and the defaults. Treat widget names as part of your public content contract.

Custom widgets. Implement the bundle's widget interface/abstract class, define the template resolution, and register the class as a service with whatever tag the bundle expects (check the compiler pass). Keep each widget narrow — one widget that "renders a product card" is better than one that "renders anything".

Data passing. Flat options (&setting=value) arrive as strings. Structured options use the documented array/assoc-array notation (`&settingArray=`[1,2,3], &settingAssocArray={'item1':'value1'}``) — note these are JS-style single-quoted literals, not JSON. Cast and validate inside the widget; never trust the editor to send the right shape.

Nested composition. Because nesting is supported, build small widgets and compose:

[[layout? &slot=`[[card? &title=`Hi` &body=`[[simple? &content=`x`]]`]]`]]

This is the real power of the package and the closest analogue to MODX snippets. Keep nesting shallow (2–3 levels) so the string stays readable and debuggable.

Caching. Parsing + rendering on every request is cheap for a couple of widgets but not for a page of twenty. Cache the rendered HTML per widget call keyed on widget name + normalised options, invalidate on template change. Do not cache when widget output depends on the current user — either exclude those widgets from cache or key on user/role.

Laravel integration. There is no Laravel package here. Realistic options, in order of cost:

  • Serve the CMS-rendered pages from a small Symfony app and keep Laravel for the rest (two runtimes — usually not worth it).
  • Port the two useful pieces: the shortcode parser and the widget registry. That is a few hundred lines and removes the Symfony dependency entirely.
  • Wrap it via a Laravel service provider that boots the bundle's container manually — brittle, and it will break on any Symfony upgrade.

Given 0 stars and 0 dependents, treat it as a reference implementation to study, not a dependency to adopt.

Gotchas and Tips

The syntax is unforgiving and failures are quiet. Widget name must end in ?; every option starts with &; every value must be wrapped in backticks; the whole thing is bounded by [[ and ]]. A missing backtick typically means the widget renders with an empty setting or doesn't match at all — no exception. When something is wrong, isolate the exact shortcode into a dd/dump of the raw string first, then test it in isolation through |widget.

Backticks collide with nesting. The parser uses backticks as the value delimiter, so nested widgets work only because the whole inner widget sits inside one backtick-quoted value. The moment an inner option value itself needs a backtick, or content contains one, parsing breaks. Sanitise or reject backticks in editor content, or escape them deliberately.

Everything is a string. Integers, booleans, and arrays all arrive as strings. &enabled=false`` is truthy in PHP. Cast explicitly, and convert the {'k':'v'} / [1,2,3] forms to PHP arrays yourself — json_decode will fail because of the single quotes.

Security: shortcodes are template execution. A widget that renders &content through Twig is a server-side template injection vector if content is user-submitted. Only trusted editors should be able to author shortcodes; otherwise run a strict allowlist of widget names and strip unknown ones before rendering.

Template resolution and path traversal. The README allows the template to be given as a path or an inline string. If that path comes from content, an editor can point at any template on disk. Constrain templates to a configured directory and prefer named templates over raw paths.

Recursion. Nested widgets plus a self-referencing widget equals infinite recursion or a stack blowout. Add a depth counter in the parser and a max_depth config, and refuse a widget that includes itself.

2017-era Symfony baggage. Expect AppKernel registration, Resources/config/services.yml, and no autowiring or autoconfiguration. On modern Symfony you'll be rewriting the service definitions. Check the bundle's PHP constraint before spending time — if it's ~5.6|~7.0, you cannot run it on PHP 8 unmodified.

Escaping in Twig. Forgetting |raw gives you a page of visible HTML tags and looks like a rendering bug when it's an autoescape issue. Conversely, don't add |raw to output that includes editor-supplied HTML without sanitising.

Debugging checklist, in order: (1) does {{ '[[simple? &content=x]]'|widget }} work at all; (2) is the filter registered; (3) is the widget name registered in the bundle's registry; (4) does the template exist and resolve; (5) validate the exact bracket/backtick/ampersand grammar character by character.

Extension point to lean on: the widget registry. Registering a new widget name + template is the intended, low-risk way to add functionality — avoid subclassing the parser or the Twig extension, since you'll be maintaining a fork of a five-year-stale package and want your diff as small as possible.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core