You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/ARCHITECTURE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -259,7 +259,7 @@ Provider priority order (highest first):
259
259
260
260
Two providers are currently registered in `default_providers()`:
261
261
262
-
-**`LaravelModelProvider`** (`virtual_members/laravel.rs`): synthesizes virtual members for classes extending `Illuminate\Database\Eloquent\Model`. Currently produces relationship properties: methods returning known relationship types (`HasMany`, `HasOne`, `BelongsTo`, etc.) generate a virtual property with the same name, typed from the relationship's generic parameters (e.g. `HasMany<Post, $this>` produces a `$posts` property typed as `\Illuminate\Database\Eloquent\Collection<Post>`). Highest priority among virtual member providers.
262
+
-**`LaravelModelProvider`** (`virtual_members/laravel.rs`): synthesizes virtual members for classes extending `Illuminate\Database\Eloquent\Model`. Produces relationship properties (methods returning `HasMany`, `HasOne`, `BelongsTo`, etc. generate a virtual property typed from the relationship's generic parameters), scope methods (`scopeActive` becomes `active()` as both static and instance), Builder-as-static forwarding (`User::where()->get()` resolves end-to-end), accessors (legacy `getXAttribute()` and modern `Attribute` casts), and cast properties (`$casts` array or `casts()` method entries are mapped to PHP types like `datetime` to `\Carbon\Carbon`, `boolean` to `bool`, custom cast classes to their `get()` return type). Highest priority among virtual member providers.
263
263
-**`PHPDocProvider`** (`virtual_members/phpdoc.rs`): parses `@method`, `@property`, `@property-read`, `@property-write`, and `@mixin` tags from the class-level docblock stored in `ClassInfo.class_docblock`. Explicit `@method` / `@property` tags are not parsed eagerly during AST extraction; instead, the raw docblock string is preserved and parsed lazily when `provide` is called. For `@mixin` tags, the provider loads the referenced classes and merges their public members. Within the provider, explicit tags take precedence over mixin members. Recurses into mixin-of-mixin chains up to `MAX_MIXIN_DEPTH`.
Copy file name to clipboardExpand all lines: docs/CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
12
-**`newCollection()` override detection.** Eloquent models that override the `newCollection()` method now resolve to the custom collection class declared in the method's return type. This is the third detection mechanism alongside `#[CollectedBy]` and `@use HasCollection<X>`. Priority order: attribute, trait, method override. Works with short names resolved via `use` imports and fully-qualified return types. The standard `Collection` return type is correctly ignored. Custom collection methods appear after `->get()`, on relationship properties, and in builder chains.
13
13
-**Body-inferred relationship properties.** Eloquent relationship methods that lack `@return` annotations now produce virtual properties by scanning the method body for patterns like `$this->hasMany(Post::class)`. The relationship type is inferred from the method name (`hasMany` to `HasMany`, `belongsTo` to `BelongsTo`, etc.) and the related model class is extracted from the first `::class` argument. Supports all 10 relationship types including `HasOneThrough`. Fully-qualified class names, extra foreign key arguments, and chained builder calls (e.g. `->latest()`) are handled. When both a `@return` annotation and a body pattern are present, the annotation takes priority. Projects that don't use Larastan no longer need to add annotations for basic relationship completion.
14
14
-**Laravel accessor and mutator virtual properties.** Eloquent models with legacy accessors (`getFullNameAttribute()`) or modern Laravel 9+ accessors (methods returning `Illuminate\Database\Eloquent\Casts\Attribute`) now produce virtual properties. The property name is derived by converting the method name to snake_case: `getFullNameAttribute` produces `$full_name`, and `avatarUrl()` produces `$avatar_url`. Legacy accessors use the method's declared return type; modern accessors use `mixed`. Works alongside relationship properties, scope methods, and builder forwarding. `getAttribute()` (a real Eloquent method) is correctly excluded.
15
+
- **Eloquent cast properties.** Properties defined in a model's `$casts` array or `casts()` method now produce typed virtual properties. Cast type strings are mapped to PHP types: `datetime`/`date` to `\Carbon\Carbon`, `immutable_datetime`/`immutable_date` to `\Carbon\CarbonImmutable`, `boolean`/`bool` to `bool`, `integer`/`int` to `int`, `float`/`double`/`real`/`decimal:N` to `float`, `string`/`encrypted`/`hashed` to `string`, `array`/`json` to `array`, `object` to `object`, `collection` to `\Illuminate\Support\Collection`. Enum casts (e.g. `Status::class`) resolve to the enum class itself. Classes implementing `Illuminate\Contracts\Database\Eloquent\Castable` also resolve to themselves. Custom cast classes are resolved by loading the class and inspecting the `get()` method's return type (native or `@return` docblock). When the `get()` method has no return type, the resolver falls back to the first generic argument from `@implements CastsAttributes<TGet, TSet>` on the cast class. A `:argument` suffix (e.g. `Address::class.':nullable'`) is stripped before resolution. Format suffixes (e.g. `datetime:Y-m-d`) are handled. Both sources are merged: the `casts()` method overrides the `$casts` property for overlapping columns, matching Laravel's runtime behaviour. Works with `$this->`, instance variables, cross-file PSR-4 resolution, indirect model subclasses, double-quoted strings, and coexists with relationship properties, scope methods, and accessors.
16
+
-**Eloquent `$attributes` default properties.** Entries in a model's `$attributes` property array now produce typed virtual properties as a fallback. Types are inferred from the literal default values: strings, booleans, integers, floats, `null`, and arrays. Columns that already have a `$casts` entry are skipped, so casts always take priority. Works with `$this->`, instance variables, cross-file PSR-4 resolution, double-quoted keys, and negative numeric literals.
15
17
16
18
-**Virtual member provider abstraction.** Introduced the `VirtualMemberProvider` trait and `VirtualMembers` struct in a new `virtual_members` module. This provides a priority-ordered pipeline for synthesizing members from `@method`/`@property` tags, `@mixin` classes, and framework-specific patterns (e.g. Laravel relationships, scopes, Builder forwarding). All completion and go-to-definition call sites now use the new `resolve_class_fully` entry point, which applies base inheritance resolution followed by virtual member providers. No providers are registered yet, so behavior is unchanged. This is the foundation for upcoming Laravel support.
17
19
- **Laravel relationship properties.** Classes extending `Illuminate\Database\Eloquent\Model` now get virtual properties synthesized from relationship methods. A method returning `HasMany<Post, $this>` produces a `$posts` property typed as `\Illuminate\Database\Eloquent\Collection<Post>`, and `HasOne<Profile, $this>` produces a `$profile` property typed as `Profile`. Supports `HasOne`, `HasMany`, `BelongsTo`, `BelongsToMany`, `MorphOne`, `MorphMany`, `MorphTo`, `MorphToMany`, and `HasManyThrough`. Generic type parameters are extracted from Larastan-style `@return` annotations. The synthesized properties sit at the highest virtual member priority, beating `@property` tags from ide-helper and `@mixin` members. Works with `$this->`, instance variables, relationship methods defined in traits, indirect Model subclasses (through a BaseModel), fully-qualified return types, and cross-file PSR-4 resolution. Chaining through relationship properties (e.g. `$user->profile->getBio()`) resolves to the related model's members.
0 commit comments