加载模型的所有关系
Load all relationships for a model
通常为了急切加载关系我会做这样的事情:
Model::with('foo', 'bar', 'baz')...
一个解决方案可能是设置 $with = ['foo','bar','baz']
但是每当我调用 Model
时它总是会加载这三个关系
是否可以这样做:Model::with('*')
?
不,至少没有一些额外的工作,因为你的模型在实际加载它们之前不知道它支持哪些关系。
我在自己的 Laravel 软件包中遇到了这个问题。无法获得模型与 Laravel 的关系列表。但是,如果您查看它们的定义方式,就会很明显。 return 一个 Relation
对象的简单函数。你甚至不能用 php 的反射 classes 得到函数的 return 类型,所以没有办法区分关系函数和任何其他函数。
为了简化操作,您可以定义一个添加所有关系的函数。
为此,您可以使用 eloquents query scopes(感谢 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 的正式部分,但很多人使用文档块来模拟它们。
Laravel 5 也将在其路由定义中使用注释,因此我认为在这种情况下这不是一个坏习惯。优点是,您不需要维护一个单独的支持关系列表。
为您的每个关系添加注释:
/**
* @Relation
*/
public function foo()
{
return $this->belongsTo('Foo');
}
并编写一个函数来解析模型中所有方法的文档块和 returns 名称。您可以在模型或父级中执行此操作 class:
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);
}
你当然也可以。这似乎比使用 withAll
更干净。但是,如果您使用注释或配置数组是一个见仁见智的问题。
您可以在模型中创建方法
public static function withAllRelations() {
return static::with('foo', 'bar', 'baz');
}
并调用 Model::withAllRelations()
或者
$instance->withAllRelations()->first(); // or ->get()
如果不自己指定它们,就无法知道所有关系是什么。贴出来的其他答案都不错,不过我想补充几点。
基础模型
我觉得你想在多个模型中这样做,所以首先我会创建一个 BaseModel
如果你还没有的话。
class BaseModel extends Eloquent {
public $allRelations = array();
}
"Config"数组
我建议您使用成员变量,而不是将关系硬编码到方法中。正如您在上面看到的,我已经添加了 $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);
}
(顺便说一句,这个函数的某些部分来自原始模型class)
用法
class MyModel extends BaseModel {
public $allRelations = array('foo', 'bar');
}
MyModel::with('*')->get();
您不能动态加载特定模型的关系。您需要告诉模型支持哪些关系。
我不会使用建议的 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
因为我遇到了类似的问题,并找到了一个很好的解决方案,这里没有描述并且不需要填充一些自定义数组或其他任何东西,我会 post 将来.
我所做的是首先创建一个 trait
,名为 RelationsManager
:
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 一起使用,例如 -
class Project extends Model
{
use RelationsManager;
//... some relations
}
然后当您需要从数据库中获取它们时:
$projects = Project::withAll()->get();
一些注意事项 - 我的示例关系 classes 列表不包括变形关系,因此如果你也想获得它们 - 你需要将它们添加到 $relationClasses
变量。此外,此解决方案仅适用于 PHP 7.
您可以尝试使用反射检测特定于您的模型的方法,例如:
$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 class 有一个 load 和一个 loadMissing 方法用于预加载。
作曲家要求adideas/laravel-get-relationship-eloquent-model
https://packagist.org/packages/adideas/laravel-get-relationship-eloquent-model
Laravel 获取所有 eloquent 个模型的关系!
您无需知道模型中方法的名称即可执行此操作。拥有一个或多个 Eloquent 模型,多亏了这个包,您可以在运行时获得它的所有关系及其类型
通常为了急切加载关系我会做这样的事情:
Model::with('foo', 'bar', 'baz')...
一个解决方案可能是设置 $with = ['foo','bar','baz']
但是每当我调用 Model
是否可以这样做:Model::with('*')
?
不,至少没有一些额外的工作,因为你的模型在实际加载它们之前不知道它支持哪些关系。
我在自己的 Laravel 软件包中遇到了这个问题。无法获得模型与 Laravel 的关系列表。但是,如果您查看它们的定义方式,就会很明显。 return 一个 Relation
对象的简单函数。你甚至不能用 php 的反射 classes 得到函数的 return 类型,所以没有办法区分关系函数和任何其他函数。
为了简化操作,您可以定义一个添加所有关系的函数。 为此,您可以使用 eloquents query scopes(感谢 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 的正式部分,但很多人使用文档块来模拟它们。 Laravel 5 也将在其路由定义中使用注释,因此我认为在这种情况下这不是一个坏习惯。优点是,您不需要维护一个单独的支持关系列表。
为您的每个关系添加注释:
/**
* @Relation
*/
public function foo()
{
return $this->belongsTo('Foo');
}
并编写一个函数来解析模型中所有方法的文档块和 returns 名称。您可以在模型或父级中执行此操作 class:
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);
}
你当然也可以withAll
更干净。但是,如果您使用注释或配置数组是一个见仁见智的问题。
您可以在模型中创建方法
public static function withAllRelations() {
return static::with('foo', 'bar', 'baz');
}
并调用 Model::withAllRelations()
或者
$instance->withAllRelations()->first(); // or ->get()
如果不自己指定它们,就无法知道所有关系是什么。贴出来的其他答案都不错,不过我想补充几点。
基础模型
我觉得你想在多个模型中这样做,所以首先我会创建一个 BaseModel
如果你还没有的话。
class BaseModel extends Eloquent {
public $allRelations = array();
}
"Config"数组
我建议您使用成员变量,而不是将关系硬编码到方法中。正如您在上面看到的,我已经添加了 $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);
}
(顺便说一句,这个函数的某些部分来自原始模型class)
用法
class MyModel extends BaseModel {
public $allRelations = array('foo', 'bar');
}
MyModel::with('*')->get();
您不能动态加载特定模型的关系。您需要告诉模型支持哪些关系。
我不会使用建议的 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
因为我遇到了类似的问题,并找到了一个很好的解决方案,这里没有描述并且不需要填充一些自定义数组或其他任何东西,我会 post 将来.
我所做的是首先创建一个 trait
,名为 RelationsManager
:
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 一起使用,例如 -
class Project extends Model
{
use RelationsManager;
//... some relations
}
然后当您需要从数据库中获取它们时:
$projects = Project::withAll()->get();
一些注意事项 - 我的示例关系 classes 列表不包括变形关系,因此如果你也想获得它们 - 你需要将它们添加到 $relationClasses
变量。此外,此解决方案仅适用于 PHP 7.
您可以尝试使用反射检测特定于您的模型的方法,例如:
$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 class 有一个 load 和一个 loadMissing 方法用于预加载。
作曲家要求adideas/laravel-get-relationship-eloquent-model
https://packagist.org/packages/adideas/laravel-get-relationship-eloquent-model
Laravel 获取所有 eloquent 个模型的关系!
您无需知道模型中方法的名称即可执行此操作。拥有一个或多个 Eloquent 模型,多亏了这个包,您可以在运行时获得它的所有关系及其类型