Flow 7.0
This major release of Flow comes with some great new features, bugfixes and a lot of modernisation of the existing code base. As usual, we worked hard to keep this release as backwards compatible as possible but, since it’s a major release, some of the changes might require manual adjustments. So please make sure to carefully read the upgrade instructions below. Flow 7 also increases the minimal required PHP version to 7.3.
New Features
PSR-15 compatible middleware chain
With version 7, Flow introduces a PSR-15 compatible HTTP middleware chain that opens up the system to a comprehensive pool of existing middleware components.
For example, in order to measure the performance of requests, a simple:
composer require middlewares/response-time
and a few lines of Settings.yaml
Neos:
Flow:
http:
middlewares:
'ResponseTime':
middleware: 'Middlewares\ResponseTime'
position: start
is enough to add a X-Response-Time
header to all responses.
Note: This is one of the few potentially breaking changes, so make sure to read the HTTP ComponentChain and ComponentContext upgrade instructions below and the HTTP Foundation chapter for comprehensive documentation.
Related issue: #1889
Extended Routing
The routing framework is one of the oldest modules of Flow. With version 7 it has been extended allowing for cross domain linking.
Furthermore the output of the CLI commands have been overhauled and two commands routing:resolve
and routing:match
have been
added enabling a greater level of testing and debugging.
See Routing chapter for details and make sure to read the Access Route Parameters when resolving routes upgrade instructions below.
Related issue: #1126
DBAL connection factory
Previously, in order to get hold of the current DBAL connection, one had to inject the doctrine EntityManager
increasing complexity and coupling.
With version 7 a ConnectionFactory
has been added, allowing for easy injection of the current database connection:
class SomeClass {
/**
* @Flow\Inject
* @var \Doctrine\DBAL\Connection
*/
protected $connection;
Related issue: #2170
StaticResource Eel Helper
A new Eel helper has been added that allows for easy rendering of static resource URIs:
<!-- create static resource uri -->
<link rel="stylesheet" href={StaticResource.uri('Neos.Demo', 'Public/Styles/Main.css')} media="all" />
And, since it sometimes make sense to inline the contents of a static resource, this is possible as well:
<!-- get static resource content -->
<style>{StaticResource.content('Neos.Demo', 'Public/Styles/Main.css')}</style>
Related issue: #2175
Human readable labels and descriptions for Roles & Privileges
The user roles and privileges can quite easily become a wood of technical labels like 'Neos.Flow:Everybody'
and
until now there was no good way to document them in a human readable way that can be used e.g. in the Neos backend.
With Flow 7, metadata can be assigned to roles and privilege targets in order to add human readable labels and descriptions:
roles:
'Neos.Neos:UserManager':
label: 'Neos User Manager'
description: 'A user with this role is able to create, edit and delete users which has the same or a subset of his own roles.'
# ...
privilegeTargets:
'Neos\Flow\Security\Authorization\Privilege\Method\MethodPrivilege':
'Neos.Neos:ContentPreview':
label: 'The privilege to see the content preview in the backend.'
# ...
Two new commands have been added that will output this information to the CLI:
./flow security:listRoles --include-abstract
./flow security:describeRole <role>
Related issue: #2162
Unidirectional OneToMany relations
Inside a single aggregate OneToMany relations are normally best modelled unidirectionally. Bidirectional relations always are harder to manage correctly and can easily lead to unintentional traversal of entity hierarchies with all the drawbacks. Since Doctrines OneToMany annotation is always bidrectional and also dictates the owning side of the relation (at the unexpected side from a modelling PoV), it is not straightforward to model this correctly.
In Flow specifically, we try to follow DDD best practices in modelling and this means, that the aggregate root is the entry point and the entity that is sent to a repository to persist it and all its sub-entities. This can not be achieved with the standard doctrine OneToMany annotation when the one side is supposed to be closer to the root.
This change allows the user to annotate such a relation simply as:
/**
* @ORM\OneToMany
* @var Collection<Comment>
*/
This is done by remapping OneToMany annotations without a mappedBy as ManyToMany with a unique constraint.
Related issue: #2054
Removed Fluid as dependency
More of a “honorable mention” than a Feature: With version 7, TYPO3Fluid is no longer a requirement to run Flow. This is part of an ongoing process to make the base distribution lighter and allow developers to opt in to features selectively.
Note: Fluid is still being used by default when creating packages with the Kickstarter and it is also still used heavily in the Neos core.
If you make use of Fluid in your packages, make sure to declare a dependency to neos/fluid-adaptor
, see `Changes related to Fluid`_ upgrade instructions below.
Related issue: #2151
Upgrade Instructions
This section contains instructions for upgrading your Flow 6.3 based applications to Flow 7.0.
We now require PHP 7.3.x or higher
If you are using a MySQL based database you must use at least MySQL 5.7.7 or MariaDB 10.2.2
In general just make sure to run the following commands:
To clear all file caches:
./flow flow:cache:flush --force
If you have additional cache backends configured, make sure to flush them too.
To apply core migrations:
./flow flow:core:migrate <Package-Key>
For every package you have control over (see Upgrading existing code below).
To validate/fix the database encoding, apply pending migrations and to (re)publish file resources:
./flow database:setcharset
./flow doctrine:migrate
./flow resource:publish
If you are upgrading from a lower version than 6.3, be sure to read the upgrade instructions from the previous Release Notes first.
Upgrading existing code
There have been major API changes in Flow 7.0 which require your code to be adjusted. As with earlier changes to Flow that required code changes on the user side we provide a code migration tool.
Given you have a Flow system with your (outdated) package in place you should run the following before attempting to fix anything by hand:
./flow core:migrate Acme.Demo
This will adjust the package code automatically and/or output further information. Read the output carefully and manually adjust the code if needed.
To see all the other helpful options this command provides, make sure to run:
./flow help core:migrate
Also make sure to read about the Potentially breaking changes below.
Inside core:migrate
The tool roughly works like this:
Collect all code migrations from packages
Collect all files from the specified package
For each migration
Check for clean git working copy (otherwise skip it)
Check if migration is needed (looks for Migration footers in commit messages)
Apply migration and commit the changes
Afterwards you probably get a list of warnings and notes from the migrations, check those to see if anything needs to be done manually.
Check the created commits and feel free to amend as needed, should
things be missing or wrong. The only thing you must keep in place from
the generated commits is the migration data in composer.json
. It is
used to detect if a migration has been applied already, so if you drop
it, things might get out of hands in the future.
Potentially breaking changes
Flow 7.0 comes with some breaking changes and removes several deprecated functionalities, be sure to read the following changes and adjust your code respectively. For a full list of changes please refer to the change log.
Removed PHP classes
The following, previously deprecated, classes have been removed:
Cli/Request::getMainRequest()
&Cli/Request::isMainRequest()
Those were deprecated with 6.0 (via #1552) and never really served a purpose since CLI requests can’t be nested
Neos\\Flow\\Persistence\\Generic\\*
Before we had doctrine, we had a custom persistence layer that was kept as “generic” persistence when we introduced doctrine ten years ago (via 90cb658)
Since 6.0 this custom persistence was deprecated in favor of the corresponding
Neos\\Flow\\Persistence\\Doctrine\\*
classesYou should be able to safely replace any remaining
Neos\Flow\Persistence\Generic\PersistenceManager
byNeos\Flow\Persistence\PersistenceManagerInterface
in most cases
Neos\\Flow\\Security\\Cryptography\\SaltedMd5HashingStrategy
md5 is unsafe and the hashing strategy was deprecated with 6.0 (via #1668)
ObjectAccess::instantiateClass()
deprecated with 5.3.16 (via #1972). With PHP 5.6+
new $className(...$arguments)
can be used instead
HttpRequestHandlerInterface
/HttpRequestHandler::getHttpResponse()
deprecated with 6.0 (via #1755) and now gone
If you need the current HTTP Response, use a middleware (see below)
Neos\\Flow\\Http\\Component\\*
The HTTP Component chain has been replaced by a PSR-15 compatible middleware implementation (see below)
Neos\\Flow\\Http\\HttpRequestHandlerInterface::getComponentContext()
was removed along the way – to get hold of the active server request, the (no longer deprecated)HttpRequestHandlerInterface::getHttpRequest()
can be usedNeos\\Flow\\Http\\HttpRequestHandlerInterface::getHttpResponse()
was also removed, the response can be altered via middleware or$this->response
in ActionControllers
HTTP ComponentChain and ComponentContext
With 6.3 support for PSR-15 middlewares was added via #1928 with the plan to replace our custom HTTP Component chain implementation. This undertaking has been finalized with 7.0 and the following classes were removed from the core:
Neos\\Flow\\Http\\Component\\ComponentInterface
(was part of the public API!)Neos\\Flow\\Http\\Component\\Exception
(public API)Neos\\Flow\\Http\\Component\\ComponentChain
(already deprecated)Neos\\Flow\\Http\\Component\\ComponentChainFactory
(already deprecated)Neos\\Flow\\Http\\Component\\ComponentContext
(already deprecated)
Due to the different nature of the two approaches, existing components can’t be migrated automatically. But replacing a classic HTTP component by a corresponding middleware is mostly very straight forward:
Example 1: Tweak the response
Before, in order to alter the response, you had to replace it in the ComponentContext
:
class SomeHttpComponent implements ComponentInterface {
public function handle(ComponentContext $componentContext) {
$httpResponse = $componentContext->getHttpResponse();
$modifiedResponse = $httpResponse->withAddedHeader('X-SomeHeader', '123');
$componentContext->replaceHttpResponse($modifiedResponse);
}
}
Now you can just return the new instance that you retrieve by invoking the next handler in the chain:
class SomeHttpMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface;
$response = $next->handle($httpRequest);
return $response->withAddedHeader('X-SomeHeader', '123');
}
}
Example 2: Interrupt the chain
Sometimes it can be useful to stop further processing of the chain, for example in order to respond to a custom AJAX request. Previously a special parameter “cancel” could be used to prevent later components from being invoked:
class SomeHttpComponent implements ComponentInterface {
public function handle(ComponentContext $componentContext) {
// ...
$componentContext->setParameter(\Neos\Flow\Http\Component\ComponentChain::class, 'cancel', true);
}
}
Now, the chain can be interrupted simply by returning a new response instead of invoking the next handler:
class SomeHttpMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface;
// ...
return new Response(200, ['Content-Type' => 'application/json'], json_encode(['success' => true]));
}
}
- Note: The behavior of the two examples differ slightly because the HTTP component chain was split into three sub-chains and
the “cancel” parameter would only stop the current sub-chain.
Example 3: Communicate between components
Before, the ComponentContext
could be used in order to pass data between HTTP components:
class SomeHttpComponent implements ComponentInterface {
public function handle(ComponentContext $componentContext) {
// read parameter
$parameterValue = $componentContext->getParameter(SomeComponent::class, 'someParameter');
// write parameter
$componentContext->setParameter(SomeComponent::class, 'someParameter', 'someValue');
}
}
Now the ServerRequestInterface
attributes can be used instead:
class SomeHttpMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface;
// read attribute
$attributeValue = $request->getAttribute('someAttribute');
// write attribute
return $next->handle($request->withAttribute('someAttribute', 'someValue'));
}
}
This has the advantage that those attributes can be accessed outside of the middleware chain as well, for example in ActionControllers via $this->request->getHttpRequest()->getAttributes()
.
NOTE: This mechanism is now also used to configure Routing Parameters (see Routing)
Behavioral changes
Relative position to non-existing key in PositionalArraySorter throws exception
The PositionalArraySorter
is used in many places in Flow & Neos, mostly when specifying the order of certain
configuration options.
Until now, an element positioned relative to a non-existing key would just be skipped silently leading to a
non-deterministic (or at least non-transparent) order:
Neos:
Flow:
mvc:
routes:
'Some.Package':
position: 'before Some.NonExistingPackage'
Previously, the corresponding routes would be inserted according to the loading order now: An InvalidPositionException
exception is thrown:
The positional string "before Some.NonExistingPackage" (defined for key "Some.Package") references a non-existing key.
To fix this case, the configuration has to be adjusted so that the position points to an absolute position
(like start
or end
) or by referring to a key that exists (like before someExistingKey
).
Related issue: #2213
Doctrine Migrations
The doctrine/migrations
package has been updated from 1.8 to 3.0.
While there are new features in Doctrine Migrations, the reason for us to do an upgrade is to move forward – the previously used version will not be maintained forever… This post also gives some background on that: https://www.doctrine-project.org/2020/04/10/doctrine-migrations-3.0.html
For a Flow user the commands remain unchanged, so far no multi-namespace migrations are supported and the features to the “official” CLI do not matter, since we embed the functionality in our own commands.
Breaking changes
There are three things that make this upgrade a breaking change:
Doctrine\\DBAL\\Migrations
moved toDoctrine\\Migrations
AbstractMigration
changed method signatures (type delcarations added)
To adjust your PHP code (the migration files), a core migration is provided that
should fix the vast majority of existing migrations. (That core migration is in Flow
and named Version20201109224100
.)
The “version” is the FQCN of the migration class (existing entries in the migrations table will be automatically updated)
The needed changes to the DB table where the migration status is stored are done
the first time a command that accesses that table is used. Make sure to have a current
backup and then run ./flow doctrine:migrationstatus --show-migrations
. If all
went well, the migrations should all be listed as a fully-qualified class name, no
longer just a date/time string. If any errors occurred during the command, restore the
backup (the migrations table is sufficient), fix the errors and try again.
See https://github.com/doctrine/migrations/blob/3.0.x/UPGRADE.md#code-bc-breaks and https://github.com/doctrine/migrations/blob/3.0.x/UPGRADE.md#upgrade-to-20 for a full list of other changes. Most of those are wrapped in Flow code and need no adjustments in userland code.
Related issue: #2122
ValueObjects are embedded by default
This makes all ValueObjects embedded by default. Embedded value objects are the preferred storage method for all
value objects, since it better reflects true value object semantics.
This requires a schema update, so you need to generate a migration for your packages and apply it.
Alternatively you can run the provided code migration or manually change all your @Flow\\ValueObject
annotations
to @Flow\\ValueObject(embedded=false)
in order to keep your current database schema.
Related issue: #2123
Type declarations in interfaces
Return and argument type hints have been added to the following interfaces:
Neos\Flow\Persistence\\PersistenceManagerInterface
Neos\Flow\Persistence\\QueryInterface
Neos\Flow\Persistence\\QueryResultInterface
Neos\Flow\Persistence\\RepositoryInterface
You’ll might have to adjust your code manually in case you implemented these or extended an implementation. See #2231
With Flow 7.0 TYPO3Fluid is no longer a dependency of neos/flow
.
Remove neos/fluid-adaptor as required package
Removes references to Fluid and the dependency to the neos/fluid-adaptor
composer package.
This is a breaking change if you relied on the fact the Flow installs all Fluid dependencies. In that case you’ll need to require them explicitly in your distribution:
composer require neos/fluid-adaptor
Related issue: #2151
Remove custom FluidAdaptor Exceptions on invalid ArgumentDefinition
With https://github.com/TYPO3/Fluid/issues/529 TYPO3Fluid introduced a change to versions 2.5.11+ and 2.6.10+ that was broke compatibility
with the custom implementation in the Neos.FluidAdaptor
package.
With version 7, the custom implementations of AbstractViewHelper::registerArgument()
and AbstractViewHelper::overrideArgument()
have
been removed in order to remedy the regression and be less prone to such changes in the future.
As a consequence, when the arguments of a ViewHelper are invalid the TYPO3Fluid\Fluid\Core\ViewHelper\Exception
will be thrown instead
of the custom Neos\FluidAdaptor\Core\ViewHelper\\Exception
.
See https://github.com/TYPO3/Fluid/issues/529 and https://github.com/neos/flow-development-collection/pull/2257#issuecomment-728825319
Raise minimum PHP version to 7.3
PHP 7.2 has reached EOL in November 2020. With version 7 Flow requires PHP 7.3 or higher.
Note: Support for PHP 8.0 has been prepared and will be finalized as soon as Doctrine supports it (see #2233)
Access Route Parameters when resolving routes
This feature allows route part handlers to access any Route Parameters that has been set for the current request. This will make it possible to implement cross-domain linking for example with relative/absolute URLs depending on the current host.
This is a potentially breaking change because it extends the ParameterAwareRoutePartInterface
by a new method resolveWithParameters
.
This means that custom RoutePartHandlers that implement this interface directly have to be
adjusted. The easiest way to adjust an existing handler is to implement this method as follows:
final public function resolveWithParameters(array &$routeValues, RouteParameters $_)
{
return $this->resolve($routeValues);
}
…basically ignoring the parameters.
Route Part handlers extending DynamicRoutePart
don’t need to be adjusted!
This also changes the (non-api) Route::resolves()
method that now expects an instance of
ResolveContext
instead of an array with the “routeValues”.
Related issue: #2141