3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-08-18 03:06:23 +00:00

Add rule for PHPStan and Attributes

This commit is contained in:
Marcus Moore
2026-08-13 16:32:52 -07:00
parent a76d4db36e
commit 3b0c02b1e6

View File

@ -20,3 +20,8 @@ Add behavior as a trait rather than pushing it into a base class.
## Casts go in the $casts property ## Casts go in the $casts property
Declare casts with `protected $casts = [...]`, not a `casts()` method, even though Laravel 12 supports the method form. Declare casts with `protected $casts = [...]`, not a `casts()` method, even though Laravel 12 supports the method form.
## New-style Attribute accessors need @return Attribute generics for Larastan
If a model attribute has a new-style accessor (`protected function locationId(): Attribute`), Larastan drops the property entirely unless the method has a generic PHPDoc: `/** @return Attribute<TGet, TSet> */` (e.g. `Attribute<int|null, int|null>`). Without it, the DB-column extension defers to the accessor extension, the accessor extension requires strict generics, and the property reads as "undefined" — that is why `@property` tags crept into model docblocks (e.g. PR #19480). Fix by adding the `@return Attribute<...>` generic instead of `@property` tags; plain columns without accessors are inferred from migrations automatically and need no annotation.
TGet is the readable type; **TSet is the writable one — the setter's argument, i.e. what may be assigned**, not what the setter stores. Reserve `never` for attributes genuinely never written: no `set:` closure **and** nothing assigns to them (computed values like `expiresDiffInDays()`). No `set:` closure alone is not enough — an accessor decorating a real column still gets assigned elsewhere, and `never` turns each assignment into `Property Setting::$header_color (never) does not accept mixed`; use `mixed` there. Same reasoning for a setter that narrows: `requestable()` stores `(int) filter_var(...)` but accepts an untyped `$value`, so it is `Attribute<int, mixed>`.