У меня есть 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.
Итак, два моих вопроса:
toString работает при вызове кода вне класса и не работает внутри метода giftableKarma?Большое спасибо!






Я не думаю, что ошибка, которую вы видите, имеет какое-либо отношение к вызову функции вне класса или внутри класса, и все, что связано с разбором строки 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...");
и это должно работать.