From b82b1d87efe0b35e9ec351cf26014d095ff6593b Mon Sep 17 00:00:00 2001 From: Giovambattista Fazioli Date: Fri, 25 Sep 2026 09:56:02 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(eloquent):=20connect=20to=20?= =?UTF-8?q?SQLite=20when=20WordPress=20runs=20on=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eloquent always registered a mysql connection built from DB_HOST, DB_NAME, DB_USER and DB_PASSWORD. On WordPress Playground, WordPress Studio and any site with the SQLite Database Integration plugin there is no MySQL, so every Eloquent query failed: on playground.wordpress.net the Database demo's Eloquent ORM page threw on every example (#110). That plugin defines DB_ENGINE ('sqlite') and FQDB (the database file), and keeps WordPress's tables as plain SQLite tables under their MySQL names. Eloquent::connection() now returns a sqlite connection on that file when DB_ENGINE is 'sqlite' and FQDB is defined, and the same mysql connection as before otherwise. Measured on a local WordPress Playground (SQLite Database Integration 3.0.2): the 11 examples of the demo page return their values, and a row written through Eloquent is read back by $wpdb. A table created with Eloquent's schema builder is not visible to WordPress (SHOW TABLES, DESCRIBE) there, because the plugin's MySQL emulation never learns about it: tables still belong to WordPress (migrations, dbDelta). --- src/Database/Eloquent.php | 42 ++++++++++++----- tests/Unit/EloquentTest.php | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 tests/Unit/EloquentTest.php diff --git a/src/Database/Eloquent.php b/src/Database/Eloquent.php index 338f6732..2158b7fd 100644 --- a/src/Database/Eloquent.php +++ b/src/Database/Eloquent.php @@ -15,16 +15,7 @@ public static function init() if (class_exists($eloquent)) { $capsule = new $eloquent(); - $capsule->addConnection([ - 'driver' => 'mysql', - 'host' => DB_HOST, - 'database' => DB_NAME, - 'username' => DB_USER, - 'password' => DB_PASSWORD, - 'charset' => 'utf8', - 'collation' => 'utf8_unicode_ci', - 'prefix' => '', - ]); + $capsule->addConnection(self::connection()); // Set the event dispatcher used by Eloquent models... (optional) // use Illuminate\Events\Dispatcher; @@ -38,4 +29,35 @@ public static function init() $capsule->bootEloquent(); } } + + /** + * The connection to WordPress's own database: MySQL, unless WordPress runs on SQLite. + * + * That is WordPress Playground, WordPress Studio, or any site with the SQLite Database + * Integration plugin, which defines DB_ENGINE and FQDB. Its tables are plain SQLite tables + * under their MySQL names, in the file FQDB points at, so Eloquent can query them directly. + * + * @return array + */ + public static function connection(): array + { + if (defined('DB_ENGINE') && DB_ENGINE === 'sqlite' && defined('FQDB')) { + return [ + 'driver' => 'sqlite', + 'database' => FQDB, + 'prefix' => '', + ]; + } + + return [ + 'driver' => 'mysql', + 'host' => DB_HOST, + 'database' => DB_NAME, + 'username' => DB_USER, + 'password' => DB_PASSWORD, + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ]; + } } diff --git a/tests/Unit/EloquentTest.php b/tests/Unit/EloquentTest.php new file mode 100644 index 00000000..a7a1f84c --- /dev/null +++ b/tests/Unit/EloquentTest.php @@ -0,0 +1,90 @@ + 'mysql', + 'host' => 'db.example', + 'database' => 'wordpress', + 'username' => 'wp', + 'password' => 'secret', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ]; + + private function defineMysqlConstants(): void + { + define('DB_HOST', 'db.example'); + define('DB_NAME', 'wordpress'); + define('DB_USER', 'wp'); + define('DB_PASSWORD', 'secret'); + } + + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] + public function test_mysql_is_the_default_and_unchanged(): void + { + $this->defineMysqlConstants(); + + $this->assertSame(self::MYSQL, Eloquent::connection()); + } + + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] + public function test_db_engine_mysql_keeps_mysql(): void + { + // What the SQLite Database Integration plugin defines when its drop-in is not loaded. + define('DB_ENGINE', 'mysql'); + define('FQDB', '/site/wp-content/database/.ht.sqlite'); + $this->defineMysqlConstants(); + + $this->assertSame(self::MYSQL, Eloquent::connection()); + } + + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] + public function test_sqlite_uses_the_file_wordpress_uses(): void + { + // WordPress Playground, measured on 2026-09-25 (SQLite Database Integration 3.0.2). + define('DB_ENGINE', 'sqlite'); + define('FQDB', '/wordpress/wp-content/database/.ht.sqlite'); + // Playground's wp-config still carries the sample MySQL values: they must not be used. + define('DB_HOST', 'localhost'); + define('DB_NAME', 'database_name_here'); + define('DB_USER', 'username_here'); + define('DB_PASSWORD', 'password_here'); + + $this->assertSame( + [ + 'driver' => 'sqlite', + 'database' => '/wordpress/wp-content/database/.ht.sqlite', + 'prefix' => '', + ], + Eloquent::connection() + ); + } + + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] + public function test_sqlite_without_a_database_file_falls_back_to_mysql(): void + { + define('DB_ENGINE', 'sqlite'); + $this->defineMysqlConstants(); + + $this->assertSame(self::MYSQL, Eloquent::connection()); + } +}