So, we move into the world of patterns inside the framework itself, starting with Builder.
Of course, the Builder pattern can be seen in more places than just your code - for example, in the /vendor
folder!
Take a look at this code.
Some Controller
return view('greeting') ->with('name', 'Victoria') ->with('occupation', 'Astronaut');
It's a typical Laravel view usage, but if we look closer, it's a Builder pattern!
Especially if we take a look at the source code of the ->with()
method, we can see that it returns $this
:
Illuminate/View/View.php
public function with($key, $value = null){ if (is_array($key)) { $this->data = array_merge($this->data, $key); } else { $this->data[$key] = $value; } return $this;}
In this case, the Builder pattern is one of many patterns used in Laravel to make things easier for developers.
Another example of a Builder pattern in Laravel is the validation system. Let's look at the dimensions
rule:
Some Controller
Validator::make($data, [ 'avatar' => [ 'required', Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2), ],]);
In this case, we can see that we were able to chain multiple methods to the dimensions
rule. This is possible because the dimensions
rule returns $this
:
Illuminate/Validation/Rules/Dimensions.php
public function maxWidth($value){ $this->constraints['max_width'] = $value; return $this;} /** * Set the "max height" constraint. * * @param int $value * @return $this */public function maxHeight($value){ $this->constraints['max_height'] = $value; return $this;} /** * Set the "ratio" constraint. * * @param float $value * @return $this */public function ratio($value){ $this->constraints['ratio'] = $value; return $this;}
Essentially, the Builder pattern allows us to chain multiple methods to a single object. This opens up many customization possibilities and makes the code more readable.
This is repeated for quite a few rules in Laravel, like:
dimensions
array
can
exists
file
image
The last example is a bit more advanced as it works in specific scenarios on the Relationships:
app/Models/User.php:
return $this->belongsToMany(Role::class) ->withPivot('active', 'created_by') ->withTimestamps();
We can see chained methods on the belongsToMany
relationship in this case. This is due to some "Laravel magic," but it's essentially the Builder pattern in action.
Let's look at the withPivot
method:
Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php:
public function withPivot($columns){ $this->pivotColumns = array_merge( $this->pivotColumns, is_array($columns) ? $columns : func_get_args() ); return $this;}
As you can see, it returns $this
, which allows us to chain more methods to the relationship. The same goes for the withTimestamps
method:
Illuminate/Database/Eloquent/Relations/BelongsToMany.php
public function withTimestamps($createdAt = null, $updatedAt = null){ $this->withTimestamps = true; $this->pivotCreatedAt = $createdAt; $this->pivotUpdatedAt = $updatedAt; return $this->withPivot($this->createdAt(), $this->updatedAt());}
In this case, it is also called the withPivot()
method, which allows us to chain more methods.
Laravel Core is full of examples like this. They might be deeply integrated or hidden behind some "magic", but they are there. And they depend on the Builder pattern to work.