【问题标题】:Eloquent relationships for not-existing model class不存在的模型类的雄辩关系
【发布时间】:2014-12-13 15:43:50
【问题描述】:

我希望在我的应用程序中包含许多模型/模块,但其中一些会为某些客户删除。

现在我有这样的关系:

public function people()
{
    return $this->hasMany('People', 'model_id');
}

当我运行 $model = Model::with('people')->get(); 时,它工作正常

但是如果People 模型不存在怎么办?

目前我得到:

ClassLoader.php 第 386 行中的 1/1 ErrorException:包含(...):失败 打开流:没有这样的文件或目录

我试过了

public function people()
{ 
    try {
       return $this->hasMany('People', 'model_id');
    }
    catch (FatalErrorException $e) {
        return null;
    }
}

或与:

public function people()
{ 
    return null; // here I could add checking if there is a Model class and if not return null
}

但是当使用这种方法时$model = Model::with('people')->get(); 不起作用。

我将有几十个关系,我无法在with 中使用它们的列表。最好的方法是使用一些 empty relation(返回 null)只是为了让 Eloquent 不做任何事情,但在这种情况下,Eloquent 仍然试图让它工作,我会得到:

哎呀,好像出了点问题。 Builder.php 第 430 行中的 1/1 FatalErrorException:调用成员函数 addEagerConstraints() on null

有什么简单的解决办法吗?

【问题讨论】:

  • 看起来很奇怪,你为什么要删除模型?
  • @JarekTkaczyk 如果客户不需要某些功能/不为他们付费,他们将没有此功能(例如那些带有模型的文件),但其他付费的客户应该有功能。而且我不想更改与可能被删除的模型相关的其他模型。这不完全是我的构想,但我试图找出是否可以做到。
  • 我理解为客户启用/禁用功能的概念。我没有得到的是您使用 removig 文件实现它的方式。如果您交付代码,而不是 saas,那么我建议您创建独立的包,您可以根据客户端简单地插入或不插入。
  • @JarekTkaczyk 我将拥有一个可以显示人员列表的模块。如果未启用其他模块,它应该只显示人员列表,但如果启用模块 A,它还应该为每个人显示来自 A 模块(关系)的一些信息,如果启用模块 B,它应该显示来自 B 模块的一些信息并且可能如果启用了 A 和 B 模块,我将不得不在人员列表上显示来自 A 和 B 模块的信息。目前只是我的客户的概念,而不是最终的解决方案。也许你可以为此推荐一些其他的实现?

标签: laravel eloquent laravel-5


【解决方案1】:

我能想出的唯一解决方案是创建自己的 Eloquent\Builder 类。

我称它为MyBuilder。让我们首先确保它被实际使用。在您的模型(最好是基本模型)中添加此 newEloquentBuilder 方法:

public function newEloquentBuilder($query)
{
    return new MyBuilder($query);
}

在自定义 Builder 类中,我们将覆盖 loadRelation 方法并在关系上调用 addEagerConstraints 之前添加一个 if null 检查(或者在您的情况下在 null 上)

class MyBuilder extends \Illuminate\Database\Eloquent\Builder {
    protected function loadRelation(array $models, $name, Closure $constraints)
    {
        $relation = $this->getRelation($name);


        if($relation == null){
            return $models;
        }


        $relation->addEagerConstraints($models);

        call_user_func($constraints, $relation);

        $models = $relation->initRelation($models, $name);

        $results = $relation->getEager();

        return $relation->match($models, $results, $name);
    }
}

函数的其余部分与原始构建器(Illuminate\Database\Eloquent\Builder)的代码基本相同

现在只需在你的关系函数中添加类似这样的东西,它应该都可以工作:

public function people()
{
    if(!class_exist('People')){
        return null;
    }
    return $this->hasMany('People', 'model_id');
}

更新:使用它喜欢关系

如果你想像对待关系一样使用它,那就有点棘手了。

您必须覆盖Eloquent\Model 中的getRelationshipFromMethod 函数。所以让我们创建一个基础模型(你的模型显然需要扩展它......)

class BaseModel extends \Illuminate\Database\Eloquent\Model {
    protected function getRelationshipFromMethod($key, $camelKey)
    {
        $relations = $this->$camelKey();

        if ( $relations instanceof \Illuminate\Database\Eloquent\Collection){
            // "fake" relationship
            return $this->relations[$key] = $relations;
        }
        if ( ! $relations instanceof Relation)
        {
            throw new LogicException('Relationship method must return an object of type '
                . 'Illuminate\Database\Eloquent\Relations\Relation');
        }

        return $this->relations[$key] = $relations->getResults();
    }
}

现在我们需要修改关系以返回一个空集合

public function people()
{
    if(!class_exist('People')){
        return new \Illuminate\Database\Eloquent\Collection();
    }
    return $this->hasMany('People', 'model_id');
}

并更改MyBuilder 中的loadRelation 函数以检查类型集合而不是null

protected function loadRelation(array $models, $name, Closure $constraints)
{
    $relation = $this->getRelation($name);

    if($relation instanceof \Illuminate\Database\Eloquent\Collection){
        return $models;
    }

    // ...
}

【讨论】:

  • 感谢您的努力,目前它似乎非常接近我的需要。但是,现在当我想为人们循环时,我需要使用类似的东西:if ($model->people() === null) {} else {if (count($model->people) == 0) {echo "no people";} else {foreach $model->people ... }。是否有机会通过关系空模型返回,在 MyBuilder 中跟踪它只是为了打破你所做的,以便使用标准访问:if (count($model->people) == 0) {echo "no people";} else {foreach $model->people ... } 我试图创建空关系但没有成功。
  • 我更新了问题。但是,我目前无法对其进行测试。让我知道它是否有效。
猜你喜欢
  • 1970-01-01
  • 2019-02-12
  • 1970-01-01
  • 1970-01-01
  • 2018-02-19
  • 2019-04-18
  • 2015-06-27
  • 2013-06-04
相关资源
最近更新 更多