PHP Composite Design Pattern Structural Pattern

PHP Composite Pattern

A short screencast: the Composite pattern in PHP — leaves and containers in one tree, a single render() call walks it all.

Composite Pattern — open the lesson page

Overview

Composite builds trees from two kinds of nodes: leaves (no children) and containers (which nest children). Both implement the same Node interface, so code treats the whole tree uniformly. We model a file system: File is a leaf, Folder is a container, and a Folder can hold both files and other folders. Calling render() on the root prints the entire tree recursively.

UML diagram

classDiagram class Node { <<interface>> +name() string +render(depth) string } class File { -string name -int size +render(depth) string } class Folder { -Node[] children +add(Node) void +render(depth) string } Node <|-- File : implements Node <|-- Folder : implements Folder o-- Node : children

Code

Node.php

interface Node
{
    public function name(): string;

    public function render(int $depth = 0): string;
}

File.php

class File implements Node
{
    public function __construct(
        private string $name,
        private int $size,
    ) {}

    public function name(): string
    {
        return $this->name;
    }

    public function render(int $depth = 0): string
    {
        return $this->name.' ['.$this->size.']';
    }
}

Folder.php

class Folder implements Node
{
    private array $children = [];

    public function __construct(private string $name) {}

    public function name(): string
    {
        return $this->name;
    }

    public function add(Node $child): void
    {
        $this->children[] = $child;
    }

    public function render(int $depth = 0): string
    {
        $out = $this->name.'/';

        foreach ($this->children as $child) {
            $out .= "\n".str_repeat('  ', $depth + 1).$child->render($depth + 1);
        }

        return $out;
    }
}

demo.php

require_once 'Node.php';
require_once 'File.php';
require_once 'Folder.php';

$root = new Folder('src');
$root->add(new File('index.php', 1024));
$root->add(new File('README.md', 512));

$utils = new Folder('Utils');
$utils->add(new File('Helper.php', 2048));
$utils->add(new Folder('Templates'));
$utils->add(new File('mail.php', 32));

$root->add($utils);

echo $root->render().PHP_EOL;

Example output

Running php demo.php in the terminal prints the tree:

src/
  index.php [1024]
  README.md [512]
  Utils/
    Helper.php [2048]
    Templates/
    mail.php [32]

Advantages

  • Treats a single object and a group of them uniformly.
  • Recursive composition: a tree is built from leaves and containers.
  • New component kinds are added without touching the client.
  • Client code stays simple: one call walks the whole tree.

When to use

  • When there is a part-whole hierarchy.
  • When the client should treat leaves and containers alike.
  • When the nesting structure is not known in advance.
  • When composite structures must be traversed recursively.

Usage in Laravel

In Laravel the composite is Blade components and slots: Illuminate\View\Component and Illuminate\View\ComponentSlot let components nest inside each other, and they render uniformly. Another example is API resources: Illuminate\Http\Resources\Json\JsonResource and ResourceCollection share the toArray() method and serialise the same way, whether it is a single item or a collection.