PHPackages                             cfxmarkets/php-jsonapi-objects - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. cfxmarkets/php-jsonapi-objects

ActiveLibrary[API Development](/categories/api)

cfxmarkets/php-jsonapi-objects
==============================

A class that provides a PHP implementation of JSON API objects. Note, this is not a protocol implementation, it is simply an implementation of classes as described by the \[json-api spec\](http://jsonapi.org/format/).

v2.1.0(7y ago)0895[7 issues](https://github.com/cfxmarkets/php-jsonapi-objects/issues)3MITPHPPHP &gt;=5.4

Since Aug 27Pushed 7y ago5 watchersCompare

[ Source](https://github.com/cfxmarkets/php-jsonapi-objects)[ Packagist](https://packagist.org/packages/cfxmarkets/php-jsonapi-objects)[ RSS](/packages/cfxmarkets-php-jsonapi-objects/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (4)Dependencies (2)Versions (64)Used By (3)

PHP JSON-API Object Library
===========================

[](#php-json-api-object-library)

This is a library comprising various classes that implement the [JSON-API Specification](http://jsonapi.org/format) objects in PHP. It is intended to ease the creation of a data system whose resource objects can be serialized to JSON-API.

In this way, it can be used to parse a received JSON-API document into parts that can be validated and persisted, and also to easily transfer data between services that speak JSON-API, such as a REST api.

> **NOTE:** At the time of this writing, almost 100% of development time has gone into the `AbstractResource` class. The rest of the classes exist to the extent that they fulfill the JSON-API spec, but they are not well made or well used.

Installation
------------

[](#installation)

This library can be installed using the standard composer process:

```
composer require cfxmarkets/php-jsonapi-objects
```

Usage
-----

[](#usage)

The library is designed to form the foundation of your JSON-API-aware data model (see [Design Philosophy](#design-philosophy) below). Thus, you'll probably use it most commonly to implement resource objects. It is intended that those objects be managed by datasource objects implementing `DatasourceInterface`.

Because this library does not contain any concrete implementations of specific resource objects (excepting the `GenericResource` class, which is not specific enough to provide a useful example), I'll demonstrate usage using a contrived example. Furthermore, because this library doesn't contain persistence logic, I'll forgo discussions of persistence in my example.

Following is an example of how you might use a `User` class as extended from `AbstractResource`:

```
// Use user info for conditional logic, say on an admin panel
if (!$user->isAtLeast('manager')) {
    throw new UnauthorizedAccessException("You must be at least a manager to view this page");
}
```

```
// User user info to prefill forms
$form = '

    ';

if ($user->likes('muffins')) {
    $form .= '
    Your Muffins';

    foreach ($user->getMuffins(['orderBy' => 'rating:desc']) as $muffin) {
        $form .= '

        '.$muffin->getType().'
        Remove
    ';
}

$form .= '
';

echo $form;
```

```
// Update user data in response to form input
$user
    ->setName($_POST['name'])
    ->setEmail($_POST['email'])
    ->setPhone($_POST['phone'])
;

$muffinData = $_POST['muffins'];
$muffins = $myBlog->muffins->newCollection();
forach($muffinData as $id => $info) {
    $muffins[] = $myBlog->muffins->get("id=$id")
        ->setRating($info['rating'])
        ->save()
    ;
}

$user
    ->setMuffins($muffins)
    ->save()
;
```

```
// Update user data from jsonapi input
$user
    ->updateFromData($jsonapiData)
    ->save();
```

The above examples demonstrate a typical way to use objects built on this library. It's worth mentioning, though, that these use-cases don't look any different from objects *not* built on this library. That's a feature, and speaks to the idea that it should be relatively easy to move objects built on this library off of it.

Back to the examples, though. In many cases, the data was accessed through normal getters and setters. In some cases, however, more complicated methods (like `User::isAtLeast` and `User::likes`, or the modified getter `getMuffins` with an `orderBy` option) were used. The level of abstraction you decide on is ultimately your choice, and this library has no intention of imposing a given philosophy on you. Your choice here should be guided by how tightly bound you want the logic your writing to be to your data. In other words, will you often want to know what a user likes or whether a user is of a minimum role in many parts of your application? If so, then you should build those methods right into your resource class. If not, though -- for example, if you're checking to see if a user has a specific type of muffin in his or her muffins collection -- then you'd do well to use more general getters to compose the logic you'd like.

You should, however, build all methods with a fundamental respect for the underlying data structure that the model is based on.

For example, the `User::isAtLeast` method might utilize a `roles` bitmask maintained in the object and persisted as an integer. That implementation might look something like this:

```
class User extends \CFX\JsonApi\AbstractResource
{
    protected $attributes = [
        // default to "end-user" role
        'roles' => 1,
        //....
    ];

    protected static function getValidRoles()
    {
        return [
            1 => 'end-user',
            2 => 'advanced-user',
            4 => 'manager',
            8 => 'site-admin',
            16 => 'sys-admin',
        ];
    }

    public function getRoleInteger()
    {
        return $this->_getAttributeValue('roles');
    }

    public function getRoles()
    {
        $roles = [];
        $userRoles = $this->getRoleInteger();
        foreach (static::getValidRoles() as $roleInt => $role) {
            if ($userRoles & $roleInt) {
                $roles[] = $role;
            }
        }
        return $roles;
    }

    public function setRoles($val)
    {
        $roles = 0;
        $validRoles = static::getValidRoles();
        $invalidRoles = [];
        if (is_array($val)) {
            foreach ($val as $role) {
                $roleInt = array_search($role, $validRoles);
                if ($roleInt === false) {
                    $invalidRoles[] = $role;
                } else {
                    $roles += $roleInt;
                }
            }
        } else {
            // Implement logic for validating integer role here....
        }

        if (count($invalidRoles) > 0) {
            $this->setError('roles', 'invalid', [
                "title" => 'Invalid Roles Provided',
                "detail" => "The following roles are invalid: `".implode("`, `", $invalidRoles)."`",
            ]);
        } else {
            $this->clearError('roles', 'invalid');
        }

        return $this->_setAttribute('roles', $roles);
    }

    public function isAtLeast($role)
    {
        $roleLevel = array_search($role, static::getValidRoles(), true);
        if ($roleLevel === false) {
            throw new \RuntimeException("Unrecognized role `$role`. Valid roles are `".implode("`, `", static::getValidRoles())."`.");
        }

        return $this->getRoleInteger() >= $roleLevel);
    }
}
```

With this code, you can persist roles as an integer in the database, but handle them with strings in the program space. This combines the usability and efficiency of named constants with the conciseness of strings.

This represents just a simple example of how to create models with this class. As you can imagine, you can do virtually anything you want with these objects. Enjoy!

Design Philosophy
-----------------

[](#design-philosophy)

Following is a more long-winded discussion of the design philosophy that birthed this library. It doesn't deal so much with the code itself, rather, it attempts to explain the methods available and what problems were being addressed when they were created.

### Problems Addressed

[](#problems-addressed)

The primary problems this library was addressing where:

1. How to define a resource class that's persistence-enabled without mandating a persistence strategy.
2. How to provide a foundation for data serialization in JSON API while allowing concrete classes to remain only loosely coupled to the foundation.
3. How to interact with the JSON API specification more easily in the program space.

These problems were solved by defining a few relatively lightweight interfaces that comprise an easy approach to model construction and use from a developer-user perspective. Specifically, `ResourceInterface` defines an interface for working with resource objects that are both persistence-enabled and JSON API-conformant; and `DatasourceInterface` defines an interface to be used by a resource object to facilitate persistence.

Read on to learn more about each.

### Resources

[](#resources)

The library centers around `AbstractResource` as the basis for a persistence-enabled, JSON API data system. As the name implies, this resource is meant to be extended to the various classes that comprise each system's Data Model. For example, a blogging platform might have the following model classes, all extending from `AbstractResource`:

- `User`
- `Post`
- `Comment`

While the low-level details surrounding datasource interactions and serialization are contained in `AbstractResource` itself, these concrete resource classes will contain their own validation rules and business logic. In fact, it is expected that 96% of all logic contained in derivative resource classes be business logic. There will only be occasional forays into implementation logic to facilitate things like the serialization of complex data or the establishment of "initial state" of complex data. This separation allows for relatively "pure" resource classes (i.e., "model layer") that can easily be adapted to work with other systems.

### Persistence

[](#persistence)

Interaction with persistence is built in through the defined `DatasourceInterface`. HOWEVER, there is no included implementation of `DatasourceInterface`. This allows us to keep this base package fairly light, while still maintaining compatibility with many different kinds of persistence.

This library's approach to persistence is to define the *way* we'd like to interact with a persistence mechanism, without defining what that persistence mechansim actually is. Furthermore, we assume here that datasources are *differentiated* -- that is, that each resource will have its own accompanying resource-specific datasource instance. With that in mind, the included `DatasourceInterface` defines the following public functions:

- `create`
- `newCollection`
- `get`
- `save`
- `delete`
- `convert`
- `getRelated`
- `inflateRelated`
- `initializeResource`
- `getCurrentData`

Some of these methods are fairly obvious, while others require a bit of explanation.

To start with the obvious, `create` and `newCollection` are instantiators: they return a new instance of whatever type of resource the datasource deals in (or a collection thereof). `get` returns a collection or specific resource, depending on the DSL string passed to it (e.g., "id=12345" or "name like '%tom%' and (role &amp; 4)"). `save` either saves a new resource or updates an existing one, depending on whether the passed resource has an id or not. `delete` deletes the given resource.

Now for the more complex methods.

#### `convert`

[](#convert)

The data system in this library was designed around the idea that each resource may have several "levels" that represent the same fundamental resource type. For example, the back-end system needs a way to set the "passwordHash" field for a user. This is not a field that we want, for example, API users to be able to set, nor do we want to include our hashing logic and keys in a publicly distributed codebase.

To solve this, we can create a "public" version of the `User` class which stores `password` as a write-only cleartext field, then extend it to create a `Private` version which implements the more complex hashing logic that results in a successful password hash.

Since instantiation is done at the datasource level, it makes sense for the datasource to handle conversions between these related resource types. Thus, `convert` takes a resource and a target "level" and attempts to convert the resource to a resource of the target level while preserving the resource's state. In the `User` example, we might request `$usersDatasource->convert($user, 'private')` to get a "private" version of the given user resource.

#### `getRelated` and `inflateRelated`

[](#getrelated-and-inflaterelated)

Since `DatasourceInterface` was conceived to handle a single resource type, we also need a way to delegate handling of different resource types to other datasources. `getRelated` and `inflateRelated` do that by allowing us the opportunity to call on different datasources according to the name of the relationship being requested. For example, if the `User` object had a `posts` relationship, we would implement logic in `getRelated` and `inflateRelated` that routes those requests to a `PostsDatasource` object.

This of course implies that the `UsersDatasource` *knows* about a `PostsDatasource`. That problem can be addressed in a number of ways. For example, you might use a `DataContext` to group a number of datasources into a sort of interrelated "directory" (a concept introduced in `cfxmarkets/php-persistence` and that we use at CFX). Or you might build consciousness of the more general data context into each datasource itself using a single common abstract datasource that contains routing and instantiation logic for the various types of sibling datasources that your application must deal with.

Regardless, `getRelated` and `inflateRelated` are two methods that allow you to delegate calls for related resources to other datasources.

#### `initializeResource`

[](#initializeresource)

It's often the case that you end up with a resource that is uninitialized. This happens, for example, when you get a relationship, which has only an ID. In such a case, you can call on the datasource to initialize the resource, which basically just gets the resource from persistence, then uses the returned data together with the resource's `restoreFromData` method to inflate the object.

That brings us to our final method, `getCurrentData`

#### `getCurrentData`

[](#getcurrentdata)

The `getCurrentData` method is a special method design to couple the public `Datasource` object and the public `Resource` object with a private handshake. It was conceived to work with the `AbstractResource` class's `restoreFromData` like so:

1. `Datasource` gets data from whatever its persistence source is and makes sure it's in JSON API format.
2. `Datasource` places that data in its protected `currentData` property.
3. `Datasource` either calls `Resource`'s `restoreFromData` method or instantiates a new resource (which implicitly calls this method).
4. `Resource` calls `Datasource`'s `getCurrentData` method, which should return the prepared data and then set `currentData` back to null.
5. `Resource` updates its fields internally using this "trusted" data from `Datasource`.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~9 days

Recently: every ~18 days

Total

62

Last Release

2587d ago

Major Versions

v0.8.1 → v1.02017-10-25

v1.4.x-dev → v2.0.02019-02-02

v1.5.x-dev → v2.1.02019-04-17

PHP version history (2 changes)v0.1PHP &gt;=5.4 &lt;7

v1.0PHP &gt;=5.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/b76588c1d06ed54f5f5fe06e466d044b0527eb6e1b68a8e3dccd13e447fa43eb?d=identicon)[kael-shipman](/maintainers/kael-shipman)

---

Top Contributors

[![kael-shipman](https://avatars.githubusercontent.com/u/3475586?v=4)](https://github.com/kael-shipman "kael-shipman (16 commits)")

---

Tags

jsonapijsonapi

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/cfxmarkets-php-jsonapi-objects/health.svg)

```
[![Health](https://phpackages.com/badges/cfxmarkets-php-jsonapi-objects/health.svg)](https://phpackages.com/packages/cfxmarkets-php-jsonapi-objects)
```

###  Alternatives

[neomerx/json-api

Framework agnostic JSON API (jsonapi.org) implementation

7373.6M27](/packages/neomerx-json-api)[cloudcreativity/laravel-json-api

JSON API (jsonapi.org) support for Laravel applications.

7881.1M5](/packages/cloudcreativity-laravel-json-api)[nilportugues/laravel5-json-api

Laravel 5 JSON API Transformer Package

31232.4k1](/packages/nilportugues-laravel5-json-api)[nilportugues/jsonapi-bundle

Symfony 2 &amp; 3 JSON API Transformer Package

11446.0k](/packages/nilportugues-jsonapi-bundle)[nilportugues/json-api

Serializer transformers outputting valid API responses in JSON, JSON API and HAL+JSON API formats.

70106.2k3](/packages/nilportugues-json-api)[alsvanzelf/jsonapi

Human-friendly library to implement JSON:API without needing to know the specification.

54150.0k5](/packages/alsvanzelf-jsonapi)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
