<p>You can determine what custom fields are relevant to an entry through its <em>field layout</em>:</p>
<pre class="lang-php prettyprint-override"><code>$layout = $entry-&gt;getFieldLayout();
</code></pre>
<p>A <a href="https://docs.craftcms.com/api/v4/craft-models-fieldlayout.html" rel="nofollow noreferrer"><code>FieldLayout</code></a> may contain more than just custom fields (like headings and native fields), but you can filter those “field layout elements” down to just the ones we care about:</p>
<pre class="lang-php prettyprint-override"><code>$fields = $layout-&gt;getCustomFields();
</code></pre>
<p>(In Craft 3, this would just be <code>$layout-&gt;getFields()</code>!)</p>
<p>Then, you’ll need to loop over this array of <a href="https://docs.craftcms.com/api/v4/craft-base-fieldinterface.html" rel="nofollow noreferrer"><code>Field</code></a>s and pull values off of the original <code>$entry</code>. Here’s a function that puts this all together:</p>
<pre class="lang-php prettyprint-override"><code>use craft\elements\Entry;

function getCustomFieldData(Entry $entry) {
    $values = [];

    // Fetch the entry’s field layout:
    /** @var craft\models\FieldLayout $layout */
    $layout = $entry-&gt;getFieldLayout();

    // Get all custom fields in the layout:
    $fields = $layout-&gt;getCustomFields();

    // Loop over the fields:
    foreach ($fields as $field) {
        // Stash a reference to the field’s handle:
        $handle = $field-&gt;handle;

        // Grab the field’s value:
        $value = $entry-&gt;getFieldValue($handle);

        // Check what field type this is, so we can deal with its value appropriately:
        switch (get_class($field)) {
            // Date + Time fields...
            case craft\fields\Date::class:
                $values[$handle] = $value-&gt;format(DateTime::ATOM);

                break;

            // Asset fields...
            case craft\fields\Assets::class:
                // Get an array of Assets by executing the element query:
                $values[$handle] = array_map(function ($asset) {
                    return $asset-&gt;url();
                }, $value-&gt;all());

                break;

            // Any other type of field we haven’t specifically handled...
            default:
                // ...just gets the raw value:
                $values[$handle] = $value;
        }
    }

    return $values;
}
</code></pre>
<p>This function can live in your <code>element-api.php</code> file, a <a href="https://github.com/craftcms/element-api#transformer" rel="nofollow noreferrer">Transformer</a>, or a <a href="https://craftcms.com/docs/4.x/extend/" rel="nofollow noreferrer">module or plugin</a>.</p>
<p>Your endpoint might look something like this:</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php

use craft\elements\Entry;

// ...function, from above!

return [
    'endpoints' =&gt; [
        'api/&lt;entryId:\d+&gt;.json' =&gt; function($entryId) {
            return [
                'resourceKey' =&gt; 'stories',
                'elementType' =&gt; Entry::class,
                'criteria' =&gt; [
                    'id' =&gt; $entryId
                ],
                'transformer' =&gt; function(Entry $entry) {
                    return [
                        'id' =&gt; $entry-&gt;id,
                        'title' =&gt; $entry-&gt;title,
                        'url' =&gt; $entry-&gt;url,
                        // From the transformer, call our custom function with the current entry:
                        'customFields' =&gt; getCustomFieldData($entry),
                    ];
                },
            ];
        }
    ]
];
</code></pre>
<p>This should return a structure that looks something like this:</p>
<pre class="lang-json prettyprint-override"><code>{
   &quot;data&quot;:[
      {
         &quot;id&quot;:&quot;28&quot;,
         &quot;title&quot;:&quot;The entry's title&quot;,
         &quot;url&quot;:&quot;http://domain.com/entry-url&quot;,
         &quot;customFields&quot;:{
            &quot;assets&quot;:[
               &quot;http://domain.com/assets/image.png&quot;,
               &quot;http://domain.com/assets/image-alt.png&quot;
            ],
            &quot;plainText&quot;:&quot;Some text&quot;
         }
      }
   ],
   &quot;meta&quot;:{
      &quot;pagination&quot;:{
         &quot;total&quot;:1,
         &quot;count&quot;:1,
         &quot;per_page&quot;:100,
         &quot;current_page&quot;:1,
         &quot;total_pages&quot;:1,
         &quot;links&quot;:{

         }
      }
   }
}
</code></pre>

CC BY-SA 4.0
2019-12-17T18:09:15.830Z
Thanks Oli. This is very helpful, but we are having trouble getting Matrix fields to work. Will respond more thoroughly when we have a solution.
Sean O'Brien
CC BY-SA 4.0
2019-12-18T10:05:12.470Z
What sort of trouble?
Oli
CC BY-SA 4.0
2020-01-27T14:08:01.733Z
@SeanO'Brien did you end up finding a solution for Matrix fields? I'm working on something similar and would appreciate you posting it.
Udo
CC BY-SA 4.0
2019-12-09T19:54:15.223Z
2023-02-06T21:44:59.397Z
2023-02-06T21:44:59.397Z
Oli
<p>How can we write Element API configuration files to dynamically output JSON payloads in Craft 3.3?</p>

<p>The goal is to create a generic function which only needs endpoint definitions to recursively loop through a data for a given section. This shortens development time because we don't have to manually define each field.</p>

<p>A departed member of our team did this with Craft 2.7, and we are having trouble duplicating the system for Craft 3.3. Has anyone done this for Craft 3.3? If so, do you have guidance or example code to share?</p>

CC BY-SA 4.0
2019-12-04T02:05:47.363Z
What you're describing sounds a lot like the functionality of the built-in [GraphQL server](https://docs.craftcms.com/v3/graphql.html)…?
August Miller
CC BY-SA 4.0
2019-12-04T02:09:18.747Z
Ohh, maybe I've misunderstood—you're talking about including all the fields + attributes of the Elements you're returning, without having to declare and type-cast each of them in the `transform` function?
August Miller
CC BY-SA 4.0
2019-12-04T13:13:58.097Z
That sounds right, @AugustMiller. Using GraphQL would still require specifying the details of each field. We want to automate that process so we only need to specify the Section details.
Sean O'Brien
CC BY-SA 4.0
2019-12-05T04:44:16.270Z
It may be worth just returning the entire Entry object, i.e. `function (Entry $entry) { return $entry }` to see what the native serializer does with the class + `ContentBehavior` trait—it may well be _too_ verbose for a JSON API… If all else fails, you could write some kind of Transformer class that iterates over `$entry->getAttributes()` and `$entry->getFieldValues()` and attempts to coerce each type of field/data into the proper format. I've done this for packing data into a CSV, but not for JSON—it becomes additionally challenging when you start dealing with relational fields.
August Miller
<plugin-elementapi>
Dynamic Element API
# Dynamic Element API

(Asked by: username_0 on 2019-12-04)

How can we write Element API configuration files to dynamically output JSON payloads in Craft 3.3?

The goal is to create a generic function which only needs endpoint definitions to recursively loop through a data for a given section. This shortens development time because we don't have to manually define each field.

A departed member of our team did this with Craft 2.7, and we are having trouble duplicating the system for Craft 3.3. Has anyone done this for Craft 3.3? If so, do you have guidance or example code to share?

**Tags:** plugin-elementapi

> **Comments:**
> **username_1**: What you're describing sounds a lot like the functionality of the built-in [GraphQL server](https://docs.craftcms.com/v3/graphql.html)…?  
> **username_1**: Ohh, maybe I've misunderstood—you're talking about including all the fields + attributes of the Elements you're returning, without having to declare and type-cast each of them in the `transform` function?  
> **username_0**: That sounds right, @AugustMiller. Using GraphQL would still require specifying the details of each field. We want to automate that process so we only need to specify the Section details.  
> **username_1**: It may be worth just returning the entire Entry object, i.e. `function (Entry $entry) { return $entry }` to see what the native serializer does with the class + `ContentBehavior` trait—it may well be _too_ verbose for a JSON API… If all else fails, you could write some kind of Transformer class that iterates over `$entry->getAttributes()` and `$entry->getFieldValues()` and attempts to coerce each type of field/data into the proper format. I've done this for packing data into a CSV, but not for JSON—it becomes additionally challenging when you start dealing with relational fields.  

---

## 1 answers

### Answer by username_2 on 2019-12-09. Score: 1

You can determine what custom fields are relevant to an entry through its field layout:
$layout = $entry->getFieldLayout();

A FieldLayout may contain more than just custom fields (like headings and native fields), but you can filter those “field layout elements” down to just the ones we care about:
$fields = $layout->getCustomFields();

(In Craft 3, this would just be $layout->getFields()!)
Then, you’ll need to loop over this array of Fields and pull values off of the original $entry. Here’s a function that puts this all together:
use craft\elements\Entry;

function getCustomFieldData(Entry $entry) {
    $values = [];

    // Fetch the entry’s field layout:
    /** @var craft\models\FieldLayout $layout */
    $layout = $entry->getFieldLayout();

    // Get all custom fields in the layout:
    $fields = $layout->getCustomFields();

    // Loop over the fields:
    foreach ($fields as $field) {
        // Stash a reference to the field’s handle:
        $handle = $field->handle;

        // Grab the field’s value:
        $value = $entry->getFieldValue($handle);

        // Check what field type this is, so we can deal with its value appropriately:
        switch (get_class($field)) {
            // Date + Time fields...
            case craft\fields\Date::class:
                $values[$handle] = $value->format(DateTime::ATOM);

                break;

            // Asset fields...
            case craft\fields\Assets::class:
                // Get an array of Assets by executing the element query:
                $values[$handle] = array_map(function ($asset) {
                    return $asset->url();
                }, $value->all());

                break;

            // Any other type of field we haven’t specifically handled...
            default:
                // ...just gets the raw value:
                $values[$handle] = $value;
        }
    }

    return $values;
}

This function can live in your element-api.php file, a Transformer, or a module or plugin.
Your endpoint might look something like this:
 [
        'api/.json' => function($entryId) {
            return [
                'resourceKey' => 'stories',
                'elementType' => Entry::class,
                'criteria' => [
                    'id' => $entryId
                ],
                'transformer' => function(Entry $entry) {
                    return [
                        'id' => $entry->id,
                        'title' => $entry->title,
                        'url' => $entry->url,
                        // From the transformer, call our custom function with the current entry:
                        'customFields' => getCustomFieldData($entry),
                    ];
                },
            ];
        }
    ]
];

This should return a structure that looks something like this:
{
   "data":[
      {
         "id":"28",
         "title":"The entry's title",
         "url":"http://domain.com/entry-url",
         "customFields":{
            "assets":[
               "http://domain.com/assets/image.png",
               "http://domain.com/assets/image-alt.png"
            ],
            "plainText":"Some text"
         }
      }
   ],
   "meta":{
      "pagination":{
         "total":1,
         "count":1,
         "per_page":100,
         "current_page":1,
         "total_pages":1,
         "links":{

         }
      }
   }
}

> **Comments:**
> **username_0**: Thanks username_2. This is very helpful, but we are having trouble getting Matrix fields to work. Will respond more thoroughly when we have a solution.  
> **username_2**: What sort of trouble?  
> **username_3**: @SeanO'Brien did you end up finding a solution for Matrix fields? I'm working on something similar and would appreciate you posting it.  

---

