- CASCADE FK → 독립 엔티티 + entity_relationships 링크 테이블 - 독립 API 10개 추가 (섹션/필드/BOM CRUD, clone, usage) - SectionTemplate 모델 제거 → ItemSection.is_template 통합 - 페이지-섹션, 섹션-필드, 섹션-BOM 링크/언링크 API 14개 추가 - Swagger 문서 업데이트
380 lines
11 KiB
PHP
380 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Services\ItemMaster;
|
|
|
|
use App\Models\ItemMaster\EntityRelationship;
|
|
use App\Models\ItemMaster\ItemBomItem;
|
|
use App\Models\ItemMaster\ItemField;
|
|
use App\Models\ItemMaster\ItemPage;
|
|
use App\Models\ItemMaster\ItemSection;
|
|
use App\Services\Service;
|
|
use Illuminate\Support\Collection;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
|
|
/**
|
|
* EntityRelationshipService - 엔티티 간 관계(링크) 관리 서비스
|
|
*/
|
|
class EntityRelationshipService extends Service
|
|
{
|
|
/**
|
|
* 페이지에 섹션 연결
|
|
*/
|
|
public function linkSectionToPage(int $pageId, int $sectionId, int $orderNo = 0): EntityRelationship
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$userId = $this->apiUserId();
|
|
|
|
// 페이지 존재 확인
|
|
$page = ItemPage::where('tenant_id', $tenantId)->find($pageId);
|
|
if (! $page) {
|
|
throw new NotFoundHttpException(__('error.page_not_found'));
|
|
}
|
|
|
|
// 섹션 존재 확인
|
|
$section = ItemSection::where('tenant_id', $tenantId)->find($sectionId);
|
|
if (! $section) {
|
|
throw new NotFoundHttpException(__('error.section_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::link(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_PAGE,
|
|
$pageId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId,
|
|
$orderNo
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 페이지에서 섹션 연결 해제
|
|
*/
|
|
public function unlinkSectionFromPage(int $pageId, int $sectionId): bool
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::unlink(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_PAGE,
|
|
$pageId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 페이지에 필드 직접 연결
|
|
*/
|
|
public function linkFieldToPage(int $pageId, int $fieldId, int $orderNo = 0): EntityRelationship
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$userId = $this->apiUserId();
|
|
|
|
// 페이지 존재 확인
|
|
$page = ItemPage::where('tenant_id', $tenantId)->find($pageId);
|
|
if (! $page) {
|
|
throw new NotFoundHttpException(__('error.page_not_found'));
|
|
}
|
|
|
|
// 필드 존재 확인
|
|
$field = ItemField::where('tenant_id', $tenantId)->find($fieldId);
|
|
if (! $field) {
|
|
throw new NotFoundHttpException(__('error.field_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::link(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_PAGE,
|
|
$pageId,
|
|
EntityRelationship::TYPE_FIELD,
|
|
$fieldId,
|
|
$orderNo
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 페이지에서 필드 연결 해제
|
|
*/
|
|
public function unlinkFieldFromPage(int $pageId, int $fieldId): bool
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::unlink(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_PAGE,
|
|
$pageId,
|
|
EntityRelationship::TYPE_FIELD,
|
|
$fieldId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 섹션에 필드 연결
|
|
*/
|
|
public function linkFieldToSection(int $sectionId, int $fieldId, int $orderNo = 0): EntityRelationship
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$userId = $this->apiUserId();
|
|
|
|
// 섹션 존재 확인
|
|
$section = ItemSection::where('tenant_id', $tenantId)->find($sectionId);
|
|
if (! $section) {
|
|
throw new NotFoundHttpException(__('error.section_not_found'));
|
|
}
|
|
|
|
// 필드 존재 확인
|
|
$field = ItemField::where('tenant_id', $tenantId)->find($fieldId);
|
|
if (! $field) {
|
|
throw new NotFoundHttpException(__('error.field_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::link(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId,
|
|
EntityRelationship::TYPE_FIELD,
|
|
$fieldId,
|
|
$orderNo
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 섹션에서 필드 연결 해제
|
|
*/
|
|
public function unlinkFieldFromSection(int $sectionId, int $fieldId): bool
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::unlink(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId,
|
|
EntityRelationship::TYPE_FIELD,
|
|
$fieldId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 섹션에 BOM 항목 연결
|
|
*/
|
|
public function linkBomToSection(int $sectionId, int $bomId, int $orderNo = 0): EntityRelationship
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
$userId = $this->apiUserId();
|
|
|
|
// 섹션 존재 확인
|
|
$section = ItemSection::where('tenant_id', $tenantId)->find($sectionId);
|
|
if (! $section) {
|
|
throw new NotFoundHttpException(__('error.section_not_found'));
|
|
}
|
|
|
|
// BOM 항목 존재 확인
|
|
$bom = ItemBomItem::where('tenant_id', $tenantId)->find($bomId);
|
|
if (! $bom) {
|
|
throw new NotFoundHttpException(__('error.bom_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::link(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId,
|
|
EntityRelationship::TYPE_BOM,
|
|
$bomId,
|
|
$orderNo
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 섹션에서 BOM 항목 연결 해제
|
|
*/
|
|
public function unlinkBomFromSection(int $sectionId, int $bomId): bool
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::unlink(
|
|
$tenantId,
|
|
EntityRelationship::TYPE_SECTION,
|
|
$sectionId,
|
|
EntityRelationship::TYPE_BOM,
|
|
$bomId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 페이지의 모든 관계 조회
|
|
*/
|
|
public function getPageRelationships(int $pageId): Collection
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
$page = ItemPage::where('tenant_id', $tenantId)->find($pageId);
|
|
if (! $page) {
|
|
throw new NotFoundHttpException(__('error.page_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::where('tenant_id', $tenantId)
|
|
->where('parent_type', EntityRelationship::TYPE_PAGE)
|
|
->where('parent_id', $pageId)
|
|
->orderBy('order_no')
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* 섹션의 모든 자식 관계 조회
|
|
*/
|
|
public function getSectionChildRelationships(int $sectionId): Collection
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
$section = ItemSection::where('tenant_id', $tenantId)->find($sectionId);
|
|
if (! $section) {
|
|
throw new NotFoundHttpException(__('error.section_not_found'));
|
|
}
|
|
|
|
return EntityRelationship::where('tenant_id', $tenantId)
|
|
->where('parent_type', EntityRelationship::TYPE_SECTION)
|
|
->where('parent_id', $sectionId)
|
|
->orderBy('order_no')
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* 특정 엔티티가 연결된 부모 목록 조회
|
|
*/
|
|
public function getParentRelationships(string $childType, int $childId): Collection
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::where('tenant_id', $tenantId)
|
|
->where('child_type', $childType)
|
|
->where('child_id', $childId)
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* 관계 순서 변경
|
|
*/
|
|
public function reorderRelationships(string $parentType, int $parentId, array $orderedChildIds): void
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
foreach ($orderedChildIds as $index => $item) {
|
|
EntityRelationship::where([
|
|
'tenant_id' => $tenantId,
|
|
'parent_type' => $parentType,
|
|
'parent_id' => $parentId,
|
|
'child_type' => $item['child_type'],
|
|
'child_id' => $item['child_id'],
|
|
])->update(['order_no' => $index]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 부모의 모든 자식 관계 일괄 삭제
|
|
*/
|
|
public function unlinkAllChildren(string $parentType, int $parentId, ?string $childType = null): int
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
return EntityRelationship::unlinkAllChildren(
|
|
$tenantId,
|
|
$parentType,
|
|
$parentId,
|
|
$childType
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 페이지 구조 조회 (섹션 + 직접 연결된 필드 포함)
|
|
*/
|
|
public function getPageStructure(int $pageId): array
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
$page = ItemPage::where('tenant_id', $tenantId)->find($pageId);
|
|
if (! $page) {
|
|
throw new NotFoundHttpException(__('error.page_not_found'));
|
|
}
|
|
|
|
// 페이지의 모든 관계 조회
|
|
$relationships = EntityRelationship::where('tenant_id', $tenantId)
|
|
->where('parent_type', EntityRelationship::TYPE_PAGE)
|
|
->where('parent_id', $pageId)
|
|
->orderBy('order_no')
|
|
->get();
|
|
|
|
$structure = [
|
|
'page' => $page,
|
|
'sections' => [],
|
|
'direct_fields' => [],
|
|
];
|
|
|
|
foreach ($relationships as $rel) {
|
|
if ($rel->child_type === EntityRelationship::TYPE_SECTION) {
|
|
$section = ItemSection::find($rel->child_id);
|
|
if ($section) {
|
|
// 섹션의 자식(필드/BOM) 조회
|
|
$sectionChildren = $this->getSectionChildren($section->id);
|
|
$structure['sections'][] = [
|
|
'section' => $section,
|
|
'order_no' => $rel->order_no,
|
|
'fields' => $sectionChildren['fields'],
|
|
'bom_items' => $sectionChildren['bom_items'],
|
|
];
|
|
}
|
|
} elseif ($rel->child_type === EntityRelationship::TYPE_FIELD) {
|
|
$field = ItemField::find($rel->child_id);
|
|
if ($field) {
|
|
$structure['direct_fields'][] = [
|
|
'field' => $field,
|
|
'order_no' => $rel->order_no,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $structure;
|
|
}
|
|
|
|
/**
|
|
* 섹션의 자식 엔티티 조회
|
|
*/
|
|
private function getSectionChildren(int $sectionId): array
|
|
{
|
|
$tenantId = $this->tenantId();
|
|
|
|
$relationships = EntityRelationship::where('tenant_id', $tenantId)
|
|
->where('parent_type', EntityRelationship::TYPE_SECTION)
|
|
->where('parent_id', $sectionId)
|
|
->orderBy('order_no')
|
|
->get();
|
|
|
|
$result = [
|
|
'fields' => [],
|
|
'bom_items' => [],
|
|
];
|
|
|
|
foreach ($relationships as $rel) {
|
|
if ($rel->child_type === EntityRelationship::TYPE_FIELD) {
|
|
$field = ItemField::find($rel->child_id);
|
|
if ($field) {
|
|
$result['fields'][] = [
|
|
'field' => $field,
|
|
'order_no' => $rel->order_no,
|
|
];
|
|
}
|
|
} elseif ($rel->child_type === EntityRelationship::TYPE_BOM) {
|
|
$bom = ItemBomItem::find($rel->child_id);
|
|
if ($bom) {
|
|
$result['bom_items'][] = [
|
|
'bom_item' => $bom,
|
|
'order_no' => $rel->order_no,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|