1

when I run PHPstan at level 8, I get for example with this code:

 /**
     * @return Collection<int, Account>
    */
    public function getCustomersAttribute(): Collection
    {
        return $this->account->children;
    }

I get the following message:

Cannot access property $children on App\Account|null

I also get this message with many other classes, only with a different attribute.

Since I cannot determine any access to this accessor e.g.

$model->customers

I also don't know whether an empty collection is always returned or zero (not my project, but taken over from an ex-colleague). So no error would be displayed:


/**
     * @return Collection<int, Account>|null
    */
    public function getCustomersAttribute(): Collection|null
    {
        return $this->account?->children;
    }

I suspect that PHPstan does not recognise that the account attribute is only referenced at runtime. Is there a PHPstan annotation for the Account class to tell PHPstan that the Account model can be null? Can anyone help me with this. I have about 100 of these messages and don't want to check for null everywhere since the code works.

I have tried in some places to check if the attribute is zero and if it is, I have returned zero, which has often led to errors. PHPstan should be able to recognise that the account model can also be null.

  • 1
    Extract a [mcve] first. Then, decide whether a bug report or a question here is in place. – Ulrich Eckhardt Jul 15 '23 at 10:47
  • 1
    What does `Account` have defined (PHPDoc block)? If you have no doc block at all inside `Account`, it will never work – matiaslauriti Jul 15 '23 at 11:44
  • 1
    You can use the [ide helper](https://github.com/barryvdh/laravel-ide-helper) to generate the implicit model properties. – apokryfos Jul 15 '23 at 12:01
  • @matiaslauriti Hello, what exactly would I need to define in the account model with the PHPDoc block? The class or do I need to create a variable there like public `Account $account`? Sorry for the stupid question but I've only been working with PHP and Laravel for a few weeks. – michael.brilz Jul 16 '23 at 10:45
  • @apokryfos Hello, I had a look at this: https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models but it didn't help me. The errors are still displayed – michael.brilz Jul 16 '23 at 11:11
  • @michael.brilz you have to use the IDE Helper that apokryfos shared. Then run `php artisan ide:model` but write `yes`, you want to stuff to be inside each model, not on a single file called `_ide_models.php` (or similar), you want the properties to be inside each model file – matiaslauriti Jul 16 '23 at 11:50

1 Answers1

0

Because Laravel uses Eloquent (Eloquent does not define the model's properties anywhere in the class for PHP to recognize), then PHPStan is not able to know what Account has as properties (remember Eloquent have them defined dynamically).

So, you have to define what the model has using PHPDoc blocks. Let me give you a super quick example of what you should expect to have inside Account:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * @property int $id
 */
class Account extends Model
{
    // ...
}

Adding @property int $id will tell the IDE and PHPStan that Account has a property, it is an integer and is called id, so if you try $account-> it should autocomplete id and show it is an integer.

Instead of you manually doing this, barryvdh/laravel-ide-helper already created a package for these types of things. Install it using composer require --dev barryvdh/laravel-ide-helper and then you have to have all your migrations run php artisan ide-helper:models -W (you need to have the schema available, not data inside the table, it does not care about data).

Using -W will automatically insert the generated PHPDoc blocks inside each model class, if you do not do use -W, it will ask you if you want to generate a file called _ide_helper_models.php and put the PHPDoc blocks there, you don't want to do that, I think PHPStan will not recognize that.

What you can also try is using php artisan ide-helper:models -M, -M will still create the _ide_helper_models.php but add a @mixin tag inside each model class, so it relates the model class with the definition inside the _ide_helper_models.php file.

In the end, this example is what you should have somewhere (and related to each model):

/**
 * App\Models\Post
 *
 * @property integer $id
 * @property integer $author_id
 * @property string $title
 * @property string $text
 * @property \Illuminate\Support\Carbon $created_at
 * @property \Illuminate\Support\Carbon $updated_at
 * @property-read \User $author
 * @property-read \Illuminate\Database\Eloquent\Collection|\Comment[] $comments
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post query()
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post whereTitle($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post forAuthors(\User ...$authors)
 * …
 */
matiaslauriti
  • 7,065
  • 4
  • 31
  • 43
  • Hello, thank you for the quick help. I have now tried this with -W instead of -M and at least a few messages from PHPStan have disappeared. I'll have to see how I can fix the rest. Thank you very much :) – michael.brilz Jul 17 '23 at 06:49
  • @michael.brilz If you are not able to fix those new issues, create a new question stating all of them and we will try to help you – matiaslauriti Jul 17 '23 at 11:10
  • Hello, the problem is still the same only with a different model `Cannot access property $catalog on App\Models\Catalog\Module|null.` Although I ran the ide-helper and the annotations were done, I get the same error only with different properties or methods. ```/***@property-read \App\Models\Catalog\Catalog|null $catalog*/ class Module extends BaseModel{} ``` – michael.brilz Jul 17 '23 at 13:15
  • Okay, create a new question or update the existing one with this info, add `---` after the last part of your question (if using the same one) and add all the new info. I would create a new question as the original error was fixed – matiaslauriti Jul 17 '23 at 14:17