If you follow me, you know that I am heavily involved in both the Ember and Laravel communities. Thus, I have taken it upon myself to help define the development story for working with Ember in Laravel projects as well as digging into what is happening at a lower level in both projects. This not only includes articles, but also creating reusable code both in the Laravel/PHP world and in the Ember world.

One of my first contributions to this effort was the creation of Eloquent-Ember. This package added functionality to your Eloquent models to allow for somewhat compatibility with the Pre 1.0 JSON API standard used by Ember's JSONAdapter. In today's Ember field, Eloquent-Ember is a closer relation to the ActiveModelAdapter available in Ember Data 1.0.0-beta.

However, I am officially deprecating Eloquent-Ember for a few major reasons. Primary of which is that I admit that it is a pattern of bad design.

Design Implications

Eloquent-Ember was a prime example of heavy coupling for to make Ember specific APIs easier to build. I always knew that this was a stop-gap solution and was not particularly the right way of going about things. It completely tied every instance of a model to output data in a way that Ember would understand.

This meant that my API presentation logic was deeply nested in the database layer of my application. Now everything in my application had to talk Ember speak. This meant my business logic and server generated content (including packages that hooked into Eloquent) had to be modified or abstracted.

This started to show itself as the API for Eloquent-Ember became more and more awkward as features were tacked on. Properties to define what relationships to load when outputting JSON, other's that would override this array and could be set on the fly. It all was a bit of a mess.

Taking Notes From Ember Data

Having taken some time to dive deeper into Ember and Ember Data's code, I found that DS.Models were never affected by the API that they were attached to. Instead, Ember uses Adapters and Serializers to get the data in a state that DS.Models can work with. This also means that down to a per request basis, the way that data is handled and interpreted can be customized to fit the usecase. The Model is then a standard version which can focus only on keeping up with properties and relationships. This is a strong pattern which allows for a ton of flexibility and means that even the craziest of data sources can be consumed and brought in to DS.Models. Some of the Adapters and Serializers I have seen range from consuming JSONAPI standards, XML APIs, CouchDB interactions (with authentication), and more.

What this means for the Laravel side of things

On the Laravel side of things we can actually mimic this pattern where our Eloquent models are decoupled from our Ember API implementation. So to put things in 1-to-1 terminology:

DS.Model - EloquentModel DS.Adapter - Controller DS.Serializer - Serializer Service

DS.Adapter & Controllers

In Ember, the implementation of an adapter really is a communication layer of where to grab data back and forth between your API and the Client-Side model. This is a bit similar to an API Controller (and a bit of the Router) on the server-side of things.

A controller sees the input that is being sent (HTTP headers, Request Payload, etc), and translates that into something that can then fetch data. But at the most basic level, the data needs to be formatting to something that the Business logic on the server will understand. This is where calling out to a Serializer helps.

DS.Serializer & Serializer Services

Serializers have one role to play in a system: translating data. If we look at the public API for the various Serializers in Ember Data, there are really two functions that we are publicly facing - serialize and normalize

serialize is taking data from the current system and translating it into something that another system will understand. normalize does the opposite taking external data and translating it into something that is meaningful to our existing business logic.

Implementation Of A Basic Adapter

In my own opinion, I do really like the new API standard expected by Ember's RESTAdapter (and the even thinner JSONAdapter). It's explicit with room to grow further API features that would not only work with Ember, but other services as well. Essentially I would like an easy path to build APIs where with just a few lines of code, I can rather reliably know that my response would look like this:

{
	"users": [
		"firstName": "John",
		"lastName": "Doe",
		"hasOneRelationship": 2,
		"embeddedAddress": {
			"street": "123 1st Street",
			"state": "NY",
		},
		"hasManyRelationship": [
			1,
			2,
			3
		]
	],
	"meta": {
		"status": 500,
		"page": 1,
		"lastPage": 50,
		"perPage": 10,
		"authenticatedUser": "..."
	}
}

But, I don't want to have to sacrifice standardized interfaces in the Laravel world to do so. This means bringing the presentation logic out of the Model, even out of the controller, and focusing on a serializer pattern.

Progress To The Future

I have written a bit on some of my early attempts to build out this pattern in Laravel (beware that code has bugs!). And I have an example application showing this pattern (and using some Ember CLI Awesomeness).

I am continuing to work on solidifying and having a good test base for this before creating a package for distribution. If you look at the Laracon Ember application, the final API for creating APIs will be very similar to what is there. So you should be able to return arrays, Collections, or Eloquent Models from a controller action and have it format the response accordingly.

public function index()
{
	return Users::paginate(20);
}

However, I am still working on the stories for both more customization (API Keys, Auto-Loaded Relationships, custom Serializers, etc), as well as more convenience (base repositories to handle queries, etc). I also am looking at making the meta data story really powerful so that this becomes a powerful API tool for all uses (not just Ember). Along those lines, I am looking at using well tested utilities such as Fractal which could help keep the package well tested with very little effort on my part.

Let me know what you think about this approach, and anything you think I may have missed the ball on.