Generating Expressive classes
- Introduction
- Generating a class
- Generating many classes
- Choosing the target class
- Selecting attributes
- Selecting relationships
- Hidden attributes
- Morph map hints
- Dry runs and overwrites
- Generated types
- Customizing the stub
Introduction
The make:expressive command generates an Expressive class from an Eloquent model. It inspects the model table, casts, relationships, and accessors, then writes a class with public typed properties.
The generator is a starting point. You may edit the generated class when your application needs a different boundary than the model's full shape.
Generating a class
Generate an Expressive class by passing the class name and model:
php artisan make:expressive User --model="App\Models\User"With the default configuration, the generated file is written to app/Expressive/User.php and uses the App\Expressive namespace.
If the model option is omitted, the command asks which model should be mapped.
Generating many classes
Use expressive:generate when you want Expressive to scan model paths and generate classes in bulk:
php artisan expressive:generateBy default, the command scans app/Models. You may pass one or more explicit app-relative paths:
php artisan expressive:generate --path="app/Models" --path="app/Domain/Billing/Models"The command only generates classes for discovered PHP classes that extend Illuminate\Database\Eloquent\Model. Non-PHP files and non-model classes are skipped.
Use --exclude to skip a model basename or fully qualified class name:
php artisan expressive:generate --exclude="AuditLog" --exclude="App\Models\LegacyUser"Bulk generation reuses the same generator options as make:expressive, including namespace, suffix, attribute selection, relationship selection, hidden attribute handling, morph map hints, dry runs, and force overwrites:
php artisan expressive:generate \
--path="app/Models" \
--namespace="App\Data" \
--suffix="Data" \
--without-relationships \
--dry-runThe command refuses to overwrite existing files unless --force is passed. It returns a failing exit code when any discovered model cannot be generated.
Choosing the target class
You may override the namespace or suffix for a single generation run:
php artisan make:expressive User \
--model="App\Models\User" \
--namespace="App\Data" \
--suffix="Expressive"This writes App\Data\UserExpressive. These options override expressive.namespace and expressive.suffix only for the current command. The same options are available on expressive:generate and apply to every generated class.
Selecting attributes
By default, the generator includes model table columns and detected virtual accessors. Use --without-attributes to generate only relationships:
php artisan make:expressive User --model="App\Models\User" --without-attributesUse --attributes to generate only selected attributes:
php artisan make:expressive User \
--model="App\Models\User" \
--attributes="name,email,display_name"The selected list may include table columns and detected virtual accessors. The command fails if a requested attribute cannot be found.
Selecting relationships
By default, the generator includes public model methods with no parameters that return an Eloquent relationship type. Use --without-relationships to skip relationship properties:
php artisan make:expressive User --model="App\Models\User" --without-relationshipsUse --relationships to generate only selected relationships:
php artisan make:expressive User \
--model="App\Models\User" \
--relationships="posts,address"The command fails if a requested relationship does not exist on the model.
Hidden attributes
Hidden model attributes are generated by default. This keeps the generated Expressive class aligned with the model's shape while serialization still respects the model's hidden rules.
Use --exclude-hidden to skip hidden attributes:
php artisan make:expressive User --model="App\Models\User" --exclude-hiddenUse --include-hidden to include hidden attributes when the generator default excludes them:
php artisan make:expressive User --model="App\Models\User" --include-hiddenMorph map hints
MorphTo relationships are generated with a broad PHPDoc type because the related model is resolved at runtime:
/** @var Expressive<Model>|null */
#[Relationship]
public ?Expressive $imageable = null;Use --hint-morph-map when your application has a morph map and you want the generator to add confident related Expressive classes to the PHPDoc:
php artisan make:expressive Image --model="App\Models\Image" --hint-morph-mapThe generator only adds a mapped model when it can find a matching inverse morph relationship.
Dry runs and overwrites
Use --dry-run to print the generated class without writing a file:
php artisan make:expressive User --model="App\Models\User" --dry-runThe single-class and bulk generators refuse to overwrite an existing class unless --force is passed:
php artisan make:expressive User --model="App\Models\User" --forceGenerated types
The generator uses Eloquent casts before falling back to column metadata. It understands common Laravel casts such as booleans, integers, floats, decimals, dates, datetimes, arrays, JSON, collections, enums, encrypted values, AsArrayObject, AsCollection, AsEnumArrayObject, AsEnumCollection, AsFluent, AsStringable, and AsUri.
Custom casts may generate a concrete type when the cast implements CastsAttributes with an analyzable @implements CastsAttributes<Inbound, Outbound> annotation. Otherwise, the generator falls back to mixed.
Customizing the stub
Publish the generator stub when you need to change the generated class template:
php artisan vendor:publish --tag="expressive-stubs"Expressive reads stubs/expressive.stub when it exists. If the file is not published, the command uses the bundled stub.