定制 brighten/immutable-model 二次开发

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

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

brighten/immutable-model

Composer 安装命令:

composer require brighten/immutable-model

包简介

An Eloquent-compatible, read-only model kernel for Laravel 11+

README 文档

README

An Eloquent-compatible, read-only model kernel for Laravel 11+

ImmutableModel provides first-class, enforceable read-only models for Laravel applications. It's perfect for SQL views, read-only tables, denormalized projections, and as a CQRS read-side primitive.

What "Immutable" Means

ImmutableModel enforces database immutability, not strict object immutability:

Operation Allowed? Example
Read from database ✅ Yes User::find(1), User::where(...)->get()
In-memory attribute changes ✅ Yes $user->computed_field = 'value'
Database persistence ❌ Throws $user->save(), $user->update(), $user->delete()
Static write methods ❌ Throws User::create(), User::insert()

This design prevents accidental database writes while remaining compatible with common Laravel patterns like adding computed properties for API responses, serialization, and working with collections.

Why ImmutableModel?

  • Enforce architectural boundaries: Prevent accidental database writes at the model level
  • Eliminate persistence bugs: Any save/update/delete attempt throws immediately - no silent failures
  • Improved performance: 47-74% faster hydration, 25-70% faster eager loading
  • Lower memory footprint: ~41% less memory (~1 KB vs ~1.65 KB per model)
  • Familiar API: Eloquent-compatible read semantics for easy adoption
  • Laravel ecosystem compatible: Works with API Resources, serialization, and other common patterns

Installation

composer require brighten/immutable-model

Requirements

  • PHP 8.2+
  • Laravel 11+

Quick Start

use Brighten\ImmutableModel\ImmutableModel;

class UserView extends ImmutableModel
{
    protected string $table = 'user_views';

    protected ?string $primaryKey = 'id';

    protected array $casts = [
        'settings' => 'array',
        'created_at' => 'datetime',
    ];
}

// Query just like Eloquent
$users = UserView::where('active', true)->get();
$user = UserView::find(1);
$user = UserView::with('posts')->first();

// In-memory changes are allowed (for computed fields, API responses, etc.)
$user->computed_field = 'some value';  // Works fine
$user->name = 'Modified';              // Works fine (in-memory only)

// But database persistence is blocked
$user->save();              // Throws ImmutableModelViolationException
$user->update([...]);       // Throws ImmutableModelViolationException
$user->delete();            // Throws ImmutableModelViolationException
UserView::create([...]);    // Throws ImmutableModelViolationException

API Reference

Model Configuration

class MyModel extends ImmutableModel
{
    // Required: The database table
    protected string $table = 'my_table';

    // Optional: Primary key (null = non-identifiable model)
    protected ?string $primaryKey = 'id';

    // Optional: Database connection (null = default)
    protected ?string $connection = null;

    // Optional: Attribute casting
    protected array $casts = [
        'settings' => 'array',
        'created_at' => 'datetime',
    ];

    // Optional: Relations to eager load by default
    protected array $with = ['author'];

    // Optional: Accessors to append to array/JSON output
    protected array $appends = ['full_name'];

    // Optional: Hidden attributes
    protected array $hidden = ['internal_id'];

    // Optional: Visible attributes (whitelist)
    protected array $visible = ['id', 'name', 'email'];
}

Querying

All standard Eloquent read operations are supported:

// Finding records
MyModel::find($id);
MyModel::findOrFail($id);
MyModel::first();
MyModel::all();

// Where clauses
MyModel::where('status', 'active')
    ->where('created_at', '>', now()->subWeek())
    ->orWhere('featured', true)
    ->whereIn('category_id', [1, 2, 3])
    ->whereNotNull('published_at')
    ->get();

// Ordering & limiting
MyModel::orderBy('created_at', 'desc')
    ->limit(10)
    ->offset(20)
    ->get();

// Aggregates
MyModel::count();
MyModel::sum('price');
MyModel::avg('rating');
MyModel::max('views');

Relationships

Supported relationship types:

class Post extends ImmutableModel
{
    protected string $table = 'posts';

    public function author()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    public function comments()
    {
        return $this->hasMany(Comment::class, 'post_id', 'id');
    }

    public function featuredImage()
    {
        return $this->hasOne(Image::class, 'post_id', 'id');
    }
}

// Eager loading
$posts = Post::with('author', 'comments')->get();

// Eager loading with constraints
$posts = Post::with(['comments' => fn($q) => $q->where('approved', true)])->get();

// Lazy loading (works, but watch for N+1)
$post = Post::find(1);
$author = $post->author;

// Relation queries
$comments = $post->comments()->where('approved', true)->get();

Casting

Full Eloquent casting support:

protected array $casts = [
    // Scalar types
    'count' => 'int',
    'price' => 'float',
    'active' => 'bool',
    'name' => 'string',

    // Date/time
    'published_at' => 'datetime',
    'birthday' => 'date',
    'updated_at' => 'immutable_datetime',
    'timestamp' => 'timestamp',

    // Complex types
    'settings' => 'array',
    'metadata' => 'json',
    'tags' => 'collection',

    // Custom casters
    'address' => AddressCast::class,
];

Custom casters must implement Illuminate\Contracts\Database\Eloquent\CastsAttributes. Only the get() method is called.

Collections

Query results return Laravel's standard Eloquent\Collection. All collection methods work normally - immutability is enforced on database operations, not on in-memory manipulation:

$users = User::all();

// All collection operations work normally
$active = $users->filter(fn($u) => $u->active);
$sorted = $users->sortBy('name');
$names = $users->pluck('name');
$mapped = $users->map(fn($u) => $u->toArray());
$users->push($newUser);     // Works - this is in-memory only
$users->transform(fn($u) => $u);  // Works

// In-memory model changes are allowed
$users->first()->name = 'New';       // Works (in-memory only)
$users->first()->computed = 'value'; // Works (add computed fields)

// But database persistence is blocked
$users->first()->save();   // Throws ImmutableModelViolationException
$users->first()->delete(); // Throws ImmutableModelViolationException

Pagination

Full pagination support:

$paginated = MyModel::paginate(15);
$simple = MyModel::simplePaginate(15);
$cursor = MyModel::cursorPaginate(15);

Chunking & Lazy Loading

// Chunk for batch processing
MyModel::chunk(1000, function ($models) {
    foreach ($models as $model) {
        // Process
    }
});

// Cursor for memory-efficient iteration
foreach (MyModel::cursor() as $model) {
    // Process one at a time
}

Global Scopes

Apply query constraints automatically using Laravel's native Scope interface:

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('tenant_id', auth()->user()->tenant_id);
    }
}

class TenantModel extends ImmutableModel
{
    protected static function booted(): void
    {
        static::addGlobalScope(new TenantScope);
    }
}

// Bypass scopes when needed
TenantModel::withoutGlobalScopes()->get();
TenantModel::withoutGlobalScope(TenantScope::class)->get();

Hydration from Raw Data

Create models from existing data without database queries:

// Single model
$user = User::fromRow(['id' => 1, 'name' => 'John']);

// Collection of models
$users = User::fromRows([
    ['id' => 1, 'name' => 'John'],
    ['id' => 2, 'name' => 'Jane'],
]);

Comparison: ImmutableModel vs Eloquent

Feature ImmutableModel Eloquent
Read queries Yes Yes
Relationships Yes Yes
Eager loading Yes Yes
Attribute casting Yes Yes
Accessors Yes Yes
Pagination Yes Yes
Global scopes Yes Yes
Write operations Throws Yes
Dirty tracking No Yes
Events/Observers No Yes
Mutators No Yes
Timestamps No Yes
Mass assignment No Yes

Performance

Benchmarks show ImmutableModel is significantly faster for read operations:

Hydration Speed

Rows Eloquent ImmutableModel Improvement
100 0.30ms 0.09ms -70%
1,000 3.09ms 0.80ms -74%
10,000 34.27ms 9.37ms -73%
100,000 447.29ms 236.63ms -47%

Memory Usage

Rows Eloquent ImmutableModel Per Model (E) Per Model (I) Savings
100 166 KB 97 KB 1.66 KB 998 B 41%
1,000 1.61 MB 973 KB 1.65 KB 996 B 41%
10,000 16.2 MB 9.56 MB 1.66 KB 1003 B 41%
100,000 161.5 MB 95.1 MB 1.65 KB 997 B 41%

Eager Loading (10 posts per user)

Users Models Eloquent Immutable Time Δ Eloquent Mem Immutable Mem Mem Δ
10 110 1.68ms 1.27ms -25% 184 KB 105 KB 43%
100 1,100 5.75ms 2.23ms -61% 1.76 MB 1.02 MB 42%
1,000 11,000 64.22ms 19.34ms -70% 17.53 MB 10.23 MB 42%

Use Cases

ImmutableModel is ideal for:

  • SQL Views: Represent database views as read-only models
  • Read Replicas: Query read-only database replicas safely
  • CQRS Read Models: Enforce read-side immutability in CQRS architectures
  • Denormalized Projections: Work with pre-computed, read-only data
  • API Responses: Build response data with computed fields, knowing it won't accidentally persist
  • Architectural Boundaries: Enforce that certain models are never written to from application code

Not Intended For

  • Models that need write operations
  • Models using Eloquent events/observers
  • Models requiring dirty tracking or timestamps
  • Drop-in replacement for all Eloquent models

Exceptions

Exception When Thrown
ImmutableModelViolationException Any database persistence attempt (save, update, delete, create, etc.)
ImmutableModelConfigurationException Invalid model configuration

Contributing

Contributions are welcome! Please ensure all tests pass before submitting a PR:

composer test

License

MIT License. See LICENSE for details.

brighten/immutable-model 适用场景与选型建议

brighten/immutable-model 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 10 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 01 月 28 日, 在 PHP 生态内属于活跃度较高的组件。

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

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

围绕 brighten/immutable-model 我们能提供哪些服务?
定制开发 / 二次开发

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

BUG 修复 & 性能优化

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

项目外包 & 长期维护

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

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

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-28