- How do computed attributes differ from Eloquent accessors in Laravel?
- Computed attributes persist their values in the database, unlike accessors which are purely virtual. This ensures consistency across reads/writes and offline-first apps, while still letting you define dynamic logic. Think of them as 'materialized accessors'—calculated once but stored for future use.
- Can I use this package with Laravel 13? What’s the upgrade path?
- Yes, the package is officially Laravel 13-compatible. No breaking changes exist; simply update via `composer require korridor/laravel-computed-attributes` and test your models. Laravel 13’s improved dependency injection (e.g., `bind()`) can now be used for recalculation services if needed.
- What database changes are required to add computed attributes to an existing model?
- Add a column to your table matching the computed attribute’s name (e.g., `total_price` for a `totalPrice` attribute). Use Laravel migrations with `Schema::table()` and rollback tests to handle failures. The package doesn’t enforce schema rules—you manage the column type (e.g., `decimal` for monetary values).
- How do I trigger recalculations when related models change (e.g., order items update)?
- Use Eloquent observers, model events (`saved`, `updated`), or Laravel queues (`ShouldQueue`). For async recalculations, leverage Laravel 13’s job middleware. Example: `OrderItem::updated(fn () => $order->refreshComputed('totalPrice'))`. Avoid expensive computations in observers; offload to queues for performance.
- Does this package support caching computed attributes to reduce database writes?
- No, caching isn’t built-in. For performance, cache the *computed logic* (e.g., `Cache::remember()` around expensive closures) or use Laravel’s query caching. The package prioritizes consistency over caching—values are always recalculated on demand unless manually synced.
- How does this handle race conditions if multiple processes update the same computed attribute?
- Race conditions can occur if two processes read-modify-write the same attribute. Mitigate this with optimistic locking (`$model->setAttribute('computed_attr', $newValue)->save()`) or transactions. For critical apps, implement a queue-based recalculation with `ShouldQueue` and `unique()` to deduplicate jobs.
- Can I use computed attributes with Laravel’s API resources or JSON:API?
- Yes, computed attributes work seamlessly with API resources. They’re treated like normal attributes during serialization (e.g., `$model->toArray()` or `JsonResource`). Cast them in the model (e.g., `$casts = ['totalPrice' => 'float']`) to ensure consistent output types.
- What are the performance implications of storing computed values in the database?
- Trade-offs include increased storage (duplicate data) and slower writes (recalculation overhead). For read-heavy apps, this improves performance by avoiding runtime calculations. Benchmark your use case: test with/without computed attributes to compare query speeds and storage costs.
- How can I test computed attributes in Laravel’s testing tools (Pest/PHPUnit)?
- Test by asserting the computed value matches expectations after model updates. Use Pest’s `assertDatabaseHas()` to verify stored values. For static analysis, integrate PHPStan (level 5) in CI to catch type mismatches (e.g., closures returning `null` instead of `int`). Example: `test('totalPrice updates correctly', fn () => $order->update(['items' => [...]])->assertDatabaseValue('total_price', 100.00));`
- Are there alternatives to this package for materialized computed attributes?
- Alternatives include custom accessors with database storage (manual implementation) or packages like `spatie/laravel-model-states` for state-based attributes. This package stands out by natively integrating with Eloquent’s accessor system, offering a declarative syntax (`computed(['totalPrice' => fn () => ...])`) and Laravel 13 support out of the box.