Skip to content

3.1: Output Formatting

Sebastian Schendel edited this page Jan 4, 2021 · 5 revisions

The module automatically runs json_encode on the output data, so your function can return a classic PHP-array and it will be transformed to a JSON-response.

<?php
namespace ProcessWire;

class Example {
  public static function test () {
    return [
      'message' => 'test successful',
      'status' => 200
    ];
  }
}

Calling that Example::test() function will result in a response like this:

{
  "message": "test successful",
  "status": 200
}

That works very well with all basic datatypes (like string, integer, boolean, ...) which json_encode can handle.

Complex data

More complex data, let's say a ProcessWire-Page object, must be transformed simpler datatypes. I added a little helper-function AppApi::getAjaxOf(), that can transform objects of ProcessWire's Page, PageArray, Template, PageImage, PageFile and PageFiles to arrays with the basic data that they contain.

<?php
namespace ProcessWire;

class Example {
  public static function pageOutput () {
    return AppApi::getAjaxOf(wire('pages')->get('/'));
  }
}

Calling that Example::pageOutput() function will result in an array of the basic information of your homepage:

{
  "id": 1,
  "name": "home",
  "title": "Homepage",
  "created": 1494796565,
  "modified": 1494796588,
  "url": "/",
  "httpUrl": "https://my-website.dev/",
  "template": {
    "id": 1,
    "name": "home",
    "label": "Home-Template"
  }
}

Look at the code of AppApi::getAjaxOf() - it does nothing overcomplicated. If these basic outputs are not sufficient for your use case, you have to convert the values of the respective field itself into something that can be output as JSON.

Let's have a look at a typical RepeaterMatrix-field, that I like to use for the main-contents of a page in most of my projects.

The field offers several different types of content blocks that can be freely created, swapped and changed. I like to use this very much instead of a complete free text field to provide some structure for the editors. In addition, these content blocks are much easier to style.

Our field named "contents" holds its data-values as an iterable list of Pages, that represent the created content-blocks. Each type can have different sub-fields. Let us concentrate on the following examples:

  • text
    • title (InputText)
    • freetext (InputTextarea)
  • image
    • image (InputImage with max 1)
  • gallery
    • title (InputText)
    • description (InputTextarea)
    • images (InputImage)

We assume, that our example-page with id 4242 does have our contents-field. We want to output its contents via api, so we must transform the values to an PHP-array of simple values.

<?php
namespace ProcessWire;

class Example {
    public static function pageOutput() {
        $page = wire('pages')->get(4242);

        if (!$page || !$page->id) {
            throw new NotFoundException();
        }

        $output = [
	        'contents' => []
		    ];
        foreach ($page->contents as $contentblock) {
            if($contentblock->type === 'text'){
                $output['contents'][] = SELF::getAjaxOfTextblock($contentblock);
            }else if ($contentblock->type === 'image') {
                $output['contents'][] = SELF::getAjaxOfImageblock($contentblock);
            }else if ($contentblock->type === 'gallery') {
                $output['contents'][] = SELF::getAjaxOfGalleryblock($contentblock);
            }
        }

        return $output;
    }

    private static function getAjaxOfTextblock($block) {
        return [
            'type' => 'text',
            'depth' => $block->depth,
            'title' => $block->title,
            'text' => $block->freetext
        ];
    }

    private static function getAjaxOfImageblock($block) {
        return [
            'type' => 'image',
            'depth' => $block->depth,
            'image' => AppApi::getAjaxOf($block->image)
        ];
    }

    private static function getAjaxOfGalleryblock($block) {
        return [
            'type' => 'gallery',
            'depth' => $block->depth,
            'images' => AppApi::getAjaxOf($block->images),
            'title' => $block->title,
            'description' => $block->freetext
        ];
    }
}

That will result in an output like the following, depending on which content-blocks were created on the page:

{
  "contents": [
    {
      "type": "text",
      "depth": 0,
      "title": "Example-Headline",
      "text": "Lorem ipsum..."
    },
    {
      "type": "image",
      "depth": 0,
      "image": {
        "basename": "example-image.0x300.jpg",
        "name": "example-image.0x300.jpg",
        "description": "",
        "created": 1596037618,
        "modified": 1596037618,
        "filesize": 71585,
        "filesizeStr": "70 kB",
        "page_id": 5678,
        "ext": "jpg",
        "basename_mini": "example-image.0x300.600x0.jpg",
        "width": 683,
        "height": 300,
        "dimension_ratio": 2.28,
        "original": {
          "basename": "example-image.jpg",
          "name": "example-image.jpg",
          "filesize": 305906,
          "filesizeStr": "299 kB",
          "ext": "jpg",
          "width": 910,
          "height": 400,
          "dimension_ratio": 2.28
        }
      }
    },
    {
      "type": "text",
      "depth": 0,
      "title": "Second Example-Headline",
      "text": "Lorem ipsum..."
    }
  ]
}

Different HTTP Status-Code

With version 1.1.0 you can influence the HTTP status code of the server response (default is 200). If you specify a 'responseCode' in the response array, this value becomes the new response code.

<?php
class AppApiTest {
    public static function test($data) {
        return [
          'success' => true,
          'responseCode' => 202
        ];
    }
}

➡️ Continue with 3.2: Error Handling
⬅️ Back to 3: Creating Endpoints