【问题标题】:Extending a trait conflicts with itself扩展 trait 与自身冲突
【发布时间】:2017-09-12 17:30:14
【问题描述】:

我正在尝试扩展一个特征。我的意思是创建一个 trait,为不同的 trait 重新定义几个方法。

这是我的代码(PHP 5.6 和 Laravel 5.4):

namespace App\Traits;

use Illuminate\Database\Eloquent\Concerns\HasTimestamps;

trait ExtendHasTimestamps
{
    use HasTimestamps {
        HasTimestamps::setCreatedAt as parentSetCreatedAt;
        HasTimestamps::setUpdatedAt as parentSetUpdatedAt;
    }

    public function setCreatedAt($value) {
        if ($value !== '') return $this;
        return $this->parentSetCreatedAt($value);
    }

    public function setUpdatedAt($value) {
        if ($value !== '') return $this;
        return $this->parentSetUpdatedAt($value);
    }
}

当我在模型中使用ExtendHasTimestamps 时出现问题,它与HasTimestamps 冲突,因为Eloquent\Model 具有use HasTimestamps。在 PHP 7 之前,如果您尝试定义相同的属性两次,特征会引发致命错误。因此,由于我在 HasTimestampsExtendHasTimestamps 中都通过 use HasTimestamps 定义了 $timestamps,所以它会中断。

简而言之:

trait HasTimestamps {
    protected $timestamps = true; //problematic property
}

trait ExtendHasTimestamps {
    use HasTimestamps;
    // add validation then call HasTimestamps method
}

class Model {
    use HasTimestamps;
}

class MyModel extends Model {
    use ExtendHasTimestamps; //everything breaks
}

有没有办法让 PHP 相信它确实是同一件事并且它可以停止与自身冲突,或者通知我正在使用的类(Eloquent\Model 的扩展)停止使用HasTimestamps支持我的特质?


This question 没有回答我的问题,因为我正在尝试使用一个 trait 来覆盖另一个 trait,而不是仅仅在我需要使用此代码的每个类中添加方法。

【问题讨论】:

  • 您在 ExtendHasTimestamps 特征中所做的事情似乎是一个修复,您是否有可能覆盖或替换原始特征而不是提供自己的特征?
  • 不幸的是,它们是 Laravel 供应商文件,这意味着如果我编辑 Eloquent\ModelHasTimestamps,它将在下次运行 composer update 或类似文件时撤消。
  • 忘记 trait 并扩展基本模型类来实现您的修复怎么样? (并使您的模型类扩展新的基类而不是 Laravel 的)
  • 是的。那会奏效。我可能最终会这样做。谢谢。
  • 对不起,伙计,我会提供一个完整的答案,但我没有在这里设置 Laravel 来正确测试它。这是我找到的链接laracasts.com/discuss/channels/eloquent/…

标签: php traits


【解决方案1】:

在我看来,您实际上并不需要首先扩展 trait。我会简单地覆盖子类中使用的特征中的方法,然后在特征中调用 parent 方法。

演示:https://3v4l.org/2DqZQ

<?php

trait HasTimestamps
{
    protected $timestamps = true;

    public function setCreatedAt($value)
    {
        echo 'HasTimestamps::setCreatedAt'.PHP_EOL;
        return $this;
    }

    public function setUpdatedAt($value)
    {
        echo 'HasTimestamps::setUpdatedAt'.PHP_EOL;
        return $this;
    }
}

trait ExtendHasTimestamps
{
    public function setCreatedAt($value)
    {
        echo 'ExtendHasTimestamps::setCreatedAt'.PHP_EOL;

        if ($value !== '') return $this;

        return parent::setCreatedAt($value);
    }

    public function setUpdatedAt($value)
    {
        echo 'ExtendHasTimestamps::setUpdatedAt'.PHP_EOL;

        if ($value !== '') return $this;

        return parent::setUpdatedAt($value);
    }
}

class Model
{
    use HasTimestamps;
}

class MyModel extends Model
{
    use ExtendHasTimestamps;
}

(new MyModel)->setCreatedAt('');

echo PHP_EOL;

(new MyModel)->setCreatedAt('time');

ExtendHasTimestamps::setCreatedAt
HasTimestamps::setCreatedAt

ExtendHasTimestamps::setCreatedAt

【讨论】:

  • 除非我遗漏了什么,否则这不允许我从 EHT 方法调用原始 HT 方法。不只是批发复制代码。
  • 我不确定bar 方法的用途。但我需要 EHT::setCreatedAt 才能调用 HT::setCreatedAt 以便在它运行我的代码后,它仍然会执行 Laravel 认为它应该做的事情
  • @amflare 嗯,有道理,抱歉我误解了。是的,您所要做的就是在您的 trait 方法中调用 parent。我已经更新了我的答案和演示来演示。
  • 这看似简单。谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-09-23
  • 1970-01-01
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多