feat: ERD 자동 생성 시스템 구축 및 모델 오류 수정
- GraphViz 설치를 통한 ERD 다이어그램 생성 지원 - BelongsToTenantTrait → BelongsToTenant 트레잇명 수정 - Estimate, EstimateItem 모델의 인터페이스 참조 오류 해결 - 60개 모델의 완전한 관계도 생성 (graph.png, 4.1MB) - beyondcode/laravel-er-diagram-generator 패키지 활용 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,17 +7,13 @@
|
||||
|
||||
/**
|
||||
* Phase 3: 제품-자재 관계 FK 제약조건 제거 (신중한 검토 필요)
|
||||
* - product_components.material_id → materials
|
||||
*
|
||||
* 목적: 자재 변경/삭제 시 유연성 확보
|
||||
* 주의사항:
|
||||
* 1. 비즈니스 로직에서 무결성 검증 필요
|
||||
* 2. 자재 삭제 시 BOM에 미치는 영향 검토 필요
|
||||
* 3. 소프트 딜리트로 대부분 처리되므로 상대적으로 안전
|
||||
* 실제 상황: product_components 테이블은 ref_type/ref_id 통합 구조 사용
|
||||
* - material_id 컬럼이 존재하지 않음 (ref_type='MATERIAL', ref_id=material.id)
|
||||
* - 물리적 FK 제약조건이 없는 상태
|
||||
*
|
||||
* 유지되는 핵심 FK:
|
||||
* - product_components.parent_product_id → products (BOM 구조 핵심)
|
||||
* - product_components.child_product_id → products (BOM 구조 핵심)
|
||||
* 목적: 현재 구조 확인 및 논리적 관계 문서화
|
||||
* 결론: 이미 FK 없는 유연한 구조로 구성되어 있음
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
@@ -55,9 +51,8 @@ private function dropForeignKeyIfExists(string $table, string $column): void
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
echo "🚀 Phase 3: 제품-자재 관계 FK 제약조건 제거 시작\n\n";
|
||||
echo "⚠️ 주의: 이 작업은 신중한 검토가 필요합니다!\n";
|
||||
echo "📋 영향 범위: BOM 시스템의 자재 참조 관계\n\n";
|
||||
echo "🚀 Phase 3: 제품-자재 관계 현황 분석 시작\n\n";
|
||||
echo "📋 분석 범위: BOM 시스템의 자재 참조 관계\n\n";
|
||||
|
||||
// product_components 테이블 존재 여부 확인
|
||||
if (!Schema::hasTable('product_components')) {
|
||||
@@ -65,74 +60,76 @@ public function up(): void
|
||||
return;
|
||||
}
|
||||
|
||||
echo "1️⃣ Product_components 테이블 FK 분석...\n";
|
||||
echo "1️⃣ Product_components 테이블 구조 분석...\n";
|
||||
|
||||
// 테이블 구조 확인
|
||||
$columns = DB::select('DESCRIBE product_components');
|
||||
$columnNames = array_map(function($col) { return $col->Field; }, $columns);
|
||||
|
||||
echo " 현재 테이블 구조:\n";
|
||||
echo " - ref_type 컬럼: " . (in_array('ref_type', $columnNames) ? "존재 (통합 참조 타입)" : "없음") . "\n";
|
||||
echo " - ref_id 컬럼: " . (in_array('ref_id', $columnNames) ? "존재 (통합 참조 ID)" : "없음") . "\n";
|
||||
echo " - material_id 컬럼: " . (in_array('material_id', $columnNames) ? "존재" : "없음 (예상대로)") . "\n\n";
|
||||
|
||||
// 현재 FK 상태 확인
|
||||
$materialFk = $this->findForeignKeyName('product_components', 'material_id');
|
||||
$parentProductFk = $this->findForeignKeyName('product_components', 'parent_product_id');
|
||||
$childProductFk = $this->findForeignKeyName('product_components', 'child_product_id');
|
||||
$refIdFk = $this->findForeignKeyName('product_components', 'ref_id');
|
||||
|
||||
echo " 현재 FK 상태:\n";
|
||||
echo " - material_id FK: " . ($materialFk ? "존재 ({$materialFk})" : "없음") . "\n";
|
||||
echo "2️⃣ FK 제약조건 상태 확인...\n";
|
||||
echo " - parent_product_id FK: " . ($parentProductFk ? "존재 ({$parentProductFk})" : "없음") . "\n";
|
||||
echo " - child_product_id FK: " . ($childProductFk ? "존재 ({$childProductFk})" : "없음") . "\n\n";
|
||||
echo " - ref_id FK: " . ($refIdFk ? "존재 ({$refIdFk})" : "없음 (유연한 구조)") . "\n\n";
|
||||
|
||||
// material_id FK만 제거 (핵심 제품 관계는 유지)
|
||||
echo "2️⃣ Material_id FK 제거 (자재 관리 유연성)...\n";
|
||||
$this->dropForeignKeyIfExists('product_components', 'material_id');
|
||||
|
||||
echo "3️⃣ 핵심 제품 관계 FK 유지 확인...\n";
|
||||
if ($parentProductFk) {
|
||||
echo "✅ 유지: parent_product_id → products (BOM 구조 핵심)\n";
|
||||
}
|
||||
if ($childProductFk) {
|
||||
echo "✅ 유지: child_product_id → products (BOM 구조 핵심)\n";
|
||||
}
|
||||
|
||||
// 성능을 위한 인덱스 확인/추가
|
||||
echo "\n4️⃣ 성능 인덱스 확인...\n";
|
||||
$materialIndexExists = DB::selectOne("
|
||||
SHOW INDEX FROM product_components WHERE Key_name = 'idx_components_material_id'
|
||||
// 성능을 위한 인덱스 확인
|
||||
echo "3️⃣ 성능 인덱스 상태 확인...\n";
|
||||
$refTypeIndexExists = DB::selectOne("
|
||||
SHOW INDEX FROM product_components WHERE Key_name LIKE '%ref_type%' OR Column_name = 'ref_type'
|
||||
");
|
||||
if (!$materialIndexExists) {
|
||||
DB::statement("CREATE INDEX idx_components_material_id ON product_components (material_id)");
|
||||
echo "✅ Added performance index: product_components.material_id\n";
|
||||
} else {
|
||||
echo "ℹ️ Index exists: product_components.material_id\n";
|
||||
$refIdIndexExists = DB::selectOne("
|
||||
SHOW INDEX FROM product_components WHERE Key_name LIKE '%ref_id%' OR Column_name = 'ref_id'
|
||||
");
|
||||
|
||||
echo " - ref_type 인덱스: " . ($refTypeIndexExists ? "존재" : "없음") . "\n";
|
||||
echo " - ref_id 인덱스: " . ($refIdIndexExists ? "존재" : "없음") . "\n";
|
||||
|
||||
// 필요시 성능 인덱스 추가
|
||||
if (!$refTypeIndexExists || !$refIdIndexExists) {
|
||||
echo "\n4️⃣ 성능 인덱스 추가...\n";
|
||||
try {
|
||||
DB::statement("CREATE INDEX idx_components_ref_type_id ON product_components (ref_type, ref_id)");
|
||||
echo "✅ Added composite index: product_components(ref_type, ref_id)\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "ℹ️ Index may already exist or not needed\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🎉 Phase 3 제품-자재 FK 제거 완료!\n";
|
||||
echo "📋 제거된 FK:\n";
|
||||
echo " - product_components.material_id → materials\n";
|
||||
echo "🔒 유지된 핵심 FK:\n";
|
||||
echo " - product_components.parent_product_id → products\n";
|
||||
echo " - product_components.child_product_id → products\n";
|
||||
echo "📈 예상 효과: 자재 변경/삭제 시 BOM 유연성 증가\n";
|
||||
echo "⚠️ 주의사항: Service 레이어에서 자재 무결성 검증 필요\n";
|
||||
echo "\n🎉 Phase 3 분석 완료!\n";
|
||||
echo "📋 현재 구조 요약:\n";
|
||||
echo " - 이미 유연한 ref_type/ref_id 구조 사용 중\n";
|
||||
echo " - material_id 컬럼 없음 (통합 구조로 대체)\n";
|
||||
echo " - 물리적 FK 제약조건 없어 관리 유연성 확보됨\n";
|
||||
echo "📈 결론: 추가 FK 제거 작업 불필요 (이미 최적화됨)\n";
|
||||
echo "✅ 권장사항: Service 레이어에서 논리적 무결성 검증 유지\n";
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
echo "🔄 Phase 3 제품-자재 FK 복구 시작...\n\n";
|
||||
echo "🔄 Phase 3 분석 롤백 시작...\n\n";
|
||||
|
||||
echo "ℹ️ 이 마이그레이션은 분석 목적으로 실행되었습니다.\n";
|
||||
echo "📋 롤백 내용:\n";
|
||||
echo " - 추가된 성능 인덱스 제거 (필요시)\n\n";
|
||||
|
||||
if (Schema::hasTable('product_components')) {
|
||||
echo "1️⃣ Material_id FK 복구...\n";
|
||||
echo "1️⃣ 성능 인덱스 제거...\n";
|
||||
try {
|
||||
Schema::table('product_components', function (Blueprint $table) {
|
||||
$table->foreign('material_id')
|
||||
->references('id')
|
||||
->on('materials')
|
||||
->nullOnDelete();
|
||||
});
|
||||
echo "✅ Restored FK: product_components.material_id → materials\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "⚠️ Could not restore FK: product_components.material_id\n";
|
||||
echo " Error: " . $e->getMessage() . "\n";
|
||||
echo " 이유: 데이터 무결성 위반이나 참조되지 않는 material_id가 있을 수 있습니다.\n";
|
||||
DB::statement("DROP INDEX idx_components_ref_type_id ON product_components");
|
||||
echo "✅ Removed index: product_components(ref_type, ref_id)\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "ℹ️ Index may not exist or already removed\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🔄 Phase 3 FK 복구 완료!\n";
|
||||
echo "📝 참고: FK 복구 실패 시 데이터 정합성을 먼저 확인하세요.\n";
|
||||
echo "\n🔄 Phase 3 분석 롤백 완료!\n";
|
||||
echo "📝 참고: 원래 ref_type/ref_id 구조가 복구되었습니다.\n";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user