如何在laravel中创建模型

时间:2014-12-03 18:46:40

标签: laravel laravel-4 eloquent

我在潜在客户和状态之间有一个很好的关系。潜在客户是潜在客户。状态可以是活动的,保留的,取消订阅的等等。我有以下表格:

| Leads   | Lead_Status  | Statuses |
|---------|--------------|----------|
|  - id   |  - lead_id   |  - id    |
|  - name |  - status_id |  - name  |

我们为每个潜在客户提供多种状态,因为我们需要跟踪分析的状态历史记录。这一切都很有效。如果在大多数查询中使用范围,则通常将global scope而不是query scope应用于模型。我的范围做了一些丑陋的事情来检索潜在客户的当前状态,并使用$lead->status之类的内容轻松访问它,就像我们得到$lead->name一样。以下是使用newQuery在获取Lead::时向初始查询添加查询信息的范围确定过程:

public function newQuery() {
  return parent::newQuery()->select(DB::raw('leads.*, status.status_id AS status'))->from(DB::raw(' leads
    LEFT OUTER JOIN (
      SELECT ls.status_id, ls.lead_id
      FROM (
         SELECT lead_id, max(created_at) AS max_created_at
         FROM lead_status GROUP BY lead_id
      ) AS ls2 INNER JOIN lead_status AS ls ON ls.lead_id = ls2.lead_id AND ls.created_at = ls2.max_created_at
    ) AS status ON leads.id = status.lead_id
  '));
}

这非常有效。它返回带有id,名称和状态的潜在客户。

现在问题

当我尝试创建新的潜在客户时,我收到错误:

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 
'
  LEFT OUTER JOIN (
    SELECT ls.status_id, ls.lead_id
    FROM (
' at line 2 
(SQL: 
  insert into  leads
  LEFT OUTER JOIN (
    SELECT ls.status_id, ls.lead_id
    FROM (
      SELECT lead_id, max(created_at) AS max_created_at
      FROM lead_status GROUP BY lead_id
    ) AS ls2 
    INNER JOIN lead_status AS ls ON ls.lead_id = ls2.lead_id 
    AND ls.created_at = ls2.max_created_at
  ) AS status ON leads.id = status.lead_id
  (`id`, `name`, `updated_at`, `created_at`) values (test5, Dustin Griffith, 2014-12-03 18:24:06, 2014-12-03 18:24:06)
)

很明显,潜在客户的目标是Lead::create()。这不是我想要的。我希望它只在我们从潜在客户表中选择数据时确定范围。所以我的主要问题是,当它是一个select语句时,我如何告诉它只有范围?

我还使用了global scope下的laravel文档中看到的ScopeInterface方法,这种方法更复杂但在某些情况下更受青睐。我可以补充一点,如果我们应该建立它而不是旧的newQuery方法。我用这种方法得到了相同的结果。

我不是在寻找一个hacky解决方案。任何输入都将非常感激,但我正在寻找处理这些情况的正确方法,因为我将来会一次又一次地这样做。

1 个答案:

答案 0 :(得分:2)

newQuery不是正确的方法。我们想要使用的是问题中提到的Global Scope方法。这允许我们定义每次调用查询时使用的范围,以及如何在不需要时删除范围。 Laravel非常聪明,可以在需要时删除范围(创建,更新,删除)。文件读到:

  

如果Eloquent模型使用具有与bootNameOfTrait命名约定匹配的方法的特征,则在引导Eloquent模型时将调用该特征方法,从而为您提供注册全局范围或执行任何其他操作的机会。范围必须实现ScopeInterface,它指定两个方法:apply和remove。

我跳过了特征部分并将以下内容放在模型本身中,但是对于组织来说,将其分解为自己的特征是明智的。我在模型中添加了以下内容:

// Import the classes
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ScopeInterface;

// Add the global scope
public static function boot() {
  parent::boot();
  static::addGlobalScope(new StatusColumnScope);
}

// Create the scope
class StatusColumnScope implements ScopeInterface {
  // Create our apply method that will be called for every query on this model
  public function apply(Builder $builder) {
    $builder->select(DB::raw('leads.*, status.status_id AS status'))->from(DB::raw(' leads
      LEFT OUTER JOIN (
        SELECT ls.status_id, ls.lead_id
        FROM (
           SELECT lead_id, max(created_at) AS max_created_at
           FROM lead_status GROUP BY lead_id
        ) AS ls2 INNER JOIN lead_status AS ls ON ls.lead_id = ls2.lead_id AND ls.created_at = ls2.max_created_at
      ) AS status ON leads.id = status.lead_id
    '));
  }

  // Create the remove method that is called every time we need to unscope
  public function remove(Builder $builder) {
    $query = $builder->getQuery();
    $query->selectRaw('select *')->from('leads');
  }
}

当我们需要取消范围时,会调用上面的remove方法。起初这让我感到困惑,因为我认为如果我们不需要范围,它就不应该是范围。这不是Laravel的工作方式。这只是在真正深入挖掘后才变得更具吸引力。我会告诉你我所看到的。让我们在Laravel的引擎盖下看一下Eloquent/Builder.php中的一些方法:

创建:

public static function create(array $attributes) {
  $model = new static($attributes);
  $model->save();
  return $model;
}

这称为保存:

public function save(array $options = array()) {
  $query = $this->newQueryWithoutScopes();
  .......
}

并调用newQueryWithoutScope:

public function newQueryWithoutScopes() {
    return $this->removeGlobalScopes($this->newQuery());
}

当然是removeGlobalScopes:

public function removeGlobalScopes($builder) {
  foreach ($this->getGlobalScopes() as $scope) {
    $scope->remove($builder, $this);
  }
  return $builder;
}

$scope->remove()是我们在remove课程中设置的StatusColumnScope方法。这被称为反转范围,但在我们的情况下,我只是完全覆盖查询,将其设置回原始查询。