首页 > 解决方案 > 使用带有 MorpMany 关系的 toHasOne 宏

问题描述

我有多个利用 HasRetirements 特征类的模型类。两种模型都使用 MorphMany 关系来定位每个模型的关联退休表模型。在 HasRetirements trait 类中,我还有一个 isRetired() 方法和一个 currentRetirement() 方法。这些方法如下所示。

我遇到了一个可以链接到 Eloquent 关系的宏,以便您可以检索单个记录。宏 toHasOne() 通过 hasMany 关系利用模型关系,但是我的问题是这也可以用于 morphMany 关系,因为它是多态的。

https://scotch.io/tutorials/understanding-and-using-laravel-eloquent-macros

public function currentRetirement()
{
    return $this->retirements()->whereNull('ended_at')->latest()->toHasOne();
}

public function isRetired()
{
    return $this->retirements()->whereNull('ended_at')->exists();
}

标签: laravel

解决方案


使用 Laravel 5.5,您可以注册一个宏,从BelongsToMany关系中返回派生类。如果您不打算在其他任何地方使用它,这个派生类也可以是匿名类。在派生类中,您需要覆盖该match方法并将单个对象作为关系返回或null以其他方式返回

BelongsToMany::macro('asSingleEntity', function() {

        return new class(
            $this->related->newQuery(),
            $this->parent,
            $this->table,        
            $this->foreignPivotKey,
            $this->relatedPivotKey,
            $this->parentKey,
            $this->relatedKey,
            $this->relationName) extends BelongsToMany {

            /**
             * 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)
            {
                $dictionary = $this->buildDictionary($results);

                // Once we have an array dictionary of child objects we can easily match the
                // children back to their parent using the dictionary and the keys on the
                // the parent models. Then we will return the hydrated models back out.
                foreach ($models as $model) {
                    if (isset($dictionary[$key = $model->{$this->parentKey}])) {
                        $model->setRelation(
                            // $relation, $this->related->newCollection($dictionary[$key])      // original code
                            $relation, array_first($dictionary[$key])
                        );
                    } else {
                        $model->setRelation($relation, null);
                    }
                }

                return $models;
            }

        };

    });

然后,您可以简单地在模型中使用它。

return $this
        ->belongsToMany(\App\Models\Entity::class, 'pivot_table_name')
        ->asSingleEntity();

推荐阅读