Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-12-17T18:09:15.830Z
Answers.Text: 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.
Answers.UserDisplayName: Sean O'Brien
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-12-18T10:05:12.470Z
Answers.Text: What sort of trouble?
Answers.UserDisplayName: Oli
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-01-27T14:08:01.733Z
Answers.Text: @SeanO'Brien did you end up finding a solution for Matrix fields? I'm working on something similar and would appreciate you posting it.
Answers.UserDisplayName: Udo
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-12-09T19:54:15.223Z
Answers.LastActivityDate: 2023-02-06T21:44:59.397Z
Answers.LastEditDate: 2023-02-06T21:44:59.397Z
Answers.UserDisplayName: Oli
Body: <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>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-12-04T02:05:47.363Z
Comments.Text: What you're describing sounds a lot like the functionality of the built-in [GraphQL server](https://docs.craftcms.com/v3/graphql.html)…?
Comments.UserDisplayName: August Miller
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-12-04T02:09:18.747Z
Comments.Text: 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?
Comments.UserDisplayName: August Miller
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-12-04T13:13:58.097Z
Comments.Text: 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.
Comments.UserDisplayName: Sean O'Brien
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-12-05T04:44:16.270Z
Comments.Text: 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.
Comments.UserDisplayName: August Miller
Tags: <plugin-elementapi>
Title: Dynamic Element API
ThreadText: # 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, @username_1. 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*:
  
  ```php
  $layout = $entry->getFieldLayout();
  
  ```
  
  A [`FieldLayout`](https://docs.craftcms.com/api/v4/craft-models-fieldlayout.html) 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:
  
  ```php
  $fields = $layout->getCustomFields();
  
  ```
  
  (In Craft 3, this would just be `$layout->getFields()`!)
  
  Then, you’ll need to loop over this array of [`Field`](https://docs.craftcms.com/api/v4/craft-base-fieldinterface.html)s and pull values off of the original `$entry`. Here’s a function that puts this all together:
  
  ```php
  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](https://github.com/craftcms/element-api#transformer), or a [module or plugin](https://craftcms.com/docs/4.x/extend/).
  
  Your endpoint might look something like this:
  
  ```php
  <?php
  
  use craft\elements\Entry;
  
  // ...function, from above!
  
  return [
      'endpoints' => [
          'api/<entryId:\d+>.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:
  
  ```json
  {
     "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 Oli. 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**: @username_0 did you end up finding a solution for Matrix fields? I'm working on something similar and would appreciate you posting it.  
  
  ---
  
  