我有一个与Section模型相关的Post模型,这取决于工作的额外条件:

<?php
class Post extends Base
{
    public function section()
    {
        return $this->belongsTo('App\Models\Section', 'id_cat')->where('website', $this->website);
    }
}


当我想检索帖子并获取它的关联部分时,可以按照以下方式进行操作:

$post = Post::first();
echo $post->section->name; // Output the section's name


但是,当尝试使用急切的负载获取该部分时:

Post::with(['section'])->chunk(1000, function ($posts) {
    echo $post->section->name;
});


Laravel抛出以下异常:

PHP error:  Trying to get property of non-object


当我对上述急切的负载查询返回的Post对象进行调试时,我注意到section关系是null
请注意,如果我从belongsTo关联中删除条件,它会正常工作。

你们有什么想法为什么会发生吗?

最佳答案

您可以通过定义自定义关系来实现。

BelongsToWith.php

<?php

declare(strict_types=1);

namespace App\Database\Eloquent\Relations;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class BelongsToWith extends BelongsTo
{
    /**
     * @var array [$foreignColumn => $ownerColumn, ...] assoc or [$column, ...] array
     */
    protected $conditions = [];

    public function __construct(array $conditions, Builder $query, Model $child, string $foreignKey, string $ownerKey, string $relation)
    {
        $this->conditions = $conditions;

        parent::__construct($query, $child, $foreignKey, $ownerKey, $relation);
    }

    public function addConstraints()
    {
        if (static::$constraints) {
            // Add base constraints
            parent::addConstraints();

            // Add extra constraints
            foreach ($this->conditions as $key => $value) {
                if (is_int($key)) {
                    $key = $value;
                }
                $this->getQuery()->where($this->related->getTable() . '.' . $value, '=', $this->child->{$key});
            }
        }
    }

    public function addEagerConstraints(array $models)
    {
        // Ignore empty models
        if ([null] === $this->getEagerModelKeys($models)) {
            parent::addEagerConstraints($models);
            return;
        }

        $this->getQuery()->where(function (Builder $query) use ($models) {
            foreach ($models as $model) {
                $query->orWhere(function (Builder $query) use ($model) {
                    // Add base constraints
                    $query->where($this->related->getTable() . '.' . $this->ownerKey, $model->getAttribute($this->foreignKey));

                    // Add extra constraints
                    foreach ($this->conditions as $key => $value) {
                        if (is_int($key)) {
                            $key = $value;
                        }
                        $query->where($this->related->getTable() . '.' . $value, $model->getAttribute($key));
                    }
                });
            }
        });
    }

    public function match(array $models, Collection $results, $relation)
    {
        $dictionary = [];

        foreach ($results as $result) {
            // Base constraints
            $keys = [$result->getAttribute($this->ownerKey)];

            // Extra constraints
            foreach ($this->conditions as $key => $value) {
                $keys[] = $result->getAttribute($value);
            }

            // Build nested dictionary
            $current = &$dictionary;
            foreach ($keys as $key) {
                $current = &$current[$key];
            }
            $current = $result;
            unset($current);
        }

        foreach ($models as $model) {
            $current = $dictionary;

            // Base constraints
            if (!isset($current[$model->{$this->foreignKey}])) {
                continue;
            }
            $current = $current[$model->{$this->foreignKey}];

            // Extra constraints
            foreach ($this->conditions as $key => $value) {
                if (is_int($key)) {
                    $key = $value;
                }
                if (!isset($current[$model->{$key}])) {
                    continue 2;
                }
                $current = $current[$model->{$key}];
            }

            // Set passed result
            $model->setRelation($relation, $current);
        }

        return $models;
    }
}


HasExtendedRelationships.php

<?php

declare(strict_types=1);

namespace App\Database\Eloquent\Concerns;

use App\Database\Eloquent\Relations\BelongsToWith;
use Illuminate\Support\Str;

trait HasExtendedRelationships
{
    public function belongsToWith(array $conditions, $related, $foreignKey = null, $ownerKey = null, $relation = null): BelongsToWith
    {
        if ($relation === null) {
            $relation = $this->guessBelongsToRelation();
        }

        $instance = $this->newRelatedInstance($related);

        if ($foreignKey === null) {
            $foreignKey = Str::snake($relation) . '_' . $instance->getKeyName();
        }

        $ownerKey = $ownerKey ?: $instance->getKeyName();

        return new BelongsToWith($conditions, $instance->newQuery(), $this, $foreignKey, $ownerKey, $relation);
    }
}


然后:

class Post extends Base
{
    use HasExtendedRelationships;

    public function section(): BelongsToWith
    {
        return $this->belongsToWith(['website'], App\Models\Section::class, 'id_cat');
    }
}




$posts = Post::with('section')->find([1, 2]);


您的急切加载查询将类似于:

select * from `sections`
where (
    (
        `sections`.`id` = {$posts[0]->id_cat}
        and `sections`.`website` = {$posts[0]->website}
    )
    or
    (
        `sections`.`id` = {$posts[1]->id_cat}
        and `sections`.`website` = {$posts[1]->website}
    )
)

08-26 08:33