Purpose
The purpose of the design pattern
To treat a group of objects the same way as a single instance of the object.
Examples
Examples of how the design pattern can be used
A form class instance handles all its form elements like a single instance of the form, when render() is called, it subsequently runs through all its child elements and calls render() on them
UML
UML design pattern diagram
Code
Code snippets
Renderable
Renderable Interface that everything implements.
namespace DesignPatterns\Structural\Composite;
interface Renderable
{
public function render(): string;
}
Form
The composite node. When render is called we loop through the attached elements and call render on those too. The node and its elements all implement the Renderable interface.
namespace DesignPatterns\Structural\Composite;
/**
* The composite node MUST extend the component contract. This is mandatory for building
* a tree of components.
*/
class Form implements Renderable
{
/**
* @var Renderable[]
*/
private array $elements;
/**
* runs through all elements and calls render() on them, then returns the complete representation
* of the form.
*
* from the outside, one will not see this and the form will act like a single object instance
*/
public function render(): string
{
$formCode = '';
}
public function addElement(Renderable $element)
{
$this->elements[] = $element;
}
}
InputElement
An InputElement which implements the Renderable Interface.
This is attached to the composite node (Form).
namespace DesignPatterns\Structural\Composite;
class InputElement implements Renderable
{
public function render(): string
{
return '';
}
}
TextElement
A TextElement which implements the Renderable Interface.
This is attached to the composite node (Form).
namespace DesignPatterns\Structural\Composite;
class TextElement implements Renderable
{
public function __construct(private string $text)
{
}
public function render(): string
{
return $this->text;
}
}
Tests
Seeing everything in action. It’s possible to attach build and attach nodes to nodes.
public function testRender()
{
// Start building form (1st)
$form = new Form();
$form->addElement(new TextElement('Email:'));
$form->addElement(new InputElement());
// Build another form (2nd)
$embed = new Form();
$embed->addElement(new TextElement('Password:'));
$embed->addElement(new InputElement());
// Attach 2nd form to 1st form
$form->addElement($embed);
// This is just an example, in a real world scenario it is
// important to remember that web browsers do not
// currently support nested forms
$this->assertSame(
'',
$form->render()
);
}