PHPackages                             colopl/laravel-spanner - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. colopl/laravel-spanner

ActiveLibrary[Database &amp; ORM](/categories/database)

colopl/laravel-spanner
======================

Laravel database driver for Google Cloud Spanner

v10.0.0(10mo ago)101124.2k—8.6%14[6 issues](https://github.com/colopl/laravel-spanner/issues)[1 PRs](https://github.com/colopl/laravel-spanner/pulls)1Apache-2.0PHPPHP ^8.2

Since Feb 20Pushed 5mo ago13 watchersCompare

[ Source](https://github.com/colopl/laravel-spanner)[ Packagist](https://packagist.org/packages/colopl/laravel-spanner)[ RSS](/packages/colopl-laravel-spanner/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (9)Versions (79)Used By (1)

laravel-spanner
===============

[](#laravel-spanner)

Laravel database driver for Google Cloud Spanner

[![License](https://camo.githubusercontent.com/50fcf6f596e66a43c9e14c26e0e04af62044914c195fcc3326e732d8229042de/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f636f6c6f706c2f6c61726176656c2d7370616e6e65722e7376673f7374796c653d666c61742d737175617265)](https://github.com/colopl/laravel-spanner/blob/master/LICENSE)[![Latest Stable Version](https://camo.githubusercontent.com/a74add671c4a05a1df0316986d9c66983e27948299f00af4a6aab84b0f4522b4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636f6c6f706c2f6c61726176656c2d7370616e6e65722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/colopl/laravel-spanner)[![Minimum PHP Version](https://camo.githubusercontent.com/59ce68c8e8232c6673fc99f60207fece550fa8b4f6b91e761b47d865dbe4a028/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f636f6c6f706c2f6c61726176656c2d7370616e6e65722e7376673f7374796c653d666c61742d737175617265)](https://secure.php.net/)

Requirements
------------

[](#requirements)

- PHP &gt;= 8.2
- Laravel &gt;= 11
- [gRPC extension](https://cloud.google.com/php/grpc)
- [protobuf extension](https://cloud.google.com/php/grpc#install_the_protobuf_runtime_library) (recommended for better performance)
- `sysvmsg`, `sysvsem`, `sysvshm` extensions (recommended for better performance)

Installation
------------

[](#installation)

Put JSON credential file path to env variable: `GOOGLE_APPLICATION_CREDENTIALS`

```
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json

```

Install via composer

```
composer require colopl/laravel-spanner
```

Add connection config to `config/database.php`

```
[
    'connections' => [
        'spanner' => [
            'driver' => 'spanner',
            'instance' => '',
            'database' => '',
        ]
    ]
];
```

That's all. You can use database connection as usual.

```
$conn = DB::connection('spanner');
$conn->...
```

Additional Configurations
-------------------------

[](#additional-configurations)

You can pass `SpannerClient` config and `CacheSessionPool` options as below. For more information, please see [Google Client Library docs](http://googleapis.github.io/google-cloud-php/#/docs/google-cloud/latest/spanner/spannerclient?method=__construct)

```
[
    'connections' => [
        'spanner' => [
            'driver' => 'spanner',
            'instance' => '',
            'database' => '',

            // Spanner Client configurations
            'client' => [
                'projectId' => 'xxx',
                ...
            ],

            // CacheSessionPool options
            'session_pool' => [
                'minSessions' => 10,
                'maxSessions' => 500,
            ],
        ]
    ]
];
```

Recommended Setup
-----------------

[](#recommended-setup)

Please note that the following are not required, but are strongly recommended for better performance.

- Install `protobuf` pecl extension for faster network communication.
- Install `sysvmsg`, `sysvsem`, `sysvshm` extensions for faster session management.
- Mount the cache directory (`./storage/framework/spanner` by default) to tmpfs for better session io performance. Cache path can be changed by setting `connections.{name}.cache_path` in your `config/database.php` file.

Unsupported features
--------------------

[](#unsupported-features)

- STRUCT data types
- Inserting/Updating JSON data types

Limitations
-----------

[](#limitations)

### SQL Mode

[](#sql-mode)

Currently only supports Spanner running GoogleSQL (PostgreSQL mode is not supported).

### Query

[](#query)

- [Binding more than 950 parameters in a single query will result in an error](https://cloud.google.com/spanner/quotas#query-limits)by the server. In order to by-pass this limitation, this driver will attempt to switch to using `Query\Builder::whereInUnnest(...)`internally when the passed parameter exceeds the limit set by `parameter_unnest_threshold` config (default: `900`). You can turn this feature off by setting the value to `false`.

### Eloquent

[](#eloquent)

If you use interleaved keys, you MUST define them in the `interleaveKeys` property, or else you won't be able to save. For more detailed instructions, see `Colopl\Spanner\Tests\Eloquent\ModelTest`.

Additional Information
----------------------

[](#additional-information)

### Migrations

[](#migrations)

Since Spanner recommends using UUID as a primary key, `Blueprint::increments` (and all of its variants) will create a column of type `STRING(36) DEFAULT (GENERATE_UUID())` to generate and fill the column with a UUID and flag it as a primary key. If you want to use `AUTO_INCREMENT`, you can do so by specifying it directly like this:

```
// `default_sequence_kind` must be set in order to use auto increment
$schemaBuilder->setDatabaseOptions([
    'default_sequence_kind' => 'bit_reversed_positive',
]);

$schemaBuilder->create('user', function (Blueprint $table) {
    $table->integer('id')->primary()->autoIncrement();
});
```

### Transactions

[](#transactions)

Google Cloud Spanner sometimes requests transaction retries (e.g. `UNAVAILABLE`, and `ABORTED`), even if the logic is correct. For that reason, please do not manage transactions manually.

You should always use the `transaction` method which handles retry requests internally.

```
// BAD: Do not use transactions manually!!
try {
    DB::beginTransaction();
    ...
    DB::commit();
} catch (\Throwable $ex) {
    DB::rollBack();
}

// GOOD: You should always use transaction method
DB::transaction(function() {
    ...
});
```

Google Cloud Spanner creates transactions for all data operations even if you do not explicitly create transactions.

In particular, in the SELECT statement, the type of transaction varies depending on whether it is explicit or implicit.

```
// implicit transaction (Read-only transaction)
$conn->select('SELECT ...');

// explicit transaction (Read-write transaction)
$conn->transaction(function() {
    $conn->select('SELECT ...');
});

// implicit transaction (Read-write transaction)
$conn->insert('INSERT ...');

// explicit transaction (Read-write transaction)
$conn->transaction(function() {
    $conn->insert('INSERT ...');
});
```

Transaction type**SELECT** statement**INSERT/UPDATE/DELETE** statementimplicit transaction**Read-only** transaction with **singleUse** option**Read-write** transaction with **singleUse** optionexplicit transaction**Read-write** transaction**Read-write** transactionFor more information, see [Cloud Spanner Documentation about transactions](https://cloud.google.com/spanner/docs/transactions)

### Stale reads

[](#stale-reads)

You can use [Stale reads (timestamp bounds)](https://cloud.google.com/spanner/docs/timestamp-bounds) as below.

```
// There are four types of timestamp bounds: ExactStaleness, MaxStaleness, MinReadTimestamp and ReadTimestamp.
$timestampBound = new ExactStaleness(10);

// by Connection
$connection->selectWithTimestampBound('SELECT ...', $bindings, $timestampBound);

// by Query Builder
$queryBuilder
    ->withStaleness($timestampBound)
    ->get();
```

Stale reads always runs as read-only transaction with `singleUse` option. So you can not run as read-write transaction.

### Snapshot reads

[](#snapshot-reads)

You can use explicit Snapshot reads, either on `Connection`, or on `Model` or `Builder` instances. When running `snapshot()` on `Connection`, you pass a `Closure` that you can use to run multiple reads from within the same Snapshot.

```
$timestampBound = new ExactStaleness(10);

// by Connection
$connection->snapshot($timestampBound, function() use ($connection) {
    $result1 = $connection->table('foo')->get();
    $result2 = $connection->table('bar')->get();

    return [$result1, $result2];
);

// by Model
User::where('foo', 'bar')
    ->snapshot($timestampBound)
    ->get();

// by Query Builder
$queryBuilder
    ->snapshot($timestampBound)
    ->get();
```

### Data Boost

[](#data-boost)

Data boost creates snapshot and runs the query in parallel without affecting existing workloads.

You can read more about it [here](https://cloud.google.com/spanner/docs/databoost/databoost-overview).

Below are some examples of how to use it.

```
// Using Connection
$connection->selectWithOptions('SELECT ...', $bindings, ['dataBoostEnabled' => true]);

// Using Query Builder
$queryBuilder
    ->useDataBoost()
    ->setRequestTimeoutSeconds(60)
    ->get();
```

Note

This creates a new session in the background which is not shared with the current session pool. This means, queries running with data boost will not be associated with transactions that may be taking place.

### Request Tags and Transaction Tags

[](#request-tags-and-transaction-tags)

Spanner allows you to attach tags to your queries and transactions that can be [used for troubleshooting](https://cloud.google.com/spanner/docs/introspection/troubleshooting-with-tags).

You can set request tags and transaction tags as below.

```
$requestPath = request()->path();
$tag = 'url=' . $requestPath;
$connection->setRequestTag($tag);
$connection->setTransactionTag($tag);
```

### Data Types

[](#data-types)

Some data types of Google Cloud Spanner does not have corresponding built-in type of PHP. You can use following classes by [Google Cloud PHP Client](https://github.com/googleapis/google-cloud-php)

- BYTES: `Google\Cloud\Spanner\Bytes`
- DATE: `Google\Cloud\Spanner\Date`
- NUMERIC: `Google\Cloud\Spanner\Numeric`
- TIMESTAMP: `Google\Cloud\Spanner\Timestamp`

When fetching rows, the library coverts the following column types

- `Timestamp` -&gt; [Carbon](https://laravel.com/api/10.x/Illuminate/Support/Carbon.html) with the default timezone in PHP
- `Numeric` -&gt; `string`

Note that if you execute a query without QueryBuilder, it will not have these conversions.

### Partitioned DML

[](#partitioned-dml)

You can run partitioned DML as below.

```
// by Connection
$connection->runPartitionedDml('UPDATE ...');

// by Query Builder
$queryBuilder->partitionedUpdate($values);
$queryBuilder->partitionedDelete();
```

However, Partitioned DML has some limitations. See [Cloud Spanner Documentation about Partitioned DML](https://cloud.google.com/spanner/docs/dml-partitioned#dml_and_partitioned_dml) for more information.

### Interleave

[](#interleave)

You can define [interleaved tables](https://cloud.google.com/spanner/docs/schema-and-data-model#creating_a_hierarchy_of_interleaved_tables) as below.

```
$schemaBuilder->create('user_items', function (Blueprint $table) {
    $table->uuid('user_id');
    $table->uuid('id');
    $table->uuid('item_id');
    $table->integer('count');
    $table->timestamps();

    $table->primary(['user_id', 'id']);

    // interleaved table
    $table->interleaveInParent('users')->cascadeOnDelete();

    // interleaved index
    $table->index(['userId', 'created_at'])->interleaveIn('users');
});
```

### Row Deletion Policy

[](#row-deletion-policy)

You can define [row deletion policy](https://cloud.google.com/spanner/docs/ttl/working-with-ttl) as below.

```
$schemaBuilder->create('user', function (Blueprint $table) {
    $table->uuid('user_id');
    $table->timestamps();

    // create a policy
    $table->deleteRowsOlderThan(['updated_at'], 365);
});

$schemaBuilder->table('user', function (Blueprint $table) {
    // add policy
    $table->addRowDeletionPolicy('udpated_at', 100);

    // replace policy
    $table->replaceRowDeletionPolicy('udpated_at', 100);

    // drop policy
    $table->dropRowDeletionPolicy();
});
```

### Sequence

[](#sequence)

If you want a simple sequence to be used as a primary key, you can use `useSequence()` method. If `useSequence()` is called without providing a `$name`, a sequence with name `user_id_sequence` will be created with `start_with_counter` set with a random value between 1 and 1,000,000.

```
$schemaBuilder->create('user', function (Blueprint $table) {
    $table->integer('id')->useSequence();
});
```

If you want more flexibility, you can also create, alter, and drop sequences directly as below.

```
$schemaBuilder->create('user_items', function (Blueprint $table) {
    $table->createSequence('sequence_name');
    $table->integer('id')->useSequence('sequence_name');

    $table->alterSequence('sequence_name')
        ->startWithCounter(100)
        ->skipRangeMin(1)
        ->skipRangeMax(10);

    $table->dropSequence('sequence_name');
});
```

### Change Streams

[](#change-streams)

Spanner supports [Change Streams](https://cloud.google.com/spanner/docs/change-streams) which allows you to listen to changes in the database. Change streams can be created/altered/dropped through the schema builder as shown below.

```
$schemaBuilder->create('user_items', function (Blueprint $table) {
    $table->createChangeStream('stream_name')
        ->for('user_items', ['userId', 'userItemId'])
        ->retentionPeriod('7d')
        ->valueCaptureType(ChangeStreamValueCaptureType::NewValues)
        ->excludeTtlDeletes(true);

    $table->createChangeStream('stream_name')
        ->excludeInsert(true)
        ->excludeUpdate(true)
        ->excludeDelete(true);

    $table->dropChangeStream('stream_name');
});
```

### Full Text Search

[](#full-text-search)

Spanner supports [Full Text Search](https://cloud.google.com/spanner/docs/full-text-search) which allows you to search for text in columns.

You can define a token list column and a search index for the column as below.

```
$schemaBuilder->create('user', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('name');
    // adds an invisible column for full text search
    $table->tokenList('UserNameTokens', TokenizerFunction::FullText, 'name', ['language_tag' => 'en']);

    // adds a SEARCH INDEX
    $table->fullText(['UserNameTokens']);
});
```

Once the schema has been applied, you can use the search methods in the query builder to search for text in the columns as below.

```
User::query()->searchFullText('UserNameTokens', 'John OR Kevin', ['enhance_query' => true])->get();
```

The methods available are `searchFullText`, `searchSubstring`, and `searchNgrams`.

### Secondary Index Options

[](#secondary-index-options)

You can define Spanner specific index options like [null filtering](https://cloud.google.com/spanner/docs/secondary-indexes#null-indexing-disable) and [storing](https://cloud.google.com/spanner/docs/secondary-indexes#storing-clause) as below.

```
$schemaBuilder->table('user_items', function (Blueprint $table) {
    $table->index('userId')
        // Interleave in parent table
        ->interleaveIn('user')
        // Add null filtering
        ->nullFiltered()
        // Add storing
        ->storing(['itemId', 'count']);
});
```

### Mutations

[](#mutations)

You can [insert, update, and delete data using mutations](https://cloud.google.com/spanner/docs/modify-mutation-api) to modify data instead of using DML to improve performance.

```
$queryBuilder->insertUsingMutation($values);
$queryBuilder->updateUsingMutation($values);
$queryBuilder->insertOrUpdateUsingMutation($values);
$queryBuilder->deleteUsingMutation($values);

```

Please note that mutation api does not work the same way as DML. All mutations calls within a transaction are queued and sent as batch at the time you commit. This means that if you make any modifications through the above functions and then try to SELECT the same records before committing, the returned results will not include any of the modifications you've made inside the transaction.

### SessionPool and AuthCache

[](#sessionpool-and-authcache)

In order to improve the performance of the first connection per request, we use [AuthCache](https://github.com/googleapis/google-cloud-php#caching-access-tokens) and [CacheSessionPool](https://googleapis.github.io/google-cloud-php/#/docs/google-cloud/latest/spanner/session/cachesessionpool).

By default, this library uses [Filesystem Cache Adapter](https://symfony.com/doc/current/components/cache/adapters/filesystem_adapter.html) as the caching pool. If you want to use your own caching pool, you can extend ServiceProvider and inject it into the constructor of `Colopl\Spanner\Connection`.

The initialization of each session takes about a second, so warming up the sessions during the boot up phase of your server is recommended. This can be achieved by running the `php artisan spanner:warmup` command. You can set the number of sessions to warm up by setting the `connections.{name}.session_pool.maxSessions` option in `config/database.php`

Similarly, the sessions remain active for 60 minutes after use so deleting the sessions during the shutdown phase of your server is recommended. This can be achieved by running the `php artisan spanner:cooldown` command.

### Queue Worker

[](#queue-worker)

After every job is processed, the connection will be disconnected so the session can be released into the session pool. This allows the session to be renewed (through `maintainSessionPool()`) or expire.

### Laravel Tinker

[](#laravel-tinker)

You can use [Laravel Tinker](https://github.com/laravel/tinker) with commands such as `php artisan tinker`. But your session may hang when accessing Cloud Spanner. This is known gRPC issue that occurs when PHP forks a process. The workaround is to add following line to `php.ini`.

```
grpc.enable_fork_support=1
```

Development
-----------

[](#development)

### Testing

[](#testing)

You can run tests on docker by the following command. Note that some environment variables must be set. In order to set the variables, rename [.env.sample](./.env.sample) to `.env` and edit the values of the defined variables.

NameValue`GOOGLE_APPLICATION_CREDENTIALS`The path of the service account key file with access privilege to Google Cloud Spanner instance`DB_SPANNER_INSTANCE_ID`Instance ID of your Google Cloud Spanner`DB_SPANNER_DATABASE_ID`Name of the database with in the Google Cloud Spanner instance`DB_SPANNER_PROJECT_ID`Not required if your credential includes the project ID```
make test
```

License
-------

[](#license)

Apache 2.0 - See [LICENSE](./LICENSE) for more information.

###  Health Score

60

—

FairBetter than 99% of packages

Maintenance62

Regular maintenance activity

Popularity47

Moderate usage in the ecosystem

Community26

Small or concentrated contributor base

Maturity88

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 82.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~30 days

Recently: every ~17 days

Total

78

Last Release

300d ago

Major Versions

5.x-dev → v8.3.02024-12-25

7.x-dev → v8.6.02025-03-17

v8.6.0 → v9.0.02025-04-07

v8.7.0 → v9.1.02025-05-29

v9.3.0 → v10.0.02025-07-23

PHP version history (5 changes)v3.2.0PHP &gt;=7.1

v3.6.0PHP &gt;=7.2

v4.0.0PHP &gt;=8

v5.1.0PHP ^8.1

v8.0.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/ec4f701f1c6b107218dbe17f7218ab1b9ff2889e7a543ab4065aba59786ac874?d=identicon)[COLOPL, Inc.](/maintainers/COLOPL,%20Inc.)

---

Top Contributors

[![taka-oyama](https://avatars.githubusercontent.com/u/748854?v=4)](https://github.com/taka-oyama "taka-oyama (299 commits)")[![castaneai](https://avatars.githubusercontent.com/u/562795?v=4)](https://github.com/castaneai "castaneai (21 commits)")[![matthewjumpsoffbuildings](https://avatars.githubusercontent.com/u/41524?v=4)](https://github.com/matthewjumpsoffbuildings "matthewjumpsoffbuildings (14 commits)")[![oprudkyi](https://avatars.githubusercontent.com/u/3018472?v=4)](https://github.com/oprudkyi "oprudkyi (11 commits)")[![zeriyoshi](https://avatars.githubusercontent.com/u/10289597?v=4)](https://github.com/zeriyoshi "zeriyoshi (6 commits)")[![tyabu12](https://avatars.githubusercontent.com/u/9159475?v=4)](https://github.com/tyabu12 "tyabu12 (3 commits)")[![erjanmx](https://avatars.githubusercontent.com/u/4899432?v=4)](https://github.com/erjanmx "erjanmx (1 commits)")[![sasezaki](https://avatars.githubusercontent.com/u/42755?v=4)](https://github.com/sasezaki "sasezaki (1 commits)")[![MatthewHallCom](https://avatars.githubusercontent.com/u/1230091?v=4)](https://github.com/MatthewHallCom "MatthewHallCom (1 commits)")[![dkop](https://avatars.githubusercontent.com/u/980674?v=4)](https://github.com/dkop "dkop (1 commits)")[![Xfaider48](https://avatars.githubusercontent.com/u/13987970?v=4)](https://github.com/Xfaider48 "Xfaider48 (1 commits)")[![shailesh-matariya](https://avatars.githubusercontent.com/u/19707310?v=4)](https://github.com/shailesh-matariya "shailesh-matariya (1 commits)")[![halnique](https://avatars.githubusercontent.com/u/10358623?v=4)](https://github.com/halnique "halnique (1 commits)")

---

Tags

google-cloud-platformgoogle-cloud-spannerlaravelphp

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/colopl-laravel-spanner/health.svg)

```
[![Health](https://phpackages.com/badges/colopl-laravel-spanner/health.svg)](https://phpackages.com/packages/colopl-laravel-spanner)
```

###  Alternatives

[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11120.2M21](/packages/anourvalar-eloquent-serialize)[leantime/leantime

Open source project management system for non-project managers. Simple like Trello, powerful like Jira. Built with neurodiversity in mind.

9.4k2.8k](/packages/leantime-leantime)[overtrue/laravel-versionable

Make Laravel model versionable.

585308.0k5](/packages/overtrue-laravel-versionable)[damienharper/auditor

The missing audit log library.

1922.8M8](/packages/damienharper-auditor)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135192.6k5](/packages/statamic-rad-pack-runway)[dragon-code/laravel-deploy-operations

Performing any actions during the deployment process

240173.5k2](/packages/dragon-code-laravel-deploy-operations)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
