DEV Community

Peter Fox
Peter Fox

Posted on • Originally published at Medium on

Laravel Internals: Complex Array building

Photo by Ben Allan on Unsplash

One of the most common issues you can have in PHP is merging arrays and the complications of having to selectively include or exclude particular keys when needed. Laravel actually has a built in trait for this that’s great to use for these situations.

If you use Laravel’s HTTP resources you’ll be familiar with this developer experience. Under the hood resources in Laravel rely on the ConditionallyLoadAttributes trait.

To keep things clear in the example I’ve created two models, Person and Address, with Address being a hasOne relation for the Person. The example class is simply called TransformObjectToArray which has a method called transform which accepts a Person and an additional array of attributes.

<?php
class Address extends Illuminate\Database\Eloquent\Model {
protected $guarded = [];
}
class Person extends Illuminate\Database\Eloquent\Model {
protected $guarded = [];
public function address()
{
return $this->hasOne(Address::class);
}
}
class TransformObjectToArray {
public function transform(Person $model, array $additional = []): array
{
$array = $model->only('name', 'phone_number');
if ($model->address && $model->language === 'en_US') {
$array['address'] = $model->address->only('first_line');
$array['address']['zip_code'] = $model->address->post_code;
} elseif ($model->address) {
$array['address'] = $model->address->only('first_line');
$array['address']['post_code'] = $model->address->post_code;
}
$array = array_merge(
$array,
$additional
);
return $array;
}
}
$model = new Person([
'name' => 'Peter',
'phone_number' => '01234567890',
'language' => 'en_GB',
]);
$model->setRelation('address', new Address([
'first_line' => '123 Fake Street',
'post_code' => 'AA10 1XX',
]));
$additional = ['secure' => true];
(new TransformObjectToArray())->transform($model, $additional);
// Produces the following array:
// [
// "name" => "Peter",
// "phone_number" => "01234567890",
// "address" => [
// "first_line" => "123 Fake Street",
// "post_code" => "AA10 1XX",
// ],
// "secure" => true,
// ]

This is workable but it often becomes complicated and harder to read.

And here’s how we could do this if we used the ConditionallyLoadAttributes trait instead.

<?php
class Address extends Illuminate\Database\Eloquent\Model {
protected $guarded = [];
}
class Person extends Illuminate\Database\Eloquent\Model {
protected $guarded = [];
public function address()
{
return $this->hasOne(Address::class);
}
}
class TransformObjectToArray {
use Illuminate\Http\Resources\ConditionallyLoadsAttributes;
public function transform(Person $model, array $additional = []): array
{
return $this->filter([
$this->merge($model->only('name', 'phone_number')),
'address' => $this->when($model->address, fn() => [
$this->merge($model->address->only('first_line')),
'post_code' => $this->when($model->language !== 'en_US', $model->address->post_code),
'zip_code' => $this->when($model->language === 'en_US', $model->address->post_code),
]),
$this->merge($additional),
]);
}
}
$model = new Person([
'name' => 'Peter',
'phone_number' => '01234567890',
'language' => 'en_GB',
]);
$model->setRelation('address', new Address([
'first_line' => '123 Fake Street',
'post_code' => 'AA10 1XX',
]));
$additional = ['secure' => true];
(new TransformObjectToArray())->transform($model, $additional);
// Produces the following array:
// [
// "name" => "Peter",
// "phone_number" => "01234567890",
// "address" => [
// "first_line" => "123 Fake Street",
// "post_code" => "AA10 1XX",
// ],
// "secure" => true,
// ]

As you can see in the example we have everything neatly occuring in a return statement that shows us what the structure of the array will look like somewhat.

If you’re struggling to understand the functionality of the when and merge methods used in the example you can read about them in the Laravel docs as part of the resources documentation. The filter method is purely there to perform the action of handling the operations to remove keys or merge arrays. All of these methods are provided via the trait.

The ConditionallyLoadAttributes trait is something truly worth using in your own classes when developing with Laravel when working with building complex arrays outside of the typical API Responses.

I’m Peter Fox, a software developer in the UK who works with Laravel among other things. Thank you for reading my article, I’ve got several more on both medium and dev.to. If you want to know more about me, head over to https://www.peterfox.me. Also feel free to follow me @SlyFireFox on twitter for more Laravel tips and tutorials in the future.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️