nette/component-model
Nette Component Model provides a lightweight component hierarchy for PHP apps. It supports parent/child relationships, naming, lookup, and lifecycle hooks, making it easy to build reusable UI or service components that can be composed, traversed, and managed consistently.
Install via Composer:
composer require nette/component-model
First Use Case: Create a reusable UI component hierarchy (e.g., a form with nested fields).
use Nette\ComponentModel\Component;
use Nette\ComponentModel\Container;
class FormField extends Component {
public function render(): void {
echo "<input type='text' name='{$this->getName()}'>";
}
}
$form = new Container;
$form->addComponent(new FormField(), 'username');
$form->addComponent(new FormField(), 'email');
// Access via ArrayAccess or getComponent()
$form['username']->render();
Where to Look First:
src/ComponentModel/Component.php (core component behavior)src/ComponentModel/Container.php (hierarchical management)src/ComponentModel/IComponent.php (interface contracts)Use Container to build nested structures (e.g., admin panels, modular UIs):
$dashboard = new Container;
$dashboard->addComponent(new UserProfile(), 'profile');
$dashboard->addComponent(new Notifications(), 'notifications');
// Access children via ArrayAccess or getComponent()
$dashboard['profile']->loadData();
Replace deprecated attached()/detached() with monitor():
class ParentComponent extends Component {
public function __construct() {
$this->monitor(ChildComponent::class, function ($child, ?Component $parent) {
if ($parent === null) {
echo "Child {$child->getName()} was detached!";
}
});
}
}
Visualize hierarchies in admin panels:
$tree = $container->getComponentTree();
dd($tree); // Returns array of component names and children
Workaround for Laravel Devs:
Use Container as a lightweight registry for non-DI components (e.g., Blade views):
// In a service provider
$viewContainer = new Container;
$viewContainer->addComponent(new HeaderView(), 'header');
$viewContainer->addComponent(new FooterView(), 'footer');
// In a Blade view
@php
$header = $viewContainer['header'];
$header->render();
@endphp
Leverage PHP 8.2+ features:
class Button extends Component {
public function __construct(
public string $label,
public string $type = 'button'
) {}
}
Deprecated Magic Properties
Avoid $component->name or $container->components—use getName()/getComponents() instead (v3.2+ deprecation).
Name Handling
getComponent() requires string names (v3.0+). Integers throw NotFoundException.ArrayAccess accepts integers but may cause confusion. Prefer strings like 'btn_submit'.Monitor Callback Reentry
Nested monitor() calls may loop if not guarded. Use refreshMonitors() carefully:
$this->refreshMonitors(); // Only call this if you’re manually managing lifecycle.
PHP 8.5 Dynamic Properties
Avoid $this->$dynamicProp in monitor() callbacks—use typed properties or #[AllowDynamicProperties].
Silent Deprecations
getComponents($deep) now always returns an array (v3.1.0+). Old code expecting void will break.$deep parameter is silently deprecated—use getComponentTree() instead.NotFoundException includes typo suggestions (e.g., “Did you mean ‘submit’?”).getComponentTree() to inspect hierarchies during development.nette/phpstan-rules for stricter type checks.Custom Container
Extend Container to add validation or auto-loading:
class ValidatedContainer extends Container {
public function addComponent(Component $component, string $name): void {
if (str_contains($name, '..')) {
throw new \InvalidArgumentException("Invalid name: {$name}");
}
parent::addComponent($component, $name);
}
}
Monitor Wrappers Create helper methods for common callbacks:
class Component {
protected function onDetach(callable $callback) {
$this->monitor(static::class, function ($c, ?Component $parent) use ($callback) {
if ($parent === null) $callback($c);
});
}
}
ArrayAccess Overrides
Customize offsetGet() for lazy-loading:
class LazyContainer extends Container implements ArrayAccess {
public function offsetGet($offset) {
if (!isset($this[$offset])) {
$this[$offset] = new DefaultComponent();
}
return parent::offsetGet($offset);
}
}
Component or Container to prevent collisions with Laravel’s Illuminate\Container.monitor(). Bridge them manually if needed:
$component->monitor(ChildComponent::class, function ($child) {
event(new ComponentAttached($child));
});
How can I help you explore Laravel packages today?