nette/component-model
Nette Component Model is a lightweight PHP package for building component-based UI structures. It provides component containers, naming and lookup, lifecycle hooks, and signal handling—forming the foundation used by Nette for reusable, composable components.
Install via Composer:
composer require nette/component-model
First Use Case: Build 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(), 'password');
// Render via ArrayAccess
$form['username']->render();
Key Files:
src/ComponentModel/Component.php (core component logic)src/ComponentModel/Container.php (hierarchical registry)src/ComponentModel/IComponent.php (interface for type safety)First Steps:
Component for your base UI/domain objects.Container to manage parent-child relationships.monitor() for lifecycle events (replaces deprecated attached()/detached()).class Dashboard extends Component {
public function __construct() {
$this->addComponent(new Sidebar(), 'sidebar');
$this->addComponent(new ContentArea(), 'content');
}
}
getComponent().getComponentTree() to serialize the hierarchy for debugging/admin panels.class UserProfile extends Component {
public function __construct() {
$this->monitor(UserProfile::class, function ($component, ?Component $parent) {
if ($parent) {
$this->onAttach($parent);
} else {
$this->onDetach();
}
});
}
}
(Component $component, ?Component $parent).$container = new Container(['name' => 'root']);
$container->addComponent(new Button(), 'submit');
$container->addComponent(new Button(), 'cancel');
// ArrayAccess support
$container['submit']->setText('Click Me');
'submit') over integers.getComponents() (deprecated in favor of getComponentTree()).Workaround for Laravel Projects (non-Nette):
// Use Container as a standalone registry (not DI)
$viewComponents = new Container;
$viewComponents->addComponent(new Alert(), 'success');
// Access via service container
app()->bind('view.components', fn() => $viewComponents);
Container as a lightweight registry, not a replacement for Laravel’s container.// In a Blade component
public function render() {
$container = new Container;
$container->addComponent(new NavBar(), 'nav');
return view('component', ['container' => $container]);
}
class Form extends Component {
public function __construct(
private string $action,
private array $fields = []
) {}
}
declare(strict_types=1) in your project.Container constructor.Deprecated Magic Properties
$component->name (use $component->getName()).$container->components (use $container->getComponents()).Constructor Pitfalls
$parent or $name to Component constructor (v3.x removes it).new Component(); $component->setName('foo');.Name Encoding
$container->getComponent(123) throws NotFoundException.$container->getComponent('btn_123').Callback Deduplication
=== fails for anonymous functions).$this->monitor(Component::class, fn($c) => $this->log($c));
// Duplicate if added again (same closure instance).
PHP 8.5 Dynamic Properties
$this->{$dynamicKey} = $value; in monitor() callbacks triggers deprecation.#[AllowDynamicProperties].Component Tree Inspection
$tree = $container->getComponentTree();
dd($tree); // Array representation of the hierarchy
Exception Messages
NotFoundException suggests similar names (e.g., 'submt' → 'submit').Monitor Callback Logging
$this->monitor(Component::class, function ($c, ?Component $p) {
error_log("Component {$c->getName()} " . ($p ? 'attached' : 'detached'));
});
Custom Container
Extend Container to add methods like findByType():
class MyContainer extends Container {
public function findButtons(): array {
return array_filter($this->getComponents(), fn($c) => $c instanceof Button);
}
}
Component Traits Add reusable behavior:
trait Clickable {
public function onClick(): void {
$this->fireEvent('click');
}
}
Event Dispatcher Bridge Integrate with Laravel Events:
use Illuminate\Support\Facades\Event;
$this->monitor(Component::class, function ($c) {
Event::dispatch("component.{$c->getName()}.attached");
});
getComponents(true): Deep traversal is O(n²). Prefer getComponentTree() for introspection.addComponent()/removeComponent(). Offload logic to async tasks if needed.Component (conflicts with Laravel’s Illuminate\View\Component).// Register Container as a singleton
app()->singleton('nette.container', fn() => new Container);
Component lacks Laravel’s Blade integration (e.g., no $this->slot). Use as a data container only.How can I help you explore Laravel packages today?