laravel-models

Installation
SKILL.md

Laravel Models

A model describes a table: its relationships, how each column casts, values derived from its own attributes, and small helpers over that data. It never runs an operation; that is an action. Its builder, factory, policy and collection are registered by attribute above the class, so everything wired to a model is visible in one place. The primary key is Laravel's auto-increment id. An entity the outside world addresses also gets a separate ulid or uuid column, and that is the only id it exposes.

Rules

  • Models describe data; actions act on it. A model method may read its attributes (isOverdue()) or change them in memory and leave the save to the caller (appendToLog()). Anything that saves, touches another model or calls a service is an action. The one exception is the updateTo{State}() and is{State}() helpers, which laravel-state-machines defines.
  • Register collaborators with attributes. #[UseEloquentBuilder] (Laravel 12.19+), #[UseFactory], #[UsePolicy] (12.18+) and #[CollectedBy] go on the class, so its wiring reads at a glance instead of hiding in method overrides and naming conventions. Below 12.19, override public function newEloquentBuilder($query): OrderBuilder instead; below 12.18, the policy is found by naming convention.
  • On Laravel 13, attributes replace model properties too. #[Table], #[Hidden], #[Appends], #[Touches] rather than $table, $hidden, $appends, $touches. Never the mass-assignment ones: models are unguarded globally (see laravel-providers).
  • Query constraints live in the model's builder. Never local scopes, #[Scope] included. laravel-query-builders owns builders.
  • Relationship return types are what the method returns. BelongsTo, HasMany, BelongsToMany. Never a union with the related model or its builder: the method never returns either.
  • Casts go in the casts() method. It can build casts with calls such as AsEnumCollection::of(Tag::class), which a $casts property can't, so every model uses the one form.
  • Cast every column to its richest type. Enums, states, DTOs and value objects, per the table below. Code then reads typed objects, never raw strings and arrays. 'array' is only for free-form JSON with no fixed shape.
  • Accessors are public, get-only Attribute methods. new Attribute(get: fn (): string => ...), typically projecting a field of a cast DTO or combining columns. A derived value has nothing to write back; writes go through actions.
  • A model in a polymorphic relation joins the enforced morph map, which laravel-providers owns.

Example

Installs
59
GitHub Stars
15
First Seen
Mar 27, 2026