Installation
composer require beyerz/simple-hmvc-bundle
Add the bundle to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
Beyerz\SimpleHMVCBundle\BeyerzSimpleHMVCBundle::class => ['all' => true],
Generate a Page Use the command to scaffold a new HMVC page:
php bin/console beyerz:simple-hmvc:page create MyPage
This creates:
src/Controller/MyPage/
├── MyPageController.php
├── MyPage.php # Page class (HMVC entry point)
└── templates/
└── MyPage.html.twig
First Use Case: Embedding a Widget
In MyPage.php, define an element to embed:
public function getElements()
{
return [
'sidebar' => 'SidebarElement',
];
}
Render it in MyPage.html.twig:
{{ render(element('sidebar')) }}
Page Structure
MyPage.php) act as root controllers, orchestrating elements.SidebarElement.php) are modular controllers with their own logic/views.getElements() in Page classes to declare embeddable elements.Element Rendering
{{ render(element('element_name')) }}.setData() in the page controller:
$this->getElement('sidebar')->setData(['title' => 'Hello']);
Nested Elements Elements can embed other elements recursively. Example:
// SidebarElement.php
public function getElements()
{
return ['user_card' => 'UserCardElement'];
}
Render in SidebarElement.html.twig:
{{ render(element('user_card')) }}
Reusing Logic Extend base controllers for shared functionality:
// BaseElementController.php
abstract class BaseElementController extends Controller
{
protected function commonLogic() { ... }
}
Routing Elements are auto-routed under their parent page. Example:
/page/my-page/page/my-page/sidebarcreateForm() in elements like any controller.setContainer().$this->get('event_dispatcher')->dispatch('sidebar.render', new SidebarEvent($data));
// src/Twig/Extension/ElementExtension.php
class ElementExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('custom_element', [$this, 'renderCustomElement']),
];
}
}
Circular Dependencies
ElementA embedding ElementB, which embeds ElementA). Symfony’s dependency injection may fail silently.if checks in Twig or lazy-load elements.Route Conflicts
/page/my-page and /page/my-page/sidebar) may cause 404s._route in Twig to specify full paths:
{{ render(element('sidebar', {'_route': 'my_page_sidebar'})) }}
Stale Cache
php bin/console cache:clear
Missing use Statements
use the ElementController trait in custom elements:
use Beyerz\SimpleHMVCBundle\Controller\ElementController;
Data Binding
setData() is not automatically available in Twig. Use getData() in the element controller and pass it to the view:
// SidebarElementController.php
public function indexAction()
{
return $this->render('SidebarElement.html.twig', [
'title' => $this->getData()['title'] ?? '',
]);
}
dump() to verify elements are registered:
{{ dump(element('sidebar')) }}
ElementController::render() to log calls:
public function render($view, array $parameters = [])
{
$this->get('logger')->info('Rendering element: '.$view);
return parent::render($view, $parameters);
}
php bin/console debug:router
Command-Line Generation Regenerate pages/elements after refactoring:
php bin/console beyerz:simple-hmvc:page update MyPage
Element Templates
Store element templates in src/Resources/views/ for better organization:
// SidebarElementController.php
public function indexAction()
{
return $this->render('@MyBundle/SidebarElement.html.twig');
}
Configuration
Override default paths in config/packages/beyerz_simple_hmvc.yaml:
beyerz_simple_hmvc:
page_dir: '%kernel.project_dir%/src/Controller/Pages'
element_dir: '%kernel.project_dir%/src/Controller/Elements'
Testing Elements
Test elements in isolation using Symfony’s WebTestCase:
public function testSidebarElement()
{
$client = static::createClient();
$client->request('GET', '/page/my-page/sidebar');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
Performance
For static elements, use Twig’s {% include %} instead of {{ render() }} to bypass controller overhead.
How can I help you explore Laravel packages today?