affordablemobiles / eloquent-datastore
A package for using Google Datastore as a database driver.
Installs: 4 377
Dependents: 1
Suggesters: 0
Security: 0
Stars: 4
Watchers: 3
Forks: 3
Type:package
pkg:composer/affordablemobiles/eloquent-datastore
Requires
- php: ^8.3
- ext-json: *
- affordablemobiles/laravel-eloquent-query-cache: ~12
- google/cloud-datastore: ^1.32.3
- illuminate/database: ~12
- illuminate/http: ~12
- illuminate/pagination: ~12
- illuminate/support: ~12
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.77
- phpunit/phpunit: ^10.5.35|^11.5.3|^12.0.1
This package is auto-updated.
Last update: 2025-10-23 14:55:55 UTC
README
A package for using Google Datastore as a database driver.
By using this package, you can use query builder and eloquent to access data from datastore.
Installation
This package requires PHP 8.3 as a minimum.
This branch targets Laravel 12.x. For compatibility with other Laravel versions, please see the appropriate branch.
You can install the package via composer:
composer require affordablemobiles/eloquent-datastore
If you are using Laravel Package Auto-Discovery, you don't need you to manually add the ServiceProvider.
Without auto-discovery:
If you don't use auto-discovery, add the below ServiceProvider to the $providers array in config/app.php file.
AffordableMobiles\EloquentDatastore\DatastoreServiceProvider::class,
Roadmap
- Read data using query builder.
- Read data using Eloquent model.
- Insert data using Eloquent model.
- Update data using Eloquent model.
- Delete data.
- Keys only queries.
- Auto-generated primary key.
- Read multiple pages of data with Datastore cursors.
- Batch update from Eloquent collection.
-
cursorPaginate(Datastore cursors are used forchunkandlazy, but full pagination is not yet implemented) - Ancestor key relations.
- Datastore Namespaces (Multi-Tenancy).
Usage
You need to add datastore connection in config/database.php file.
'connections' => [ ... 'datastore' => [ 'driver' => 'datastore', 'transport' => env('DATASTORE_TRANSPORT', 'grpc'), ], ... ],
Access using Eloquent Model
You need to extend AffordableMobiles\EloquentDatastore\Eloquent\Model class instead of Laravel's default Eloquent model class.
Example-
<?php namespace App\Models; use AffordableMobiles\EloquentDatastore\Eloquent\Model; class Project extends Model { // Your works here }
Access using Query Builder
Example-
DB::connection('datastore') ->table('projects') ->where('project_id', '>', 5) ->skip(3) ->take(5) ->get();
It will return a collection.
Understanding Primary Keys: __key__ vs. $primaryKey vs. id
To use this driver effectively, it's important to understand how it handles Datastore's keys.
1. The "Ground Truth": Datastore's __key__
In Google Datastore, the true primary key for every entity is the __key__ property. This is a complex Key object that contains the Kind, the scalar identifier (a string name or numeric ID), and any ancestor information. All final database operations (lookups, saves, deletes) must use this Key object.
2. The Eloquent Layer (Recommended)
The Eloquent Model (e.g., App\Models\User) provides a high-level, model-aware abstraction.
- The
$primaryKeyis an ALIAS: The$primaryKeyproperty on your model (which defaults to'id') is just a convenient alias for the scalar identifier (the string/int) part of the__key__. - Customization: You can change this alias. If you set
protected $primaryKey = 'uuid';on yourUsermodel, the driver will automatically handle all the mapping. - Two-Way Mapping:
- Query (Out):
User::where('uuid', 'my-uuid-string')->first()is automatically translated by the driver into a...WHERE __key__ = Key('User', 'my-uuid-string')query. - Hydration (In): When the driver fetches data, it automatically populates the
uuidattribute on your model with the key's identifier.
- Query (Out):
// With: protected $primaryKey = 'uuid'; // GOOD: This all works as you'd expect. $user = User::where('uuid', 'my-uuid-string')->first(); $user = User::find('my-uuid-string'); $user = User::firstOrCreate(['uuid' => 'my-uuid-string']); $uuid = $user->uuid;
3. The Base Query Builder (The "Escape Hatch")
If you bypass Eloquent and use the base Query Builder (e.g., DB::table('users') or User::query()->toBase()), you are in a model-agnostic layer. This layer does not know about your model's $primaryKey = 'uuid' alias.
- Fixed Convention: To provide a consistent way to query keys at this level, the driver's query processor synthesizes the key's scalar identifier into a single, hardcoded property named
id. - You MUST use
id: When using the base Query Builder, you must use'id'to query the key's identifier, regardless of what your Eloquent model's$primaryKeyis set to.
// GOOD: This works, even if the model's $primaryKey is 'uuid'. $user = DB::table('users')->where('id', 'my-uuid-string')->first(); // BAD: This will NOT work. // The query builder will look for a *data property* named 'uuid', // not the entity's key, because it is model-agnostic. $user = DB::table('users')->where('uuid', 'my-uuid-string')->first();
Tested Builder Functions
-
connection -
table -
from -
namespace(Datastore Namespace: Multi Tenancy) -
select(for projection query) -
kind(same as table) -
where(Available: = , > , < , >= , <= ) -
limit/take -
skip -
orderBy -
distinct -
get -
pluck -
exists -
count(Note: This performs a keys-only query and counts the results; it is not a high-performance aggregation. Avoid usingcount()withoutwhereclauses on very large kinds, as it may be slow and costly.) -
simplePaginate -
paginate(works same as simplePaginate) -
first -
delete -
insert -
_upsert(different and incompatible with defaultupsert) -
find/lookup -
chunk/chunkMap/each -
lazy/cursor
Contribution Guide
You can contribute by reporting bugs, fixing bugs, submitting and reviewing pull requests.
Go to issues section, and you can start working on an issue immediately.
License
The MIT License (MIT). Please see License File for more information.