Composite Pattern in PHP | Using Design Patterns in 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Using the Composite Design Pattern in PHP

The composite pattern in PHP is a structural design pattern that composes objects into tree-like structures in a hierarchical way. It uses a recursive approach to treat this structure as a part-whole such that every node/composition is treated uniformly. Here’s a heads-up on the complete architecture of the composite design pattern.

Composite Pattern in PHP

Don’t stress if all this sounds too technical and jargon-laden, as we will demystify this design pattern in this article.

Definition | Composite Design Pattern in PHP

“Composite design pattern is a structural design pattern that allows us to form tree-like structures with related but distinct types of objects and then treat them uniformly.”

Composite Pattern in PHP
Composite Design Pattern

The main objective of this pattern is to compose objects in a tree structure and then recursively parse the tree regardless of the classes and hierarchies.

FoodNet is Back | A Real-World Example

Remember FoodNet from the iterator design pattern article? It has a new change, something we have never thought of initially. We suggest reading that article to have a better understanding because we will build upon those ideas, and some context of the iterator will help you here.

As a quick recap, FoodNet was an application that was supposed to integrate menus of all eateries in an area. The application collaborated with sponsors CafeInn and LuxDine and integrated their menus.

At the time of integration, we learned about the differences in the internal implementations of the LuxDine and CafeInn classes. On deep analysis, we identified that traversal or iterations have to be encapsulated, and thus came iteration pattern into the picture.

Interesting, right? But now, we have a new opportunity, which comes at the cost of a new problem. 

Problem 🙁

So, what is the new change? And how does that impact the design in place? Let’s have a look! 

Time for some desserts!

LuxDine has added a dessert sub-menu. 

Desserts sub-menu
Desserts sub-menu

But how’s that a problem? Remember that we used to iterate over the MenuItem type. The client also expected MenuItem. Adding a sub-menu adds a secondary hierarchy. Our design was good for multiple menus but multiple hierarchies! That’s a problem.

Let’s visualize this scenario using a tree model. Let’s have a peek at what we had before vs. now.

Tree model of the Menus
Tree model of the Menus

Lack of generalization

Our application is not flexible in terms of the structures and nesting. It always expects a menu to have menu items. It should have been robust enough to tackle deeply nested structures like the one we have now. 

Iterations worked well with more linear traversable. Now the application can’t cope with nested and recurring structures, and that’s the main problem here.

Solution 🙂

Tree & Recursion

So, we have already seen a tree model for what we have inside our application. A tree model helps visualize these sorts of recurring structures. Think of it for a while; the dessert sub-menu is still a menu with leaf nodes representing MenuItem just as the other leaf nodes.

Composite Pattern in PHP
Components of a Tree

Using a recursive approach allows us to treat the entire tree as one giant menu and go through the whole tree without worrying much about node and leaf.

So, treating the composition as a part-whole hierarchy adds a lot of flexibility in terms of menu nesting. 

Composition and Nodes

What do we mean by a “Composition.” Well, it is a term that has been introduced to treat every node and object uniformly.  Think of a menu as a composition of sub-menus, sub-sub menus, and menu items. 

Our application should make no difference in a menu, sub-menu, and menu item. But does that make sense? They are not the same things, right?

That’s one reason why recursion can be confusing. Let’s look at it this way. We had menu and menu items before this sub-menu thing came up. We went to a Menu and iterated over its menu items.

Now have a secondary-level hierarchy or a sub-menu. So, we have something similar to do here as well, “Go to the sub-menu and fetch the menu items.” If the sub-menu is also a menu so, why make any difference? Just apply the same action on the sub-menu node.

Using the Composite Design Pattern

The MenuComponent, Menu & MenuItem

We need to define a MenuComponent class for Menu and MenuItem classes. Obviously, a common parent class can allow us to treat them uniformly and refer to them polymorphically.

MenuComponent Class
MenuComponent Class

The MenuComponent class unifies the methods relevant to both sub-classes. Though Menu class will never use getName() and getPrice(). Similarly, MenuItem has nothing to do with getMenuName() and getMenuPrice()

This class provides some default implementation for all the methods, and sub-classes can resort to the defaults for methods that don’t relate to them.

The MenuComponent Class Pseudocode

Composite Pattern in PHP
MenuComponent Pseudocode

Any method not overridden in the sub-class will resort to this default implementation.

The Menu Class PseudoCode

Menu Class Pseudocode
Menu Class Pseudocode

See the print() method. It expects a collection of MenuComponent without worrying much about if it is a MenuItem or a Menu (or a sub-menu). If it is a MenuItem, it will print the item name and price. Else if it is a Menu, it will keep on digging.

The MenuItem Class PseudoCode

Composite Pattern in PHP
MenuItem Pseudocode

MenuItem print() is simple and obvious because there’s nothing beyond it. It is a leaf node.

The client class

Client Class
Client Class

The Client can use the print() on the tree’s root element; the rest is just recursion. No matter how complex the structure is, the recursive calls will take care of it.

Benefits of the Composite Design Pattern

  • Recursion allows us to add as many nesting (sub-menus) to the menu tree as possible.
  • More flexible and generalized solutions.
  • Treats complex and simple types uniformly.
  • Uses a generic Component class to treat all parts of the hierarchies polymorphically, eliminating type errors and concrete type implementations for different classes in the hierarchy.

Complete Architecture | Composite Design Pattern in PHP

Composite Pattern in PHP
Composite Pattern in PHP

Composite Design Pattern in PHP

Following is the complete composite design pattern in PHP.

Code | Composite Design Pattern in PHP

<?php
 
class MenuComponent {
 
   function add($menuComponent) {
       throw new Exception('Unsupported Operation');
   }
 
   function getName() {
       throw new Exception('Unsupported Operation');
   }
 
   function getPrice() {
       throw new Exception('Unsupported Operation');
   }
 
   function getMenuName() {
       throw new Exception('Unsupported Operation');
   }
 
   function getMenuDescription() {
       throw new Exception('Unsupported Operation');
   }
 
   function print() {
       throw new Exception('Unsupported Operation');
   }
}
 
class MenuItem extends MenuComponent {
 
   private $name;
   private $price;
 
   function __construct($name, $price) {
       $this->name  = $name;
       $this->price = $price;
   }
 
   function getName() {
       return $this->name;
   }
 
   function getPrice() {
       return $this->price;
   }
 
   function print() {
       echo("Item Name: ".$this->getName().PHP_EOL);
       echo("Item Price: ".$this->getPrice().PHP_EOL);
   }
}
 
class Menu extends MenuComponent {
 
   private $menuComponents;
   private $name;
   private $description;
 
   function __construct($name, $description) {
       $this->name = $name;
       $this->description = $description;
       $this->menuComponents = [];
   }
 
   function add($menuComponent) {
      array_push($this->menuComponents, $menuComponent);
   }
 
   function getMenuName() {
       return $this->name;
   }
 
   function getMenuDescription() {
       return $this->description;
   }  
 
   function print() {
       foreach($this->menuComponents as $menuComponent) {
           $menuComponent->print();
       }
   }
}
 
class CafeInnMenu extends Menu {
   function __construct() {
       parent::__construct("CafeInnMenu", "CafeInn's Breakfast Menu");
 
       parent::add(new MenuItem('Esspresso', 5));
       parent::add(new MenuItem('Hot Mocha', 6));
       parent::add(new MenuItem('Cappuccino', 4.5));
       parent::add(new MenuItem('Signature Sandwich', 8));
       parent::add(new MenuItem('Special Salad', 5));
   }
}
 
class LuxDineMenu extends Menu {
 
   function __construct() {
       parent::__construct("LuxDineMenu", "LuxDine's Dinner Menu");
 
       parent::add(new MenuItem('Turkey Wrap', 6.5));
       parent::add(new MenuItem('The Original Burger', 8));
       parent::add(new MenuItem('Smoked Fish', 25));
       parent::add(new MenuItem('Sour Chicken', 25));
       parent::add(new MenuItem('Nachos', 3.5));
       parent::add(new MenuItem('Huge Steak', 15));
      
       $dessertsMenu = new Menu("Desserts sub-menu", "LuxDine's Desserts Sub-menu");
       $dessertsMenu->add(new MenuItem('Cheese Cake (Slice)', 9));
       $dessertsMenu->add(new MenuItem('Brownie', 4.95));
       $dessertsMenu->add(new MenuItem('Mango Sundae', 5));
       $dessertsMenu->add(new MenuItem('Molten Lava Cake', 12));
 
       parent::add($dessertsMenu);
     
   }
}
 
class Client {
  
   private $allMenus;
 
   function __construct($allMenus) {
       $this->allMenus = $allMenus;
   }
 
   function printFullMenu() {
       $this->allMenus->print();
   }
}
 
function main() {
 
   $allMenus = new Menu("All Menus", "Include all menus");
   $allMenus->add(new LuxDineMenu());
   $allMenus->add(new CafeInnMenu());
   $client = new Client($allMenus);
 
   $client->printFullMenu();
}
 
main();
?>

Output | Composite Design Pattern in PHP

Item Name: Turkey Wrap
Item Price: 6.5
Item Name: The Original Burger
Item Price: 8
Item Name: Smoked Fish
Item Price: 25
Item Name: Sour Chicken
Item Price: 25
Item Name: Nachos
Item Price: 3.5
Item Name: Huge Steak
Item Price: 15
Item Name: Cheese Cake (Slice)
Item Price: 9
Item Name: Brownie
Item Price: 4.95
Item Name: Mango Sundae
Item Price: 5
Item Name: Molten Lava Cake
Item Price: 12
Item Name: Espresso
Item Price: 5
Item Name: Hot Mocha
Item Price: 6
Item Name: Cappuccino
Item Price: 4.5
Item Name: Signature Sandwich
Item Price: 8
Item Name: Special Salad

Pros and Cons of the Composite Pattern in PHP

ProsCons
Satisfies the open-closed principle, we can add as many new classes in the hierarchy.It is often difficult to generalize two classes that differ a lot. Overgeneralization is one way, but it makes code difficult to understand
Adds flexibility and generalization using recursion.The recursive approach can be difficult to comprehend if you are not used to it.

Where is the Composite Pattern Used?

  • When you have a tree-like structure in your application with different types at different levels of nesting.
  • When you want to generalize over two related classes with recurring occurrences. 

Frequently Asked Questions on the Composite Design Pattern

What is the composite pattern?

The composite design pattern is a structural design pattern that allows us to form tree-like structures with related but distinct types of objects and then treat them uniformly.

Why use the composite design pattern?

The composite design pattern helps generalize and treat the recurring structure uniformly when your application has a complex tree-like structure with varying hierarchies.

What are the advantages of the composite pattern?

  • A generalized solution to a complex tree-like structure.
  • Uses recursion through the tree regardless of the levels and objects.
  • Provides a common interface to somehow related sets of classes, thereby leveraging polymorphism.
  • Makes your application more flexible and extendable.

Using the Composite Pattern in PHP Today

Let’s review what we have learned in this article. The composite pattern is a structural pattern that lets you compose objects into trees and treat these components as individual objects. This approach allows for more generalized solutions to problems where the structure can get more complex with deep nesting.

We continue with the FoodNet application and face a new issue when LuxDine adds a sub-menu to their main menu. Our iteration pattern design is not flexible enough to deal with nested tree structures because it is adapted to linear aggregates.

We use the composite pattern to come up with a more generalized solution. This pattern defines a common parent for both Menu and MenuItem and lets the Menu define a recursive print() function.

This way, it recursively calls print() on the Menu class and stops at terminal nodes where MenuItem returns the item name and price. With these modifications, the application generalizes well to any tree structure with any objects at any level of hierarchy as long as they all follow the same common parent: MenuComponent.

So, that’s all about it. We hope you are now well aware of this design pattern and can implement it whenever possible.

See you in another design pattern article. Stay tuned at FuelingPHP.

Books on Design Patterns

Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns: Elements of Reusable Object-Oriented Software book

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.

Check it out on amazon

Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.

Check it out on amazon

Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.

Design Patterns in PHP Learning Series.

This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.