'integer', 'unit_price' => 'decimal:2', 'total_price' => 'decimal:2', 'options' => 'array', 'depth' => 'integer', 'sort_order' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; // ---- 트리 관계 ---- /** * 상위 노드 */ public function parent(): BelongsTo { return $this->belongsTo(self::class, 'parent_id'); } /** * 하위 노드 */ public function children(): HasMany { return $this->hasMany(self::class, 'parent_id')->orderBy('sort_order'); } // ---- 비즈니스 관계 ---- /** * 수주 마스터 */ public function order(): BelongsTo { return $this->belongsTo(Order::class); } /** * 해당 노드에 속한 수주 품목 */ public function items(): HasMany { return $this->hasMany(OrderItem::class, 'order_node_id'); } // ---- 트리 헬퍼 ---- public function isRoot(): bool { return $this->parent_id === null; } public function isLeaf(): bool { return $this->children()->count() === 0; } /** * 하위 노드 포함 전체 트리 재귀 로드 */ public function scopeWithRecursiveChildren($query) { return $query->with(['children' => function ($q) { $q->orderBy('sort_order')->with('children', 'items'); }, 'items']); } }