Andrew Welch
Published , updated · 5 min read · RSS Feed
Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.
Connecting to an External Database in Craft CMS
Learn how to connect to an external database using Craft CMS by leveraging the built-in flexibility of application components
There may be times you are working on a website using Craft CMS when you find the need to connect to an external database in addition to the database that Craft itself uses.
Perhaps you need to display some information from a legacy database or integrate with existing systems.
In fact, I had this very need when working on a client project, so I created the Connect plugin that allowed you to do so.
However, in evaluating porting the Connect plugin to Craft CMS 5, I realized it wasn’t needed at all, so I am deprecating it.
Let’s get rid of plugins we don’t need!
In this article, I’ll show you how to connect to an external database using nothing but Twig and a little config.
This technique described in this article works in Craft 3, Craft 4, Craft 5, and likely beyond because it is integral to how Craft works.
Link Application Components
You may have seen the app.php file in your config/ directory whilst working on your website.
This seemingly innocuous file is actually a very powerful application configuration that lets you override or add to the configuration of the Craft CMS web application.
This configuration file is combined with the default Craft app.php file when Craft bootstraps itself.
Most of the things you access via craft.app. in your Twig templates or Craft::$app-> in PHP are components that are defined in this config file.
This means that we can replace any Craft component via this config/app.php and we can also add new components.
Link Database Connections
The way Craft connects to its own database is via a Connection component that is accessible via craft.app.getDb() in Twig or Craft::$app->getDb() in PHP.
This all works transparently for you, to the point where you may not even have been aware that such a Connection component exists. Craft creates this component using the configuration you have in your config/db.php and/or the CRAFT_DB_ environment variables, and away you go.
As it turns out, The Yii2 framework that Craft uses is very flexible out of the box, and allows you to connect to a variety of databases (even those not supported by Craft itself):
- MySQL
- MariaDB
- SQLite
- PostgreSQL: version 8.4 or higher
- CUBRID: version 9.3 or higher.
- Oracle
- MSSQL: version 2008 or higher.
If you need to work with databases that aren’t bundled with Yii2, extensions are available for more databases:
Let’s see how we can use this to our advantage!
Link Making a Connection
With a little bit of config in config/app.php and a little bit of Twig, we can do exactly what my old Connect plugin did, but natively in Craft.
First, in our config/app.php file let’s add our new Connection component:
<?php
/**
* Yii Application Config
*
* Edit this file at your own risk!
*
* The array returned by this file will get merged with
* vendor/craftcms/cms/src/config/app.php and app.[web|console].php, when
* Craft's bootstrap script is defining the configuration for the entire
* application.
*
* You can define custom modules and system components, and even override the
* built-in system components.
*
* If you want to modify the application config for *only* web requests or
* *only* console requests, create an app.web.php or app.console.php file in
* your config/ folder, alongside this one.
*
* Read more about application configuration:
* https://craftcms.com/docs/4.x/config/app.html
*/
use craft\helpers\App;
use craft\db\Connection;
return [
'id' => App::env('CRAFT_APP_ID') ?: 'CraftCMS',
'components' => [
'externalDb' => [
'class' => Connection::class,
'dsn' => App::env('EXTERNAL_DB_DSN').'',
'username' => App::env('EXTERNAL_DB_USER'),
'password' => App::env('EXTERNAL_DB_PASSWORD'),
],
],
];
Here we’re defining a new component named externalDb which is of the class Connection, and passing in the dsn, username, and password properties to configure it from the respective EXTERNAL_DB_DSN, EXTERNAL_DB_USER, and EXTERNAL_DB_PASSWORD environment variables in our .env file.
Now that we’ve done this, our new Connection component will magically become available via craft.app.externalDb in Twig, and Craft::$app->externalDb in PHP.
Here’s what our .env file might look like:
EXTERNAL_DB_DSN=mysql:host=127.0.0.1;dbname=demo;port=3306
EXTERNAL_DB_USER=root
EXTERNAL_DB_PASSWORD=ry4nzG0b4g
It’s likely that everything except the dsn property looks familiar. dsn stands for Data Source Name, and is a string that defines how PHP can connect to a database using the PHP Data Objects (PDO) abstraction layer.
Just think of it as the credentials needed to connect to an arbitrary database.
You might be used to specifying things like the database host, database name, port, etc. separately. A dsn is just all of that mashed together into a single string.
The format is: {database_driver}:host={database_host};dbname={database_name};port={database_port}
Now that the setup is out of the way, let’s see how we can access our database!
{% set dbQuery = create('craft\\db\\Query') %}
{% set results = dbQuery
.from('books')
.select('id', 'author')
.where({genre: 'fiction'})
.all(craft.app.externalDb)
%}
We’re creating Query object that we defined in our config file, and then we’re constructing the query we want to use, and returning the results via .all()
The chained syntax you use for a Query should look familiar to you since Craft Element Queries are just Query objects customized for querying Craft Elements.
The only difference here is that we tell it what database Connection to use in the .all() method that executes the Query.
All of the action methods that execute a Query, such as .all(), .one(), .collect(), .column(), etc. take a database Connection as an argument.
You can also chain it all together and eliminate the intermediate dbQuery variable if you like:
{% set results = create({
class: 'craft\\db\\Query',
})
.from('books')
.select('id', 'author')
.where({genre: 'fiction'})
.all(craft.app.externalDb)
%}
Or:
{% set results = create('craft\\db\\Query')
.from('books')
.select('id', 'author')
.where({genre: 'fiction'})
.all(craft.app.externalDb)
%}
For completeness, the equivalent of the above in PHP would be:
$results = new craft\db\Query
->from('books')
->select('id', 'author')
->where(['genre' => 'fiction'])
->all(Craft::$app->externalDb);
Link Performance Considerations
It’s worth noting that if you’re connecting to an external database, if the database is hosted on a separate server, there can be performance implications.
It would be best for performance if the database is hosted on the same server as your Craft CMS install, or at least on the same subnet. This will result in much lower latency connections to the database, resulting in better performance.
Link SQLite real-world example
For a real-world example, check out the CraftQuest SQLite repository that was created for a LiveStream demo on CraftQuest.io.
It shows how to connect to an SQLite database via Craft using the techniques described in this article. You can easily run the project in your web browser or clone it down and run it locally!
First, you need to ensure that your VPS or Docker container has the sqlite binary installed.
Next, we need to add the db-sqlite PHP package to our composer.json file:
"yiisoft/db-sqlite": "^1.2"
Then here is the config/app.php for connecting to SQLite:
<?php
/**
* Yii Application Config
*
* Edit this file at your own risk!
*
* The array returned by this file will get merged with
* vendor/craftcms/cms/src/config/app.php and app.[web|console].php, when
* Craft's bootstrap script is defining the configuration for the entire
* application.
*
* You can define custom modules and system components, and even override the
* built-in system components.
*
* If you want to modify the application config for *only* web requests or
* *only* console requests, create an app.web.php or app.console.php file in
* your config/ folder, alongside this one.
*
* Read more about application configuration:
* https://craftcms.com/docs/4.x/config/app.html
*/
use craft\helpers\App;
use Yiisoft\Db\Sqlite\Dsn;
use yii\db\Connection;
/**
* Database is from https://www.sqlitetutorial.net/sqlite-sample-database/
*/
const SQLITE_DB_PATH = '/sqlite-db/chinook.db';
$sqliteDsn = (new Dsn('sqlite', dirname(__DIR__, 1) . SQLITE_DB_PATH))->__toString();
return [
'id' => App::env('CRAFT_APP_ID') ?: 'CraftCMS',
'components' => [
'sqliteDb' => [
'class' => Connection::class,
'dsn' => $sqliteDsn,
],
],
];
The documentation for connecting to SQLite appears to be outdated (it looks like it’s for Yii 1.x), so use the example above instead.
We’re just specifying the path to the SQLite database that it should use (in our case, a sample database), and then away you go: craft.app.sqliteDb will be a Connection to the SQLite database.
You can access it as described previously in this article:
{% set artists = create({
class: 'craft\\db\\Query',
})
.from('artists')
.all©aft.app.sqliteDb) %}
Link Getting Querious
That’s all there is to it! You can leverage the familiar Query syntax you’re used to from Craft while accessing an external database.
And you can do it all without the need for any kind of plugin or custom module.
That’s the extensible power of Craft CMS and the underlying Yii2 framework.