spicywebau / craft-neo

A Matrix-like field type for Craft CMS that uses existing fields
Other
402 stars 63 forks source link

When using Element API, not all child blocks are returned #708

Closed mike-moreau closed 1 year ago

mike-moreau commented 1 year ago

Bug Description

We are using a Neo field with blocks nested 3 to 4 levels.

When querying and looping over the blocks in a .twig template, the structure matches what we're seeing in the back end.

{% set structure = entry.blocks.all() %}
<ol>
    {% nav block in structure %}
        <li>
            Level #{{block.level}} - {{ block.type.handle }} (id = {{block.id}})
            {% ifchildren %}
                <ol>
                    {% children %}
                </ol>
            {% endifchildren %}
        </li>
    {% endnav %}
</ol>

output of the twig code above:

Screen Shot 2023-02-06 at 12 28 52 PM

However, when requesting a block and it's children with the Element API, not all of the child blocks are returned.

Here is a look at the API request for a block by its id:

// config/element-api.php
//...

'api/accordions/<id:\d+>' => function ($id) {
    return [
        'elementType' => Block::class,
        'cache' => false,
        'one' => true,
        'criteria' => [
            'id' => $id
        ],
        'transformer' => function (Block $block) {

            return [
                'id' => $block->id,
                'heading' => Transformer::heading($block->heading),
                'excerpt' => trim($block->excerpt),
                'key' => hash('sha256', Craft::$app->request->fullUri),
                'accordions' => $block->children->collect()->map(function ($item) use ($block) {
                    return [
                        'id' => $item->id,
                        'label' => trim($item->label),
                        'summary' => trim($item->summary),
                        'swatch' => $item->background->label === 'white'
                            ? array_merge(Transformer::swatch($item->background), ['class' => 'bg-white'])
                            : Transformer::swatch($item->background),
                        'content' => preg_replace('/\n\s+/', PHP_EOL, Craft::$app->view->renderTemplate('helpers/_blocks', [
                            'entry' => $block->owner,
                            'rootBackground' => $block->background,
                            'blocks' => $item->children->all()
                        ]))
                    ];
                })
            ];
        }
    ];
},

//...

But the JSON returned by this request only contains for first child block instead of all.

Screen Shot 2023-02-06 at 12 35 05 PM

Instead of child blocks an empty array is returned.

Steps to reproduce

  1. Make an Element API request for a block by it's id and attempt to retrieve all child blocks.

Expected behaviour

All child blocks would be returned by Element API.

Neo version

3.6.2

Craft CMS version

4.3.7.1

What is the affected Neo field's propagation method?

Only save blocks to the site they were created in.

Does this issue involve templating, and if so, is eager-loading used?

This is not a templating issue

mike-moreau commented 1 year ago

A note that a similar issue with an empty array being returned instead of child blocks with element API: https://github.com/spicywebau/craft-neo/issues/691

ttempleton commented 1 year ago

Thanks for bringing this to our attention @mike-moreau, I can reproduce this. It's related to the block belonging to multiple structures e.g. those created for provisional drafts, with the old structure element data not being deleted with the provisional draft for some reason. I'll look at a fix for that, but in the meantime, if possible could you please take a database backup, then run the following query (adding your table prefix if any) to remove the old data:

DELETE FROM structures
WHERE id in (
  SELECT structureId FROM structureelements se
  INNER JOIN neoblocks nb ON se.elementId = nb.id
)
AND id NOT IN (
  SELECT structureId FROM neoblockstructures
);

and let me know if the Element API request then works as expected?

mike-moreau commented 1 year ago

Thanks for the reply @ttempleton. Yes, running the query causes the Element API to work as expected. If an author adds new blocks to the entry, the same issues occurs.

Looking forward to a resolution, and happy to provide any other data that would be helpful.

ttempleton commented 1 year ago

Thanks @mike-moreau - I've registered that query to run with Craft's garbage collection, and made changes so that block structures belonging to provisional drafts get hard-deleted when saving the entry. Both of those changes are in Neo 3.6.3, and the issue should then be resolved after you run php craft gc/run. Please do let me know if you still have this issue with 3.6.3, though.

mike-moreau commented 1 year ago

@ttempleton I've updated to 3.6.3, re-saved the entry, run garbage collection and am not seeing what I expect.

The blocks output in blocks.twig and the blocks returned by the element api don't match up. The parent block id is the same but the children are different.

Screen Shot 2023-02-08 at 8 44 29 AM
ttempleton commented 1 year ago

Does the block's owner entry have any provisional drafts or other drafts that the block belongs to? If so, then you'll need to specify the ownerId in your block query to get the right descendant blocks, if the descendant blocks differ between the live entry and the draft.

If that's not the case, or if that doesn't resolve the issue, then if possible, could you please send your composer.json/lock and database backup to plugins@spicyweb.com.au, and we'll have a look at it.

mike-moreau commented 1 year ago

@ttempleton I'm not seeing any drafts that would be causing this.

I've sent an email to the address provided. Thanks for re-opening the issue. Let me know if I can provide any other info that would be helpful.

ttempleton commented 1 year ago

Thanks for sending that @mike-moreau. The block does belong to a provisional draft, created by a different user. However, setting ownerId on the criteria still doesn't return the correct data, due to the same bug as #682. Unfortunately, #682 is proving hard to fix without breaking other functionality, but I'm working on it and you can follow that issue for further progress.

ttempleton commented 1 year ago

I've made a change in Neo 3.6.4 which offers a workaround for your use case. If you add the ownerId and siteId to the block criteria along with the id, you should get the correct results.

mike-moreau commented 1 year ago

Thank you @ttempleton, really appreciate you looking into this! Confirming that the additional criteria do return the correct blocks.