6

I've got a relatively simple setup running using SilverStripe 3.2.1 with the restfulserver addon and using a variety of widgets which are associated to a Page using the elemental addon.

When I make a GET request via the API to retrieve some of Page #1's data, I can see the associated ElementAreaID:

# GET /api/v1/Page/1.json?fields=Title,URLSegment,Content,ElementArea 
{
  "Title": "Welcome",
  "URLSegment": "home",
  "Content": "A bunch of HTML here from all the widgets in the page...",
  "ElementArea": {
    "className": "ElementalArea",
    "href": "http://ss.local:3000/api/v1/ElementalArea/11.json",
    "id": "11"
  }
}

If I follow the links through the ElementalArea API calls it will list out all of the elements in my page:

# GET /api/v1/ElementalArea/11.json
{
  "ID": "11",
  "Widgets": [
    {
      "className": "Widget",
      "href": "http://ss.local:3000/api/v1/Widget/9.json",
      "id": 9
    },
    {
      "className": "Widget",
      "href": "http://ss.local:3000/api/v1/Widget/8.json",
      "id": 8
    },
    ...
  ]
}

And if I follow those API paths it will serve up the contents of the latest version of each of the Widgets.

My question is how can I include certain fields from the Widget DataObjects within the original Page field list?

I'd like ideally to have the Content field from each Widget be returned in an array with the initial Page API request.


For reference:

  • A Page has one ElementArea
  • An ElementArea has many Widgets
  • A Widget contains content that I want for my Page
scrowler
  • 24,273
  • 9
  • 60
  • 92
  • What would that additional array contain? Just the Contents? Or objects, with Widget-ID, href and Content field? – bummzack Mar 01 '16 at 19:40
  • Not too fussed really. Ultimately I need the Content from the widgets in the page via the ElementalArea pivot table to be returned with the Page. – scrowler Mar 01 '16 at 19:41

1 Answers1

2

Preamble: It doesn't seem there's currently a way to output array-like datastructures with the RESTful server module (except for relations of course). The proposed solution is a hack that abuses how JSONDataFormatter formats output.

Since JSONDataFormatter uses forTemplate to render a field before converting it to JSON, we can create our own Object renderer that returns an array instead of a string via forTemplate. This might look like this:

class FlatJSONDataList extends ViewableData
{
    protected $list;

    public function __construct(array $list)
    {
        parent::__construct();
        $this->list = $list;
    }

    public function forTemplate()
    {
        return $this->list;
    }
}

Then in your Page, it should be sufficient to have an additional method, like so:

public function getWidgetContents()
{
    return FlatJSONDataList::create(
        $this->ElementArea()->Widgets()->column('Content')
    );
}

Then you can include WidgetContents in your Field-list to get all widget Content fields in an array:

GET /api/v1/Page/1.json?fields=Title,URLSegment,Content,WidgetContents 
bummzack
  • 5,805
  • 1
  • 26
  • 45
  • Thanks - I'll give this a go later today. I assume that because the Widget itself isn't a relation of Page (ElementalArea is instead) that it's not available in the field list for Page by default? – scrowler Mar 01 '16 at 20:55
  • @RobbieAverill Yeah, it seems like relation depths greater than one level aren't implemented yet, otherwise something like this would probably be a valid field-name: `ElementArea.Widgets`. For now I think you're stuck with a hack or you could implement your own subclass of `DataFormatter`. The latter will mean that you'll have to write more code, but is probably less prone to break with a future update. – bummzack Mar 01 '16 at 22:26
  • Thanks - your answer was incredibly helpful. Unfortunately as it turns out the Elemental addon uses different field names for different data types, so I've had to add a static property to define which fields to return and loop the Elements retrieving each field before formatting it. It's working a charm though - very helpful answer! – scrowler Mar 02 '16 at 00:09