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

Builder Bundle Laravel Package

akyos/builder-bundle

Visual page builder for Symfony with reusable components, templates, translatable values, and Twig front-end rendering. Requires PHP 8.4+, Symfony 8, and akyos/core-bundle.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing question. akyos/builder-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/builder.php, no Blade views, no facade. You cannot composer require it into a Laravel app and get a page builder. So "leveraging it" from a Laravel codebase means one of three concrete things:

  1. Spike it in a throwaway Symfony 8 skeleton to evaluate the content model, then rebuild the small parts you need with Blade/Livewire or Filament.
  2. Run it as a separate Symfony admin app sharing the same database (or an API) with your Laravel front end.
  3. Read it as a reference implementation for how to structure pages → blocks → translatable values → render, which is the cheapest option given its maturity signals (0 stars, 0 dependents, README-only docs).

Minimal setup (Symfony-side spike)

composer require akyos/builder-bundle:^8.0

Pin ^8.0.2. 8.0.2 is the release that replaced Request::get() — removed in Symfony 8 — with explicit query/request bags. Without it, editing a component in the admin throws UndefinedMethodError. On Symfony 8 that makes 8.0.2 the effective floor, not an optional patch.

Register the bundle:

// config/bundles.php
return [
    // ...
    Akyos\BuilderBundle\AkyosBuilderBundle::class => ['all' => true],
];

Check the hard requirements before you invest an hour. PHP >= 8.4 and Symfony ^8.0, plus akyos/core-bundle ^8.0. That is a very narrow window: no Symfony LTS, no PHP 8.2/8.3. Confirm your CI images, your FPM base image, and your host all run 8.4+ before writing any integration code.

Where to look first — the source, not the README. The README is a single paragraph plus a composer command. It tells you what the bundle is and nothing about how to configure it. Order of reading:

  • vendor/akyos/builder-bundle/src/ — entities, Builder/, form types, controllers. This is your only API reference.
  • vendor/akyos/builder-bundle/src/DependencyInjection/Configuration.php — the real config key names and defaults. Any third-party example you find online may use invented keys; this file is authoritative.
  • vendor/akyos/builder-bundle/templates/ (or Resources/views/) — the exact override paths and the Twig variables each block template receives.
  • vendor/akyos/core-bundle/ — since Builder depends on it, some of what you need (translation helpers, base entities) lives there.

First use case: one page, two blocks, rendered end to end. Don't start with a full CMS. Create a single page entity implementing the bundle's page contract, register two block types, render it through Twig, then open that page in the admin editor and save a component. That last step is the one that exercises the request-handling path that 8.0.2 fixed — do it on day one, because it is the highest-probability breakage and it fails loudly at save time, not at install time.

If you are staying in Laravel: treat the spike as a schema and UX reference. What you'd port is the block registry (tagged services → a Laravel container tag or a config array), the per-block Twig template (→ Blade component or @include), and the translatable value storage. That is maybe two days of work and removes the entire Symfony dependency.

Implementation Patterns

Content model workflow. The bundle's shape is: a page holds an ordered list of blocks; each block has a type and a bag of fields; fields hold translatable values; each block type maps to a Twig template that renders it. Design your block types around what the marketer edits, not around your database columns — the whole value of a page builder is that the block vocabulary stays small and stable while page composition changes daily.

Admin editing is request-transport sensitive. The component editor reads input from the request. Mirror the bundle's 8.0.2 behaviour anywhere you extend or wrap that flow — custom admin controller, form handler, listener:

// GET filters
$componentId = $request->query->get('component');
// POST form body
$payload = $request->request->all();
// Router-injected attributes
$locale = $request->attributes->get('_locale');
// JSON bodies (JS editor / headless admin)
$payload = $request->getPayload()->all();

Never $request->get(). It returns null silently in the wrong-bag case, which presents to the user as "the builder doesn't save my title."

Template overrides. Copy the file out of the vendor templates directory into your project templates/ at the identical relative path, then edit. Use {{ parent() }} if you're layering on top. Clear cache after — Twig caches the resolved path, and a wrong-depth override produces "template not found" that looks like a missing file rather than a path typo.

Rendering is Twig-native. Front rendering goes through the bundle's renderer service or a Twig function that composes the block templates in order. For a Laravel consumer, the pragmatic integration is render on the Symfony side, cache the HTML, serve it from Laravel, with an invalidation key of page_id + locale + updated_at. That keeps you from reimplementing every block template in Blade while you're still evaluating.

Laravel-side integration tips

  • Do not share Doctrine entities. Give Laravel read-only Eloquent models over the same tables, or (cleaner) expose an internal JSON endpoint that returns {blocks: [{type, data}]}. A shared schema with two ORMs is survivable; a shared entity object graph is not.
  • Define the JSON block contract early. { type: string, data: object, locale: string } is enough. Once that contract exists, swapping Symfony for a Filament page builder — or vice versa — is a rendering change, not a migration.
  • Resolve translations at render time, per locale. Store one value per locale rather than a serialized array, and test the missing locale explicitly. Fallback behaviour is not documented, so treat it as undefined until you've proven it.
  • Cache aggressively. Render a page to HTML once, store it, bust on save. Block-based pages are many small Twig renders; the naive path re-executes all of them on every request.
  • Keep the asset pipelines separate. The Symfony side uses whatever it uses (Encore, AssetMapper); your Laravel side uses Vite. Sharing compiled CSS across two apps is a maintenance tax with no payoff.
  • Boundary wrapper, even in a spike. Put every bundle call behind one of your own services. When the package stalls (0 stars, 0 dependents, single vendor) you want to swap it in one file.

Smoke test the editor in CI. This is the test that would have caught the 8.0.2 regression for you:

public function testAdminComponentEditorSaves(Client $client): void
{
    $client->request('POST', '/admin/builder/component', [
        'component' => 'header',
        'title'     => 'Welcome',
    ]);

    $this->assertResponseIsSuccessful();
}

Custom block types are the main extension surface: subclass the bundle's abstract block type, declare getBlockType() and getFields(), and register the service with the bundle's block-type tag. Verify the tag name with bin/console debug:container --tag=<guess> rather than trusting an example — a mistyped tag means the type silently never registers.

Gotchas and Tips

Symfony 8 removed Request::get() — and your own code probably uses it too. Symptom: UndefinedMethodError when saving a component in admin. Fix: upgrade to 8.0.2, then grep your own admin code, which will break identically:

grep -rn "request->get(" src/ templates/

Zero hits before you call the upgrade done.

Zero stars and zero dependents is a real signal, not a badge. No issue history to search, no Stack Overflow corpus, no third-party tutorials to sanity-check against. Every "how do I…" question is answered by reading vendor/. Budget for that. Commit composer.lock, pin exact versions, and keep the dependency behind a boundary so a stalled release isn't a rewrite.

The dependency chain forces bleeding edge. akyos/core-bundle ^8.0 plus Symfony ^8.0 plus PHP 8.4. Check that your other Symfony dependencies (EasyAdmin, Vich, API Platform, whatever your admin uses) actually support Symfony 8 before you build on this. If the bundle is the only thing pulling you to Symfony 8, that's a strong argument for option 3 — borrow the model, not the code.

Config key names in examples are unreliable. Read DependencyInjection/Configuration.php. If your YAML uses a key that doesn't exist, Symfony's config tree usually throws — but nested keys under a flexible node can be silently ignored, so a block that "doesn't appear in the picker" is often a config typo rather than a code bug.

Block schema changes silently corrupt existing rows. Block data is stored as a structure tied to the field definitions at save time. Renaming a field or changing its type reinterprets old rows without error — pages just render blank fields. Version your block definitions (header_v2) or write a migration that rewrites stored data before you change the type.

Translations have undocumented fallback. Missing locale on a field may render empty, render the default locale, or throw depending on the value object. Test it: create a page with only one locale populated and request the other.

Debugging, in order of usefulness:

  1. Log the raw request bags, not the merged result — it tells you instantly which transport the editor used and why a field vanished:
    $this->logger->debug('component edit', [
        'query'      => $request->query->all(),
        'request'    => $request->request->all(),
        'attributes' => $request->attributes->all(),
    ]);
    
  2. bin/console debug:container --tag=... and debug:config to confirm registration and config actually landed.
  3. Dump the stored block JSON for one page and read it — most "renders wrong" reports are a stored-data problem, not a template problem.
  4. Symfony profiler for the render path; Twig's dump() inside a block override shows exactly which variables the bundle hands you.

Security is on you. Block content is typically raw HTML by design (rich-text blocks). Sanitize on input or output — stored XSS through a builder field is a one-line mistake with a long tail. Lock the admin down by path:

# config/packages/security.yaml
access_control:
    - { path: ^/admin/builder, roles: ROLE_ADMIN }

Extension points worth knowing before you fork: custom block types via tagged services, Twig template overrides at mirrored paths, repository extension for custom queries, and the Symfony event dispatcher for page lifecycle hooks. Between those four you can usually avoid touching vendor code — which matters, because with this release cadence you will be upgrading.

The Laravel verdict. Viable today only as a separate Symfony admin app or as a design reference. If your team is Laravel-first, the honest recommendation is to spend the spike reproducing the block-registry-plus-Blade-templates pattern in Filament or Livewire. You lose the ready-made admin UI, but you gain PHP 8.2/8.3 support, a supported framework version, documentation, and a package with more than one user.

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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus