Featured image of post 用 sqliteCreateFunction 讓 SQLite 支援 MySQL 專屬函式

用 sqliteCreateFunction 讓 SQLite 支援 MySQL 專屬函式

SQLite 測試遇到 MySQL 專屬 function 如 FIELD 會噴 no such function 錯誤,用 sqliteCreateFunction 自行補上即可。

用 SQLite 跑測試時,遇到 MySQL 專屬的 function(像 FIELD)會直接噴 no such function 錯誤。

為什麼會出錯

每個資料庫內建的 function 不一樣,SQLite 沒有 MySQL 的 FIELD function。假設程式裡有這樣的查詢:

1
2
3
Route::get('/', function() {
    return User::query()->orderByRaw('FIELD(id, 3, 5, 4, 1, 2)')->get();
});

用 MySQL 跑沒問題,但測試用 SQLite 就會失敗:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    public function test_sql_function(): void
    {
        User::factory()->count(5)->create();

        $data = $this->get('/')->assertStatus(200)->collect();

        self::assertEquals([3, 5, 4, 1, 2], $data->pluck('id')->toArray());
    }
}

用 sqliteCreateFunction 自己補上

PHP 的 SQLite PDO 支援自定義 function,在 TestCasesetUp 裡加上就好:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace Tests;

use Illuminate\Database\SQLiteConnection;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\DB;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    protected function setUp(): void
    {
        parent::setUp();
        $connection = DB::connection();
        if (is_a($connection, SQLiteConnection::class)) {
            $connection->getPdo()->sqliteCreateFunction(
                'FIELD',
                static fn($id, ...$array) => array_search($id, $array)
            );
        }
    }
}

這樣 SQLite 就能認得 FIELD function,測試正常通過。同樣的方式也可以補上其他 MySQL 專屬的 function。