Weave Code
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

Component Model Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. Extend Component for your base UI/domain objects.
  2. Use Container to manage parent-child relationships.
  3. Leverage monitor() for lifecycle events (replaces deprecated attached()/detached()).

Implementation Patterns

1. Component Hierarchy

class Dashboard extends Component {
    public function __construct() {
        $this->addComponent(new Sidebar(), 'sidebar');
        $this->addComponent(new ContentArea(), 'content');
    }
}
  • Lazy Initialization: Components are instantiated only when accessed via getComponent().
  • Tree Traversal: Use getComponentTree() to serialize the hierarchy for debugging/admin panels.

2. Lifecycle Management

class UserProfile extends Component {
    public function __construct() {
        $this->monitor(UserProfile::class, function ($component, ?Component $parent) {
            if ($parent) {
                $this->onAttach($parent);
            } else {
                $this->onDetach();
            }
        });
    }
}
  • Callback Signature: (Component $component, ?Component $parent).
  • Reentry Protection: Callbacks run once per state change (no infinite loops).

3. Container as Registry

$container = new Container(['name' => 'root']);
$container->addComponent(new Button(), 'submit');
$container->addComponent(new Button(), 'cancel');

// ArrayAccess support
$container['submit']->setText('Click Me');
  • Named Access: Prefer string names (e.g., 'submit') over integers.
  • Bulk Operations: Use getComponents() (deprecated in favor of getComponentTree()).

4. Integration with Laravel

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);
  • Avoid DI Conflicts: Treat Container as a lightweight registry, not a replacement for Laravel’s container.
  • Blade Integration:
    // In a Blade component
    public function render() {
        $container = new Container;
        $container->addComponent(new NavBar(), 'nav');
        return view('component', ['container' => $container]);
    }
    

5. Type Safety

class Form extends Component {
    public function __construct(
        private string $action,
        private array $fields = []
    ) {}
}
  • Strict Types: Enforce declare(strict_types=1) in your project.
  • PHP 8.2+ Features: Use named arguments in Container constructor.

Gotchas and Tips

Pitfalls

  1. Deprecated Magic Properties

    • $component->name (use $component->getName()).
    • $container->components (use $container->getComponents()).
  2. Constructor Pitfalls

    • ❌ Passing $parent or $name to Component constructor (v3.x removes it).
    • ✅ Correct: new Component(); $component->setName('foo');.
  3. Name Encoding

    • $container->getComponent(123) throws NotFoundException.
    • ✅ Use strings: $container->getComponent('btn_123').
  4. Callback Deduplication

    • Closures are deduplicated by loose comparison (=== fails for anonymous functions).
    • Example:
      $this->monitor(Component::class, fn($c) => $this->log($c));
      // Duplicate if added again (same closure instance).
      
  5. PHP 8.5 Dynamic Properties

    • $this->{$dynamicKey} = $value; in monitor() callbacks triggers deprecation.
    • ✅ Use typed properties or #[AllowDynamicProperties].

Debugging Tips

  1. Component Tree Inspection

    $tree = $container->getComponentTree();
    dd($tree); // Array representation of the hierarchy
    
  2. Exception Messages

    • NotFoundException suggests similar names (e.g., 'submt''submit').
  3. Monitor Callback Logging

    $this->monitor(Component::class, function ($c, ?Component $p) {
        error_log("Component {$c->getName()} " . ($p ? 'attached' : 'detached'));
    });
    

Extension Points

  1. 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);
        }
    }
    
  2. Component Traits Add reusable behavior:

    trait Clickable {
        public function onClick(): void {
            $this->fireEvent('click');
        }
    }
    
  3. Event Dispatcher Bridge Integrate with Laravel Events:

    use Illuminate\Support\Facades\Event;
    
    $this->monitor(Component::class, function ($c) {
        Event::dispatch("component.{$c->getName()}.attached");
    });
    

Performance Notes

  • Avoid getComponents(true): Deep traversal is O(n²). Prefer getComponentTree() for introspection.
  • Monitor Callbacks: Heavy callbacks delay addComponent()/removeComponent(). Offload logic to async tasks if needed.

Laravel-Specific Quirks

  • Namespace Collisions: Avoid naming classes Component (conflicts with Laravel’s Illuminate\View\Component).
  • Service Container Integration:
    // Register Container as a singleton
    app()->singleton('nette.container', fn() => new Container);
    
  • Blade Component Limitation: Nette’s Component lacks Laravel’s Blade integration (e.g., no $this->slot). Use as a data container only.
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope