2

我很好奇是否有一种简单的方法可以使用 eloquent 实现材料清单(组件)类型的递归系统?这是我正在使用的两个表结构:

inventory桌子:

+----+------------+-------------+ | id | name | is_assembly | +----+------------+-------------+ | 1 | Table | 1 | +----+------------+-------------+ | 2 | Table Top | 0 | +----+------------+-------------+ | 3 | Table Legs | 0 | +----+------------+-------------+

inventory_assemblies桌子:

+----+--------------+---------+----------+ | id | inventory_id | part_id | quantity | +----+--------------+---------+----------+ | 1 | 1 | 1 | 1 | +----+--------------+---------+----------+ | 2 | 1 | 2 | 1 | +----+--------------+---------+----------+ | 3 | 1 | 3 | 4 | +----+--------------+---------+----------+

此组装表应表示“1 个桌子包含 1 个桌面和 4 个桌腿”。

库存模型:

class Inventory extends Eloquent
{
    public function assemblies()
    {
        return $this->hasMany('InventoryAssembly', 'inventory_id', 'id');
    }

    /**
     * Returns all of the assemblies items recursively.
     *
     * @param bool $recursive
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getAssemblyItems($recursive = true)
    {
        /*
         * Grab all of the current item's assemblies not including itself
         */
        $assemblies = $this->assemblies()->where('part_id', '!=', $this->id)->get();

        $items = new Collection();

        // We'll go through each assembly
        foreach ($assemblies as $assembly)
        {
            // Get the assembly part
            $part = $assembly->part;

            if ($part)
            {
                // Dynamically set the quantity attribute on the item
                $part->quantity = $assembly->quantity;

                // Dynamically set the assembly ID attribute to the item
                $part->assembly_id = $assembly->id;

                // If recursive is true, we'll go through each assembly level
                if($recursive)
                {
                    if($part->is_assembly)
                    {
                        /*
                         * The part is an assembly, we'll create a new
                         * collection and store the part in it's own array key,
                         * as well as the assembly.
                         */
                        $nestedCollection = new Collection([
                            'part' => $part,
                            'assembly' => $part->getAssemblyItems(),
                        ]);

                        $items->add($nestedCollection);

                    } else
                    {
                        // The part isn't an assembly, we'll just add it to the list
                        $items->add($part);
                    }
                } else
                {
                    /*
                     * Looks like the dev only wants one level
                     * of items, we'll just add the part to the list
                     */
                    $items->add($part);
                }
            }
        }

        return $items;
    }
}

InventoryAssembly 模型:

class InventoryAssembly extends BaseModel
{
    public function item()
    {
        return $this->belongsTo('Inventory', 'inventory_id', 'id');
    }

    public function part()
    {
        return $this->belongsTo('Inventory', 'part_id', 'id');
    }
}

现在这可行,但是如果我将一个项目添加到自身的程序集中,它会得到一个无限循环。所以这是我的问题:

  • 如何防止无限递归查询?
  • 我这样做对吗?
  • 有没有更简单的方法?
  • BoM 模型是否更适合嵌套集设计?

我真的很难理解自引用重复查询。我真的很感谢任何帮助,在此先感谢!

编辑:使用 user3158900 建议的库存模型本身的属于多个关系,我能够执行递归汇编查询,如下所示:

库存模型(从下面的答案修改):

class Inventory extends Eloquent {

    public function assemblies()
    {
        return $this->belongsToMany('Inventory', 'inventory_assemblies', 'inventory_id', 'part_id')
            ->withPivot(['quantity']);
    }

    public function assembliesRecursive()
    {
        return $this->assemblies()->with('assembliesRecursive');
    }

}

检索单个级别的程序集:

$item = Inventory::with('assemblies')->find(1);

$items = $item->assemblies;

检索完整的递归程序集结果:

$item = Inventory::with('assembliesRecursive')->find(1);

$items = $item->assembliesRecursive;

$nestedItems = $items->get(0)->assemblies;

$nestedNestedItems = $items->get(0)->assemblies->get(0)->assemblies;
4

1 回答 1

1

这变得容易多了。这可能看起来不像,但它实际上是属于许多人的,其中 Inventory 属于许多人本身并且inventory_assembly是一个数据透视表,实际上甚至不需要模型来配合它。

这是库存模型

class Inventory extends Eloquent {

    public function assemblies()
    {
        return $this->belongsToMany('Inventory', 'inventory_assemblies', 'inventory_id', 'part_id')
            ->withPivot(['quantity']);
    }

}

这就是我如何获得某个库存项目的集合的方法。

$item = Inventory::with('assemblies')->find(1);

$assemblies = $item->assemblies;

编辑:刚刚意识到您使用的是 Laravel 4。删除了命名空间。

另一个编辑: 我还没有解决这个问题。例如,如果腿是需要腿和硬件的组件,硬件是需要不同螺母/螺栓/工具组的组件,如果工具组是需要扳手 a 和螺丝刀 b 等的组件......这种方法只会让你和腿一样深,而忽略其他一切。

在这种情况下,我们所做的一切都错了,忽略了我所说的一切。这就是所谓的嵌套集合模型。你可以在这里阅读更多关于它的信息http://en.wikipedia.org/wiki/Nested_set_model

在这种情况下,还有一个 Laravel 包应该为你处理这种关系。它使我免于将头撞到墙上几次。 https://github.com/etrepat/baum#node-relations

对于 Eloquent 中的嵌套关​​系,这也是可能的,您将不再需要您的assemblyRecursive函数。您可以根据需要深入。

$item = Inventory::with('assemblies.assemblies.assemblies')->find(1);

foreach($item->assemblies as $assembly) {
    if($assembly->is_assembly) {
        // Do things for parent items
        foreach($assembly->assemblies as $nested_assembly_a) {
            // Do things for 1 deep nests

            if($nested_assembly_a->is_assembly) {
                foreach($nested_assembly_a->assemblies as $nested_assembly_b) {
                    // Do things for 2 deep nests

                }
            } else {
                // Non-assembly 1 deep child
            }
        }
    } else {
        // Non-Assembly parent
    }
}
于 2015-05-14T20:07:42.583 回答