Blog

REST API support for one|content

At some point, delius needed a flexible toolkit for Joomla to display (and manage) structured content: concepts related to each other, residing in a variety of data sources, from databases to SOAP APIs. Nothing was available at the time, so we developed one|content

The development of one:content has been fairly slow over the past years, but recently a version compatible with Joomla 3.x has been setup. There are still areas of work to be completed, but frontend functionality is operational. In one project, the need for a REST interface to one:content schemes suggested an extension to one:content for that purpose. This article describes the approach we used, avoiding (true to form) the need for coding.

The objective was to add metadata to the scheme definition to describe which routes would be available for the scheme at a REST entry point. This makes it possible to define routes as you want, provide some REST API parts and not others, use standard one|content authorisation and allow a definition of which attributes should be exposed.

Here's how a REST request is handled:

  • define a one:content menu item as a REST entry point by selecting this view, and specify the schemes that wil be accessible at this entry point
  • the toplevel REST controller creates an instance of Slim to handle all REST-specific handling. You will need to add Slim using composer
  • based on the scheme metadata, the controller will setup the appropriate routes for the Slim instance to understand
  • the Slim callbacks call a One_Rest_Controller instance, which handles the REST/JSON request and response objects and dispatches the REST-specific tasks to the scheme's controller. The tasks are rest_getsingle, rest_getmany, rest_create, rest_update and rest_delete, which you can authorise in the standard one:content way. You can also add passthrough methods that are simply passed on to the scheme controller, in case you need specific API actions or want to override the standard implementation
  • default actions have been defined to provide the regular implementation of these tasks, so for most simple REST use cases, you won't have to write any code at all

The restable behaviour activates the availability of REST functionality for your scheme:

<behaviors>
    <behavior name="restable">
        <route pattern="/report/test"         method="get"    action="pass:test"/>
        <route pattern="/report/{id}/nice"    method="get"    action="pass:nice"/>
        <route pattern="/reports/short"       method="get"    action="pass:short"/>
        <route pattern="/reports"             method="get"    action="getMany"  allowSearch="1"/>
        <route pattern="/report/{idOrAlias}"  method="get"    action="getSingle"/>
        <route pattern="/report"              method="post"   action="post"/>
        <route pattern="/report/{idOrAlias}"  method="delete" action="delete"/>
        <route pattern="/report/{idOrAlias}"  method="put"    action="put"/>
    </behavior>
</behaviors>

This means you can get a single instance by using a query like

<your entrypoint>/report/12

while selecting multiple instances also automatically allows search (see allowSearch parameter) using a query like

<your entrypoint>/reports?year=2012&op:year=lt

Simply put, return a JSON array with a REST representation of reports where the year is less than 2012. Of course, ordering and limit/start is also available on this call.

Which attributes are returned can be defined in the metadata as follows:

<attributes>
    <attribute name="id" type="int" identity="true" autoinc="true" restable="1"/>
    <attribute name="name" type="string" restable="1"/>
    <attribute name="description" type="string"/>
    <attribute name="year" type="string" restable="1"/>
    <attribute name="published" type="boolean" restable="1"/>
    <attribute name="image" type="string"/>
    <attribute name="file" type="string"/>
 </attributes>

As a result, the JSON representation of a report will look something like this (listing only the id, name, year and published attributes):

{
    "id": "10",
    "name": "",
    "published": "",
    "year": "2016"
}

Many more options are available,security (using Slim or a one:content driven auth strategy) first on the list. Send me a PM or email in case you are interested!

Reading material:

  • the Slim framework, version 3.0, different from 2.0 but more architecturally sound (and les well documented IMO)
  • the unirest rest client I used for a test script, and which may one day become the basis for a one|content store, allowing you to consume a REST API in the same way as a MySQL table