neos-ui
neos-ui copied to clipboard
!!! FEATURE: Overhaul `NodeCreationHandlerInterface`
Neos.Neos adjustment https://github.com/neos/neos-development-collection/pull/4630
Replaces: https://github.com/neos/neos-ui/pull/3720 for Neos9 Resolves: https://github.com/neos/neos-ui/issues/3615 Resolves: https://github.com/neos/neos-ui/issues/3509
The NodeCreationHandlerInterface
Contract to hook into the process before the node creation command is handled by the content repository
You can add additional steps to the node creation.
For example adding initial properties NodeCreationCommands::withInitialPropertyValues()
,
or queuing additional commands like to create a child via NodeCreationCommands::withAdditionalCommands()
The node creation handlers factory can be registered on a NodeType:
Vendor.Site:Content:
options:
nodeCreationHandlers:
myHandler:
factoryClassName: 'Vendor\Site\MyHandlerFactory'
position: end
The factory must implement the ContentRepositoryServiceFactoryInterface
and
return an implementation with this NodeCreationHandlerInterface
interface.
The current content-repository or NodeType-manager will be accessible via the factory dependencies.
The new signature of the interface is as follows:
interface NodeCreationHandlerInterface extends ContentRepositoryServiceInterface
{
/**
* @param NodeCreationCommands $commands original or previous commands,
* with the first command being the initial intended node creation
* @param NodeCreationElements $elements incoming data from the creationDialog
* @return NodeCreationCommands the enriched node creation commands,
* to be passed to the next handler or run at the end
*/
public function handle(NodeCreationCommands $commands, NodeCreationElements $elements): NodeCreationCommands;
}
Additionally the interface was relocated to Neos\Neos\Ui\Domain\NodeCreation\NodeCreationHandlerInterface
.
The NodeCreationCommands
A collection of commands that describe a node creation from the Neos Ui. The node creation can be enriched via a node creation handler. The first command points to the triggered node creation command. To not contradict the users intend it is ensured that the initial node creation will be mostly preserved by only allowing to add additional properties. Additional commands can be also appended, to be run after the initial node creation command. All commands will be executed blocking. You can retrieve the subgraph or the parent node (where the first node will be created in) the following way:
$subgraph = $contentRepository->getContentGraph()->getSubgraph(
$commands->first->contentStreamId,
$commands->first->originDimensionSpacePoint->toDimensionSpacePoint(),
VisibilityConstraints::frontend()
);
$parentNode = $subgraph->findNodeById($commands->first->parentNodeAggregateId);
The NodeCreationElements
Holds the deserialized elements of the submitted node creation dialog form
Elements are configured like properties or references in the schema, but its up to the node-creation-handler if they are handled in any way or just left out.
Elements that are of simple types or objects, will be available according to its type. For example myImage will be an actual image object instance.
Vendor.Site:Content:
ui:
creationDialog:
elements:
myString:
type: string
myImage:
type: Neos\Media\Domain\Model\ImageInterface
Elements that refer to nodes are of type references
or reference
.
They will be available as NodeAggregateIds collection.
Vendor.Site:Content:
ui:
creationDialog:
elements:
myReferences:
type: references
The naming references
in the element
configuration does not refer to the content repository reference edges.
Referring to a node will just denote that an editor will be used capable of returning node ids.
The node ids might be used for setting references but that is up to a node-creation-handler.
To promoted properties / references the same rules apply:
Vendor.Site:Content:
properties:
myString:
type: string
ui:
showInCreationDialog: true
@bwaidelich suggested, that instead of passing the ContentRepository
into the NodeCreationHandlerInterface::handle()
we could also consider using the new domain services from the ESCR.
Those domain services can be build via ContentRepositoryRegistry::getService($crId, $serviceFactory)
https://github.com/neos/neos-development-collection/blob/f3d553315045c23b9deb7d9b897c84e39d399305/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php#L107
This would in turn mean, that the NodeCreationHandlerInterface
would extend the ContentRepositoryServiceInterface
(the domain service)
interface NodeCreationHandlerInterface extends ContentRepositoryServiceInterface
{
public function handle(NodeCreationCommands $commands, array $data): NodeCreationCommands;
}
and we wouldn't register the NodeCreationHandlerInterface
directly in the configuration anymore, but its factory (the above mentioned $serviceFactory
):
'Neos.Neos:Content':
options:
nodeCreationHandlers:
fooBar:
factoryClassName: 'Some\NodeCreationHandlerFactoryInterface'
the factory would look like:
interface NodeCreationHandlerFactoryInterface extends ContentRepositoryServiceFactoryInterface
{
public function build(ContentRepositoryServiceFactoryDependencies $serviceFactoryDependencies): NodeCreationHandlerInterface;
}
and the $serviceFactoryDependencies
would hold among others the ContentRepository:
https://github.com/neos/neos-development-collection/blob/01f7f8f6e5d7daa50a8bc68bdc280ddf3ccdb7f9/Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryServiceFactoryDependencies.php#L45
🎥 End-to-End Test Recordings
These videos demonstrate the end-to-end tests for the changes in this pull request.
Thank you so much for your kind words and the extensive feedback! ❤️
I could answer a few questions already:
It is even possible to replace the original intent of creating a node of the selected node type (correct me if I'm wrong).
It is technically, but 100% marked as footgun and not to do, as it contradicts the intention, and will crash if the ui cant find the node id afterwards ;)
This is by no means a problem. I rather think that it points us towards a more generic task-based interface, that is not NodeType-centric.
Hmm i think as well that this would be moving to fast and maybe we can implement this for a NodeCreationDialog
version 3
For now i would say strong coupling to the content repository (via the cr services https://github.com/neos/neos-ui/pull/3519#discussion_r1513491762) is already a good step forward.
As discussed with Wilhelm and Christian we will make the node creation handlers internal for now, as there is not much demand for the api and the main consumers are the NeosUi itself or Flowpack.NodeTemplates. Currently its not worth to draft out the perfect api and declare it as such, even though the implementation will unlikely change in the next time (hopefully ^^)
Additionally using the ContentRepositoryServiceInterface
as discussed in https://github.com/neos/neos-ui/pull/3519#issuecomment-1584046648 was reverted in favour of a much simpler factory interface which will not leak the hardcore ContentRepositoryServiceFactoryDependencies
:
interface NodeCreationHandlerFactoryInterface
{
public function build(ContentRepository $contentRepository): NodeCreationHandlerInterface;
}
but as its internal now either way, it shouldnt matter too much ^^