Skip to content

Generating Expressive classes

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:

shell
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:

shell
php artisan expressive:generate

By default, the command scans app/Models. You may pass one or more explicit app-relative paths:

shell
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:

shell
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:

shell
php artisan expressive:generate \
    --path="app/Models" \
    --namespace="App\Data" \
    --suffix="Data" \
    --without-relationships \
    --dry-run

The 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:

shell
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:

shell
php artisan make:expressive User --model="App\Models\User" --without-attributes

Use --attributes to generate only selected attributes:

shell
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:

shell
php artisan make:expressive User --model="App\Models\User" --without-relationships

Use --relationships to generate only selected relationships:

shell
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:

shell
php artisan make:expressive User --model="App\Models\User" --exclude-hidden

Use --include-hidden to include hidden attributes when the generator default excludes them:

shell
php artisan make:expressive User --model="App\Models\User" --include-hidden

Morph map hints

MorphTo relationships are generated with a broad PHPDoc type because the related model is resolved at runtime:

php
/** @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:

shell
php artisan make:expressive Image --model="App\Models\Image" --hint-morph-map

The 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:

shell
php artisan make:expressive User --model="App\Models\User" --dry-run

The single-class and bulk generators refuse to overwrite an existing class unless --force is passed:

shell
php artisan make:expressive User --model="App\Models\User" --force

Generated 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:

shell
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.