Skip to main content

Bolt, https://boltcms.io/, is an open source CMS, a Content Management System. Bolt simplifying some installations by using flat files instead of a database. Bolt was quick to set up, easy to configure, and used elegant templates. Now installation is blocked by the use of Composer.

Part of this test is a reprint of my 2020 test as the current Bolt failed to install from the code download.

Almost flat

Actually Bolt does use a database but can run with SQLite which stores the SQL database in a single file you can copy from machine to machine. Install SQLite if it is missing. There is no configuration required once the SQL lite package is installed. You get the easy copy advantage of a flat file system with the options to expand up to a large database like MariaDB.

SQLite is a compromise. You cannot use an SQLite file unless SQLite is installed. You cannot open the SQLite file and read it as plain text. You cannot share an SQLite database across multiple servers.

Drupal is another CMS with an option for SQLite.

Silex and Symfony

Bolt is built on Silex which is built on Symfony. Silex is supposed to be a lightweight version of Symfony but anything other than a really simple Web site quickly bulges with Symfony bloat. When a page request, or a code change, has to go through Bolt then Silex then Symfony, there is a good argument for cutting out one layer.

Bolt uses the Twig template component from Symfony. Twig is probably the best part of Symfony and can be used independently without the Symfony gorilla. When Twig is fully used, you get templateing plus caching of the template process output, reducing the template overhead. What you do not get is good cache management.

Bolt says the backend part is responsive. Weird. Responsive design is for the front end. I think they mean there is a presentation layer the Bolt core and that presentation layer is responsive.

You can have content types but have to configure them in YAML files. YAML is supposed to fix a major problem with other types of configuration files but YAML creates a major problem.

Bolt 3.7 requires PHP 7.1.2 or higher and there is an older Bolt 3.6 for older versions of PHP. There is no mention of which version of Symfony or any of the other components. The Bolt site says it uses Composer but does not say how. Hopefully Bolt does not require Composer.

Requirements

Bolt recommends a minimum of 32 MB of memory for PHP, a refreshingly small amount compared to most full function content management software.

PHP

The documentation says PHP has to be 7.2.9 or later. You have to install using Composer and the Composer file says PHP 7.3. Both are obsolete and not supported.

Database

Bolt uses the SQLite single file database to remove the database configuration problem. You can switch to MySQL or PostgreSQL for larger Web sites.

PHP extensions

You need the following PHP extensions installed with most being standard inclusions. You need PDO for database access, mysqlnd for MySQL or pgsql for PostgreSQL, curl, exif, fileinfo, gd, json, openssl, posix, xml, and zip. Recommended and optional are intl, mbstring, opcache.

Web browser

This is a sticking point with Bolt and a few other CMSs. You have to make all your visitors switch to a new Web browser. They do not maintain the really simple compatibility adjustments for older browsers.

Bolt supplied templates do adjust (respond) to the smaller screen sizes on handheld devices.

Download

core-5.2.2.tar.gz is a 1.1 MB download expanding to 843 items using 5.4 MB of disk.

Installation

There are no installation instructions for the download. Instead there are only instructions on how to install a package named Composer and how to tell Composer to install Bolt. That means you have no control over the installation. Despite the small download file, Composer can install masses of junk making Bolt a typical Symfony monster.

First use

Back when I tested Bolt in 2020, there were endless errors on first use. now I cannot get to first use as there is no information on how to install Bolt the regular way. The following tests are from 2020 when you could actually install Bolt from a download.

Home page (2020)

The home page contains the following message about blocks. The message describes the use of the slug field that appears in tables including blocks, entries, and pages.

'There currently is no Block with 'about-us' as its slug. If you create one, it will be shown here. Go to 'Configuration' > 'Check Database' and select 'Add sample Records' to automatically generate this Block."

app/config/config.yml

"# Database setup. The driver can be either 'sqlite', 'mysql' or 'postgres'.

# For SQLite, only the databasename is required. However, MySQL and PostgreSQL

# also require 'username', 'password', and optionally 'host' ( and 'port' ) if the database

# server is not on the same host as the web server.

# If you're trying out Bolt, just keep it set to SQLite for now.

database:

driver: sqlite

databasename: bolt"

I switched to MySQL.phpmy

PHPMyAdmin: Create database with same name and grant all privileges.

Database (2020)

Bolt creates 12 tables in the database, authtoken, blocks, cron, entries, field_value, log_change, log_system, pages, relations, showcases, taxonomy, and users. The names are all prefixed with bolt_.

The users table has the user entry and authtoken has the login for a session.

Field_value, entries, blocks, pages, and showcases look like content. Taxonomy would be classification.

All the tables use character set utf8 and collation utf8_unicode_ci. Some fields are long text fields for content. Long text fields have a performance impact that varies across databases. For some types of database, you can improve performance by moving long text fields into separate tables. This is not relevant to SQLite or the default InnoDB as they use only one file for all tables.

Users (2020)

The table is named bolt_users and contains the following fields.

`id` int(11) not null

* This is the primary key and is used in authtoken.

`username` varchar(32) not null

* This has to be unique to allow a unique login.

`password` varchar(128) not null

`email` varchar(254) not null

* This has to be unique to communicate with the user.

`lastseen` datetime default null

`lastip` varchar(45) default null

`displayname` varchar(32) not null

`stack` longtext not null comment '(DC2Type:json)',

`enabled` tinyint(1) not null default '1'

* This is indexed. The index must speed up a search.

`shadowpassword` varchar(128) default null

`shadowtoken` varchar(128) default null

`shadowvalidity` datetime default null

`failedlogins` int(11) not null default '0'

`throttleduntil` datetime default null

`roles` longtext not null comment '(DC2Type:json)'

* The roles field contains entries like "root" and "everyone".

authtoken

bolt_authtoken is the logged in session authorisation with the following fields. One entry is created for the user on first use. The user name is empty as that is in the users table.

`id` int(11) not null

`user_id` int(11) default null * This is the id field in the users table and is indexed.

`username` varchar(32) default null

`token` varchar(128) not null

`salt` varchar(128) not null

`lastseen` datetime default null

`ip` varchar(45) default null

`useragent` varchar(128) default null

`validity` datetime default null

Field value

Table bolt_field_value contains the following fields. This looks like an overly generic way to store any item of data. Compare this to the opposite style of storage in Drupal where every type of field has a separate table. In Drupal, individual fields perform better due to smaller tables but the mess of tables slows down some accesses.

Fields contenttype and content_id are joined in an index. Content id must be the connector to content and the contenttype field must be a connector to something within the content. Content id is not defined and might connect to entries or blocks or pages. contenttype will be a performance issue for some types of database as contenttype is a string.

contenttype and content_id appear in the taxonomy table. contenttype and id appear in the relations table with id appearing as from_id then to_id. The database would be more understandable if the "id" fields were given their full names like content_id. from_id might then be from_content_id.

`id` int(11) not null

`contenttype` varchar(64) not null

`content_id` int(11) not null

`name` varchar(64) not null

`grouping` int(11) not null default '0'

`block` varchar(64) default null

`fieldname` varchar(255) not null

`fieldtype` varchar(255) not null

`value_string` varchar(255) default null

`value_text` longtext

`value_integer` int(11) default null

`value_float` double default null

`value_decimal` decimal(18,9) default null

`value_date` date default null

`value_datetime` datetime default null

`value_json_array` longtext not null comment '(DC2Type:json)'

`value_boolean` tinyint(1) not null default '0'

Entries

Table bolt_entries contains the following fields. This appears to be a generic content container.

Both the blocks table and the pages table repeat many of these these fields, making the blocks and pages tables specific use cases for entries style data. All share slug, datecreated, datechanged, datepublish, datedepublish, ownerid, status, and title.

`id` int(11) not null

`slug` varchar(128) not null * Indexed.

`datecreated` datetime not null * Indexed.

`datechanged` datetime not null * Indexed.

`datepublish` datetime default null * Indexed.

`datedepublish` datetime default null * Indexed.

`ownerid` int(11) default null

`status` varchar(32) not null * Indexed.

`templatefields` longtext comment '(DC2Type:json)'

`title` varchar(256) default null

`teaser` longtext

`body` longtext

`image` longtext comment '(DC2Type:json)'

`video` longtext comment '(DC2Type:json)'

Blocks

Table bolt_blocks contains the following fields.

`id` int(11) not null

`slug` varchar(128) not null * Indexed.

`datecreated` datetime not null * Indexed.

`datechanged` datetime not null * Indexed.

`datepublish` datetime default null * Indexed.

`datedepublish` datetime default null * Indexed.

`ownerid` int(11) default null

`status` varchar(32) not null * Indexed.

`templatefields` longtext comment '(DC2Type:json)'

`title` varchar(256) default null

`content` longtext

`contentlink` varchar(256) default null

`image` longtext comment '(DC2Type:json)'

Pages

Table bolt_pages contains the following fields.

`id` int(11) not null

`slug` varchar(128) not null * Indexed.

`datecreated` datetime not null * Indexed.

`datechanged` datetime not null * Indexed.

`datepublish` datetime default null * Indexed.

`datedepublish` datetime default null * Indexed.

`ownerid` int(11) default null

`status` varchar(32) not null * Indexed.

`templatefields` longtext comment '(DC2Type:json)'

`title` varchar(256) default null

`image` longtext comment '(DC2Type:json)'

`teaser` longtext

`body` longtext

`template` varchar(256) default NULL

Showcases

Table bolt_showcases contains the following fields. Note the similarity to entries, blocks and pages.

`id` int(11) not null

`slug` varchar(128) not null * Indexed.

`datecreated` datetime not null * Indexed.

`datechanged` datetime not null * Indexed.

`datepublish` datetime default null * Indexed.

`datedepublish` datetime default null * Indexed.

`ownerid` int(11) default null

`status` varchar(32) not null * Indexed.

`templatefields` longtext comment '(DC2Type:json)'

`title` varchar(256) default null

`html` longtext

`textarea` longtext

`markdown` longtext

`geolocation` longtext comment '(DC2Type:json)'

`video` longtext comment '(DC2Type:json)'

`image` longtext comment '(DC2Type:json)'

`imagelist` longtext comment '(DC2Type:json)'

`file` varchar(256) default null

`filelist` longtext comment '(DC2Type:json)'

`checkbox` tinyint(1) not null default '0'

`datetime` datetime default null

`date` date default null

`integerfield` int(11) not null default '0' * Indexed.

`floatfield` double not null default '0'

`selectfield` longtext

`multiselect` longtext comment '(DC2Type:json)'

`selectentry` longtext

Taxonomy

Table bolt_taxonomy contains the following fields.

`id` int(11) not null

`content_id` int(11) not null * Indexed.

`contenttype` varchar(32) not null * Indexed.

`taxonomytype` varchar(32) not null * Indexed.

`slug` varchar(64) not null * Indexed.

`name` varchar(64) not null

`sortorder` int(11) not null default '0' * Indexed.

Relations

Table bolt_relations contains the following fields.

`id` int(11) not null

`from_contenttype` varchar(32) not null * Indexed.

`from_id` int(11) not null

`to_contenttype` varchar(32) not null * Indexed.

`to_id` int(11) not null

Cron

Table bolt_cron contains the following fields. The interim field is indexed but not unique. I wonder where it connects to an actual cron request or run.

`id` int(11) not null

`interim` varchar(16) not null * Indexed.

`lastrun` datetime not null

Log tables

The log tables are bolt_log_change and bolt_log_system. Table bolt_log_change contains the following fields.

`id` int(11) not null

`date` datetime not null * Indexed.

`ownerid` int(11) default null * Indexed.

`title` varchar(256) not null

`contenttype` varchar(128) not null * Indexed.

`contentid` int(11) not null * Indexed.

`mutation_type` varchar(16) not null * Indexed.

`diff` longtext not null comment '(DC2Type:json)'

`comment` varchar(150) default NULL

Table bolt_log_system contains the following fields.

`id` int(11) not null

`level` int(11) not null * Indexed.

`date` datetime not null * Indexed.

`message` varchar(1024) not null

`ownerid` int(11) default null * Indexed.

`requesturi` varchar(128) not null

`route` varchar(128) not null

`ip` varchar(45) not null

`context` varchar(32) not null * Indexed.

`source` longtext not null comment '(DC2Type:json)'

Conclusion

Bolt is at the high end of flat file CMSs when you use SQLite as the database. Bolt is flexible but lacks full Web browser compatibility. You might look at Bolt for corporate use but would have to work hard to make Bolt compatible across the full range of Web browsers used by your customers and the public.

Tags: