PHP和Laravel性状

问题描述 投票:10回答:3

我使用Laravel 5.1,并想从特质上的型号访问数组时模型之前型号采用appends阵列。

我想某些项添加到阵列追加如果从我的特质存在。我不想为了实现这个编辑模型。是特质在这种情况下实际可用或者我应该使用继承?

array_push($this->appends, 'saucedByCurrentUser');

这是我目前的设置是如何工作的。

特征

<?php namespace App;

trait AwesomeSauceTrait {

  /**
   * Collection of the sauce on this record
   */
  public function awesomeSauced()
  {
    return $this->morphMany('App\AwesomeSauce', 'sauceable')->latest();
  }
  public function getSaucedByCurrentUserAttribute()
  {
    if(\Auth::guest()){
        return false;
    }
    $i = $this->awesomeSauced()->whereUserId(\Auth::user()->id)->count();
    if ($i > 0){
        return true;
    }
    return false;
  }
}

模型

<?php namespace App;

use App\AwesomeSauceTrait;
use Illuminate\Database\Eloquent\Model;

class FairlyBlandModel extends Model {
    use AwesomeSauceTrait;

    protected $appends = array('age','saucedByCurrentUser');

}

我想这样做是值得作为实现扩展类相同的效果。我有几个相似的特征,所以使用继承获取有点丑陋。

trait AwesomeSauceTrait {
 function __construct() {
     parent::__construct();
     array_push($this->appends, 'saucedByCurrentUser');
 }
}

我有seen some workarounds for this,但没有人似乎不仅仅是手动添加项目到数组更好/更清洁。任何想法表示赞赏。

更新


我发现完成的事情,我需要一个特质的这种方式,但它仅适用于一个特点,我没有看到使用这种在继承的优势。

特征

protected $awesomeSauceAppends = ['sauced_by_current_user'];

protected function getArrayableAppends()
{
    array_merge($this->appends, $this->awesomeSauceAppends);
    parent::getArrayableAppends();
}

如何我目前正在处理我的模型,它是什么值得。

模型

public function __construct()
{
    array_merge($this->appends, $this->awesomeSauceAppends);
}
php laravel laravel-5 traits
3个回答
12
投票

性状有时被描述为“编译器辅助的复制和粘贴”;用特质的结果总是可以写出为在自己的权利的有效类。因此,存在处于性状没有parent的概念,因为一旦性状已被应用,其方法是从那些在同一时间在类本身定义的,或从其它性状导入不可区分的。

同样,作为the PHP docs say

如果两个性状插入具有相同名称的方法,产生一个致命的错误,如果冲突没有明确解决。

因此,他们不是很适合你想要在同一块行为的多个变体混合的情况下,因为没有办法的基本功能和功能混合,互相交谈的通用方法。

在我的理解,你实际上要解决的问题是这样的:

  • 添加自定义存取函数,以雄辩的模型类
  • 添加其他项目的保护$appends阵列匹配这些方法

一种方法是继续使用特征,并使用Reflection动态发现哪些方法已被添加。然而,提防反射有一个被相当缓慢的声誉。

要做到这一点,我们首先实现一个循环,我们可以通过一种特定的方式命名的方法勾入只是一个构造函数。这可以被放置到其自身的特质(或者,你可以子类雄辩Model类用自己的增强版):

trait AppendingGlue {
  public function __construct() {
    // parent refers not to the class being mixed into, but its parent
    parent::__construct();

    // Find and execute all methods beginning 'extraConstruct'
    $mirror = new ReflectionClass($this);
    foreach ( $mirror->getMethods() as $method ) {
      if ( strpos($method->getName(), 'extraConstruct') === 0 ) {
        $method->invoke($this);
      }
    }
  }
}

然后任意数量的不同实施名为extraConstruct方法性状:

trait AwesomeSauce {
  public function extraConstructAwesomeSauce() {
    $this->appends[] = 'awesome_sauce';
  }

  public function doAwesomeSauceStuff() {
  }
}

trait ChocolateSprinkles {
  public function extraConstructChocolateSprinkles() {
    $this->appends[] = 'chocolate_sprinkles';
  }

  public function doChocolateSprinklesStuff() {
  }
}

最后,我们混合了所有的特性转化为一个普通的模型,检查结果:

class BaseModel {
  protected $appends = array('base');

  public function __construct() {
    echo "Base constructor run OK.\n";
  }

  public function getAppends() {
    return $this->appends;
  }
}

class DecoratedModel extends BaseModel {
  use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
}

$dm = new DecoratedModel;
print_r($dm->getAppends());

我们可以设置$appends的装饰,模型本身内部的初始内容,并且将取代BaseModel定义,但不会中断其他特性:

class ReDecoratedModel extends BaseModel {
  use AppendingGlue, AwesomeSauce, ChocolateSprinkles;

  protected $appends = ['switched_base'];
}

但是,如果在同一时间作为AppendingGlue混合过度乘坐的构造函数,你需要做一些额外的工作,discussed in this previous answer。它类似于在继承情况下调用parent::__construct,但你必须别名性状的构造,以访问:

class ReConstructedModel extends BaseModel {
  use AppendingGlue { __construct as private appendingGlueConstructor; }
  use AwesomeSauce, ChocolateSprinkles;

  public function __construct() {
    // Call the mixed-in constructor explicitly, like you would the parent
    // Note that it will call the real parent as well, as though it was a grand-parent
    $this->appendingGlueConstructor();

    echo "New constructor executed!\n";
  }
}

这可以通过从其中要么存在代替AppendingGlue性状,或者已经使用它一个类继承来避免:

class GluedModel extends BaseModel {
  use AppendingGlue;
}
class ReConstructedGluedModel extends GluedModel {
  use AwesomeSauce, ChocolateSprinkles;

  public function __construct() {
    // Standard call to the parent constructor
    parent::__construct();
    echo "New constructor executed!\n";
  }
}

这里有一个live demo of all of that put together


2
投票

吻:

我看不出有任何理由你应该使用性质时,你只是附加属性。

我只想建议使用特质却没有构造函数像你在做什么,只有当你的模型越来越漂亮笨重,你想瘦下来的事情。

也请注意,这不是附加属性的正确方法

    protected $appends = array('age','saucedByCurrentUser');

你可以这样做:

    protected $appends = array('age','sauced_by_current_user');

追加属性名应当在其方法名称的snake_case

编辑:

背后的想法是追加动态地添加未在数据库表中存在到模型字段,以便以后你可以这样做:

  $model = FairlyBlandModel ::find(1); 
  dd($model->sauced_by_current_user);

2
投票

我以为我想补充的更新2019,因为这是试图做类似的事情时弹出的第一次讨论的一个。我使用Laravel 5.7和时下Laravel会做IMSOP提到的反射。

性状已经启动后,将Laravel然后调用initializeTraitName()构建的对象(其中TraitName为特征的全名)。

要添加额外的项目,从性状$appends,你可以简单地做到这一点...

trait AwesomeSauceTrait {

  public function initializeAwesomeSauceTrait()
  {
    $this->appends[] = 'sauced_by_current_user';
  }

  public function getSaucedByCurrentUserAttribute()
  {
    return 'whatever';
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.