PHP Fractal – Make Your API’s JSON Pretty, Always!

Share this article

PHP Fractal – Make Your API’s JSON Pretty, Always!

This article was peer reviewed by Viraj Khatavkar. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!


If you’ve built an API before, I’ll bet you’re used to dumping data directly as a response. It may not be harmful if done right, but there are practical alternatives that can help solve this small problem.

One of the available solutions is Fractal. It allows us to create a new transformation layer for our models before returning them as a response. It’s very flexible and easy to integrate into any application or framework.

Image of fractals with PHP embedded in them

Installation

We will be using a Laravel 5.3 app to build an example and integrate the Fractal package with it, so go ahead and create a new Laravel app using the installer or via Composer.

laravel new demo

or

composer create-project laravel/laravel demo

Then, inside the folder, we require the Fractal package.

composer require league/fractal

Creating the Database

Our database contains a users and roles table. Every user has a role, and each role has a list of permissions.

// app/User.php

class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'password',
        'role_id',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function role()
    {
        return $this->belongsTo(Role::class);
    }
}
// app/Role.php

class Role extends Model
{
    protected $fillable = [
        'name',
        'slug',
        'permissions'
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function users()
    {
        return $this->hasMany(User::class);
    }
}

Creating Transformers

We’re going to create a transformer for each model. Our UserTransformer class looks like this:

// app/Transformers/UserTransformer.php

namespace App\Transformers;

use App\User;
use League\Fractal\TransformerAbstract;

class UserTransformer extends TransformerAbstract
{
    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }
}

Yup, that’s all it takes to create a transformer! It just transforms the data in a way that can be managed by the developer, and not left to the ORM or the repository.

We extend the TransformerAbstract class and define the transform method that will be called with a User instance. The same thing goes for the RoleTransformer class.

namespace App\Transformers;

use App\Role;
use League\Fractal\TransformerAbstract;

class RoleTransformer extends TransformerAbstract
{
    public function transform(Role $role)
    {
        return [
            'name' => $role->name,
            'slug' => $role->slug,
            'permissions' => $role->permissions
        ];
    }
}

Creating Controllers

Our controllers should transform the data before sending it back to the user. We’re going to work on the UsersController class and only define the index and show actions for the moment.

// app/Http/Controllers/UsersController.php

class UsersController extends Controller
{
    /**
     * @var Manager
     */
    private $fractal;

    /**
     * @var UserTransformer
     */
    private $userTransformer;

    function __construct(Manager $fractal, UserTransformer $userTransformer)
    {
        $this->fractal = $fractal;
        $this->userTransformer = $userTransformer;
    }

    public function index(Request $request)
    {
        $users = User::all(); // Get users from DB
        $users = new Collection($users, $this->userTransformer); // Create a resource collection transformer
        $users = $this->fractal->createData($users); // Transform data

        return $users->toArray(); // Get transformed array of data
    }
}

The index action will query all users from the database, create a resource collection with the list of users and the transformer, and then perform the actual transformation process.

{
  "data": [
    {
      "name": "Nyasia Keeling",
      "email": "crooks.maurice@example.net"
    },
    {
      "name": "Laron Olson",
      "email": "helen55@example.com"
    },
    {
      "name": "Prof. Fanny Dach III",
      "email": "edgardo13@example.net"
    },
    {
      "name": "Athena Olson Sr.",
      "email": "halvorson.jules@example.com"
    }
    // ...
  ]
}

Of course, it doesn’t make sense to return all the users at once, and we should implement a paginator for this.

Pagination

Laravel tends to make things simple. We can accomplish pagination like this:

$users = User::paginate(10);

But to make this work with Fractal, we may need to add a little bit more code to transform our data before calling the paginator.

// app/Http/Controllers/UsersController.php

class UsersController extends Controller
{
    // ...

    public function index(Request $request)
    {
        $usersPaginator = User::paginate(10);

        $users = new Collection($usersPaginator->items(), $this->userTransformer);
        $users->setPaginator(new IlluminatePaginatorAdapter($usersPaginator));

        $users = $this->fractal->createData($users); // Transform data

        return $users->toArray(); // Get transformed array of data
    }
}

The first step is to paginate data from the model. Next, we create a resource collection like before, and then we set the paginator on the collection.

Fractal provides a paginator adapter for Laravel to convert the LengthAwarePaginator class, and it also has one for Symfony and Zend.

{
    "data": [
        {
            "name": "Nyasia Keeling",
            "email": "crooks.maurice@example.net"
        },
        {
            "name": "Laron Olson",
            "email": "helen55@example.com"
        },
        // ...
    ],
    "meta": {
        "pagination": {
            "total": 50,
            "count": 10,
            "per_page": 10,
            "current_page": 1,
            "total_pages": 5,
            "links": {
                "next": "http://demo.vaprobash.dev/users?page=2"
            }
        }
    }

}

Notice that it adds extra fields for pagination details. You can read more about pagination in the documentation.

Including Sub-Resources

Now that we’ve become familiar with Fractal, it’s time to learn how to include sub-resources (relations) with the response when it’s requested by the user.

We can request extra resources to be included with the response like this http://demo.vaprobash.dev/users?include=role. Our transformer can automatically detect what’s being requested and parse the include parameter.

// app/Transformers/UserTransformer.php

class UserTransformer extends TransformerAbstract
{
    protected $availableIncludes = [
        'role'
    ];

    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }

    public function includeRole(User $user)
    {
        return $this->item($user->role, App::make(RoleTransformer::class));
    }
}

The $availableIncludes property tells the transformer that we may need to include some extra data with the response. It will call the includeRole method if the include query parameter is requesting the user roles.

// app/Http/Controllers/UsersController.php

class UsersController extends Controller
{
    // ...

    public function index(Request $request)
    {
        $usersPaginator = User::paginate(10);

        $users = new Collection($usersPaginator->items(), $this->userTransformer);
        $users->setPaginator(new IlluminatePaginatorAdapter($usersPaginator));

        $this->fractal->parseIncludes($request->get('include', '')); // parse includes
        $users = $this->fractal->createData($users); // Transform data

        return $users->toArray(); // Get transformed array of data
    }
}

The $this->fractal->parseIncludes line is responsible for parsing the include query parameter. If we request the list of users we should see something like this:

{
    "data": [
        {
            "name": "Nyasia Keeling",
            "email": "crooks.maurice@example.net",
            "role": {
                "data": {
                    "name": "User",
                    "slug": "user",
                    "permissions": [ ]
                }
            }
        },
        {
            "name": "Laron Olson",
            "email": "helen55@example.com",
            "role": {
                "data": {
                    "name": "User",
                    "slug": "user",
                    "permissions": [ ]
                }
            }
        },
        // ...
    ],
    "meta": {
        "pagination": {
            "total": 50,
            "count": 10,
            "per_page": 10,
            "current_page": 1,
            "total_pages": 5,
            "links": {
                "next": "http://demo.vaprobash.dev/users?page=2"
            }
        }
    }
}

If every user had a list of roles, we could change the transformer to be like this:

// app/Transformers/UserTransformer.php

class UserTransformer extends TransformerAbstract
{
    protected $availableIncludes = [
        'roles'
    ];

    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }

    public function includeRoles(User $user)
    {
        return $this->collection($user->roles, App::make(RoleTransformer::class));
    }
}

When including a sub-resource, we can nest relations by using the a dot notation. Let’s say every role has a list of permissions stored in a separate table and we wanted to list users with their role and permissions. We can do it like this include=role.permissions.

Sometimes, we are required to include some necessary relations by default, like an address relation for example. We can do that by using the $defaultIncludes property inside the transformer.

class UserTransformer extends TransformerAbstract
{
    // ...

    protected $defaultIncludes = [
        'address'
    ];

    // ...
}

One of the things I really love about the Fractal package is the ability to pass parameters to include parameters. A good example from the documentation is order by. We can apply it to our example like so:

// app/Transformers/RoleTransformer.php

use App\Role;
use Illuminate\Support\Facades\App;
use League\Fractal\ParamBag;
use League\Fractal\TransformerAbstract;

class RoleTransformer extends TransformerAbstract
{
    protected $availableIncludes = [
        'users'
    ];

    public function transform(Role $role)
    {
        return [
            'name' => $role->name,
            'slug' => $role->slug,
            'permissions' => $role->permissions
        ];
    }

    public function includeUsers(Role $role, ParamBag $paramBag)
    {
        list($orderCol, $orderBy) = $paramBag->get('order') ?: ['created_at', 'desc'];

        $users = $role->users()->orderBy($orderCol, $orderBy)->get();

        return $this->collection($users, App::make(UserTransformer::class));
    }
}

The important part here is list($orderCol, $orderBy) = $paramBag->get('order') ?: ['created_at', 'desc'];, this will try to get the order parameter from the users include and will apply it to the query builder.

We can now order our included users list by passing parameters (/roles?include=users:order(name|asc)). You can read more about including resources in the documentation.

But, what if the user didn’t have any roles attached? It will halt with an error because it was expecting valid data instead of null. Let’s make it delete the relation from the response instead of showing it with a null value.

// app/Transformers/UserTransformer.php

class UserTransformer extends TransformerAbstract
{
    protected $availableIncludes = [
        'roles'
    ];

    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email
        ];
    }

    public function includeRoles(User $user)
    {
        if (!$user->role) {
            return null;
        }

        return $this->collection($user->roles, App::make(RoleTransformer::class));
    }
}

Eager Loading

Because Eloquent will lazy load models when accessing them, we may encounter the n+1 problem. This can be solved by eager loading relations at once to optimize the query.

class UsersController extends Controller
{

    // ...

    public function index(Request $request)
    {
        $this->fractal->parseIncludes($request->get('include', '')); // parse includes

        $usersQueryBuilder = User::query();
        $usersQueryBuilder = $this->eagerLoadIncludes($request, $usersQueryBuilder);
        $usersPaginator = $usersQueryBuilder->paginate(10);

        $users = new Collection($usersPaginator->items(), $this->userTransformer);
        $users->setPaginator(new IlluminatePaginatorAdapter($usersPaginator));
        $users = $this->fractal->createData($users); // Transform data

        return $users->toArray(); // Get transformed array of data
    }

    protected function eagerLoadIncludes(Request $request, Builder $query)
    {
        $requestedIncludes = $this->fractal->getRequestedIncludes();

        if (in_array('role', $requestedIncludes)) {
            $query->with('role');
        }

        return $query;
    }
}

This way, we won’t have any extra queries when accessing model relations.

Conclusion

I came across Fractal while reading the Building APIs you won’t hate by Phil Sturgeon, which was a great and informative read that I wholeheartedly recommend.

Do you use transformers when building your API? Do you have any preferred package that does the same job, or do you just dump the json_encodes? Let us know in the comments section below!

Frequently Asked Questions (FAQs) about PHP Fractal

What is PHP Fractal and why is it important?

PHP Fractal is a powerful tool that helps in presenting and transforming data for APIs. It is important because it provides a standardized way to output complex, nested data structures, ensuring that your API’s data output is consistent, well-structured, and easy to understand. This makes it easier for developers to work with your API and reduces the likelihood of errors.

How does PHP Fractal work?

PHP Fractal works by taking complex data structures and transforming them into a more consumable format. It does this through two main components: Transformers and Serializers. Transformers are responsible for converting the complex data into a simpler format, while Serializers format the final output.

What are Transformers in PHP Fractal?

Transformers in PHP Fractal are classes that define how data from your application should be outputted in the API response. They take complex data structures and transform them into simpler, more consumable formats. This allows you to control exactly what data is included in the API response and how it is structured.

What are Serializers in PHP Fractal?

Serializers in PHP Fractal are responsible for formatting the final output of your API. They take the data that has been transformed by the Transformers and format it into a specific structure. This allows you to ensure that your API’s output is consistent and easy to understand.

How can I implement PHP Fractal in my project?

Implementing PHP Fractal in your project involves installing the Fractal library via Composer, creating Transformers for your data, and then using the Fractal class to transform your data using the Transformers. You can then output the transformed data using one of Fractal’s Serializers.

Can I use PHP Fractal with any PHP project?

Yes, PHP Fractal is a standalone library that can be used with any PHP project. It is not tied to any specific framework or platform, making it a versatile tool for any PHP developer.

What are the benefits of using PHP Fractal?

Using PHP Fractal provides several benefits. It ensures that your API’s output is consistent and well-structured, making it easier for developers to work with. It also provides a standardized way to transform complex data structures, reducing the likelihood of errors and making your code easier to maintain.

How does PHP Fractal compare to other data transformation tools?

PHP Fractal stands out for its simplicity and flexibility. It provides a straightforward way to transform complex data structures, and its use of Transformers and Serializers allows for a high degree of customization. This makes it a powerful tool for any developer working with APIs.

Can I customize the output of PHP Fractal?

Yes, PHP Fractal is highly customizable. You can create custom Transformers to control exactly how your data is transformed, and you can use different Serializers to format the output in different ways. This allows you to tailor the output of your API to meet your specific needs.

Where can I learn more about PHP Fractal?

There are many resources available to learn more about PHP Fractal. The official PHP League website provides comprehensive documentation, and there are numerous tutorials and blog posts available online. Additionally, the PHP Fractal GitHub repository is a great place to explore the code and see examples of how it can be used.

Younes RafieYounes Rafie
View Author

Younes is a freelance web developer, technical writer and a blogger from Morocco. He's worked with JAVA, J2EE, JavaScript, etc., but his language of choice is PHP. You can learn more about him on his website.

apiBrunoSfractaljsonlaravelleagueOOPHPPHPphpleague
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week