Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlowQuery operations: incompatible check on first array element in canEvaluate #3624

Open
patricekaufmann opened this issue Feb 24, 2022 · 4 comments

Comments

@patricekaufmann
Copy link

patricekaufmann commented Feb 24, 2022

Description

When using references node properties, it is possible for the editors to delete the referenced elements, eg. a Neos.Neos:Document. The referenced identifier remains in the database as part of the property value.

Once the editor deletes the first referenced element, it leads to problems in most of the FlowQuery operations. The problem has its root in the canEvaluate function of most FlowQuery operations. Inside those functions, the first array element is accessed via $context[0]. This leads to problems when Neos filters the deleted elements as the array will not be re-indexed and therefore it's no longer safe to access the first element via [0]. The key might be different.

Affected Operations:

phpstorm64_2022-02-24_13-34-59

Steps to Reproduce

  1. Create references property and select 3 elements
  2. Delete the first element
  3. Use the references array of nodes in a FlowQuery operation

Expected behavior

FlowQuery operations should not perform checks in canEvaluate that are not relevant for the actual execution of the evaluate function. Most operations use a forEach loop in the evaluate function and don't rely on the first ([0]) element.

Workaround

Manually reindex the array by using Array.slice(array,0) in Fusion.

Possible Solution that requires PHP 7.3.0

It's possible to check the first element by the first key dynamically to bypass the problem of the values still being stored in the database:

    public function canEvaluate($context)
    {
        $firstKey = is_array($context) ? array_key_first($context) : null;
        return count($context) === 0 || (isset($firstKey) && isset($context[$firstKey]) && ($context[$firstKey] instanceof NodeInterface));
    }

Possible Solution that does not require PHP 7.3.0

This solution does not require PHP 7.3.0 - however it is not guaranteeed that the first element is checked because the pointer might point on a different item. From what I can tell, the ´evaluate´ method does not rely on the first element anyway.

    public function canEvaluate($context)
    {
        return count($context) === 0 || (current($context) instanceof NodeInterface);
    }
@mhsdesign
Copy link
Member

I think the [0] check was made to ducktype the array and it will assume, if the first (0) element is a node, all will be nodes. So current($context) might be okay? Neos 5.3 support will be ending soon, and then we only need to support php 7.3...

@mhsdesign
Copy link
Member

can we get the first $el via foreach and break?

@daniellienert
Copy link
Member

daniellienert commented May 4, 2022

I would suggest to change it to

return is_array($context) && reset($context) instanceof TraversableNodeInterface;

@mhsdesign
Copy link
Member

mhsdesign commented Nov 12, 2023

I think instead of fixing eel we should instead avoid returning non 0 indexed arrays:

${q(node).property("someReference")}

This might indeed return an array with holes like [1 => NODE, 2 => NODE, 5 => NODE] if the identifiers in fields 0, 3 and 4 are not resolvable.

Thats because of the "unsafe" array_filter method in resolvePropertyReferences

protected function resolvePropertyReferences(array $value = []): array
which will leave holes in the array.

Using array_filter was introduced with Neos 2.2 so this is technically a regression of 87804e1 ^^

I think its fine to say flowquery can only work properly on lists (0 indexed arrays) and we just need to be a bit more careful with that. This will not be a problem in Neos 9 and i would argue to rather fix Node::getProperty instead of flowquery like you proposed. I will close this stale pr for now #3946

My fix regarding Node::getProperty can be reviewed here: #4731

mhsdesign added a commit to mhsdesign/neos-development-collection that referenced this issue Nov 12, 2023
…eferences

EEL can only operate on an array of nodes if the `[0]` item is a node (simple duck typing)

Instead of fixing eel like proposed here neos#3946 with this fix we avoid returning non 0 indexed arrays here:

```
${q(node).property("someReference")}
```

Currently, this might indeed return an array with holes like `[1 => NODE, 2 => NODE, 5 => NODE]` if the identifiers in fields 0, 3 and 4 are not resolvable.

Thats because of the "unsafe" `array_filter` method in `resolvePropertyReferences` https://github.com/neos/neos-development-collection/blob/378a029d0cc7ea6acb853751e7592873584a4aac/Neos.ContentRepository/Classes/Domain/Model/Node.php#L961 which will leave holes in the array.

Using `array_filter` was introduced with Neos 2.2 so this is technically a regression of neos@87804e1 ^^
mhsdesign added a commit that referenced this issue Jan 31, 2024
…ArrayWithHolesForReferences

BUGFIX: #3624 Node::getProperty does not always return list for references
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants