Laravel ORM从自引用表中获取N级层次结构JSON

15

我正在使用带有MySQL后端的LARAVEL 4。

我有一个自引用表,其中包含列id、name、type和parent。这里,父级是列Id的外键。表中的数据如下:

id  name          type         parent 
1   General       group        NULL
2   What is..?    question     1
3   aa            answer       2
4   bb            answer       2
5   cc            answer       2
6   How is..?     question     1
7   ba            answer       6
8   bb            answer       6
9   Where is..?   question     4
10  ca            answer       9
11  cb            answer       9
12  Who is..?     question     6
13  da            answer       12
14  db            answer       12
15  Specific      group        NULL
16  When is..?    question     15
17  ea            answer       16
18  eb            answer       16
19  Whome is..?   question     2
20  fa            answer       19
21  fb            answer       19
22  fc            answer       19

我希望得到一个函数,使用这个关联数据返回嵌套的JSON。例如:

[{
  "id" : 1, 
  "name" : "Geneal", 
  "type" : "group", 
  "children" : [{
      "id" : 2, 
      "name" : "What is..?", 
      "type" : "question", 
      "children" : [{
         "id" : 3, 
         "name" : "aa", 
         "type" : "answer"
      },
      {
         "id" : 4, 
         "name" : "bb", 
         "type" : "answer"
      },
      {
         "id" : 5, 
         "name" : "cc", 
         "type" : "answer"
      }]},
      {
      "id" : 6, 
      "name" : "How is..?", 
      "type" : "question", 
      "children" : [{
         "id" : 7, 
         "name" : "ba", 
         "type" : "answer"
      },
      {
         "id" : 8, 
         "name" : "bb", 
         "type" : "answer"
      }]
   }]
... and so on
}]
我已经创建了一个名为Surveymodel,如下所示:
class Survey extends BaseModel{

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

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

    public function children()
    {
        return $this->hasMany('Survey', 'parent');
    }   
}

调用了controller,代码如下:

$user = Survey::all();
$parent = $user->parent()->first();
$children = $user->children()->get();

但是我没有得到我在上面提到的JSON中所提到的正确结果。

print_r($parent->toJson()); 

仅提供一个层次结构记录(即组和问题,而不是答案)。

同时

print_r($children ->toJson());

只提供问题(不包括组和答案)。

我希望得到具有N层级嵌套的自引用数据完整的JSON格式

我也尝试过

$user = Survey::with('parent', 'children')->get();

但是发现和上面的$parent一样。

有没有办法让我得到想要的结果呢?

提前感谢。


我认为LARAVEL是一个无用的框架。在它的文档中,我找不到任何帮助。 - Dev
1
阅读文档http://laravel.com/docs/eloquent#eager-loading 嵌套关系,如果您仍需要帮助,请尝试它,然后回来。 - Jarek Tkaczyk
@deczo 我在问题中已经提到我使用了急加载。请看 $user = Survey::with('parent', 'children')->get(); 这个语句只检索组和问题,而不是答案。我希望它作为嵌套数组或JSON与N级一起。如何实现? - Dev
@Dev 我在谈论嵌套关系。无论如何,请检查我的答案。 - Jarek Tkaczyk
@deczo 在stackoverflow这个令人惊叹的网站上有LARAVEL专家。 - Dev
显示剩余2条评论
2个回答

56

以下是手动检索嵌套关系的方法:

$collection = Model::with('relation1.relation2.relation3')->get();

所以在你的情况下,它将是:

$surveys = Survey::with('children.children.children')->get();

显然,当关系固定时,这样做可以完成工作,但对于向同一表的递归关系来说并不是正确的方法。

幸运的是,您可以使这样的关系递归,然后您所需要检索整个树的只有这个:

$surveys = Survey::with('childrenRecursive');

然而,我不会以这种方式为每一行加载父级。

以下是您所需的全部内容:

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

// recursive, loads all descendants
public function childrenRecursive()
{
   return $this->children()->with('childrenRecursive');
   // which is equivalent to:
   // return $this->hasMany('Survey', 'parent')->with('childrenRecursive);
}

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

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

编辑:为了获取真正的树形结构,第一个查询必须仅限于根节点:

$surveys = Survey::with('childrenRecursive')->whereNull('parent')->get();

1
将 200,000 行加载到数组中始终会导致内存问题。请分块处理、分页或采取其他措施。如果不知道您对数据的处理方式,我无法提出建议。 - Jarek Tkaczyk
不要加载“parent”。重复项很明显,因为您需要首先仅选择根级行。检查编辑。 - Jarek Tkaczyk
如果我想将另一个表的列添加到这个模型中怎么办?例如,有一个名为“template”的表,它引用了“survey”表中的列“survey.template_id”。现在我想要从“templates”表中获取“template.title”列。我该如何实现?我尝试过左连接,但没有成功。 - Dev
我不理解您所说的维护层次结构是什么意思。无论如何,请查看相关链接,使用 leftJoin,如果这不是您需要的,请用适当的描述提出新问题。 - Jarek Tkaczyk
有没有人能够解决我的问题?在 https://stackoverflow.com/questions/48768670/illegal-offset-type-error-on-fetching-a-relations-to-self-class-in-laravel 上找到了相同的问题。我尝试了这个答案,但是我收到了“非法偏移类型”错误提示。 - Ahmad Badpey
显示剩余10条评论

3
如此简单!!!
1- 将您的“children”关系加载到您的模型中,像这样:
class Survey extends BaseModel{

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

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

    public function children()
    {
       // change this: return $this->hasMany('Survey', 'parent');

        return $this->hasMany('Survey', 'parent')->with('children);
    }   
}

2- 你只需要像这样在你的关系中添加闭包函数:
$surveys = Survey::with(['children' => function($q) {
            $q->with('children');
       },
       'parent' => function($q) {
            $q->with('parent');
       });

然后您可以像这样访问结果中的嵌套子元素:
$surveys->children

"并且支持无限嵌套:"
$surveys->children->first()->children

等等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接