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

TASK: Render menu prototypes without fluid #4623

Merged
merged 5 commits into from
Oct 26, 2023

Conversation

mficzel
Copy link
Member

@mficzel mficzel commented Oct 16, 2023

The prototypes Neos.Neos:Menu, Neos.Neos:DimensionsMenu and Neos.Neos:BreadcrumbMenu are now implemented in pure fusion. The prototype Neos.Neos:MenuItemListRenderer is added that is used by those prototypes to render the result as a very basic unordered list.

The option calculateItemStates is added to the prototypes Neos.Neos:MenuItems, Neos.Neos:DimensionsMenuItems and Neos.Neos:BreadcrumbMenuItems with the default value being false. Once enabled the itemState of each menuItem is calculated. While the calculation of itemState is not overly expensive the calculation depends on the current documentNode and this is only valid for the single document. This makes sharing menu caches between documents harder.

In addition the following issues are fixed:

  • DimensionMenuItems did not contain a url
  • MenuItems did not calculate item states other than normal
  • the signature of NodeAggregateIds::fromNodes(array $nodes) is changed to NodeAggregateIds::fromNodes(Nodes $nodes) and the method
    NodeAggregateIds::contain(NodeAggregateId $nodeAggregateId) is added. There were no users of this to be found so this should be safe.
  • RenderAttributesTrait will now use the value of backedEnums which allows to use Enums in Neos.Fusion:Tag.attributes and Neos.Fusion:Augmenter this was needed for the MenuItemListRenderer

Upgrade instructions

Since the Menus are no longer Templates the templatePathAndFilename was removed together with all state specific attribute rendering properties. You should adjust your rendering accordingly or create custom Menu prototypes that either have a renderer the suits your needs or are based on a Fluid Template again.

Checklist

  • Code follows the PSR-2 coding style
  • Tests have been created, run and adjusted as needed
  • The PR is created against the lowest maintained branch
  • Reviewer - PR Title is brief but complete and starts with FEATURE|TASK|BUGFIX
  • Reviewer - The first section explains the change briefly for change-logs
  • Reviewer - Breaking Changes are marked with !!! and have upgrade-instructions

@github-actions github-actions bot added the 9.0 label Oct 16, 2023
@mficzel mficzel force-pushed the task/renderMenusWithoutFluid branch 4 times, most recently from 9a9645c to 63c8ea7 Compare October 18, 2023 21:49
@mficzel
Copy link
Member Author

mficzel commented Oct 19, 2023

Have a question wether we really need the calculateItemState flag if the cost is merely a single findAncestors.

Also the findAncestors could be used for detecting the starting point aswell so we ideally would end up with 2 queries findAncestors and findSubtree for the generation of a menu.

@mficzel mficzel force-pushed the task/renderMenusWithoutFluid branch from 63c8ea7 to 9e33add Compare October 19, 2023 07:25
The prototypes `Neos.Neos:Menu`, `Neos.Neos:DimensionsMenu` and `Neos.Neos:BreadcrumbMenu` are now implemented in pure fusion.
The prototype `Neos.Neos:MenuItemListRenderer` is added that is used by those prototypes to render the result as a very basic unordered list.

The flag `calculateItemStates` is added to the prototypes `Neos.Neos:MenuItems`, `Neos.Neos:DimensionsMenuItems` and `Neos.Neos:BreadcrumbMenuItems` with the default value being false. Once enabled the itemState of each menuItem is calculated.

In addition the following issues are fixed:
- DimensionMenuItems did not contain a url
- MenuItems did not calculate item states.
@mficzel mficzel force-pushed the task/renderMenusWithoutFluid branch from 9e33add to 8e04b9a Compare October 19, 2023 07:29
@mhsdesign
Copy link
Member

Have a question wether we really need the calculateItemState flag if the cost is merely a single findAncestors.

Good point indeed. Hmm we seem to need the ancestors to highlight the trace:

image

But when we would use findSubtree as you suggested, which we dont do at the moment? The ancestor information would ship free house as its the parent in the tree ... in that case its no expense at all to calculate and we can drop the flag without doubt.

But i think that @bwaidelich is currently creating test cases for the menu so that we can refactor it to use findSubtree - so thats on the list for a next pr.

@mficzel
Copy link
Member Author

mficzel commented Oct 19, 2023

But i think that @bwaidelich is currently creating test cases for the menu so that we can refactor it to use findSubtree - so thats on the list for a next pr.

@mhsdesign that is a good point and could help to keep this pr in scope. I thought about refactoring this now but it makes more sense with tests.

Regarding the calculateItemState i think it makes sense to have and to disable it by default because those states make sharing of menu caches between documents more "interesting".

@mficzel mficzel marked this pull request as ready for review October 19, 2023 18:20
@mficzel mficzel changed the title WIP TASK: Render menu prototypes without fluid TASK: Render menu prototypes without fluid Oct 19, 2023
@github-actions github-actions bot added the Task label Oct 19, 2023
@mficzel
Copy link
Member Author

mficzel commented Oct 19, 2023

But when we would use findSubtree as you suggested, which we dont do at the moment? The ancestor information would ship free house as its the parent in the tree ... in that case its no expense at all to calculate and we can drop the flag without doubt.

I think first should be a findAncestors from currentDocument up. Needed to determine the starting point via entryLevel. From there a single findSubtree should be enough. 2 queries for a menu sounds quite ok to me.

Copy link
Member

@bwaidelich bwaidelich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, this looks and feels so much nicer already! <3

A couple of thoughts (in addition to the - mostly nitpicky – comments):

We should always have large sites in the back of our heads. Even if we only load document ancestor nodes, those can be tens of thousands so we should not load them into memory (Neos already has quite an excessive RAM usage).

So calculating the active state will probably have to be calculated for every item in the menu.

But IMO the bigger issue is that this results in an individual cache entry for every menu for every page which – in turn – means that you'll basically have to flush the whole content cache if you change anything in the menu implementation.

That's why I like the calculateItemState prop and IMO we could even make the @cache configuration depend on it, such that we create a common cache entry if it is disabled

Neos.Neos/Classes/Fusion/DimensionMenuItem.php Outdated Show resolved Hide resolved
Neos.Neos/Classes/Fusion/MenuItem.php Outdated Show resolved Hide resolved
Neos.Neos/Classes/Fusion/MenuItemsImplementation.php Outdated Show resolved Hide resolved
@mficzel
Copy link
Member Author

mficzel commented Oct 20, 2023

Short note. After this is merged and with tests for menu items in place we should try to cleanup the dependencies between the MenuItems prototypes (that all are derived from MenuItems).

I would like all *MenuItems prototypes to be standalone fusion prototypes with an implementation class.

@bwaidelich
Copy link
Member

After this is merged and with tests for menu items in place we should try to cleanup the dependencies between the MenuItems prototypes

Yes, great idea! And we could use that chance to replace the MenuItemState class with a proper enum (and potentially remove the absent case that should IMO never happen)

@mficzel
Copy link
Member Author

mficzel commented Oct 20, 2023

Absent is a state that can happen in dimension menus. Using enums is a good idea we can aswell implement here.

mficzel and others added 3 commits October 20, 2023 16:19
… nodes

This is more memory efficient and we only compare the AggregateIds anyways

In addition the signature of `NodeAggregateIds::fromNodes(array $nodes)` is changed to `NodeAggregateIds::fromNodes(Nodes $nodes)` and the method
`NodeAggregateIds::contain(NodeAggregateId $nodeAggregateId)` is added. There were no users of this to be found so this should be safe.
@mficzel mficzel force-pushed the task/renderMenusWithoutFluid branch 2 times, most recently from 850ea62 to d2ebddb Compare October 20, 2023 15:29
…mState to a BackedEnum

In addition the RenderAttributesTrait was adjusted to handle backed enums like stringables.
@mficzel mficzel force-pushed the task/renderMenusWithoutFluid branch from d2ebddb to 300f84f Compare October 20, 2023 15:37
@mficzel
Copy link
Member Author

mficzel commented Oct 20, 2023

@bwaidelich i think your issues are addressed. We could think about backporting the backedEnum in attributes support to 8.3 but i would decide upon that later.

@bwaidelich
Copy link
Member

i think your issues are addressed

@mficzel you're the man, this looks awesome!

Just one last thing for now (and we can also postpone it to a separate PR): Do you think it makes sense to make the @cache config dependent on the calculateItemState flag such that the same cache entry can be shared?

@mficzel
Copy link
Member Author

mficzel commented Oct 20, 2023

@bwaidelich not safely as entryLevel -x also depends on the current document

would try that separately

Copy link
Member

@bwaidelich bwaidelich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it! +1 by reading

@mhsdesign
Copy link
Member

I like to wait with merges until Bastians tests are ready ;)

@mficzel
Copy link
Member Author

mficzel commented Oct 22, 2023

@mhsdesign this changes nothing that was not broken/missing before. Other than the renderers only the state calculation and uris for the dimension menu were added again. By waiting we will only make this more complex by fixing more stuff in a single pr.

@bwaidelich
Copy link
Member

I like to wait with merges until Bastians tests are ready ;)

For me it's the other way around.
I would suggest to merge this one ASAP so that I can rebase the test-PR on it.
The tests will fail because MenuItemsImplementation still behaves differently than < Neos 9. But we can then start to rewrite it (replacing nasty array juggling with Subgraph queries along the way) in that PR until the tests are green

Copy link
Member

@bwaidelich bwaidelich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's potentially some follow up work to be done (backports, more tests, cache mode tricks to allow reusability) but this can all be done in separate PRs.

I really love this, thanks for taking care! <3

Copy link
Member

@kitsunet kitsunet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Amazing :)

documentNode = ${Neos.Caching.entryIdentifierForNode(documentNode)}
}
entryTags {
1 = ${Neos.Caching.nodeTypeTag('Neos.Neos:Document', documentNode)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we flush on any document change and not just the current one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because any document below the starting point could require invalidation.

So descendantOfTag with the startingPoint would be the right choice but that collides with this entry level magic. Also it would invalidate on any content change below.

I really think we should make startingPoint a requirement with default ${site} but that is for another pr.

documentNode = ${Neos.Caching.entryIdentifierForNode(documentNode)}
}
entryTags {
1 = ${Neos.Caching.nodeTypeTag('Neos.Neos:Document', documentNode)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we only include the document nodes parents for flushing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already flush for all documents

Copy link
Member

@Sebobo Sebobo Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this way the DimensionsMenu and the Breadcrumb of every site is flushed when any document is changed in the website...

So IMO just including parents and the current node for both cases would solve both and reduce the flushing by a lot

Copy link
Member Author

@mficzel mficzel Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided not to do this as this pr is about de-fluidizing not optimizing. Before the prototype inherited the cache annotations of menu which flush for all documents.

There are plans to optimize the menus further so this is only the first step. This pr should not make anything worse but as it is but also should not need to fix more than it promises.

Would like to get this merged to look into the optimizing topic without piling too many changes upon each other.

Copy link
Member

@mhsdesign mhsdesign left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to merge this one ASAP so that I can rebase the test-PR on it.

yes i agree as discussed ;)

Copy link
Member

@Sebobo Sebobo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now. Just hope the caching topic for this case doesn't get lost. Performance is really important and didn't get enough attention in the past, especially if we want to intrigue our users with the performance of the new CR.

@bwaidelich
Copy link
Member

Just hope the caching topic for this case doesn't get lost

I agree!
We focused on a 1:1 replacement for now, but the new implementation already does not calculate the item state per menu which will allow for a single reusable cache entry

@mhsdesign mhsdesign merged commit f271bd3 into neos:9.0 Oct 26, 2023
5 checks passed
ahaeslich added a commit to ahaeslich/neos-development-collection that referenced this pull request Nov 9, 2023
Adjust rendering to match previous fluid based output. Remove unused fluid template.

Relates: neos#4537
Relates: neos#4623
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

6 participants