Laravel 从多对多关系创建一对一关系

Laravel create a 1-1 relationship from a many-many relationship

嗨,我在尝试 return 多对多关系中的单个记录时遇到困难。

所以在我的应用程序中我有 Clubs 和 Addresses 实体。

俱乐部有0、1或n个地址,其中一个可能是主要地址。

地址也可以被其他一些实体使用(如活动、会员等)

我的表格如下:

我现在可以像这样请求我俱乐部的所有地址:

class Club {
    public function addresses()
    {
        return $this->belongsToMany('Address', 'club_address', 'club_id', 'address_id')->withPivot('is_main'); // club_address
    }
}

现在我想要的是在请求俱乐部时获取主地址或空值。

我不能满足于简单地添加 ->wherePivot('is_main', '=', 1) 因为当我想要数组或 null.

我想要这样的东西

class Club {
    // Get all the addresses in an array
    public function addresses()
    {
        return $this->belongsToMany('Address', 'club_address', 'club_id', 'address_id')->withPivot('is_main'); // club_address
    }

    // Get the main address or null
    public function address()
    {
        return $this->addresses()->wherePivot('is_main', '=', 1)->first();
    }
}

但问题是我无法预先加载 address 因为它不是 return 关系模型 ...

first() 在 eloquent 模型上 returns 集合 - 即使该集合为零(这基本上就是您所说的)。

不过first()上了一个合集returnsnull就是没有第一个...

所以..

class Club {
    public function addresses()
    {
        return $this->belongsToMany('Address', 'club_address', 'club_id', 'address_id')->withPivot('is_main');
    }

    public function address()
    {
        // First first is on the model, second first 
        // is on the collection
        return $this->addresses()
            ->wherePivot('is_main', '=', 1)
            ->first()
            ->first(); 
    }
}

注意 这本质上只是一个shorthand技巧。您的属性可以很容易地使用类似这样的东西,并且可能更具可读性:

class Club {
    public function addresses()
    {
        return $this->belongsToMany('Address', 'club_address', 'club_id', 'address_id')->withPivot('is_main');
    }

    public function address()
    {
        $record = $this->addresses()
            ->wherePivot('is_main', '=', 1)
            ->first();

        return count($record) === 0 ? null : $record;
    }
}

我找到了一种方法,通过扩展 BelongsToMany 关系 class 并用它们的 BelongsTo 关系等效覆盖两个方法。

但我不会说使用它是谨慎的,但它似乎适合我的使用。

namespace App\Relations;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class BelongsToOneFromMany extends BelongsToMany {

    /**
     * Initialize the relation on a set of models.
     *
     * @param  array   $models
     * @param  string  $relation
     * @return array
     */
    public function initRelation(array $models, $relation)
    {
        foreach ($models as $model)
        {
            $model->setRelation($relation, null);
        }

        return $models;
    }

    /**
     * Match the eagerly loaded results to their parents.
     *
     * @param  array   $models
     * @param  \Illuminate\Database\Eloquent\Collection  $results
     * @param  string  $relation
     * @return array
     */
    public function match(array $models, Collection $results, $relation)
    {
        $foreign = $this->foreignKey;

        $other = $this->otherKey;

        // First we will get to build a dictionary of the child models by their primary
        // key of the relationship, then we can easily match the children back onto
        // the parents using that dictionary and the primary key of the children.
        $dictionary = array();

        foreach ($results as $result)
        {
            $dictionary[$result->getAttribute($other)] = $result;
        }

        // Once we have the dictionary constructed, we can loop through all the parents
        // and match back onto their children using these keys of the dictionary and
        // the primary key of the children to map them onto the correct instances.
        foreach ($models as $model)
        {
            if (isset($dictionary[$model->$foreign]))
            {
                $model->setRelation($relation, $dictionary[$model->$foreign]);
            }
        }

        return $models;
    }

}