【问题标题】:Laravel Eloquent get N Level hierarchy records from self-referencing table with other table's columnsLaravel Eloquent 从具有其他表列的自引用表中获取 N 级层次结构记录
【发布时间】:2015-01-21 08:05:24
【问题描述】:

我正在使用Laravel 4MySQL 后端。

我有两个数据库表,即 - surveyestemplates

这两个表都是self-referencing,并且它们的column 名为parent,即该表本身的foreign key。他们的记录在self referencing parent-child 关系中。

表格设计如下:

调查:

(PK)                        (FK_Surveyes)
Id  title       type        parent          sort_order  deleted_at
1   General     group       NULL            1           NULL
2   Where..?    question    1               1           NULL
3   abc..       answer      2               1           NULL
4   def..       answer      2               2           NULL
5   efg..       answer      2               3           NULL
6   ghi..       answer      2               4           NULL
7   What..?     question    1               2           NULL
8   hij..       answer      7               1           NULL
9   ijk..       answer      7               2           NULL
10  How..?      question    8               1           NULL
11  jkl..       answer      10              1           NULL
12  mnm..       answer      10              2           NULL
13  Special     group       NULL            2           NULL
14  Whom..?     question    13              1           NULL
15  opq..       answer      14              1           NULL
16  rst..       answer      14              2           NULL

模板:

(PK)(FK_surveyes)               (FK_Templates)
Id  survey_id       type        parent          sort_order  deleted_at
1   NULL            group       NULL            1           NULL
2   14              question    1               1           NULL
3   15              answer      2               1           NULL
4   16              answer      3               2           NULL
5   NULL            group       NULL            2           NULL
6   2               question    5               1           NULL
7   3               answer      6               1           NULL
8   4               answer      6               2           NULL
9   5               answer      6               3           NULL
10  6               answer      8               4           NULL
11  7               question    10              2           NULL
12  8               answer      10              1           NULL
13  9               answer      10              2           NULL

现在,我希望他们的记录也与 N 层次结构级别相同。

所以我为templates 创建了一个model,如下所示:

class Template extends BaseModel{

    protected $table = 'templates';
    protected $softDelete = false;

    // loads only direct children - 1 level
    public function child()
    {
       return $this->hasMany('Template', 'parent');
    }

    // recursive, loads all descendants
    public function children()
    {
       return $this->child()->with('children')->orderBy('sort_order');
    }

    // parent
    public function parent()
    {
       return $this->belongsTo('Template','parent');
    }

    // all ascendants
    public function parentRecursive()
    {
       return $this->parent()->with('parentRecursive');
    }
}

我正在使用下面的方法(效果很好)来获取模板的N 层次结构:

public function getTemplates(){
    $templates = Template::with('children')
                ->whereNull('parent')
                ->orderBy('sort_order', 'ASC');

    return $templates->toJson();
}

但是现在,我想要 surveyes 表中的 title 列。表templates 中的列survey_id 是表surveyesforeign key

我怎样才能实现它?

我在Template 模型中更新了以下方法:

public function children()
{
   return $this->child()->with('children')->orderBy('sort_order')
        ->leftJoin('surveyes', 'surveyes.id', '=', 'templates.survey_id')->select('templates.*','surveyes.title');
}

但它不提供分层记录并且浏览器会挂起templates 表中只有 1500 条记录。

有人知道如何实现吗?

编辑:

我在模板中添加了一个获取Survey模型的方法,如下所示:

public function survey()
    {
        return $this->belongsTo('D2D\Models\Survey','survey_id');
    }

并更新children()方法如下:

public function children()
{
   return $this->child()
   ->with('survey','children')
   ->orderBy('sort_order');
}

现在我可以获取Survey 模型的记录,但它返回Survey 的所有列,如下所示:

{
    "id": 2,
    "survey_id": 522,
    "title": "Abc....?",
    "type": "question",
    "parent": 1200
    "survey": {
        "id": 522,
        "type": "question",
        "subtype": null,
        "title": "Abc....?",
        "parent": 1
    },
    "children": [{
        "id": 3,
        "survey_id": 526,
        "title": "aaa",
        "type": "answer",
        "parent": 2
        "survey": {
            "id": 526,
            "type": "answer",
            "subtype": null,
            "title": "aaa",
            "parent": 522
        },
        "children": []
    },
    {
        "id": 4,
        "survey_id": 527,
        "title": "bbb",
        "type": "answer",
        "parent": 2
        "survey": {
            "id": 527,
            "type": "answer",
            "title": "bbb",
            "parent": 522,
        },
        "children": []
    },
    ...
}

但我想要如下所示:

{
    "id": 2,
    "survey_id": 522,
    "title": "Abc....?", // Must be from Survey table
    "type": "question",
    "parent": 1200
    "children": [{
        "id": 3,
        "survey_id": 526,
        "title": "aaa", // Must be from Survey table
        "type": "answer",
        "parent": 2
        "children": []
    },
    {
        "id": 4,
        "survey_id": 527,
        "title": "bbb", // Must be from Survey table
        "type": "answer",
        "parent": 2
        "children": []
    },
    ...
}

有什么方法可以实现吗?

我尝试过更新调查()方法如下:

public function survey()
{
    return $this->belongsTo('D2D\Models\Survey','survey_id')->select(array('title'));
}

但在这种情况下,它会为每个元素提供 title=NULL。

请告诉我如何实现这一目标?

谢谢。

【问题讨论】:

  • 参见:stackoverflow.com/questions/24989245/… 递归方法已单独定义。您还需要设置关系。
  • 那是不同的。我不想存储任何东西,但我想获得另一个表中的列。另外我认为如果我使用它,那么在每个 for 循环迭代中它都会进行单独的数据库调用。将来我可以有数千条记录,在这种情况下可能会出现性能问题。
  • 你是不是在添加leftJoin后才得到这个错误?
  • @deczo 是的。如果我使用前面提到的 ->get() 语句,我会收到此错误。
  • 是的,但是关系中没有leftJoin,就没有错误?

标签: php laravel laravel-4


【解决方案1】:

解决此问题的一种可能方法是使用嵌套集模型。这避免了递归查询的问题。有两个可用的 Laravel 包可以扩展 Eloquent 以实现嵌套集:Laravel-NestedSetBaum。我了解您有现有数据的问题;但是,制作一次性迁移脚本应该不会太难。您可以使用您选择的任何嵌套集库创建新模型;这些模型将具有设置调查父节点的方法。您可以在新表中拥有新的调查数据(您可以覆盖 Eloquent 中的表名;see here. 然后,编写脚本或受保护的控制器操作将现有调查迁移到新表。它将像这样(使用 laravel-nestedset 语法;Baum 类似):

$oldSurveys = Survey::all();
$oldSurveys->each(function($survey) {
    $newSurvey = NestedSurvey::create(array($survey->name, $survey->type ...etc));
});

$newSurveys = NestedSurvey::whereNotNull('parent_id');
$newSurveys->each(function($survey) {
    $parent = NestedSurvey::find($survey->parent_id);
    $survey->appendTo($parent)->save();
});

将所有调查导入新表后,您可以切换应用程序以使用新模型。

现在,您将能够在线性时间内获取任意深度的层次结构。由于您的模型扩展了 Eloquent,因此您可以像往常一样获取任何相关的列。

【讨论】:

  • 我已经使用了 laravel baum 包,现在看起来工作正常。非常感谢。 +1 为您提供帮助。
猜你喜欢
  • 1970-01-01
  • 2014-08-31
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多