Back to Course |
Design Patterns in Laravel 11

Builder Pattern: You're Already Using It Daily

What if I told you that Builder is a design pattern you already use daily without even noticing?


Query Builder Example

Take a look at this code.

$user = User::query()
->where('is_active', 1)
->orderBy('updated_at', 'desc')
->first();

Wait, it's a typical Eloquent query. It's not any Builder pattern, right? Well...

You see, this pattern is exactly about that: building an object and adding methods to it, like in the example above.

So, we have an Eloquent model, and then we're building the query on top of it, finally calling the ->first() to retrieve the data.

If we take a look at the source of the ->where() method, we find this:

/**
* Add a basic where clause to the query.
*
* @param \Closure|string|array|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure && is_null($operator)) {
$column($query = $this->model->newQueryWithoutRelationships());
 
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
} else {
$this->query->where(...func_get_args());
}
 
return $this;
}

As you can see, it called some methods and returns the object itself: return $this;. This is exactly the behavior of the Builder pattern.

And isn't it a coincidence that it's called a Query Builder?

So, essentially, you use a Builder pattern if you have a class with internal methods that change the behavior of the class object and return the object itself.

It means that when using the class, you can chain the methods, building more and more functionality of the object.


Mail Message Example

Have you seen this code in Laravel docs for Notifications?

public function toMail(object $notifiable): MailMessage
{
$url = url('/invoice/'.$this->invoice->id);
 
return (new MailMessage)
->greeting('Hello!')
->line('One of your invoices has been paid!')
->lineIf($this->amount > 0, "Amount paid: {$this->amount}")
->action('View Invoice', $url)
->line('Thank you for using our application!');
}

See, we're building the MailMessage object, adding more and more methods to it in a chain.

And look what's inside the greeting() or action() methods, for example:

Illuminate/Notifications/Messages/SimpleMessage.php:

class SimpleMessage
{
public $greeting;
 
public $actionText;
 
public $actionUrl;
 
// ... more properties and methods
 
public function greeting($greeting)
{
$this->greeting = $greeting;
 
return $this;
}
 
public function action($text, $url)
{
$this->actionText = $text;
$this->actionUrl = $url;
 
return $this;
}
}

See that return $this. Does it feel familiar now?


Create Your Own Builder Classes

Ok, cool, now you understand that Query/Mail Builder is following a Builder pattern, so what?

The thing is that you can choose to adopt this pattern in your classes.

Let's take a look at an example of a Service class to calculate the price.

app/Services/PricingService.php:

class PricingService {
 
function calculateTotalPrice($orderPrice, $discount, $taxPercent, $shippingFee): int {
 
return ($orderPrice - $discount)
* (1 + $taxPercent / 100)
+ $shippingFee;
}
}

Then you would call this Service class in some Controller:

public function show(Order $order, PricingService $service) {
$totalPrice = $service->calculateTotalPrice(
$order->price,
$order->discount,
$order->taxPercent,
$order->shippingFee
);
}

But what if you have more parameters in the future? Adding the fifth, sixth, and other parameters is not fancy, right?

Instead, you may refactor those parameters as their own setter methods, returning the same PricingService object.

class PricingService {
 
public int $orderPrice;
public int $discount;
public int $taxPercent;
public int $shippingFee;
 
public function setOrderPrice($orderPrice): PricingService
{
$this->orderPrice = $orderPrice;
 
return $this;
}
 
public function setDiscount($discount): PricingService
{
$this->discount = $discount;
 
return $this;
}
 
public function setTaxPercent($taxPercent): PricingService
{
$this->taxPercent = $taxPercent;
 
return $this;
}
 
public function setShippingFee($shippingFee): PricingService
{
$this->shippingFee = $shippingFee;
 
return $this;
}
 
function calculateTotalPrice(): int { // See, no parameters!
 
// Calculations from internal $this->xxxxx properties!
return ($this->orderPrice - $this->discount)
* (1 + $this->taxPercent / 100)
+ $this->shippingFee;
}
}

See, each of those methods returns the PricingService itself!

And then, in your Controller, you would build the object, method by method:

public function show(Order $order, PricingService $service) {
$totalPrice = $service->setOrderPrice($order->price)
->setDiscount($order->discount)
->setTaxPercent($order->taxPercent)
->setShippingFee($order->shippingFee)
->calculateTotalPrice();
}

See, the Builder pattern in action!

And the benefit is not just more elegant code. It gives you much more flexibility with the parameters:

  • Typically, you would be able to call them in whichever order you want
  • You may skip some parameters without changing the Service class
  • If you want to add more parameters, just add a property and a setter method in the Service class, and you don't need to change the parameters of the main calculateTotalPrice() method

I hope this quick example will give you some ideas of potentially refactoring your classes into a Builder pattern if it makes sense in your scenario.


Next: Facades

It's time to tackle one of the most used but misunderstood words in Laravel history. Facades are the topic of the next lesson.