38

I've played around with a few implementations but I still don't feel like either is right.

I first came across building the JSON itself. This would be in a file at "craft/templates/entries.json".

[
  {% for entry in craft.entries.section('news').find() %}
    {
      "title": "{{ entry.title | e('js') }}",
      "body": "{{ entry.body | e('js') }}"
    } {% if not loop.last %},{% endif %}
  {% endfor %}
]

This worked but I didn't feel right about writing my own JSON so I came up with this approach instead.

{% set results = [] %}
{% for entry in craft.entries.section('news').find() %}
{%
  set results = results | merge([{
    'title': entry.title,
    'body': entry.body
  }])
%}
{% endfor %}
{{ results | json_encode() | raw }}

This works, the JSON will definitely end up valid and as a bonus it'll be condensed but it's not very reusable and when I add fields I need to adjust it.

What approach should I take to make a reliable way to query data from Craft and return json?

2
  • Haven't tried this, but could you just output the array returned by craft.entries? {{ craft.entries.section('news').find() | json_encode() | raw }} Commented Jun 12, 2014 at 1:24
  • I gave that a shot at the start but it just returned two empty objects. Commented Jun 12, 2014 at 1:27

5 Answers 5

27

Twig is great for outputting HTML/XML markup, but it’s not exactly the best option when you just want to output some data as JSON.

The Element API plugin aims to make outputting entries (and other element types) as JSON as easy as possible, and it’s completely customizable from its PHP-based API configuration file.

To achieve your desired JSON output, you would set your craft/config/elementapi.php file to this:

<?php
namespace Craft;

return [
    'endpoints' => [

        'entries.json' => [
            'elementType' => 'Entry',
            'criteria' => ['section' => 'news'],
            'transformer' => function(EntryModel $entry) {
                return [
                    'title' => $entry->title,
                    'body' => (string) $entry->body
                ];
            }
        ],

    ]
];
1
  • That is the ultimate best answer.
    – Gfra54
    Commented Feb 18, 2020 at 6:34
28

I just threw together a add-on that will provide a Twig filter called prune to allow you to more easily do what you're asking here.

https://github.com/mattstauffer/craftcms-prune

With prune you can now just do the following:

{{ craft.entries.section('news').find() | prune(['title', 'body']) | json_encode() | raw }}

Just pass in an array of the fields you want to pluck out. It's pretty new and could use some love, but it'll get the basics done for sure.

2
  • Beautiful. This will give me something to work off of. Commented Jun 12, 2014 at 13:33
  • That's just an awesome little plugin, gj! Commented Jun 12, 2014 at 15:21
12

You could also loop through all fields and output it depending on the field-type.

Example:

{% spaceless %}

[
    {% for entry in craft.entries.section('news').limit(1) %}
    {
        {# General Fields #}
        'slug': {{ entry.slug | json_encode() | raw }},
        'postDate': {{ entry.postDate | json_encode() | raw }},
        'title': {{ entry.title | json_encode() | raw }},
        'author': {{ entry.author.name | json_encode() | raw }},


        {# Your Fields #}
        {% for field in entry.getType().getFieldLayout().getFields() %}
            {% set field_attr = attribute(entry, field.field.handle) %}

            {% switch field.field.type %}
                {% case 'Assets' %}
                    "{{ field.field.handle | escape('js') }}": { {% for asset in field_attr %} { 'title': {{ asset.title | json_encode() | raw }}, 'url': {{ asset.url | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Categories' %}
                    "{{ field.field.handle | escape('js') }}": { {% for category in field_attr %} { 'title': {{ category.title | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Checkboxes' %}
                    "{{ field.field.handle | escape('js') }}": { {% for option in field_attr %} { 'label': {{ option.label | json_encode() | raw }}, 'value': {{ option.value | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Date' %}
                    "{{ field.field.handle | escape('js') }}": {{ field_attr | json_encode() | raw }},

                {% case 'DropDown' %}
                    "{{ field.field.handle | escape('js') }}": { 'label': {{ field_attr.label | json_encode() | raw }}, 'value': {{ field_attr.value | json_encode() | raw }} },

                {% case 'Entries' %}
                    "{{ field.field.handle | escape('js') }}": { {% for field_entry in field_attr %} { 'title': {{ field_entry.title | json_encode() | raw }}, 'slug': {{ field_entry.slug | json_encode() | raw }}, 'url': {{ field_entry.url | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Matrix' %}
                    "{{ field.field.handle | escape('js') }}": {{ 'This is a matrix-field' | json_encode() | raw }},

                {% case 'MultiSelect' %}
                    "{{ field.field.handle | escape('js') }}": { {% for option in field_attr %} { 'label': {{ option.label | json_encode() | raw }}, 'value': {{ option.value | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Radiobutton' %}
                    "{{ field.field.handle | escape('js') }}": { 'label': {{ field_attr.label | json_encode() | raw }}, 'value': {{ field_attr.value | json_encode() | raw }} },

                {% case 'Tags' %}
                    "{{ field.field.handle | escape('js') }}": { {% for tag in field_attr %} { 'name': {{ tag.name | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% case 'Users' %}
                    "{{ field.field.handle | escape('js') }}": { {% for user in field_attr %} { 'name': {{ user.name | json_encode() | raw }}, 'id': {{ user.id | json_encode() | raw }} }, {% else %}null{% endfor %} },

                {% default %}
                    "{{ field.field.handle | escape('js') }}": {{ field_attr | json_encode() | raw }},
            {% endswitch %}
        {% endfor %}
    }
    {% endfor %}
]

{% endspaceless %}

You might need to customize it, depending on what attributes you need

To make it work with Matrix you'd need to create a Matrix case and copy the whole loop inside that case and set it to use the block var.

4
  • 1
    Thanks! I was hoping to get a little bit of Matt's answer with a little bit of this answer. I'm going to try and combine them both with a "json" route maybe as see how that all goes. Commented Jun 12, 2014 at 13:45
  • 2
    @BillCriswell I think there may be space for us to make prune smart enough (or add an additional filter) to do special handling for any other types. I thought about building it all out, but figured it would be better to start with just the pruning part. :) Commented Jun 12, 2014 at 13:54
  • @Matt Would love to see a plugin which handles all the json stuff :)
    – Victor
    Commented Jun 12, 2014 at 14:15
  • 1
    @victorIn Ha, I was actually going to add a second filter that was pruneToJson that was Prune + json_encode + raw, but I ran out of time. I'll do that soon, but the tougher part is making sure it handles all the other fields correctly like you mentioned here. I'll try to dig in on some lunch break soon and will post back here. Commented Jun 12, 2014 at 14:18
5

You could also do what Victor In has suggested, but deal with everything in arrays and hashes instead of building a big string. That way you only have to encode once at the end, and it's easier (for me) to debug.

{% spaceless %}
  {% set entries = [] %}

  {% for entry in craft.entries.section('people') %}
    {% set thisEntry = {} %}

    {% for fieldModel in entry.getType().getFieldLayout().getFields() %}
      {% set fieldHandle = fieldModel.field.handle %}
      {% set fieldAttr = attribute(entry, fieldHandle) %}

      {% switch fieldModel.field.type %}

        {% case 'Assets' %}
          {% set enumerable = [] %}
          {% for asset in fieldAttr %}
            {% set enumerable = enumerable|merge([{
              'title': asset.title,
              'url': asset.url
            }]) %}
          {% endfor %}
          {% set fieldValue = enumerable %}

        {% default %}
          {% set fieldValue = fieldAttr %}
      {% endswitch %}

      {% set thisEntry = thisEntry|merge({(fieldHandle): fieldValue}) %}
    {% endfor %}

    {% set entries = entries|merge([thisEntry]) %}
  {% endfor %}
  {{ entries | json_encode() | raw }}
{% endspaceless %}
1
  • However, I think expanding on Prune's functionality is the way to go!
    – Tim Kelty
    Commented Sep 16, 2014 at 19:21
1

I found another plugin that returns JSON format.

Reference Link: https://github.com/wesrice/rest-easy

I tested it out. It allows you to configure the parameters you need in the URL.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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