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

Block Bundle Laravel Package

rmzamora/block-bundle

rmzamora/block-bundle is a Laravel/PHP package for organizing reusable content blocks in your applications. It provides a simple way to manage, compose, and render blocks, helping keep templates clean and content flexible.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the package is a fork, not a product. rmzamora/block-bundle is a verbatim fork of sonata-project/block-bundle (same Sonata\BlockBundle namespace) pushed to keep an old Symfony/Sonata version matrix installable. Zero stars, zero dependents, archived. Before writing a line of code, answer one question: does the project already pull this in transitively (typically via sonata-project/page-bundle or an old sonata-project/admin-bundle)? If yes, you inherit it and the guidance below applies. If no, composer require sonata-project/block-bundle instead — the API is identical and it still gets security fixes.

Assuming you're on the fork:

  1. Install — there is no useful tagged release. Add a VCS repository entry in composer.json and require the branch/commit your project is pinned to, alongside sonata-project/core-bundle (hard dependency for the older versions) and whichever cache/menu packages that Sonata generation expects.
  2. Register new Sonata\BlockBundle\SonataBlockBundle() in AppKernel/bundles.php, plus dependencies first.
  3. Minimal config — nothing is strictly required to render; this is the smallest useful config:
    sonata_block:
        default_contexts: [cms]
        blocks:
            sonata.block.service.text:
                contexts: [cms]
    
  4. First use case — the built-in text block, no custom code:
    {{ sonata_block_render({'type': 'sonata.block.service.text'}, {'content': '<p>Hello</p>'}) }}
    
    The first array is the block identity (type + settings that participate in the cache key); the second is runtime settings merged on top. From PHP: $container->get('sonata.block.renderer')->render('sonata.block.service.text', ['content' => '…']).
  5. Verify wiring: bin/console debug:container --tag=sonata.block lists every registered type. If your type isn't there, nothing else will work.

Reading order for the source (you will need it — docs for this generation are thin): Resources/config/block.xml (service/tag wiring) → Block/BlockServiceManager.php (type → service registry) → Block/BlockContextManager.php (settings merge, defaults, cache key generation) → Block/BlockRenderer.php (exception handling, response shape) → Twig/Extension/BlockExtension.php (sonata_block_render, output escaping) → Block/Service/* for the built-in catalog (text, container, template, rss, menu…).

Implementation Patterns

1. Custom block — the pattern you'll write weekly. Extend AbstractBlockService, implement three methods, tag the service, add a config entry:

class PromoBlockService extends AbstractBlockService
{
    public function getName(): string { return 'acme.block.promo'; }

    public function setDefaultSettings(OptionsResolverInterface $resolver): void
    {
        $resolver->setDefaults([
            'template' => 'AcmeBundle:Block:promo.html.twig',
            'title'    => null,
            'ttl'      => 3600,
        ]);
    }

    public function execute(BlockContextInterface $context, Response $response = null): Response
    {
        return $this->renderResponse($context->getTemplate(), [
            'block' => $context,
            'title' => $context->getSetting('title'),
        ], $response);
    }
}
<service id="acme.block.promo" class="Acme\Block\PromoBlockService">
    <tag name="sonata.block" type="acme.block.promo"/>
    <argument type="service" id="twig"/>
</service>

The type attribute, getName(), and the sonata_block.blocks config key must be the same string. setDefaultSettings() is your entire validation layer — anything an editor can put into a block goes through the OptionsResolver, so use setAllowedTypes/setAllowedValues there rather than validating in execute().

2. Three rendering surfaces — pick deliberately.

  • Twig function for anything declared in a template: static marketing slots, footer, sidebar. Best default.
  • Renderer service from PHP when a controller needs composed markup (emails, PDFs, JSON APIs).
  • sonata_block_render_event('sidebar.left') in the template + a BlockEvent listener in your app — this is the underused one. It lets a bundle or feature module inject a block into a slot it doesn't own, keeping templates free of cross-module imports. Use it for plugin-style extension in your own codebase.

3. Contexts are a contract, not decoration. Declare meaningful names (cms, sidebar, email) in sonata_block.contexts and restrict each block with contexts:. The context list is what SonataAdmin's block picker filters on and what default_contexts seeds for ad-hoc rendering — it's admin UX and a safety rail against putting a 600px hero into a 60px email slot. Blocks rendered without a matching context still render; the restriction is advisory, so don't treat it as access control.

4. Caching is opt-in per block and is the main performance lever. Set a cache: service per block in config and a ttl in the settings defaults. When output varies by anything other than settings, override:

public function getCacheKeys(BlockInterface $block): array
{
    return ['locale' => $this->requestStack->getCurrentRequest()->getLocale()];
}

or pass extra_cache_keys through the render call's options array. Anything user-scoped (cart, "my orders", greeting-with-name) needs this or ttl: 0 — see the gotcha below.

5. Containers are your repeater. sonata.block.service.container lets an editor nest arbitrary child blocks; children are serialized as JSON in the code setting and a layout manager renders the wrapper (stacked/band styles ship with the bundle). For CMS-style pages this is the standard way to build multi-column regions — compose container blocks rather than writing a custom "row" block, and override the container template if you need different markup.

6. Static vs. dynamic split. Twig-declared blocks are versioned with your templates; DB-backed blocks (via SonataAdmin's block admin or your own entity) are edited by content people and need cache TTLs. Keep the two catalogs mentally separate — the most common support ticket in a Sonata site is "someone edited the block but the page didn't change," which is always the TTL.

7. Test at the renderer, not the service. Instantiate BlockRenderer through the container in a KernelTestCase and assert on rendered HTML plus the resolved BlockContext. That single test covers settings merging, template resolution, and cache-key shape — the three things that actually break.

Gotchas and Tips

  • Assume it's frozen. No security releases, no Symfony 4+ compatibility (the OptionsResolverInterface signature here was removed in 3.0; Twig_Markup is gone in Twig 2+). Pin it, don't build new features on it, and put "migrate to sonata-project/block-bundle" on the backlog. The migration is mostly a composer change plus service-id renames — do it in an isolated PR with your block-render test as the safety net.
  • Never install both packages. Identical namespaces mean a fatal Cannot redeclare class at autoload time. Use composer replace/conflict to enforce exactly one.
  • Cache key leakage is the #1 production bug here. Defaults derive only from block type + settings. A block that renders the current user's name will be cached and shown to everyone. Audit every custom block for request-dependent output and fix with getCacheKeys(), extra_cache_keys, or ttl: 0.
  • Settings must be scalar/array. Cache keys are serialized; passing entities, closures, or resources into block settings produces broken or colliding keys. Pass IDs and re-load inside execute().
  • execute() returning null is legal — it means "skip this block," not an error. Keep the $response parameter for BC and forward it to renderResponse(); that's what makes a block ESI/sub-request friendly.
  • Exceptions are swallowed by the renderer. BlockRenderer catches and renders block_exception.html.twig — in prod you get an empty region, not a stack trace. Debug from var/logs, not the page source. debug:container --tag=sonata.block and debug:twig are your first two commands; there's no block-specific debug command in this generation.
  • Template paths are Symfony-version specific. The bundle's own names are SonataBlockBundle:Block:block_*.html.twig; override under app/Resources/SonataBlockBundle/views/ on Symfony 3, templates/bundles/SonataBlockBundle/ on 4+ (which this fork won't run on anyway).
  • Malformed container JSON fails silently. If you expose the code setting to editors, validate it before save or you'll get an empty region with no log entry.
  • type mismatch fails loudly, config mismatch fails quietly. A missing service tag throws BlockNotFoundException; forgetting the sonata_block.blocks entry just means no cache/context config is applied and you'll chase phantom staleness.
  • Extension points if you must patch behavior: sonata.block.manager, sonata.block.context_manager, and sonata.block.renderer are all container aliases — decorate them instead of editing vendor/, otherwise the next composer update silently reverts your fix (and on an archived fork, "next update" may be never, which is worse: the fix is invisible to your team).
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.
skyraptor/chatter
edlugz/sasapay
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