12

I am trying to use EE templates the same way I use CI controllers. Normally, when I need an "API" wherein my JS can call a URL (maybe with some POST variables) and get a JSON (or JSONP) response back, I'll just put these API methods in a CI controller and make sure I set the content type before returning a response.

There doesn't seem to be a JSON template type. So, I've installed this http://devot-ee.com/add-ons/http-header and set the content type using the add on.

Is there a native way to do this in EE? Or was I right to use the add on?

11 Answers 11

15

There isn't a native way to do JSON output in EE for channel data (AFAIK).

Have you seen Rob Sanchez's JSON plugin? You just pass params as you would with {exp:channel:entries}.

You can specify which fields to output, optionally set it to only output if an xhr is detected, and it will output member data too. You can then wrap the rest of your template in an ajax detect conditional (I use Mo'Variables for this) and you've got a single template that serves either HTML or JSON depending on the request type, no controller required.

That might just do the trick in one hit, or at least provide a solid basis for custom development. At the very least you want to avoid formatting JSON by hand - it's way too pernickity to be any fun.

10

I have recently used EE templates to make JSON output from channels that contain a range of custom fields, including P&T’s Playa, Matrix and Wygwam fieldtypes. Those that are not familiar with JSON might find this useful. While it is a bit fiddly, all that is really required is a resource who is familiar with the JSON syntax/format to help you craft the template properly.

Add-ons

Low_Replace This add-on is used to find and replace entities that need to be either stripped or changed in your resulting data to validate as proper JSON. For example: double quotes, line breaks, or tabs.

HTTP_Header This add-on is used to set the proper content type in your http header to JSON. Place this tag at the top of your template: {exp:http_header content_type="application/json"}

Links

JSON Validator, Formatting EE Entries as JSON

End of Line Syntax

Dealing with end of line syntax (comma or no comma), is the biggest challenge I came across. I used the backspace param on the main channel entry tag and conditional tags when building arrays (matrix or playa fields).

Matrix Example

{if balloons != ""},
    "Balloons": [ 
    {balloons}{
        "colour": "{colour}",
        "size": "{size}"
    }{if row_count != total_rows},{/if}
    {/balloons} 
    ]
{/if}

Playa Example

{if blocks != ""},
    "Blocks": [ 
    {blocks}{
        "colour": "{colour}", 
        "size": "{size}"
    }{if {exp:playa:total_children field="blocks"} != "{count}"},{/if}
    {/blocks} 
    ]
{/if}

I put the previous line’s end-of-line comma at the beginning of the next conditional. That way, if balloons or blocks is empty and nothing prints, then the previous line will be the last line and there should be no comma. To deal with the same issue within the matrix or playa arrays, I use row_count, count, and total:children variables to figure out if a line is not the last line and should thus have a comma.

Remove/Replace Entities

I use the Low Replace tags to escape double quotes, replace line breaks and remove tabs like thus:

{exp:low_replace multiple="yes" regex="yes" find="QUOTE|NEWLINE|/\t/" replace="\QUOTE|SPACE|"}
    {body}
{/exp:low_replace} 

This works nicely when using a P&T Wygwam field that may have odd things in them. You can use regex here, so look into that when needed.

Tips Watch for extra spaces when using the backspace param. Your closing channel entry tag needs to immediately following the last comma like thus:

{exp:channel:entries channel="ch_member_profiles" backspace="1"}
    …
},{/exp:channel:entries}
]
5

You could set the template type to javascript and do something like this:

{exp:channel:entries 
    channel="locations"
    limit="5"
}
{
    "Store": "{title}",
    "Address": "{address}",
    "City": "{city}",
    "State": "{state}",
    "Zip": "{zip}",
    "Phone": "{phone}",
},
{/exp:channel:entries}
4

Did you guys have a look at Chris Imrie's data api add-on on github. Still alpha but interesting nonetheless.

3

IMHO, you really don't want to use templates as controllers; consider them as views instead. You sound like you know about PHP so, if I were you, I'd write an extension with one of the template hooks instead.

  • I'm new to EE. In SO, someone equated templates to controllers. I was initially thinking I should equate them to views, though. – ExpressionEngineNewbie Nov 28 '12 at 21:32
  • They're more of a presenter/view, with the controller/router being the template_group/template system (or an alternative like structure, freeway or the pages module) – Tom Davies Nov 28 '12 at 21:38
  • Ah; welcome! The way I think about it is that the templates are views and EE tags are like helper methods meant to only help with outputting view data. You rarely, almost never, want to have deep logic or function inside a template. Best leave all that to extensions or actions. – Eric Lamb Nov 28 '12 at 21:38
  • I like Eric's idea, however, When you need to return various image sizes (CE Image) in your JSON that are resized and cached on Amazon S3 is when this becomes problematic. – Bransin Oct 14 '14 at 20:39
3

Nope, there's no native EE method of outputting a template as JSON. The HTTP Header plugin is exactly what you need. Just format your template in JSON and you're good to go.

2

I haven't used the plugins mentioned here, but you can use PHP in your template (on output) and do this by mixing the templating language and PHP. Not the cleanest, but it works:

<?php
header("Content-Type:application/json");
$events = array();
?>
{exp:channel:entries channel="events"}
<?php
$a = array('id' => {entry_id}, 'title' => '{title}');
array_push($events, $a);
?>
{/exp:channel:entries}
<?php
echo(json_encode($events));
?>

You'd probably want to encode the output from your entries - the above example would break if there was an apostrophe in the title.

1

You might be able to in a default template if you place curly brackets on their own lines, like so:

{
  "success": 1,
  "errors": [],
  "count": 15
}
0

simple grid example (without comma in the last iteration):

{exp:channel:entries channel="demo" url_title="entry1" status="open" disable="categories|member_data|category_fields|pagination" dynamic="no"} [ {grid_field orderby="random"}

{

    "id": "id{grid_field:count}",

    "title": "{grid_field:title}"


}{if gridfield:count < gridfield:total_rows},{/if}

{/gridfield} ] {/exp:channel:entries}

0

I was modifying my locations json file this morning, so I thought I would weigh in. I'm using the JSON Encode add-on inside of a low_search results. Of course, the Low Search results could be switched with Channel Entries or any other tag which gets your data.

This is my full json_locations.html template:

{exp:http_header content_type="application/json"}
{"locations": [

{exp:low_search:results
    channel="locations"
    limit="1000"
    dynamic="no"
    distance:to="locations_latitude|locations_longititude"
    distance:unit="mi"
    require_all="category"
}

    {
        "id": "{entry_id}",
        "url_title": "{url_title}",
        "type": "{categories show_group="6" backspace="1"}{category_id}|{/categories}",
        "region": "{categories show_group="7" backspace="1"}{category_id}|{/categories}",
        "name": {exp:json_encode}{locations_displayed_name}{/exp:json_encode},
        "address": {exp:json_encode}{locations_address_1}{/exp:json_encode},
        "city": {exp:json_encode}{locations_city}{/exp:json_encode},
        "state": {exp:json_encode}{locations_state}{/exp:json_encode},
        "zip": {exp:json_encode}{locations_zip}{/exp:json_encode},
        "phone": {exp:json_encode}{locations_primary_phone_number}{/exp:json_encode},
        "geo_lat": {exp:json_encode}{locations_latitude}{/exp:json_encode},
        "geo_lng": {exp:json_encode}{locations_longititude}{/exp:json_encode},
        "additional_data": {exp:json_encode}{categories show_group="6"}<i class='fa fa-map-marker {category_url_title}'></i> {exp:ce_str:ing ucwords}{category_name}{/exp:ce_str:ing}<br> {/categories}{/exp:json_encode}
    }{if count<absolute_results}, {/if}

{/exp:low_search:results}

]}

I'm calling this from Javascript to render map locations on a Google Map. This is the line from JS:

var location_json = "/locations/json_locations";

This is what it would look like if you used exp:channel:data:

{exp:http_header content_type="application/json"}
{"locations": [

{exp:channel:entries
    channel="locations"
    limit="2"
    dynamic="no"
}

    {
        "id": "{entry_id}",
        "url_title": "{url_title}",
        "type": "{categories show_group="6" backspace="1"}{category_id}|{/categories}",
        "region": "{categories show_group="7" backspace="1"}{category_id}|{/categories}",
        "name": {exp:json_encode}{locations_displayed_name}{/exp:json_encode},
        "address": {exp:json_encode}{locations_address_1}{/exp:json_encode},
        "city": {exp:json_encode}{locations_city}{/exp:json_encode},
        "state": {exp:json_encode}{locations_state}{/exp:json_encode},
        "zip": {exp:json_encode}{locations_zip}{/exp:json_encode},
        "phone": {exp:json_encode}{locations_primary_phone_number}{/exp:json_encode},
        "geo_lat": {exp:json_encode}{locations_latitude}{/exp:json_encode},
        "geo_lng": {exp:json_encode}{locations_longititude}{/exp:json_encode},
        "additional_data": {exp:json_encode}{categories show_group="6"}<i class='fa fa-map-marker {category_url_title}'></i> {exp:ce_str:ing ucwords}{category_name}{/exp:ce_str:ing}<br> {/categories}{/exp:json_encode}
    }{if count<absolute_results}, {/if}

{/exp:channel:entries}

]}

Then point your browser to http://yourwebsite.com/locations/locations_json to see the results. You should see this:

{"locations": [

{
        "id": "10477",
        "url_title": "my-url-1",
        "type": "343",
        "region": "51",
        "name": "Caf\u00e9 Degas",
        "address": "2020 Smith Ave",
        "city": "New Orleans",
        "state": "LA",
        "zip": "70119",
        "phone": "504-777-5635",
        "geo_lat": "29.98032300",
        "geo_lng": "-90.08639902",
        "additional_data": "<i class='fa fa-map-marker'><\/i> My Location 1<br> "
    },{
        "id": "10233",
        "url_title": "my-location-2",
        "type": "48",
        "region": "51|173|57",
        "name": "My Location 2 \u2013 Bloomfield",
        "address": "4500 Archer Pkwy., 1st Floor",
        "city": "Metairie ",
        "state": "LA",
        "zip": "70004",
        "phone": "504-555-8563",
        "geo_lat": "30.017772",
        "geo_lng": "-90.1880092",
        "additional_data": "<i class='fa fa-map-marker'><\/i> My Location 2<br> "
    }

]}
0

I am using the {exp:http_header content_type="application/json"} but now I get the error when validating:

Invalid encoding, expecting UTF-8, UTF-16 or UTF-32.

any advice?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.