模型的正常方法被属性误认为

时间:2019-03-20 22:07:31

标签: laravel eloquent

我有一个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;
    }
}

如果我从类外部(例如$telegramUser->toString())调用toString方法,那么一切都会按预期工作,但是如果我从TelegramUser模型内部调用toString,则会发生这种情况:

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“ attribute”

所以我的两个问题是:

  1. 为什么toString方法在类外部调用代码时起作用,而在giftableKarma方法内部不起作用?
  2. 为什么将getDisplayName放在日志字符串中会引发相同的异常?

非常感谢!

1 个答案:

答案 0 :(得分:0)

我认为您所看到的错误与在类外部或类内部调用函数无关,而与PHP字符串解析和Laravel的魔术方法无关。

PHP's string parsing允许您插入变量名,但不能插入函数。换句话说,如果您执行以下操作:"$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中的一个漂亮功能是virtual accessor,您可以这样定义它:

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神奇地将其映射到您所插入的任何单词,因此您可以像原生属性一样访问它。 / p>

然后,您可以执行以下操作:

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

它应该可以工作。