Skip to content
Home » Builder Pattern in PHP | Learn Design Patterns in 2023

Builder Pattern in PHP | Learn Design Patterns in 2023

Builder Pattern in PHP

The builder design pattern in PHP introduces some new terminologies and classes to support the construction of a complex object step-wise. The final product can look different and depends on what order or combination the builder produces these objects. Here’s what the final architecture of the Personify application looks like.

Builder Design Pattern in PHP

That seems intimidating, especially if you have never seen the pattern. Worry not, though! We will be making Personify application using the patterns, and you will be able to see why we use the builder pattern and how we implement it in PHP. 

Definition | Builder Design Pattern in PHP

“The builder design pattern is a creational design pattern that allows you to construct complex objects step-by-step. This pattern can construct different variants of the complex object using the same set of common builder methods.”

Builder Design Pattern
Builder Design Pattern

The objective of this pattern is to define the building steps in one place and call them step-wise. The end product could look different depending on how and which methods you call on the Builder object.

Personify | A Real World Example of Builder Pattern in PHP

Personify is a UI template generator for personal portfolio websites. It is a low-code solution where you can pick one or more types of UIs with various UI components and features. 

At the moment, the application offers these UI elements and features.

  • Header.
  • Navigation Bar.
  • Main section.
  • Footer.
  • Mobile Responsiveness.
  • Email Integration.
  • Hosting

Personify application | Version 1.0

Developers start with a basic “Page” class and decide to span sub-classes for every combination. At the moment, we could have 128 different combinations of UI elements and hence 128 different sub-classes. Now, what if we add a new UI element? The combination would grow to 256, and the number of sub-classes will increase exponentially.

And that’s an awful design. So, developers develop another design and pass all the configurations to the “Page” class constructor.

Builder Pattern in PHP
Page class with a bloated constructor

And that’s yet another bad design. But what is wrong with this implementation?

So, What’s Wrong with this Implementation?

Single Responsibility Principle

The Single Responsibility Principle says, “A class should have one and only one responsibility.” Given the Page class, it should be responsible for modeling a page class and should not be concerned with all the complex build steps.

Open-Closed Principle

The Open-Closed Principle says, “A class should be open to extension but closed to modification.” Now, what happens as you add new UI elements and features? Correct, you will add more parameters and code and modify the existing constructor. 

Ugly constructor calls

The constructor call expects 12 different parameters at the moment. Most of the time, you won’t use all these arguments; hence, they will be useless. Besides, adding more UI elements or features will bloat this constructor call quickly.

But that’s not one problem. Let’s see yet another one that will make this design more inflexible and non-maintainable.

Different implementations

That’s an interesting feature the team plans to add in the future. This feature adds different implementation or building mechanisms. Let’s zoom in on that. There are countless ways to design UI. The Personify application uses HTML and CSS for now. In the future, we could have Tailwind and Bootstrap for designing these UI elements.

Now, what happens to the Page class? It has an added responsibility now besides taking care of all these arguments combinations. That is deciding on the type of implementation. On top of that, you will be modifying the existing Page class.

inflexible code

See, the current software design messes up with these changes. So the developers rework the solution. 

Encapsulate the Building Steps…

So, what’s the most variable aspect of the application? It is the building mechanism of the complex Page object. You may add more elements, features, and implementations, which would hurt the Page class.

So, the developers develop a class that defines these building steps in one class. This class helps us deal with two challenges we are facing now. First, it lets you encapsulate the building steps in a class of its own, and the client code is unaware of what’s happening behind these scenes.

Secondly, it lets us define sub-classes of the class to cater to different implementations.

What is a Builder?

A Builder is a class that encapsulates the building steps in class methods where the client can call these methods to build an object. You can call these methods in any combination and order, and the Builder class takes care of it.

Builder Pattern in PHP
PageBuilder Class

Builder sub-classes

You can have different types of Builders that implement these methods differently. For instance – a Tailwind builder will implement the UI using Tailwind components, and the Bootstrap builder will implement UI using Bootstrap components.

So, the application would yield three Builder sub-classes to cover the different types of combinations.

Developers rather develop a PageBuilder interface and define the building steps there. Then it allows the different implementation sub-classes to implement this interface.

Builder Interface with sub-classes
Builder Interface with sub-classes

What are the benefits of this design?

  • Extendable design as developers can add more and more implementations by extending the base PageBuilder interface.
  • The Builder classes call these building steps in turns and have the leverage to call them in different orders and combinations. Therefore, it results in different combinations.
  • With an additional Director class, we can add another abstraction layer so that the client doesn’t have to bind to concrete Builder classes.
  • The Director class lets you define some pre-configurations so you can reuse them in your application. 

What is a Director Class?

You can always call the Builder methods from a client code, which isn’t bad. However, a Director is useful if you want to implement some pre-configured steps. Director gives you methods that you can reuse. Let’s clarify that a bit more. Suppose the application adds three types of UI layout – Basic, Standard, and Premium.

The Basic layout includes a bare-bone UI layout, including a header, main, and footer.

The Standard layout includes a Basic layout with added features like a navigation bar and mobile responsiveness.

The Premium layout includes everything a Standard layout has, with added features like Email integration and hosting.

Besides, the Director class adds a layer of abstraction so that the client code is unaware of what the Builder has been doing behind the curtains. It just needs to call the method and expect to receive a Page object.

Builder Pattern in PHP
The Director class

Complete Architecture | Builder Design Pattern in PHP

Builder Design Pattern in PHP

Builder Design Pattern in PHP

The following code recreates the builder design pattern in PHP for the Personify application. We won’t use any actual styling code as it is not the focus here. 

Code | Builder Design Pattern in PHP

<?php
class Page {
    private $header;
    private $navBar;
    private $main;
    private $footer;
    private $mobileResponsiveness;
    private $emailIntegration;
    private $hosting;

    function printPageDetails() {
        echo $this->getHeader() !== null ? $this->getHeader().PHP_EOL : "Header not set".PHP_EOL;
        echo $this->getNavBar() !== null ? $this->getNavBar().PHP_EOL : "Navbar not set".PHP_EOL;
        echo $this->getMain() !== null ? $this->getMain().PHP_EOL : "Main not set".PHP_EOL;
        echo $this->getFooter() !== null ? $this->getFooter().PHP_EOL : "Footer not set".PHP_EOL;
        echo $this->getMobileResponsiveness() !== null ? $this->getMobileResponsiveness().PHP_EOL : "Mobile responsiveness not set".PHP_EOL;
        echo $this->getEmailIntegration() !== null ? "Email integrated".PHP_EOL : "Email integration not set".PHP_EOL;
        echo $this->getHosting() !== null ? "Hosting enabled".PHP_EOL : "Hosting is not enabled".PHP_EOL;
        echo PHP_EOL; 
    }

	function getHeader() {
		return $this->header;
	}

	function setHeader($header) {
		$this->header = $header;
	}

	function getNavBar() {
		return $this->navBar;
	}

	function setNavBar($navBar) {
		$this->navBar = $navBar;
	}

	function getMain() {
		return $this->main;
	}

	function setMain($main) {
		$this->main = $main;
	}

	function getFooter() {
		return $this->footer;
	}

	function setFooter($footer) {
		$this->footer = $footer;
	}

	function getMobileResponsiveness() {
		return $this->mobileResponsiveness;
	}

	function setMobileResponsiveness($mobileResponsiveness) {
		$this->mobileResponsiveness = $mobileResponsiveness;
	}

	function getEmailIntegration() {
		return $this->emailIntegration;
	}

	function setEmailIntegration($emailIntegration) {
		$this->emailIntegration = $emailIntegration;
	}

	function getHosting() {
		return $this->hosting;
	}

	function setHosting($hosting) {
		$this->hosting = $hosting;
	}
}

interface PageBuilder {
    function reset();
    function buildHeader();
    function buildNavBar();
    function buildMain();
    function buildFooter();
    function buildMobileResposiveness();
    function buildEmailIntegration();
    function buildHosting();
}

class CSSPageBuilder implements PageBuilder{
    private $page;

    function __construct() {
        $this->page = new Page();
    }

    function getPage() {
        return $this->page;
    }

    function setPage($page) {
        $this->page = $page;
    }

    function reset() {
        $this->page = new Page();
    }

    function buildHeader() {
        $this->page->setHeader("<header>CSS Header</header>");
    }

    function buildNavBar() {
        $this->page->setNavBar("<nav>CSS Navbar</nav>");
    }

    function buildMain() {
        $this->page->setMain("<main>CSS Main</main>");
    }

    function buildFooter() {
        $this->page->setFooter("<footer>CSS Footer</footer>");
    }

    function buildMobileResposiveness() {
        $this->page->setMobileResponsiveness("CSS MediaQueries");
    }

    function buildEmailIntegration() {
        $this->page->setEmailIntegration(true);
    }

    function buildHosting() {
        $this->page->setHosting(true);
    }
}

class TailwindPageBuilder implements PageBuilder{
    private $page;

    function __construct() {
        $this->page = new Page();
    }

    function getPage() {
        return $this->page;
    }

    function setPage($page) {
        $this->page = $page;
    }

    function reset() {
        $this->page = new Page();
    }

    function buildHeader() {
        $this->page->setHeader("<header>Tailwind Header</header>");
    }

    function buildNavBar() {
        $this->page->setNavBar("<nav>Tailwind Navbar</nav>");
    }

    function buildMain() {
        $this->page->setMain("<main>Tailwind Main</main>");
    }

    function buildFooter() {
        $this->page->setFooter("<footer>Tailwind Footer</footer>");
    }

    function buildMobileResposiveness() {
        $this->page->setMobileResponsiveness("Tailwind Responsive");
    }

    function buildEmailIntegration() {
        $this->page->setEmailIntegration(true);
    }

    function buildHosting() {
        $this->page->setHosting(true);
    }
}

class BootstrapPageBuilder implements PageBuilder{
    private $page;

    function __construct() {
        $this->page = new Page();
    }

    function getPage() {
        return $this->page;
    }

    function setPage($page) {
        $this->page = $page;
    }

    function reset() {
        $this->page = new Page();
    }

    function buildHeader() {
        $this->page->setHeader("<header>Bootstrap Header</header>");
    }

    function buildNavBar() {
        $this->page->setNavBar("<nav>Bootstrap Navbar</nav>");
    }

    function buildMain() {
        $this->page->setMain("<main>Bootstrap Main</main>");
    }

    function buildFooter() {
        $this->page->setFooter("<footer>Bootstrap Footer</footer>");
    }

    function buildMobileResposiveness() {
        $this->page->setMobileResponsiveness("Bootstrap Responsive");
    }

    function buildEmailIntegration() {
        $this->page->setEmailIntegration(true);
    }

    function buildHosting() {
        $this->page->setHosting(true);
    }
}

class Director {

    function makeBasicPage($builder) {
        $builder->reset();
        $builder->buildHeader();
        $builder->buildMain();
        $builder->buildFooter();
    }

    function makeStandardPage($builder) {
        $this->makeBasicPage($builder);
        $builder->buildNavBar();
        $builder->buildMobileResposiveness();
    }

    function makePremiumPage($builder) {
        $this->makeBasicPage($builder);
        $this->makeStandardPage($builder);
        $builder->buildEmailIntegration();
        $builder->buildHosting();
    }
}

function main() {
    $cssBuilder = new CSSPageBuilder();
    $tailWindBuilder = new TailwindPageBuilder();
    $bootstrapBuilder = new BootStrapPageBuilder();

    $director = new Director();

    $director->makeBasicPage($cssBuilder);
    $director->makeStandardPage($tailWindBuilder);
    $director->makePremiumPage($bootstrapBuilder);

    $basicCSSPage = $cssBuilder->getPage();
    $standardTailwindPage = $tailWindBuilder->getPage();
    $premiumBootstrapPage = $bootstrapBuilder->getPage();

    $basicCSSPage->printPageDetails();
    $standardTailwindPage->printPageDetails();
    $premiumBootstrapPage->printPageDetails();
}

main();
?>

Output | Builder Design Pattern in PHP

<header>CSS Header</header>
Navbar not set
<main>CSS Main</main>
<footer>CSS Footer</footer>
Mobile responsiveness not set
Email integration not set
Hosting is not enabled
 
<header>Tailwind Header</header>
<nav>Tailwind Navbar</nav>
<main>Tailwind Main</main>
<footer>Tailwind Footer</footer>
Tailwind Responsive
Email integration not set
Hosting is not enabled
 
<header>Bootstrap Header</header>
<nav>Bootstrap Navbar</nav>
<main>Bootstrap Main</main>
<footer>Bootstrap Footer</footer>
Bootstrap Responsive
Email integrated
Hosting enabled

Pros and Cons of the Builder Pattern in PHP

ProsCons
Satisfies the Single Responsibility and Open-Closed Principles.A difficult pattern to comprehend.
It uses method calls as steps to build complex objects and allows flexibility to call these in any order and combination.Adds code complexity as you add classes to implement this pattern.
Using a Director class, you can define pre-configurations in a method and reuse the method in your application.
Open to extension for new implementations or types of builders.

Where is the Builder Pattern Used?

  • Use the Builder pattern to avoid bloating your constructor code.
  • When the classes use different arguments to define different configurations and combinations. You can define Builder classes with these building steps as methods. You can call them in any order and combination to get different types of objects.
  • If you want to have more than one implementation for building objects. Builder interface lets you extend and define different builder classes, which would have different implementations for the same methods.
  • You can use Builder methods recursively to build a composite tree of complex objects.

Frequently Asked Questions

What type of design pattern is the builder?

The builder design pattern is a creational design pattern that allows you to construct complex objects step-by-step. This pattern can construct different variants of the complex object using the same set of common builder methods.

Is the Director class necessary in the builder pattern?

No, having a director-class is unnecessary unless you want to define a common way of building a particular type of object. For instance – A Basic page. Otherwise, you can call the builder methods directly from the client code.

When to consider using the builder pattern in PHP?

When you have an object with different possible configurations and implementations. You shouldn’t include sub-classes for all the combinations or define a fat constructor with many arguments. Instead, you must define a builder class with a standard set of builder methods and reuse them across applications per the desired combination.

Using the Builder Pattern in PHP Today

Voila! That’s all about the builder design pattern in PHP. Let’s recap what we have seen thus far. The builder design pattern is a creational design pattern that allows you to construct complex objects step-by-step. This pattern can construct different variants of the complex object using the same set of common builder methods.

The article introduces the concept of an application that we call Personify. It is a low-code solution and features ready-made UI templates for personal portfolios and branding. The initial application includes 7 UI elements and features and expects to combine them to form these pages. Thus, users can pick any of these and form a page.

Developers try different ideas, including sub-classes for different page variations, and find out that they need 128 different sub-classes to cover all the combinations. These numbers increase exponentially as we add more UI elements and features.

The developers then move to a naive approach of having a bloated constructor that violates the fundamental design principles and makes the application hard to modify and maintain. Thus, comes the Builder class.

The Builder class encapsulates these building steps in a common set of methods and lets different sub-classes implement them in their way. That’s how we abstract the object-building process and open up ways to add more types of builders.

Apart from that, developers add a Director class to introduce a reusable chunk of building methods that return an object with the same configuration. The implementation could be different, depending on the builder passed.

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

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.