Нормальный метод модели по ошибке - PullRequest
4 голосов
/ 21 марта 2019

У меня есть 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");
 ...
}

Tring для помещения 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 вызовет то же исключение, если оно помещено в строку журнала?

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

1 Ответ

0 голосов
/ 04 апреля 2019

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...