PHP Composite Pattern
A short screencast: the Composite pattern in PHP — leaves and containers in one tree, a single render() call walks it all.
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
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.