Conversation
There was a problem hiding this comment.
Pull request overview
Introduces a first Laravel Schema/Migration “driver” foundation for SurrealDB so migrations can target Surreal-backed application data via Laravel’s Schema API.
Changes:
- Registers a new
surrealdatabase driver/connection for schema operations. - Adds Surreal schema grammar/builder/manager to support create/add/drop columns and create/drop tables for a narrow set of column types.
- Adds a Surreal-backed Pest feature test and documents current scope/limitations in the README.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
config/database.php |
Adds a surreal connection entry pointing at the new surreal driver. |
app/Providers/AppServiceProvider.php |
Registers the surreal database driver via DatabaseManager::extend(). |
app/Services/Surreal/Schema/SurrealSchemaConnection.php |
New Laravel Connection implementation used for Surreal schema operations. |
app/Services/Surreal/Schema/SurrealSchemaBuilder.php |
New schema builder that delegates table/column introspection & drops to Surreal. |
app/Services/Surreal/Schema/SurrealSchemaGrammar.php |
New schema grammar compiling Laravel schema commands into Surreal DEFINE/REMOVE statements. |
app/Services/Surreal/Schema/SurrealSchemaManager.php |
New manager running INFO queries and executing schema statements through the Surreal CLI. |
tests/Feature/SurrealSchemaDriverTest.php |
End-to-end test proving create/alter/drop via Schema::connection('surreal'). |
README.md |
Documents how to use Surreal-backed migrations and clarifies current scope. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $config, | ||
| ); | ||
| } | ||
|
|
There was a problem hiding this comment.
SurrealSchemaConnection is constructed with an in-memory SQLite PDO. That means any accidental use of DB::connection('surreal')->select()/insert()/transaction() (or other non-schema operations that hit the base Connection implementation) would silently run against SQLite instead of SurrealDB, producing incorrect behavior without obvious failures. Consider explicitly disabling query/transaction APIs on this connection (override the relevant methods to throw a clear RuntimeException), and/or override getDriverName() to surreal to avoid Laravel treating it as SQLite anywhere internally.
| public function getDriverName(): string | |
| { | |
| return 'surreal'; | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; data queries are not supported. | |
| * | |
| * @param string $query | |
| * @param array<int, mixed> $bindings | |
| * @param bool $useReadPdo | |
| * @return array<int, mixed> | |
| */ | |
| public function select($query, $bindings = [], $useReadPdo = true) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support select queries. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; data queries are not supported. | |
| * | |
| * @param string $query | |
| * @param array<int, mixed> $bindings | |
| * @param bool $useReadPdo | |
| * @return \Generator | |
| */ | |
| public function cursor($query, $bindings = [], $useReadPdo = true) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support cursors. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; data queries are not supported. | |
| * | |
| * @param string $query | |
| * @param array<int, mixed> $bindings | |
| * @return bool | |
| */ | |
| public function insert($query, $bindings = []) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support insert queries. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; data queries are not supported. | |
| * | |
| * @param string $query | |
| * @param array<int, mixed> $bindings | |
| * @return int | |
| */ | |
| public function update($query, $bindings = []) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support update queries. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; data queries are not supported. | |
| * | |
| * @param string $query | |
| * @param array<int, mixed> $bindings | |
| * @return int | |
| */ | |
| public function delete($query, $bindings = []) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support delete queries. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; transactions are not supported. | |
| * | |
| * @param \Closure $callback | |
| * @param int $attempts | |
| * @return mixed | |
| */ | |
| public function transaction(\Closure $callback, $attempts = 1) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support transactions. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; transactions are not supported. | |
| */ | |
| public function beginTransaction() | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support transactions. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; transactions are not supported. | |
| */ | |
| public function commit() | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support transactions. Use SurrealConnection for data access.'); | |
| } | |
| /** | |
| * SurrealSchemaConnection is schema-only; transactions are not supported. | |
| * | |
| * @param int|null $toLevel | |
| */ | |
| public function rollBack($toLevel = null) | |
| { | |
| throw new \RuntimeException('SurrealSchemaConnection does not support transactions. Use SurrealConnection for data access.'); | |
| } |
| function retryStartingSurrealSchemaServer(SurrealCliClient $client, string $storagePath, int $attempts = 3): array | ||
| { | ||
| $lastException = null; | ||
|
|
||
| for ($attempt = 1; $attempt <= $attempts; $attempt++) { | ||
| $port = reserveSurrealSchemaPort(); | ||
| $endpoint = sprintf('ws://127.0.0.1:%d', $port); | ||
| $process = $client->startLocalServer( | ||
| bindAddress: sprintf('127.0.0.1:%d', $port), | ||
| datastorePath: $storagePath, | ||
| username: 'root', | ||
| password: 'root', | ||
| storageEngine: 'surrealkv', | ||
| ); | ||
|
|
||
| if ($client->waitUntilReady($endpoint)) { | ||
| return [ | ||
| 'endpoint' => $endpoint, | ||
| 'port' => $port, | ||
| 'process' => $process, | ||
| ]; | ||
| } | ||
|
|
||
| $process->stop(1); | ||
| $lastException = new RuntimeException(sprintf('SurrealDB did not become ready on %s.', $endpoint)); | ||
| } | ||
|
|
||
| throw $lastException ?? new RuntimeException('Unable to start the SurrealDB schema test runtime.'); | ||
| } | ||
|
|
||
| function reserveSurrealSchemaPort(): int | ||
| { | ||
| $socket = stream_socket_server('tcp://127.0.0.1:0', $errorCode, $errorMessage); | ||
|
|
||
| if ($socket === false) { | ||
| throw new RuntimeException(sprintf('Unable to reserve a free TCP port: %s (%d)', $errorMessage, $errorCode)); | ||
| } | ||
|
|
||
| $address = stream_socket_get_name($socket, false); | ||
|
|
||
| fclose($socket); | ||
|
|
||
| if ($address === false) { | ||
| throw new RuntimeException('Unable to determine the reserved TCP port.'); | ||
| } | ||
|
|
||
| $port = (int) ltrim((string) strrchr($address, ':'), ':'); | ||
|
|
||
| if ($port <= 0) { | ||
| throw new RuntimeException(sprintf('Unable to parse the reserved TCP port from [%s].', $address)); | ||
| } | ||
|
|
||
| return $port; | ||
| } |
There was a problem hiding this comment.
This test duplicates the same port-reservation + retry-start-server helpers already present in other Surreal feature tests (e.g. retryStartingWorkspaceServer / reserveWorkspacePort). Consider extracting these helpers into a shared test support file/trait (or a Pest helper) to reduce duplication and keep the startup logic consistent across tests.
bbb174f to
e9ee757
Compare
Summary
Testing
Closes #113