Cms: Converting Collection to JSON results in infinite augmentation

Created on 22 Jan 2021  路  14Comments  路  Source: statamic/cms

Bug Description

I am attempting to pass through a collection (pages) that has an 'entries' field type within called 'featured_projects'.
These entries are projects which also contain 'related_projects'.
This is resulting in an infinite loop and causing Statamic to crash.

How to Reproduce

<home-controller data='{{ collection:pages as="page"}}{{ page | to_json | entities }}{{ /collection:pages }}'></home-controller>
Have an entries field type in which one of the entries inside has another entries field type that links back to the original.

Extra Detail

Environment

**Statamic version: Latest 3.0.40

PHP version: 7.4

Install method (choose one):

  • Fresh install from statamic/statamic

Most helpful comment

You had me at coffee.

All 14 comments

If one entry is linking to the other and vice versa, an infinite loop would be expected.

Do you need all the fields in the entry?

This issue is related to https://github.com/statamic/cms/issues/2855. I think the problem could be solved by only augmenting the entries for a specified amount of times (default 1 level) in the json modifier (after specified amount of times it'll do a shallow augment). This would then have to trickle down into the Augmentable interface.

We have the "shallow" augmentation feature for exactly this reason. It's just that the to_json modifier doesn't use it. Hang tight I'll come up with an example for you.

You guys have coordinated on the reactions really well for this issue lol

Yes mate and there are multiple coffees involved if you can supply a solution haha :D

You had me at coffee.

php please make:modifier ToShallowAugmentedJson

<?php

namespace App\Modifiers;

use Statamic\Modifiers\Modifier;

class ToShallowAugmentedJson extends Modifier
{
    public function index($entries)
    {
        return $entries->map(function ($entry) {
            return $entry
                ->toAugmentedCollection()
                ->withShallowNesting()
                ->toArray();
        })->toJson();
    }
}
{{ collection:pages as="pages"}}
    {{ pages | to_shallow_augmented_json | entities }}
{{ /collection:pages }}

You deserved the coffee and a break for sure... 馃槃

Would it be possible to have that logic in the core json modifier? Something along the lines of to_json:shallow or comparable?

Thanks Jason this works really well - is it possible in any shape or form for it to get augment the image data?
This looks very similar to the data returned from the API now. Assets are just objects with URL's in etc but no meta data.

We could include it in the core, I suppose. But as soon as you want something custom - and it'll happen - see drikish immediately asked for something different 馃槃 - you need a different solution.

If you know what fields are important to you, you can pass them in, and _don't_ ask for shallow nesting.

        return $entries->map(function ($entry) {
            return $entry
                ->toAugmentedCollection(['title', 'images'])
                ->toArray();
        })->toJson();

In this example, only title and images (an asset field) will be output, and you'll get the full data of the assets.

So far I've set the selectedQueryColumns on every entry so the to_json modifier wouldn't augment everything, but that's almost the same as the things you're suggesting above. I guess if we write some more documentation about it could also help, but having some way that prevents this by default would be really nice. Maybe we could do something with the ->withShallowNesting() instead?

For anyone else who wants this solution but to also allow you to get the extra augmented data for things like images:

class ToShallowAugmentedJson extends Modifier
{
  public function index($entries)
  {
    return $entries->map(function ($entry) {
      $shallow = $entry
      ->toAugmentedCollection()
      ->withShallowNesting()
      ->toArray();

      $assetFields = $entry->blueprint()->fields()->all()->filter(function ($field) {
        return $field->fieldtype()->handle() == 'assets';
      })->keys()->all();

      if (empty($assetFields)) {
        return $shallow;
      }

      return array_merge($shallow, $entry->toAugmentedCollection($assetFields)->toArray());
    })->toJson();
  }
}

Closing this. If having this in the core is really important to you, we'll take a look at a PR. Otherwise you can just drop in a modifier using the snippets in this thread.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sandervanh picture sandervanh  路  4Comments

austenc picture austenc  路  3Comments

filipac picture filipac  路  4Comments

jimblue picture jimblue  路  3Comments

riasvdv picture riasvdv  路  4Comments