TableRepository Guide#
Introduction#
TableRepository gives repository-oriented static ergonomics while keeping DBLayer’s
repository-first architecture.
It is useful when your app wants a class-centric API such as
User::find(1) and User::query()->... without adopting ORM behavior.
Class: Infocyph\DBLayer\Repository\TableRepository
When to Use TableRepository#
Use TableRepository when you want:
one class per table/service boundary
repository-oriented static method ergonomics
centralized table + default connection mapping
reusable repository/query configuration hooks
Avoid it when plain DB::table() one-off queries are sufficient and you do
not need a class abstraction.
Minimal Setup#
use Infocyph\DBLayer\Repository\TableRepository;
use Infocyph\DBLayer\Query\Repository;
final class User extends TableRepository
{
protected static string $table = 'users';
protected static ?string $connection = 'main';
protected static function configureRepository(Repository $repository): Repository
{
return $repository->enableSoftDeletes()->setDefaultOrder('id', 'desc');
}
}
$one = User::find(1); // Repository dispatch
$rows = User::query()->limit(20)->get(); // QueryBuilder dispatch
$stats = User::stats(); // DB facade dispatch
Dispatch Rules#
Unknown static calls resolve by priority:
Repository method
QueryBuilder method
DB facade method
Because repository/query are checked first, use explicit raw SQL helpers for DB-style raw operations:
sqlSelect()sqlStatement()sqlScalar()
Connection Control#
Set a default connection in static property:
protected static ?string $connection = 'main';
Override per call when needed:
$defaultRows = User::query()->get();
$reportRows = User::query('reporting')->get();
$reportCount = User::sqlScalar('select count(*) from users', [], 'reporting');
$reportRepoCount = User::repository('reporting')->count();
Scope Boundaries#
Use hooks for clear ownership:
configureRepository(): table policy and behaviorconfigureQuery(): query shape defaults
use Infocyph\DBLayer\Query\QueryBuilder;
use Infocyph\DBLayer\Query\Repository;
protected static function configureRepository(Repository $repository): Repository
{
return $repository
->forTenant(10)
->enableSoftDeletes()
->enableOptimisticLocking('version');
}
protected static function configureQuery(QueryBuilder $query): QueryBuilder
{
return $query->where('active', '=', 1);
}
Important chain behavior:
each static call creates a fresh repository/query context
chain in one expression when applying temporary scopes
// Good: temporary scope used immediately on same repository instance
$rows = User::forTenant(42)->get();
// Not persistent across future static calls:
User::forTenant(42);
$count = User::count(); // fresh context; does not reuse previous temporary call
Practical Use Cases#
Tenant-Aware Service Repository#
final class User extends TableRepository
{
protected static string $table = 'users';
public static function activeForTenant(int $tenantId)
{
return static::forTenant($tenantId)
->get(fn ($q) => $q->where('active', '=', 1));
}
}
Read/Write Split by Repository Class#
final class UserWriteRepository extends TableRepository
{
protected static string $table = 'users';
protected static ?string $connection = 'main';
}
final class UserReadRepository extends TableRepository
{
protected static string $table = 'users';
protected static ?string $connection = 'reporting';
}
Transactional Workflow#
User::transaction(function (): void {
$user = User::find(1);
if ($user !== null) {
User::where('id', '=', 1)->update(['name' => 'Updated']);
}
}, attempts: 2);
Operational DB Access Through Repository#
$health = User::health();
$caps = User::capabilities();
$version = User::version();
Raw SQL Through Repository#
$exists = User::sqlScalar('select count(*) from users where email = ?', ['[email protected]']);
User::sqlStatement('update users set active = ? where id = ?', [0, 10]);
$rows = User::sqlSelect('select id, email from users where active = ?', [1]);
Non-ORM Boundary#
TableRepository is intentionally not ORM:
no relationship mapping API
no unit-of-work
no dirty-state tracking
It is a static delegation layer over DBLayer components.
See Also#
api-table-repositoryfor method referencechoosing-apifor DB vs QueryBuilder vs Repository decisionsrepositoryfor repository capabilities used underTableRepository