Laravel属于条件和急切负荷

时间:2016-05-18 02:44:26

标签: laravel eager-loading belongs-to

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

<?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关联中删除条件,它工作正常。

你们有没有想过为什么会这样?

2 个答案:

答案 0 :(得分:1)

正如我的评论中所提到的,func updateMapForCoordinate(coordinate: CLLocationCoordinate2D) { let camera = MKMapCamera(lookingAtCenterCoordinate: coordinate, fromDistance: 1000, pitch: 0, heading: 0) mapView.setCamera(camera, animated: false) var center = coordinate; center.latitude -= self.mapView.region.span.latitudeDelta / 6.0; mapView.setCenterCoordinate(center, animated: false); } 不应该用在关系定义中。因此,您的关系定义仅适用于

where

你可以用这种方式急切加载(不用chunk等给出确切的查询)

public function section()
{
    return $this->belongsTo('App\Models\Section', 'id_cat');
}

即。当你在请求中传递变量Post::with(['section' => function ($query) use ($request) { $query->where('website', $request['website']) }])->get()->first(); 或者以类似的方式使用任何其他变量时。

我希望这可以解释。如果有任何不清楚的地方,请添加评论。

答案 1 :(得分:0)

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

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}
    )
)