2020 summer release is here

This release is focused on performances and OpenID authentication. After many big websites, we needed to look further into Doctrine and the way we store your nodes content and how we can improve that. Especially when you have to deal with more than 30 node-types, your MySQL server is crying because of too long queries (too many INNER JOIN
). So we let developers choose Doctrine inheritance mapping strategy according to each website needs.
In the same time, we keep on developing our themes and new sidekick libraries in order to use Roadiz as a headless CMS in the next releases.
OpenID and JWT based authentication
Roadiz is now supporting OpenID authentication scheme for backoffice users as well as custom authentication for your website visitors.
We chose OpenId because it is based on OAuth2 and JWT and use auto-discovery which make it really easy to configure. You can them choose to attach a Role to all external accounts, or implement your own JwtRoleStrategy
to decide how to authorize your SSO users based on their JWT claims.
Due to many performance limitations using Doctrine class (or joined) table inheritance (too many INNER JOIN when lots of node-types, in fact, one additional inner join for each new node-type), we added the choice, at first install, to switch to Doctrine single-table inheritance. This means that all node-types data will be stored in a unique table with all fields as columns. There are several drawbacks with this mode:
- You cannot create two different node-type fields with the name if they have a different types (because it will be the same column in your DB table);
- You cannot add indexed fields (you’ll lose some performance when querying over node-type fields)
But in exchange you’ll get much better performances when browsing through nodes and you won’t be limited in node-type count.
So if you need less than 20 node-types but complex ones and indexed fields, juste stay using joined table inheritance type. If you need to create lots of node-types but with very little fields or with the same fields over and over (excerpt
, content
, …), go for the single table inheritance type.
Sadly, there is no migration tool to switch an existing website to single-table inheritance because we would have to move every data to a single table and lose some during the process.
This strategy is very efficient for querying across all types in the hierarchy or for specific types. No table joins are required, only a WHERE clause listing the type identifiers. In particular, relationships involving types that employ this mapping strategy are very performing.
This strategy inherently requires multiple JOIN operations to perform just about any query which can have a negative impact on performance, especially with large tables and/or large hierarchies.
Other major changes
- Podcast RSS download. Roadiz can download all episode from a single podcast RSS feed (may take time if episode files are large).
- Changed URL generator usage to comply with next Symfony
UrlGeneratorInterface
signature: you cannot generate URL from an object anymore. https://github.com/roadiz/roadiz/issues/365
Twig path
and url
methods still use the same signature to keep the ease of use: {{ path(nodeSource) }}
is internally interpreted as:
$this->get('urlGenerator')->generate(
RouteObjectInterface::OBJECT_BASED_ROUTE_NAME,
array_merge($parameters, [RouteObjectInterface::ROUTE_OBJECT => $nodeSource]),
Router::ABSOLUTE_PATH
);
- MySQL 5.7 or MariaDB 10.2.3 are now required: As we migrated to MySQL JSON native type for Doctrine
array
and json
types. This makes these fields searchable using the MySQL 5.7 JSON_
functions
- You can use
JSON_CONTAINS
verb to build Doctrine simple queries, for example to filter out nodes using a multiple choice field (without needing any relation table and heavy SQL query :
$this->get('nodeSourceApi')->getBy([
'node.nodeType' => $this->get('nodeTypesBag')->get(NSBlogPost::class),
'places' => ['JSON_CONTAINS', 'Paris']
])
- Improved User serialization for checking equality
- Removed all newsletter related features, definitively
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C322DB1917;
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C37808B1AD;
DROP TABLE newsletter_subscriber;
DROP TABLE newsletters;
DROP TABLE subscribers;
DROP INDEX IDX_409B1BCC845B4F32 ON node_types;
ALTER TABLE node_types DROP newsletter_type;
- Support non-interactive
theme:migrate
command to be able to run it at any Docker image startup. Here is an exemple of ./docker/php74-nginx-alpine/before_launch.sh
script that you could add in your project Docker image:
# Fix volume permissions
/bin/chown -R www-data:www-data /var/www/html/files;
/bin/chown -R www-data:www-data /var/www/html/web;
/bin/chown -R www-data:www-data /var/www/html/app;
# This line ensure that your project data structure is always up to date
/usr/bin/sudo -u www-data bin/roadiz themes:migrate -n Awesome
Then you will be able to setup automatic docker container restart with tools like Watchtower
- Added
documents:prune:unused
command to delete unused documents from CLI (when more than 200 documents can be too long to prune from web-server)
- Added
nodes:empty-trash
command to empty deleted nodes from CLI (if it takes too long time to run it from web-server)
- Improved Custom-forms exported answers document, added document upload
Better Roadiz sidekicks
Combine TreeWalker and AbstractApiTheme and transform Roadiz into a headless CMS.
AbstractApiTheme now supports listing and detail views for each node-types. TreeWalker is a standalone PHP library made to create data graphs from Data type definitions, in other terms, you could create an entire JSON page+blocks graph just by tell him how to fetch children nodes for each node-types. For the moment we are using it to build website navigation (to loop over pages without getting blocks), breadcrumb (just rewind your navigation TreeWalker) and to build pages by looping on inner blocks.
Minor changes
- Roadiz Role does not extend deprecated Symfony Role anymore
- Simplify Node route matcher to use only one SQL query for nodeName and UrlAlias lookup.
- Added missing annotations and metadata cache clearers (when using JMS Serializer)
- Added new
ROLE_ACCESS_LOGS
role to give unit access to CMS logs without needing an administrator account
- Moved cssMainColor and loginImagePage outside of /rz-admin for front-proxy caching
- Implements
EquatableInterface
for Roadiz User and OpenIdAccount for better control over authentication and token validation
- Backoffice main-tree UI are loaded in background to speed up first load.
- Added
username
to Roadiz log, in order to track SSO backend users history too
Migration
bin/roadiz orm:schema-tool:update --dump-sql --force
should give you to following migration:
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C322DB1917;
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C37808B1AD;
CREATE TABLE custom_form_answers_documents (customformfieldattribute_id INT NOT NULL, document_id INT NOT NULL, INDEX IDX_E979F877C84CA2FC (customformfieldattribute_id), INDEX IDX_E979F877C33F7837 (document_id), PRIMARY KEY(customformfieldattribute_id, document_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
ALTER TABLE custom_form_answers_documents ADD CONSTRAINT FK_E979F877C84CA2FC FOREIGN KEY (customformfieldattribute_id) REFERENCES custom_form_field_attributes (id) ON DELETE CASCADE;
ALTER TABLE custom_form_answers_documents ADD CONSTRAINT FK_E979F877C33F7837 FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE;
DROP TABLE newsletter_subscriber;
DROP TABLE newsletters;
DROP TABLE subscribers;
ALTER TABLE documents DROP FOREIGN KEY FK_A2B0728826CBD5A5;
ALTER TABLE documents ADD CONSTRAINT FK_A2B0728826CBD5A5 FOREIGN KEY (raw_document) REFERENCES documents (id) ON DELETE SET NULL;
CREATE INDEX IDX_C6B7DA87A58FA4853F824FD6 ON translations (available, override_locale);
ALTER TABLE log ADD username VARCHAR(255) DEFAULT NULL;
CREATE INDEX IDX_8F3F68C5F85E0677 ON log (username);
CREATE INDEX IDX_7C7DED6D4AD26064 ON nodes_sources (discr);
CREATE INDEX IDX_7C7DED6D4AD260649CAA2B25 ON nodes_sources (discr, translation_id);
CREATE INDEX IDX_7C7DED6DE0D4FDE14AD260649CAA2B25 ON nodes_sources (published_at, discr, translation_id);
DROP INDEX IDX_409B1BCC845B4F32 ON node_types;
ALTER TABLE node_types DROP newsletter_type;