Нормальный метод модели ошибочен из-за атрибута

У меня есть TelegramUserрасширение Model с двумя простыми идентичными функциями:

public function toString() {
    if ($this->telegram_first_name != null) {
        return $this->telegram_first_name;
    } else if ($this->telegram_username != null) {
        return $this->telegram_username;
    }
}

public function getDisplayName() {
    if ($this->telegram_first_name != null) {
        return $this->telegram_first_name;
    } else if ($this->telegram_username != null) {
        return $this->telegram_username;
    }
}

Если я вызываю метод toString извне класса (например, $telegramUser->toString()), все работает так, как ожидалось, но если я вызываю toString внутри модели TelegramUser, это происходит:

local.ERROR: LogicException: App\TelegramUser::toString must return a relationship instance. in webapp/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:416
Stack trace:
#0 webapp/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(399): Illuminate\Database\Eloquent\Model->getRelationshipFromMethod('toString')
#1 webapp/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(329): Illuminate\Database\Eloquent\Model->getRelationValue('toString')
#2 webapp/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1519): Illuminate\Database\Eloquent\Model->getAttribute('toString')
#3 webapp/app/TelegramUser.php(79): Illuminate\Database\Eloquent\Model->__get('toString')

это функция внутри модели TelegramUser, где я вызываю функцию toString:

public function giftableKarma($chatId, $karmaType, $karmaCount) {
    Log::debug("$this->toString() wants to donate $karmaCount unit(s) of $karmaType->name");
 ...
}

Попробуйте поместить toString вне строки следующим образом:

    Log::debug($this->toString() . " wants to donate $karmaCount unit(s) of $karmaType->name");

приводит к той же ошибке.

Вот почему я определил getDisplayName, который работает только если, я поместил его вне строки, поэтому:

    Log::debug($this->getDisplayName() . " wants to donate $karmaCount unit(s) of $karmaType->name");

Будет счастливо работать и регистрировать имя пользователя или имя пользователя, а это:

    Log::debug("$this->getDisplayName() wants to donate $karmaCount unit(s) of $karmaType->name");

Приведет к той же ошибке LogicException, на этот раз явно относящейся к «атрибуту» getDisplayName.

Итак, два моих вопроса:

  1. Почему метод toString работает при вызове кода вне класса и не работает внутри метода giftableKarma?
  2. Почему getDisplayName вызовет то же исключение, если оно помещено в строку журнала?

Большое спасибо!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Laravel Scout - это популярный пакет, который предоставляет простой и удобный способ добавить полнотекстовый поиск в ваше приложение Laravel. Он...
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
4
0
68
1

Ответы 1

Я не думаю, что ошибка, которую вы видите, имеет какое-либо отношение к вызову функции вне класса или внутри класса, и все, что связано с разбором строки PHP и магическими методами Laravel.

Разбор строки PHP позволяет вставлять имена переменных, но не функции. Другими словами, если вы сделаете следующее: "$this->toString()" PHP будет думать, что имя вашей переменной $this->toString, а не $this->toString().

Итак, когда вы используете его таким образом, он пытается получить доступ к свойству класса с именем toString вместо выполнения метода с именем toString().

В Laravel у моделей есть волшебный метод (__get()), который позволяет вам динамически разрешать как атрибуты модели, так и отношения, что позволяет вам использовать, например, $mymodel->myattribute без необходимости явно определять это свойство в классе. Итак, когда вы пытаетесь вызвать $this->toString (без завершающих скобок), Laravel сначала проверяет, есть ли у этой модели атрибут с именем toString, и если да, возвращает его значение. Если нет, он ищет функцию с именем toString() и ожидает, что она определит отношение. Это последнее, что происходит с вами.

Я бы не стал использовать toString для имени метода, потому что он слишком близок к волшебному методу под названием __toString(), который позволяет определить неявное преобразование класса в строку (на самом деле Laravel уже определил это в своем классе Model, т.к. хорошо).

Для вашей конкретной проблемы, если вы хотите использовать значение внутри строки, вы можете обойти ограничение синтаксического анализа строки, окружив его фигурными скобками (что в любом случае является хорошей привычкой, чтобы избежать путаницы):

Log::debug("{$this->toString()} wants to...");

В качестве альтернативы, отличная функция в Laravel — это виртуальный аксессор, которую вы можете определить следующим образом:

public function getDisplayNameAttribute() {
    if ($this->telegram_first_name != null) {
        return $this->telegram_first_name;
    } else if ($this->telegram_username != null) {
        return $this->telegram_username;
    }
}

По сути, вы определяете функцию, которая начинается с get и заканчивается на Attribute, и Laravel волшебным образом сопоставляет ее с любым словом (словами), которые вы вставляете между ними, чтобы вы могли получить к ней доступ как к нативному свойству.

Затем вы можете сделать:

Log::debug("$this->displayName wants to...");

и это должно работать.

Другие вопросы по теме