PhpStorm: есть ли способ принудительно указать тип оператора return с помощью встроенной аннотации PHPDoc?

Рассмотрим следующий код:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
    public static function getTheFirstCar(string $color): ?self
    {
        /** @var ?self */ // <-- Doesn't apply! Is there any alternative?
        return (new self())->newQuery()->firstWhere('color', '=', $color);
    }
}

Код работает правильно; тем не менее PhpStorm жалуется:

Ожидается, что возвращаемое значение будет 'Car|null',
возвращено '\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model'

Аннотация типа PhpStorm PHPDoc в операторе возврата

Присвоение результата выражения аннотированной переменной устраняет предупреждение, но при этом вводит избыточную переменную!

/** @var ?self $redundant */
$redundant = (new self())->newQuery()->firstWhere('color', '=', $color);
return $redundant;

Итак, есть ли в PhpStorm способ применить встроенную аннотацию типа для значения выражения оператора return явно как Car|null, не вводя избыточную переменную или не указывая все ожидаемые возвращаемые типы?


person goodUser    schedule 05.03.2021    source источник
comment
Вы можете добавить @return ?static|Model над методом. Сама проблема выглядит как эта ошибка   -  person Dmitrii    schedule 05.03.2021
comment
@Dmitrii Дмитрий Я не хочу вносить двусмысленность, добавляя не относящийся к контексту тип Model (метод возвращает только Car или null)   -  person goodUser    schedule 07.03.2021
comment
Какую версию phpstorm вы используете?   -  person Dmitry    schedule 09.03.2021
comment
Я использовал 2020.3.2, и он отлично работает. Возможно, вам следует обновить файл phpstorm.   -  person Dmitry    schedule 09.03.2021
comment
@Dmitry Я также использую PhpStorm v2020.3.2 (сборка № PS-203.7148.74); Уточните, пожалуйста, что вам подходит? (Это /** @var ?self */ перед оператором возврата (что соответствует тому, что мне нужно); или добавление нерелевантного к контексту типа Model - что я объяснил!)   -  person goodUser    schedule 09.03.2021
comment
@goodUser В моем случае PhpStorm не жалуется   -  person Dmitry    schedule 09.03.2021
comment
Также вы можете попробовать перезагрузить phpstom с кешем. file > invalidate Caches/Restart...   -  person Dmitry    schedule 09.03.2021


Ответы (2)


Вы можете подавить это предупреждение, добавив аннотацию @noinspection PhpIncompatibleReturnTypeInspection перед вашим утверждением.
Лично я бы этого не сделал, но это единственный ответ на ваш вопрос о том, как принудительно применить возвращаемый тип и подавить предупреждение афаик.

    /** @noinspection PhpIncompatibleReturnTypeInspection */
    return (new self())->newQuery()->where('color', '=', $color)->first();

Если вы решите соблюдать предупреждение, то, вероятно, это причина и решение для него: newQuery() создаст новый запрос в таблице моделей (скорее всего: cars) без установки соответствующей модели (Car) .
Внутри вы теперь выполняете голый запрос на cars. Поэтому вы получите соответствующую запись, но это будет не экземпляр Car, а экземпляр Model. Поэтому PhpStorm ожидает несколько дополнительных типов возвращаемых данных и печатает это предупреждение в вашем заявлении, поскольку оно отличается от возвращаемого типа методов ?self.

Быстрое решение — изменить newQuery() на newModelQuery(). Это создаст новый запрос и установит модель Car в созданном запросе и вернет соответствующий экземпляр или ноль.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Car extends Model
{
    public static function getTheFirstCar(string $color): ?self
    {
        return (new self())->newModelQuery()->firstWhere('color', '=', $color);
        // I'd use this statement though:
        // return self::where('color', '=', $color)->first();
    }
}
person Tschitsch    schedule 10.03.2021
comment
Красивое и нативное решение с подробными описаниями; Я действительно ценю это; благодарю вас. Мне также было интересно, почему вместо этого вы предпочитаете использовать подход магического метода, поскольку он вызывает просто еще одно предупреждение? - person goodUser; 10.03.2021
comment
Я знаю, что фасады laravel — спорный подход. Мне лично нравится такой подход, и я уже привык к нему. Если мне действительно нужно какое-то автозаполнение или я хотел бы опустить предупреждение, я просто создаю аннотацию \@method или \@property. Еще один хороший вариант — использовать ide-plugin, но в своих недавних проектах я даже не думал добавлять его в свои зависимости. - person Tschitsch; 10.03.2021
comment
Стоит отметить, что newModelQuery не регистрирует глобальные области видимости и не полностью аналогичен newQuery с набором моделей. Документы: Получите новый построитель запросов, который не имеет глобальных областей действия или быстрой загрузки. - person Kurt Friars; 13.03.2021

Вам нужно добавить блок документов в свой класс:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
 * Class Car
 * @package App\Models
 *
 * @method self|Builder newQuery()
 * @method ?self firstWhere($column, $operator = null, $value = null, $boolean = 'and')
 */
class Car extends Model
{
    public static function getTheFirstCar(string $color): ?self
    {
        return (new self())->newQuery()->firstWhere('color', '=', $color);
    }
}
person train_fox    schedule 09.03.2021
comment
Но если вы добавите @method self|Builder newQuery() в док-блок уровня класса, встроенный док-блок станет ненужным. Но ОП хочет использовать этот способ. - person Adam P.; 13.03.2021