It’s time to announce our latest Neos 9 Beta.
It contains many bugfixes, big refactorings and great new features.
Thanks to all who contributed <3.
- Bernhard Schmitt
- Sebastian Kurfürst
- Denny Lubitz
- Simon Krull
- Bastian Waidelich
- Christian Müller
- Marc Henry Schultz
Upgrade instructions from Beta 11 / Beta 13
previous beta → Neos 9 Beta-11 Release
In your composer.json you should require 9.0.0-beta14
for all Neos packages from the development distribution and for the NeosUi as well as Flow:
"neos/flow": "9.0.0-beta14",
"neos/neos": "9.0.0-beta14",
"neos/neos-ui": "9.0.0-beta14"
The database can be migrated the following way:
A setup to apply the latest schema adjustments can be run via (feel free to inspect via cr:status
what will be adjusted)
./flow doctrine:migrate
./flow cr:setup
Additionally, PRs marked with ⛁ might need a dedicated migration.
- ⛁¹ Migration for creating missing Neos.Neos metadata and role assignments for the workspace
./flow migrateevents:migrateWorkspaceMetadataToWorkspaceService
Features
!!! 1.) Highlight Workspaces with metadata and role assignment
- ⛁¹ !!! FEATURE: Extract workspace metadata and user-assignment to Neos
- FEATURE: Deprecate
WorkspaceFinder
and introduce new API - BUGFIX: Fix workspace role assignment after migration, import and site creation
- TASK: Remove
workspaceTitle
,workspaceDescription
andworkspaceOwner
use from behat core tests - TASK: Adjust to extract workspace metadata and user-assignment to Neos
Introduction of Neos.Neos workspace metadata and user-assignment
A new WorkspaceService
was introduced as central authority to manage Neos workspaces (Neos\Neos\Domain\Service\WorkspaceService
).
With new features and possibilities that could not be part of the content repository core, setting the first step for security and policies in Neos 9.
The new service handles metadata like workspace title, description as well as owner assignments and new assignments of individual users to a workspace or also flow roles.
Following new commands were introduced to work with workspaces:
./flow workspace:createroot
: Create a new root workspace for a content repository./flow workspace:createpersonal
: Create a new personal workspace for the specified user./flow workspace:createshared
: Create a new shared workspace./flow workspace:settitle
: Set/change the title of a workspace./flow workspace:setdescription
: Set/change the description of a workspace./flow workspace:assignrole
: Assign a workspace role to the given user/user group./flow workspace:unassignrole
: Unassign a workspace role from the given user/user group./flow workspace:show
: Display details for the specified workspace
Fetching the user workspace
The previous ways of retrieving a workspace or a building a workspace name are replaced:
- Neos 8.3:
Neos\Neos\Utility\User::getPersonalWorkspaceNameForUsername()
Neos\Neos\Domain\Service\WorkspaceNameBuilder::fromAccountIdentifier()
WorkspaceFinder::getPersonalWorkspaceForUser()
Instead, the new WokspaceService
must be used:
+ #[Flow\Inject]
+ protected WorkspaceService $workspaceService;
+
+ $user = $this->userService->getBackendUser();
+ $this->workspaceService->getPersonalWorkspaceForUser($contentRepositoryId, $user->getId());
Deprecation of the WorkspaceFinder
(removal with next beta)
Using WorkspaceFinder
(retrieved via ContentRepository::getWorkspaceFinder
) to fetch workspaces is deprecated as a new API will fully replace it in the next beta.
- $contentRepository->getWorkspaceFinder()->findOneByName($workspaceName);
+ $contentRepository->findWorkspaceByName($workspaceName);
- $contentRepository->getWorkspaceFinder()->findAll();
+ $contentRepository->findWorkspaces();
- $contentRepository->getWorkspaceFinder()->findByBaseWorkspace($workspaceName);
+ $contentRepository->findWorkspaces()->getDependantWorkspaces($workspaceName);
- $contentRepository->getWorkspaceFinder()->findOutdated();
+ $contentRepository->getWorkspaces()->filter(
+ fn (Workspace $workspace) => $workspace->status === WorkspaceStatus::OUTDATED
+ );
The use of WorkspaceFinder::findOneByCurrentContentStreamId
is discouraged, and it should just be operated on workspace names instead.
If deemed necessary it still can be archived by filtering all workspaces:
- $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId();
+ $contentRepository->getWorkspaces()->find(
+ fn (Workspace $workspace) => $workspace->currentContentStreamId->equals($contentStreamId)
+ );
Deprecation of RenameWorkspace
and ChangeWorkspaceOwner
commands (removal with next beta)
please use WorkspaceService::setWorkspaceTitle
and setWorkspaceDescription
instead. Changing the owner is not possible but you grant a user access via assignWorkspaceRole
.
Deprecation of title
, description
and owner
in CreateWorkspace
and CreateRootWorkspace
(removal of fields with next beta)
To create a root, shared or user workspace, the WorkspaceService
should be leveraged instead:
WorkspaceService::createRootWorkspace()
WorkspaceService::createPersonalWorkspace()
WorkspaceService::createSharedWorkspace()
To assign a role like Neos.Neos:LivePublisher
or Neos.Neos:AbstractEditor
to the workspace look into assignWorkspaceRole
. The permission will be evaluated fully with the next beta.
$this->workspaceService->assignWorkspaceRole($contentRepositoryId, $workspaceName, WorkspaceRoleAssignment::createForGroup(
'Neos.Neos:AbstractEditor',
WorkspaceRole::COLLABORATOR,
));
For granting a user access to a workspace WorkspaceRoleAssignment::createForUser
can be used.
Refactoring of Workspace
“active record” model
A WorkspacePublishingService
was introduced as replacement for the current Neos 9 Workspace
model as well as the WorkspaceProvider
.
Publishing via Workspace::publishChangesInSite()
can be replaced with WorkspacePublishingService::publishChangesInSite()
and other use cases as well.
!!! 2.) Overhauled Node field access in Fusion
- TASK Deprecate overhauled temporary FlowQueries and use node field access instead
- !!! TASK: Introduce streamlined
Neos.Node.nodeType(node)
and deprecateNeos.Node.getNodeType(node)
- !!! TASK: Remove leftovers from magic underscore access for internal node properties
- TASK: Make single-field Value Objects stringable
String-able aggregateId
, nodeTypeName
, … for improved use in Fusion
To simplify node field access of string value objects in Fusion we made the following fields automatically string casting (using php’s __toString
)
contentRepositoryId
workspaceName
aggregateId
nodeTypeName
name
That means one does not need to specify .value
on those fields anymore:
- ${node.name.value}
+ ${node.name}
Deprecation of FlowQuery accessors id()
nodeTypeName()
and label()
(old syntax removal with next beta)
With the node fields being string able, the need for dedicated FlowQueries to simplify the casting of .value
is obsolete.
Its recommended to just leverage the Node fields directly. The FlowQuery helpers will be removed with the next beta.
- ${node.identifier} // 8.3
- ${q(node).id()}
+ ${node.aggregateId}
- ${node.nodeType.name} // 8.3
- ${q(node).nodeTypeName()}
+ ${node.nodeTypeName}
Additionally, our rethought philosophy being: Use FlowQuery for traversal and Helpers for direct access, the label retrieval of Nodes is moved to a Helper Neos.Node.label()
:
- ${node.label} // 8.3
- ${q(node).label()}
+ ${Neos.Node.label(node)}
Rename of helper Neos.Node.getNodeType()
(old syntax removal with next beta)
Also, the previous getNodeType()
would return a NodeType of Neos.Neos:FallbackNode
while nodeType()
returns NULL
for non-existing NodeTypes.
To check if a NodeType exists - if really necessary - Neos.Node.isNodeTypeExistent()
could be leveraged although being @internal
.
- ${node.nodeType} // 8.3
- ${Neos.Node.getNodeType(node)}
+ ${Neos.Node.nodeType(node)}
Full removal of node magic underscore _*
access: q(node).property('_identifier')
In Neos 8 the _*
underscore access syntax can be used to access actual php object fields of the Node.
With the Neos 9 rewrite the Node object looks completely different and has way less getters / fields (comparison).
The only overlap of fields to the old node are _name
and the unknown _nodeTypeName
.
To avoid confusion about which magic underscore properties still exist in 9.0 - as rule of thumb - None. The syntax will be removed.
Special cases that had yet no equivalent were:
- Access the Node’s
_hidden
state can be now done viaNeos.Node.isDisabled(node)
- The sort operation handled in 8.3 also
_creationDateTime
,_lastModificationDateTime
and_lastPublicationDateTime
. For Neos 9.0 the newsortByTimestamp(created|lastModified|originalCreated|originalLastModified)
FlowQuery can be used.
Simple cases:
- ${q(node).property('_identifier')}
+ ${node.aggregateId}
- ${q(node).property('_name')}
+ ${node.name}
The following information is not directly extractable from the Node in EEL, and thus we use the Neos.Node
helper.
- ${q(node).property('_nodeType')}
+ ${Neos.Node.nodeType(node)}
- ${q(node).property('_hidden')}
+ ${Neos.Node.isDisabled(node)}
- ${q(node).property('_path')}
+ ${Neos.Node.path(node)} // @deprecated
- ${q(node).property('_depth')}
+ ${Neos.Node.depth(node)} // @deprecated
A special cases is fetching the path to render a content collection which can be simplified:
content = Neos.Neos:ContentCollection {
- nodePath = ${q(site).children('footer').property('_path')}
+ @context.node = ${q(site).children('footer').get(0)}
}
Partial deprecation of FlowQuery q(node).property("title")
The property operation was the recommended way in Neos 8 to access properties of a Node. With Neos 9 simple cases of accessing properties can be done with node.properties.title
instead as properties
don’t contain references anymore and the performance overhead is gone:
- ${q(node).property("title")}
+ ${node.properties.title}
For dynamic access:
- ${q(node).properties(props.propertyName)}
+ ${node.properties[props.propertyName]}
For references - where the property
operation implements a thin b/c layer - its recommend to be explicit and use the new referenceNodes
operation instead:
- ${q(node).properties("someReferenceName")}
+ ${q(node).referenceNodes("someReferenceName").get(0)}
For accessing a property in a FlowQuery chain - e.g. after fetching the parent - using the property
operation is still the recommended way.
!!! 3.) Refactoring of the ControllerInterface
and dispatching in FLOW.
- !!! TASK: Remove dispatched state from ActionRequest
- !!! FEATURE: Dispatcher psr overhaul
- !!! TASK:
Mvc\Dispatcher::afterControllerInvocation
will not be called for CLI requests anymore. - TASK: Adjust to Flow Controller changes
To ease the implementation of a custom controller now use a direct pattern of f(ActionRequest) => PsrResponseInterface
.
This is breaking if you implemented your own ControllerInterface
or overwrote low level parts and methods of the ActionController
.
class Dispatcher
{
public function dispatch(ActionRequest $request): ResponseInterface;
}
interface ControllerInterface
{
public function processRequest(ActionRequest $request): ResponseInterface;
}
Also, it’s discouraged to manipulate $this->response
in controllers (the ActionResponse
is deprecated), although it’s still supported in actions for now, please consider returning a new response instead.
Explanation of the legacy MVC response object.
Previously Flows MVC needed a single mutable response which was passed from dispatcher to controllers and even further to the view and other places via the controller context: `ControllerContext::getResponse()`. This allowed to manipulate the response at every place. With the dispatcher and controllers now directly returning a response, the mutability is no longer required. Additionally, the abstraction offers naturally nothing, that cant be archived by the psr response, as it directly translates to one: `ActionResponse::buildHttpResponse()`. So you can and should use the immutable psr {@see ResponseInterface} instead where-ever possible. For backwards compatibility, each controller will might now manage an own instance of the action response via `$this->response` `AbstractController::$response` and pass it along to places. But this behaviour is deprecated! Instead, you can directly return a PSR repose `\GuzzleHttp\Psr7\Response` from a controller:public function myAction(): ResponseInterface
{
return (new Response(status: 200, body: $output))
->withAddedHeader('X-My-Header', 'foo');
}
Further the ForwardException
does not extend the StopActionException
anymore, meaning a try catch must be adjusted.
4.) Overhaul content cache flusher
- FEATURE: Overhaul ContentCacheFlusher
- TASK: Improve performance on content cache flush
- BUGFIX: Flush content cache on rebase workspace
- BUGFIX: Handle non-existing workspaces gracefully
- BUGFIX: Improve performance on find*Aggregates
The content cache flusher got a tremendous overhaul fixing several problems:
- the flush is also executed on rebase and further edge cases
- partially publishing does not lead to dead cache entries and cache flooding
- performance of the used sql queries was improved resulting in a faster replay
5.) Simplification of Runtime caches
- FEATURE: extract content subgraph inmemory caching
- BUGFIX: Mark projection as stale after applying an event.
- TASK: 5243 remove ContentGraphFinder runtime cache
Previously, ContentGraph::getSubgraph()
returned an instance of ContentSubgraphWithRuntimeCaches
to increase performance of multiple reads in the same process.
Caching database access on such a low level has drawbacks and this caching layer was extracted to ContentRepositoryRegistry::subgraphForNode()
.
This allowed the getContentGraph()
cache to be removed.
7.) Rewrite asset usage as CatchupHook
Overhauls the latest “asset usage” implementation being a dedicated projection.
It was not handling all necessary events and thus prone to bugs.
A rewrite to a catchup hook stabilizes this feature.
The index can be build individually - which might be required after updating and not running a full replay:
./flow assetusage:index --contentrepository default
6.) Other refactorings / improvements
- BUGFIX: Restore
Neos.Neos:NodeUri
,neos:link.node
andLinkingService
- TASK: Add indices to improve parent/child query performance
- TASK: Improve error messages in case old transformations have been used
- FEATURE: Add UpdateRootNodeAggregateDimensions as transformation & TASK: Remove moveDimensionSpacePoint and refreshRootNodeDimensions command actions
- TASK: Split up EmbedsContentStreamAndNodeAggregateId interface
Bugfixes
Neos Content Repository
- BUGFIX: Prevent augmenter from applying data of multiple nodes into the same element
- BUGFIX: 5265 - Ignore null values in SearchTermMatcher
- BUGFIX: #5234 Display site-name on neos login view
- BUGFIX: Remove default value for subtreetags to fix MySQL support
Flow
Dev only relevant
List of things
Neos
- TASK: Streamline value object mapping usages
- TASK: Check against LiveWorkspaceName Instead Of Comparing CS Ids
- TASK: Remove obsolete content cache flusher unit test
- TASK: Improve JSON error handling
- TASK: remove ui script tag neos 9 followup
- TASK: Update GitHub actions
- TASK: Add and enforce
strict_type
declarations - TASK: Rename parent nodes to ancestor nodes and improve ancestor determination
- TASK: Streamline workspacename fields
Neos Ui 9.0
- TASK: Adjust to Add workspace content stream mapping to content graph projection
- TASK: Dont use react inside
terminateDueToFatalInitializationError
- TASK: remove ui script tag neos 9 followup
Neos Ui 8.4
Known issues
Not working ACL for workspaces without metadata
After importing via cr:import
or creating a workspace the workspace permissions might not be correct due to a bug.
If you experience issues (especially with shared workspaces) as hotfix you can run this command (adjust the workspace name in the command):
./flow workspace:settitle live "Live workspace"
Alternatively this PR can be used directly as a patch:
{
"name": "your/distribution",
"config": {
"allow-plugins": {
// ...
"cweagans/composer-patches": true
}
},
"require": {
// ...
"cweagans/composer-patches": "^1.7.3"
},
"extra": {
"patches": {
"neos/neos": {
"BUGFIX: Fix admin workspace permissions for workspaces without metadata": "https://github.com/neos/neos-development-collection/pull/5290.patch"
}
}
}
}
Partial publish / rebase breaks change projection
this will be fixed with the next beta