Laravel Eloquent为复合密钥找到


Laravel Eloquent find for composite key

我最近开始使用Laravel 5作为一个框架。到目前为止,一切都很顺利,能和它一起工作真是太好了。然而,目前我在Eloquent的一款车型上遇到了一些麻烦。

数据库表reports具有以下模式:

| id | group_id | score | ... |

group_id是引用groups表的外键。该表的主键是idgroup_id的组合。id也不是自动增加的,因为它是一个外部id,我正在使用它来简化内部处理。

每个外部id可以为我的每个组出现一次,并且每个组都有不同的分数,因此是复合主键。

当访问我的某个页面时,我想从外部数据源获取最新的记录,并将它们与相应的数据库行进行匹配。如果它们不存在,我想创建它们。

我目前对这条路线的编码是:

public function showReports ($id)
{
   $group = Group::findOrFail($id);
   foreach ($group->getLatestReports(20) as $reportElement)
   {
       $report = Report::find($reportElement['id']);
       if (is_null($report))
       {
           $report = new Report;
           // Fill values ...
           $report->save();
       }
   }
}

这显然没有如预期的那样工作,因为它只查找id
::find($reportElement['id'])),而不是用于group_id。和往常一样,我的问题答案可能非常简单,但我现在似乎找不到。

Model::find仅适用于单列键。您可以向它传递一个数组,但这只会使它查找多行。

您需要将两个where链接在一起才能进行查询:

$report = Report::where('id', '=', $reportElement['id'])
                ->where('group_id', '=', $group->id)
                ->first();

您可以创建一个Trait MutiPrimaryKey,然后扩展getKey并查找方法:1)在App''Classes''Database中创建以下文件MultiPrimaryKey.php

<?php
namespace App'Classes'Database;
use Illuminate'Database'Eloquent'Builder;
trait MultiPrimaryKey {
    protected function setKeysForSaveQuery(Builder $query)
    {
        $keys = $this->getKeyName();
        if(!is_array($keys)){
            return parent::setKeysForSaveQuery($query);
        }
        foreach($keys as $keyName){
            $query->where($keyName, '=', $this->getKeyForSaveQuery($keyName));
        }
        return $query;
    }
    protected function getKeyForSaveQuery($keyName = null)
    {
        if(is_null($keyName)){
            $keyName = $this->getKeyName();
        }
        if (isset($this->original[$keyName])) {
            return $this->original[$keyName];
        }
        return $this->getAttribute($keyName);
    }
    public function getKey()
    {
        $keys = $this->getKeyName();
        if(!is_array($keys)){
            return parent::getKey();
        }
        $pk = [];
        foreach($keys as $keyName){
            $pk[$keyName] = $this->getAttribute($keyName);
        }
        return $pk;
    }
    public function find($id, $columns = ['*'])
    {
        if (is_array($id) || $id instanceof Arrayable) {
            $out = null;
            foreach ($id as $key => $value) {
                //echo "{$key} => {$value} ";
                if ($out == null)
                {
                    $out = $this->where($key, $value);
                }
                else
                {
                    $out = $out->where($key, $value);
                }
            }
            return $out->first($columns);
        }
        return $this->whereKey($id)->first($columns);
    }
}

然后您必须为自定义关系进行分类:2) 在App''Classes''Database中创建以下文件HasMultiPKRelationships.php

<?php
namespace App'Classes'Database;
use App'Classes'Database'Eloquent'Relations'BelongsToMultiPK;
use Illuminate'Database'Eloquent'Builder;
use Illuminate'Database'Eloquent'Relations'BelongsTo;
trait HasMultiPKRelationships {
    public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
    {
        if ($relation === null) {
            $relation = $this->guessBelongsToRelation();
        }
        $instance = $this->newRelatedInstance($related);
        if ($foreignKey === null) {
            $foreignKey = Str::snake($relation) . '_' . $instance->getKeyName();
        }
        $ownerKey = $ownerKey ?: $instance->getKeyName();
        if ( is_array($ownerKey))
        {
            return new BelongsToMultiPK($instance->newQuery(), $this, $foreignKey, $ownerKey, $relation);
        }
        else
        {
            return new BelongsTo($instance->newQuery(), $this, $foreignKey, $ownerKey, $relation);
        }
    }
}

然后,您需要自定义BelongsTo 3)在App''Classes''Database''Eloquent''Relations中创建以下文件BelongsToMultiPK.php

<?php
namespace App'Classes'Database'Eloquent'Relations;
use Illuminate'Database'Eloquent'Relations'BelongsTo;
use Illuminate'Database'Eloquent'Model;
use Illuminate'Database'Eloquent'Builder;
use Illuminate'Database'Eloquent'Collection;
use Illuminate'Database'Eloquent'Relations'Concerns'SupportsDefaultModels;
class BelongsToMultiPK extends BelongsTo
{
    public function __construct(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
    {
        parent::__construct($query, $child, $foreignKey, $ownerKey, $relation);
    }
    public function addConstraints()
    {
        if (static::$constraints) {
            // For belongs to relationships, which are essentially the inverse of has one
            // or has many relationships, we need to actually query on the primary key
            // of the related models matching on the foreign key that's on a parent.
            $table = $this->related->getTable();
            if (! is_array($this->ownerKey) )
            {
                $this->query->where($table.'.'.$this->ownerKey, '=', $this->child->{$this->foreignKey});
            }
            else
            {
                foreach ($this->ownerKey as $key)
                {
                    $this->query->where($table.'.'.$key, '=', $this->child->{$this->foreignKey}[$key]);
                }
            }                   
        }
    }
}