【问题标题】:Model's normal method mistaken by attribute模型的正常方法被属性错误
【发布时间】:2019-08-11 17:50:50
【问题描述】:

我有一个 TelegramUserextending 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()),一切都会按预期工作,但如果我从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“属性”

所以我的两个问题是:

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

非常感谢!

【问题讨论】:

    标签: laravel eloquent


    【解决方案1】:

    我认为您看到的错误与在类外或类内调用函数无关,与 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 神奇地将其映射到您插入的任何单词,因此您可以像访问本机属性一样访问它。

    然后,你可以这样做:

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

    它应该可以工作。

    【讨论】:

      猜你喜欢
      • 2017-02-27
      • 1970-01-01
      • 2018-03-07
      • 1970-01-01
      • 2013-06-21
      • 2023-03-21
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多