定制 scafera/database 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

scafera/database

Composer 安装命令:

composer require scafera/database

包简介

Database persistence for the Scafera framework

README 文档

README

Database persistence for the Scafera framework. Wraps Doctrine ORM/DBAL internally — userland code never imports Doctrine types outside of entities and repositories.

Provides: Database persistence for Scafera — EntityStore (read/write), Transaction (savepoint-based nesting; unflushed writes throw at end-of-request), Scafera-owned mapping attributes under Scafera\Database\Mapping\Field\*, and a Doctrine-free Schema API for migrations. Doctrine ORM/DBAL is wrapped internally.

Depends on: A Scafera host project with a DATABASE_URL env var (set in config/config.yaml under env: or as OS env), entities in src/Entity/, repositories in src/Repository/, migrations in support/migrations/, and seeders in support/seeds/.

Extension points:

  • Contract — SeederInterface (auto-tagged scafera.seeder, collected by db:seed)
  • Migrations — extend Scafera\Database\Migration; up() / down() use the Schema API (Schema::create / modify / drop, chainable column modifiers, no Doctrine imports)
  • Mapping attributes — 18 field types under Scafera\Database\Mapping\Field\* (Id, Varchar, Text, Integer, Decimal, Money, DateTime, Json, Uuid, … plus Column as an escape hatch for custom Doctrine types); #[Table] for table-name override; Auditable trait for createdAt / updatedAt
  • Controlled zones — src/Entity/ forbids Doctrine imports (Scafera mapping only); src/Repository/ allows Doctrine QueryBuilder / DQL / DBAL
  • Config — override via doctrine: in config/config.yaml (note: engine name leaks; future database: key planned)

Not responsible for: Doctrine imports outside src/Entity/ and src/Repository/ (blocked by DoctrineBoundaryPass) · lifecycle callbacks on entities (detected and rejected) · table-name pluralization (singular snake_case per ADR-050) · engine abstraction in config (doctrine: key leaks engine name; mapping to database: planned) · test fixtures beyond seeders.

This is a capability package. It adds optional persistence to a Scafera project. It does not define folder structure or architectural rules — those belong to architecture packages.

Installation

composer require scafera/database

The bundle is auto-discovered via Scafera's symfony-bundle type detection. No manual registration needed.

Requirements

  • PHP 8.4+
  • scafera/kernel ^1.0
  • A DATABASE_URL environment variable (set in config/config.yaml under env: or as an OS env var)

Runtime API

EntityStore

The primary persistence interface. Inject it in repositories.

use Scafera\Database\EntityStore;

final class OrderRepository
{
    public function __construct(
        private readonly EntityStore $entityStore,
    ) {}

    public function find(int $id): ?Order
    {
        return $this->entityStore->find(Order::class, $id);
    }

    public function save(Order $order): void
    {
        $this->entityStore->persist($order);
    }

    public function remove(Order $order): void
    {
        $this->entityStore->remove($order);
    }
}

Transaction

Wraps writes in an explicit transaction. All persist() and remove() calls must be committed through Transaction::run() — unflushed writes are detected and throw at the end of the request/command.

use Scafera\Database\EntityStore;
use Scafera\Database\Transaction;

final class OrderService
{
    public function __construct(
        private readonly EntityStore $entityStore,
        private readonly Transaction $tx,
    ) {}

    public function placeOrder(array $data): Order
    {
        return $this->tx->run(function () use ($data): Order {
            $order = new Order();
            $order->setTotal($data['total']);
            $this->entityStore->persist($order);

            return $order;
        });
    }
}

Nested Transaction::run() calls are safe — inner calls use database savepoints. Only the outermost level flushes and commits. If an inner call throws and the outer catches it, the inner changes are rolled back via savepoint while the outer transaction can still commit.

Entities

Entities use Scafera mapping attributes from Scafera\Database\Mapping\Field. These map to Doctrine types internally but keep your entities free of Doctrine imports.

use Scafera\Database\Mapping\Field;

final class Order
{
    #[Field\Id]
    private ?int $id = null;

    #[Field\Decimal(precision: 10, scale: 2)]
    private string $total;

    public function __construct(
        #[Field\Varchar]
        private string $customerName,
    ) {}
}

Available field attributes: Id, Varchar, VarcharShort, Text, Integer, IntegerBig, IntegerBigPositive, Boolean, Decimal, Money, Percentage, Date, DateTime, Time, UnixTimestamp, Json, Uuid, Column (escape hatch for custom Doctrine types).

For types not covered by the built-in attributes, use Column as an escape hatch:

#[Field\Column(type: 'string', options: ['length' => 15])]
private string $isoCode;

Use the Auditable trait for createdAt/updatedAt timestamp fields. You must initialize $this->createdAt = new \DateTimeImmutable() in your entity constructor — the AuditableInitValidator will warn if you forget.

Table Names

Table names are derived as singular snake_case from the class name: Orderorder, BlogPostblog_post. No pluralization is applied (see ADR-050). To override the default, use #[Table]:

use Scafera\Database\Mapping\Field;
use Scafera\Database\Mapping\Table;

#[Table(name: 'categories')]
final class Category
{
    #[Field\Id]
    private ?int $id = null;
}

Repositories

Repositories are the only zone where direct Doctrine usage is allowed. The DoctrineBoundaryPass enforces this at build time.

Zone Doctrine Imports Notes
src/Entity/ Forbidden Use Scafera\Database\Mapping\Field attributes. Lifecycle callbacks detected and rejected.
src/Repository/ Allowed Controlled leakage — QueryBuilder, DQL, DBAL all permitted.
Everywhere else Forbidden Except Doctrine\Common\Collections (data structure, not behavioral).

Migrations

Migration files live in support/migrations/ and use the Scafera Schema API — zero Doctrine imports.

Schema API

use Scafera\Database\Migration;
use Scafera\Database\Schema\Schema;
use Scafera\Database\Schema\Table;

final class Version20260403080718 extends Migration
{
    public function up(Schema $schema): void
    {
        $schema->create('page', function (Table $table) {
            $table->id();
            $table->string('title', 255);
            $table->string('slug', 255);
            $table->text('content');
            $table->boolean('published');
            $table->timestamp('createdAt');
        });
    }

    public function down(Schema $schema): void
    {
        $schema->drop('page');
    }
}

Column Types (v1)

Method Doctrine Type Arguments
id() integer (auto-increment PK)
string($name, $length) string length (default 255)
text($name) text
integer($name) integer
bigInteger($name) bigint
smallInteger($name) smallint
boolean($name) boolean
timestamp($name) datetime_immutable
date($name) date_immutable
decimal($name, $precision, $scale) decimal precision (default 8), scale (default 2)
json($name) json

Column Modifiers

Column methods return a ColumnBuilder. Chain modifiers on it:

$table->string('bio')->nullable();
$table->boolean('active')->default(true);
$table->string('notes')->nullable()->default('');

Schema Operations

// Create a table
$schema->create('users', function (Table $table) { ... });

// Drop a table (destructive)
$schema->drop('users');

// Modify an existing table
$schema->modify('users', function (Table $table) {
    $table->string('email', 255);       // add column
    $table->dropColumn('legacy_field');  // drop column (destructive)
});

Planned Schema Enhancements

The following operations are planned as future additions to the Schema API:

  • Indexes (regular, unique, composite)
  • Foreign key constraints

Destructive Detection

Operations are classified as safe or destructive by type:

Operation Destructive
CreateTable No
AddColumn No
DropTable Yes
DropColumn Yes

db:migrate checks pending migrations before execution:

  • Development (APP_ENV=dev): warns about destructive operations, proceeds
  • Production (APP_ENV=prod): blocks execution unless --force is passed

CLI Commands

All commands are available via vendor/bin/scafera:

# Generate a migration from entity changes
vendor/bin/scafera db:migrate:diff

# Create a blank migration for manual editing
vendor/bin/scafera db:migrate:create

# Generate a migration to drop a specific table
vendor/bin/scafera db:migrate:drop orders

# Run pending migrations
vendor/bin/scafera db:migrate

# Show migration status
vendor/bin/scafera db:migrate:status

# Rollback the last migration
vendor/bin/scafera db:migrate:rollback

# Drop all tables and re-run all migrations (requires --force)
vendor/bin/scafera db:reset --force

# Run seeders from support/seeds/
vendor/bin/scafera db:seed

# List all database tables with column/row counts
vendor/bin/scafera db:schema:list

# Show column definitions for a table
vendor/bin/scafera db:schema:show orders

# Show mismatches between entities and database
vendor/bin/scafera db:schema:diff

Seeding

Seeders live in support/seeds/ and are auto-discovered via the scafera.seeder tag:

namespace App\Seed;

use Scafera\Database\EntityStore;
use Scafera\Database\SeederInterface;
use Scafera\Database\Transaction;

final class PageSeed implements SeederInterface
{
    public function __construct(
        private readonly EntityStore $entityStore,
        private readonly Transaction $transaction,
    ) {}

    public function run(): void
    {
        $this->transaction->run(function (): void {
            $page = new \App\Entity\Page();
            $page->setTitle('Welcome');
            $page->setSlug('welcome');
            $page->setContent('Hello world.');
            $page->setPublished(true);
            $this->entityStore->persist($page);
        });
    }
}

Configuration

The bundle configures Doctrine defaults automatically:

  • DBAL: reads DATABASE_URL from env
  • ORM: maps App\Entity from src/Entity/ with attribute mapping
  • Migrations: stores migration files in support/migrations/ under the App\Migrations namespace

To override Doctrine config, add a doctrine: section to config/config.yaml. Note: this leaks the engine name — a database: config key mapping is planned (see Roadmap).

License

MIT

scafera/database 适用场景与选型建议

scafera/database 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 18 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 13 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「database」 「orm」 「persistence」 「php」 「doctrine」 「scafera」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 scafera/database 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 scafera/database 我们能提供哪些服务?
定制开发 / 二次开发

基于 scafera/database 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 18
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 37
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-13