php 加载模型的所有关系

vof42yt1  于 2022-10-30  发布在  PHP
关注(0)|答案(9)|浏览(151)

通常为了急切地载入一段感情我会这样做:
$with = ['foo','bar','baz']
一个解决方案可能是设置$with = ['foo','bar','baz'],但无论何时调用Model,都会加载这三个关系
有没有可能做这样的事情:Model::with('*')

7kqas0il

7kqas0il1#

不,它不是,至少没有一些额外的工作,因为您的模型不知道它支持哪些关系,直到他们实际上被加载。
我在我自己的一个Laravel包中遇到了这个问题。没有办法用Laravel得到一个模型的关系列表。如果你看看它们是如何定义的,这是很明显的。简单的函数返回一个Relation对象。你甚至不能用php的反射类得到一个函数的返回类型。因此没有办法区分关系函数和任何其他函数。
为了简化这一过程,可以定义一个函数来添加所有关系,为此,可以使用雄辩的查询作用域(感谢Jarek Tkaczyk在评论中提到它)。

public function scopeWithAll($query) 
{
    $query->with('foo', 'bar', 'baz');
}

使用作用域而不是静态函数不仅允许您直接在模型上使用函数,而且还允许您以任何顺序链接查询构建器方法(例如where):

Model::where('something', 'Lorem ipsum dolor')->withAll()->where('somethingelse', '>', 10)->get();

获取受支持关系的替代方法

虽然Laravel不支持这样的开箱即用,你可以随时添加它自己。

注解

我使用了注解来确定一个函数是否是一个关系。注解不是php的正式部分,但是很多人使用doc块来模拟它们。Laravel 5也将在它的路由定义中使用注解,所以我认为在这种情况下这不是一个坏的做法。优点是,你不需要维护一个单独的支持关系的列表。
为每个关系添加注解:

/**
 * @Relation
 */
public function foo() 
{
    return $this->belongsTo('Foo');
}

然后编写一个函数,解析模型中所有方法的doc块并返回名称,你可以在模型或父类中这样做:

public static function getSupportedRelations() 
{
    $relations = [];
    $reflextionClass = new ReflectionClass(get_called_class());

    foreach($reflextionClass->getMethods() as $method) 
    {
        $doc = $method->getDocComment();

        if($doc && strpos($doc, '@Relation') !== false) 
        {
            $relations[] = $method->getName();
        }
    }

    return $relations;
}

然后在withAll函数中使用它们:

public function scopeWithAll($query) 
{
    $query->with($this->getSupportedRelations());
}

有些人喜欢php中的注解,有些人不喜欢。我喜欢这个简单的用例。

支持的关系数组

你也可以维护所有支持的关系的数组。但是这需要你总是将它与可用的关系同步,特别是当有多个开发人员参与时,并不总是那么容易。

protected $supportedRelations = ['foo','bar', 'baz'];

然后在withAll函数中使用它们:

public function scopeWithAll($query) 
{
    return $query->with($this->supportedRelations);
}

当然,你也可以使用override with like lukasgeiter mentioned in his answer,这看起来比使用withAll更简洁,但如果你使用注解或配置数组,那就见仁见智了。

vmjh9lq9

vmjh9lq92#

没有办法知道所有的关系是什么,如果不指定他们自己。如何其他答案张贴是好的,但我想补充几件事。

基本型号

我有一种感觉,你想在多个模型中这样做,所以如果你还没有的话,首先我会创建一个BaseModel

class BaseModel extends Eloquent {
    public $allRelations = array();
}

“配置”数组

我建议您使用成员变量,而不是将关系硬编码到方法中。正如您在上面看到的,我已经添加了$allRelations。请注意,您不能将其命名为$relations,因为Laravel已经在内部使用了它。

覆盖with()

既然你想要with(*),你也可以这样做。把这个添加到BaseModel

public static function with($relations){
    $instance = new static;
    if($relations == '*'){
        $relations = $instance->allRelations;
    }
    else if(is_string($relations)){
        $relations = func_get_args();
    }
    return $instance->newQuery()->with($relations);
}

(By这个函数的某些部分来自原始的Model类)

用法

class MyModel extends BaseModel {
    public $allRelations = array('foo', 'bar');
}

MyModel::with('*')->get();
js5cn81o

js5cn81o3#

我不会像建议的那样使用static方法,因为......它是Eloquent ;)只需利用它已经提供得功能-范围.
当然它不会为你做它(主要问题),但这绝对是要走的路:

// SomeModel
public function scopeWithAll($query)
{
    $query->with([ ... all relations here ... ]); 
    // or store them in protected variable - whatever you prefer
    // the latter would be the way if you want to have the method
    // in your BaseModel. Then simply define it as [] there and use:
    // $query->with($this->allRelations); 
}

这样,您就可以随意使用它:

// static-like
SomeModel::withAll()->get();

// dynamically on the eloquent Builder
SomeModel::query()->withAll()->get();
SomeModel::where('something', 'some value')->withAll()->get();

而且,事实上,你可以让Eloquent为你做这件事,就像Doctrine做的那样--使用doctrine/annotations和DocBlocks。你可以这样做:

// SomeModel

/**
 * @Eloquent\Relation
 */
public function someRelation()
{
  return $this->hasMany(..);
}

在这里包含它有点太长了,所以了解它是如何工作的:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

nwlqm0z1

nwlqm0z14#

由于我遇到过类似的问题,并找到了一个很好的解决方案,这里没有描述,也不需要填充一些自定义数组或其他什么,我会在未来发布它。
我首先创建一个名为RelationsManagertrait

trait RelationsManager
{
    protected static $relationsList = [];

    protected static $relationsInitialized = false;

    protected static $relationClasses = [
        HasOne::class,
        HasMany::class,
        BelongsTo::class,
        BelongsToMany::class
    ];

    public static function getAllRelations($type = null) : array
    {
        if (!self::$relationsInitialized) {
            self::initAllRelations();
        }

        return $type ? (self::$relationsList[$type] ?? []) : self::$relationsList;
    }

    protected static function initAllRelations()
    {
        self::$relationsInitialized = true;

        $reflect = new ReflectionClass(static::class);

        foreach($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            /**@var ReflectionMethod $method */
            if ($method->hasReturnType() && in_array((string)$method->getReturnType(), self::$relationClasses)) {
                self::$relationsList[(string)$method->getReturnType()][] = $method->getName();
            }
        }
    }

    public static function withAll() : Builder
    {
        $relations = array_flatten(static::getAllRelations());

        return $relations ? self::with($relations) : self::query();
    }
}

现在你可以在任何类上使用它,比如-

class Project extends Model
{
    use RelationsManager;

    //... some relations

}

然后当您需要从数据库中获取它们时:

$projects = Project::withAll()->get();

一些注意事项--我的关系类示例列表不包括变形关系,所以如果你也想得到它们--你需要把它们添加到$relationClasses变量中。

mgdq6dx1

mgdq6dx15#

您可以尝试使用反映来侦测特定于模型的方法,例如:

$base_methods = get_class_methods('Illuminate\Database\Eloquent\Model');
$model_methods = get_class_methods(get_class($entry));
$maybe_relations = array_diff($model_methods, $base_methods);

dd($maybe_relations);

然后尝试在一个控制良好的try/catch中加载每一个。Laravel的Model类有一个load和一个loadMissing方法用于快速加载。
请参阅api reference.

e37o9pze

e37o9pze6#

您可以在模型中创建方法

public static function withAllRelations() {
   return static::with('foo', 'bar', 'baz');
}

并调用Model::withAllRelations()
或者
$instance->withAllRelations()->first(); // or ->get()

af7jpaap

af7jpaap7#

对于某个模型,你不能有一个动态的关系加载。你需要告诉模型支持哪些关系。

hs1ihplo

hs1ihplo8#

composer 需要adideas/laravel-get-relationship-雄辩的-模型
https://packagist.org/packages/adideas/laravel-get-relationship-eloquent-model
拉瑞威把关系弄得全是口若悬河的模特!
您不需要知道模型中方法的名称就可以做到这一点。有了一个或多个Eloquent模型,多亏了这个包,您可以在运行时获得它的所有关系及其类型

kyxcudwk

kyxcudwk9#

最佳解决方案

首先创建一个名为RelationsManager特征:

<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use ReflectionClass;
use ReflectionMethod;

trait RelationsManager
{
    protected static $relationsList = [];

    protected static $relationsInitialized = false;

    protected static $relationClasses = [
        HasOne::class,
        HasMany::class,
        BelongsTo::class,
        BelongsToMany::class,

        HasOneThrough::class,
        HasManyThrough::class,

        MorphTo::class,
        MorphOne::class,
        MorphMany::class,
        MorphToMany::class,

    ];

    public static function getAllRelations($type = null): array
    {
        if (!self::$relationsInitialized) {
            self::initAllRelations();
        }

        return $type ? (self::$relationsList[$type] ?? []) : self::$relationsList;
    }

    protected static function initAllRelations()
    {
        self::$relationsInitialized = true;

        $reflect = new ReflectionClass(static::class);

        foreach ($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
            /**@var ReflectionMethod $method */
            if ($method->hasReturnType() && in_array((string) $method->getReturnType(), self::$relationClasses)) {
                self::$relationsList[(string) $method->getReturnType()][] = $method->getName();
            }
        }
    }

    public static function withAll(): Builder
    {
        $relations = array_flatten(static::getAllRelations());
        return $relations ? self::with($relations) : self::query();
    }
}

现在你可以在任何类上使用它,比如-

class Company extends Model
{
    use RelationsManager;

    //... some relations

}

然后当您需要从数据库中获取它们时:

$companies = Company::withAll()->get();

此解决方案仅适用于PHP7或更高版本。

已完成

相关问题