Database
FloCMS sites use MySQL, MariaDB or, since core 2.9, SQLite. A site doesn't need a database to run: the welcome page, static pages and the documentation you're reading work without one. You add a database when your site stores data.
Supported databases
| Database | Website connection (App::db()) |
CLI connection (php flo) |
|---|---|---|
| MySQL | Supported through pdo_mysql; the default |
Supported; the default DB_TYPE |
| MariaDB | Supported through the MySQL driver (pdo_mysql) |
Supported with DB_TYPE=mysql or DB_TYPE=mariadb |
| SQLite | Supported with DB_TYPE=sqlite and pdo_sqlite, core 2.9 or newer |
Supported with DB_TYPE=sqlite and pdo_sqlite |
| PostgreSQL and other engines | Not supported | A custom DB_DSN can open a PDO connection, but this does not provide full query-builder or migration support |
The query builder uses backtick-quoted identifiers, which MySQL, MariaDB and SQLite all accept. PDO connectivity alone does not make PostgreSQL or another engine supported.
MySQL and MariaDB
Use MySQL or MariaDB for most websites, and on shared hosting such as cPanel. Both use the same connection settings below and require PHP's pdo_mysql extension. MariaDB does not need a separate PDO driver.
SQLite
SQLite keeps the whole database in one file. It suits small sites, local development and tests. Set:
DB_TYPE=sqlite
DB_NAME=storage/database.sqlite
DB_NAME is the file, relative to the project root unless it is a full path. SQLite doesn't use DB_HOST, DB_PORT, a username or a password, and PHP needs pdo_sqlite. Create the file with the migrations:
php flo migrate
php flo db:check
- The file's folder must exist and be writable by PHP and the web server, since SQLite also writes a journal file next to it.
- The website connection never creates the file. When it is missing, the page says so and names
DB_NAME, so a typo can't look like an empty site. - Foreign keys are switched on, and a write waits up to 5 seconds for another one to finish.
- Each migration's SQL must also work on SQLite. The migration helpers
id()andtableOptions()adapt to MySQL and SQLite; application and package migrations may still contain engine-specific SQL. See migrations. - Keep the file out of the web root.
storage/is a good place: the web server serves onlypublic/.
Important
With core 2.8 or older, these settings apply to php flo only, and the website keeps connecting to MySQL. Run composer update hostkurd/flocms-core to get 2.9.
Configuration
The connection is set in .env:
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=my_site
DB_USERNAME=my_user
DB_PASSWORD=secret
DB_CHARSET=utf8mb4
| Key | Default | |
|---|---|---|
DB_HOST |
localhost |
On cPanel usually localhost |
DB_PORT |
3306 |
|
DB_NAME |
empty | Leave empty until the database exists |
DB_USERNAME, DB_PASSWORD |
empty | |
DB_CHARSET |
utf8mb4 |
Full Unicode, including Arabic, Kurdish and emoji |
config/config.php copies these into the db.* settings, which the connection reads. While DB_NAME or DB_USERNAME is empty, the site runs without a database. With SQLite, only DB_NAME is needed.
Note
App::db() reads DB_TYPE (or db.type) since core 2.9; any value other than sqlite means MySQL. DB_DSN is read by the CLI connection factory only. A custom DB_DSN takes precedence over DB_TYPE for CLI connections and requires the corresponding PDO extension. It does not guarantee that CLI database commands, the query builder, or migrations work with that engine.
The connection
FloCMS opens the connection the first time a query needs it, and every model and query in the request shares it. Pages that never query the database never connect.
In a model, the connection is $this->db. Anywhere else:
use FloCMS\Core\App;
$db = App::db(); // FloCMS\Core\Database, or null when not configured
if ($db !== null) {
$count = $db->table('posts')->count();
}
The connection uses real prepared statements, throws an exception on every SQL error, and gives up connecting after 2 seconds. When the connection fails, the failure is remembered for the rest of the request, so a slow or missing server costs that time only once.
Build queries with the query builder, or run SQL with query(). See models.
When the database is missing
| Situation | What happens |
|---|---|
DB_NAME or DB_USERNAME empty |
App::db() returns null. A model's first query throws DatabaseNotConfiguredException. |
| Server unreachable | DatabaseConnectionException. The visitor sees nodbserver.html with status 503. |
| Wrong user or password, unknown database | DatabaseConnectionException. The visitor sees dberror.html. |
You don't need to catch these exceptions; the error handler shows the page. In debug mode the page also shows the driver's own message. See error pages.
Checking the connection
App::dbStatus() reports the state without throwing, for setup screens and health checks:
$status = App::dbStatus();
// ['configured' => true, 'connected' => false, 'reason' => 'unknown_database',
// 'code' => 1049, 'message' => 'Database not found.', 'detail' => '...']
reason is not_configured, server_unavailable, access_denied, unknown_database or other. message is safe to show to visitors. detail is the driver's message, which names hosts and users, so only show it in debug mode.
From the command line:
php flo db:check # is the database configured and reachable?
php flo db:show # the connection and every table with its row count
php flo doctor # the whole installation, the database included
Creating tables
Tables are created with migrations: PHP files in database/migrations/ that every copy of the site runs the same way. A new site's migrations create the users table and the API tables:
php flo migrate
Starting data, such as categories or a demo account, comes from seeders.