fix: UpdateLogicalRelationships 명령 개선
- use 문 파싱 추가하여 짧은 클래스명을 FQCN으로 변환 - self/static 자기 참조 관계 정상 처리 - Polymorphic 관계 지원 (morphTo, morphMany, morphOne) - 클래스 존재 확인 및 안전한 에러 처리 - ::class 문자열 오류 수정 마이그레이션 실행 시 'Class BoardSetting::class not found' 에러 해결
This commit is contained in:
265
CURRENT_WORKS.md
265
CURRENT_WORKS.md
@@ -1,5 +1,270 @@
|
||||
# SAM API 저장소 작업 현황
|
||||
|
||||
## 2025-10-14 (화) - UpdateLogicalRelationships 명령 개선
|
||||
|
||||
### 주요 작업
|
||||
- **use 문 파싱 추가**: 짧은 클래스명을 완전한 클래스명(FQCN)으로 변환
|
||||
- **self/static 처리**: 자기 참조 관계 정상 처리
|
||||
- **Polymorphic 관계 지원**: morphTo, morphMany, morphOne 관계 추가
|
||||
- **에러 처리 개선**: 클래스 존재 확인 및 안전한 처리
|
||||
|
||||
### 수정된 파일:
|
||||
- `app/Console/Commands/UpdateLogicalRelationships.php` - 관계 추출 로직 전면 개선
|
||||
- `LOGICAL_RELATIONSHIPS.md` - 자동 생성된 모델 관계 문서
|
||||
|
||||
### 작업 내용:
|
||||
|
||||
#### 1. 문제 상황
|
||||
|
||||
**마이그레이션 실행 시 에러:**
|
||||
```
|
||||
Class "BoardSetting::class" not found
|
||||
위치: UpdateLogicalRelationships.php:203
|
||||
```
|
||||
|
||||
**근본 원인:**
|
||||
- 정규식으로 모델명 추출 시 `::class` 부분까지 포함됨
|
||||
- 짧은 클래스명만 추출되어 완전한 클래스명으로 변환 불가
|
||||
- use 문을 파싱하지 않아 네임스페이스 해석 불가
|
||||
|
||||
#### 2. use 문 파싱 구현
|
||||
|
||||
**새로운 메서드 추가:**
|
||||
```php
|
||||
/**
|
||||
* 파일 내용에서 use 문들을 추출
|
||||
*/
|
||||
private function extractUseStatements(string $content): array
|
||||
{
|
||||
$useStatements = [];
|
||||
|
||||
// use Full\Namespace\ClassName; 파싱
|
||||
if (preg_match_all('/use\s+([^;]+);/', $content, $matches)) {
|
||||
foreach ($matches[1] as $useStatement) {
|
||||
// as 별칭 처리
|
||||
if (strpos($useStatement, ' as ') !== false) {
|
||||
[$fullClass, $alias] = array_map('trim', explode(' as ', $useStatement));
|
||||
$useStatements[$alias] = $fullClass;
|
||||
} else {
|
||||
$fullClass = trim($useStatement);
|
||||
$shortName = class_basename($fullClass);
|
||||
$useStatements[$shortName] = $fullClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $useStatements;
|
||||
}
|
||||
```
|
||||
|
||||
**결과:**
|
||||
```php
|
||||
// use App\Models\Commons\BoardSetting;
|
||||
// → ['BoardSetting' => 'App\Models\Commons\BoardSetting']
|
||||
```
|
||||
|
||||
#### 3. namespace 추출 구현
|
||||
|
||||
```php
|
||||
/**
|
||||
* 파일 내용에서 namespace 추출
|
||||
*/
|
||||
private function extractNamespace(string $content): ?string
|
||||
{
|
||||
if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) {
|
||||
return trim($matches[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 클래스명 해석 로직 구현
|
||||
|
||||
```php
|
||||
/**
|
||||
* 짧은 클래스명을 완전한 클래스명으로 변환
|
||||
*/
|
||||
private function resolveClassName(
|
||||
string $className,
|
||||
array $useStatements,
|
||||
?string $currentNamespace,
|
||||
string $currentClassName
|
||||
): string {
|
||||
// 1. 이미 완전한 클래스명인 경우
|
||||
if (strpos($className, '\\') !== false) {
|
||||
return ltrim($className, '\\');
|
||||
}
|
||||
|
||||
// 2. self/static 처리 - 현재 클래스로 대체
|
||||
if ($className === 'self' || $className === 'static') {
|
||||
return $currentClassName;
|
||||
}
|
||||
|
||||
// 3. use 문에서 찾기
|
||||
if (isset($useStatements[$className])) {
|
||||
return $useStatements[$className];
|
||||
}
|
||||
|
||||
// 4. 같은 namespace에 있다고 가정
|
||||
if ($currentNamespace) {
|
||||
return $currentNamespace . '\\' . $className;
|
||||
}
|
||||
|
||||
// 5. 그 외의 경우 그대로 반환
|
||||
return $className;
|
||||
}
|
||||
```
|
||||
|
||||
**해석 순서:**
|
||||
1. 이미 FQCN인지 확인 (백슬래시 포함)
|
||||
2. self/static → 현재 클래스로 대체
|
||||
3. use 문에서 매핑 찾기
|
||||
4. 같은 namespace로 추정
|
||||
5. 실패 시 그대로 반환
|
||||
|
||||
#### 5. Polymorphic 관계 지원
|
||||
|
||||
**새로운 관계 타입 추가:**
|
||||
```php
|
||||
$patterns = [
|
||||
'belongsTo' => '/...',
|
||||
'hasMany' => '/...',
|
||||
'hasOne' => '/...',
|
||||
'belongsToMany' => '/...',
|
||||
'morphTo' => '/public\s+function\s+(\w+)\s*\([^)]*\)\s*[^{]*{\s*return\s+\$this->morphTo\s*\(/',
|
||||
'morphMany' => '/public\s+function\s+(\w+)\s*\([^)]*\)\s*[^{]*{\s*return\s+\$this->morphMany\s*\(\s*([^,\)]+)/',
|
||||
'morphOne' => '/public\s+function\s+(\w+)\s*\([^)]*\)\s*[^{]*{\s*return\s+\$this->morphOne\s*\(\s*([^,\)]+)/',
|
||||
];
|
||||
```
|
||||
|
||||
**morphTo 특별 처리:**
|
||||
```php
|
||||
// morphTo는 관련 모델이 없으므로 특별 표시
|
||||
if ($type === 'morphTo') {
|
||||
$relationships[] = [
|
||||
'method' => $match[1],
|
||||
'type' => $type,
|
||||
'related_model' => '(Polymorphic)',
|
||||
'foreign_key' => null,
|
||||
'local_key' => null
|
||||
];
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
#### 6. 에러 처리 개선
|
||||
|
||||
**클래스 존재 확인:**
|
||||
```php
|
||||
// Polymorphic 관계는 특별 표시
|
||||
if ($rel['related_model'] === '(Polymorphic)') {
|
||||
$content .= "- **{$rel['method']}()**: {$rel['type']} → `(Polymorphic)`\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// 클래스가 존재하는지 확인
|
||||
if (!class_exists($rel['related_model'])) {
|
||||
$this->warn("모델 클래스가 존재하지 않음: {$rel['related_model']}");
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
**try-catch로 안전하게 처리:**
|
||||
```php
|
||||
try {
|
||||
$relatedTable = (new $rel['related_model'])->getTable();
|
||||
$content .= "- **{$rel['method']}()**: {$rel['type']} → `{$relatedTable}`";
|
||||
// ...
|
||||
} catch (\Exception $e) {
|
||||
$this->warn("관계 처리 실패: {$rel['method']} - " . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
#### 7. 실행 결과
|
||||
|
||||
**이전 (에러 발생):**
|
||||
```
|
||||
Class "BoardSetting::class" not found
|
||||
모델 클래스가 존재하지 않음: BoardSetting (68개 경고)
|
||||
```
|
||||
|
||||
**현재 (정상 실행):**
|
||||
```
|
||||
🔄 논리적 관계 문서 업데이트 시작...
|
||||
📄 문서 업데이트: /Users/hskwon/Works/@KD_SAM/SAM/api/LOGICAL_RELATIONSHIPS.md
|
||||
✅ 논리적 관계 문서 업데이트 완료!
|
||||
```
|
||||
|
||||
#### 8. 생성된 문서 예시
|
||||
|
||||
**LOGICAL_RELATIONSHIPS.md:**
|
||||
```markdown
|
||||
### files
|
||||
**모델**: `App\Models\Commons\File`
|
||||
|
||||
- **uploader()**: belongsTo → `users`
|
||||
- **fileable()**: morphTo → `(Polymorphic)`
|
||||
|
||||
### products
|
||||
**모델**: `App\Models\Products\Product`
|
||||
|
||||
- **category()**: belongsTo → `categories`
|
||||
- **componentLines()**: hasMany → `product_components`
|
||||
- **children()**: belongsToMany → `products`
|
||||
- **files()**: morphMany → `files`
|
||||
|
||||
### departments
|
||||
**모델**: `App\Models\Tenants\Department`
|
||||
|
||||
- **parent()**: belongsTo → `departments`
|
||||
- **children()**: hasMany → `departments`
|
||||
- **permissionOverrides()**: morphMany → `permission_overrides`
|
||||
```
|
||||
|
||||
#### 9. 주요 개선 사항
|
||||
|
||||
✅ **완전한 클래스명 해석:**
|
||||
- `BoardSetting` → `App\Models\Commons\BoardSetting`
|
||||
- `Post` → `App\Models\Boards\Post`
|
||||
- `User` → `App\Models\Members\User`
|
||||
|
||||
✅ **자기 참조 관계 처리:**
|
||||
- `self` → 현재 클래스의 FQCN
|
||||
- `Category::self` → `App\Models\Commons\Category`
|
||||
|
||||
✅ **Polymorphic 관계 지원:**
|
||||
- `morphTo` → `(Polymorphic)` 표시
|
||||
- `morphMany`, `morphOne` → 관련 모델 정상 해석
|
||||
|
||||
✅ **에러 없는 실행:**
|
||||
- 클래스 존재 확인
|
||||
- 안전한 인스턴스 생성
|
||||
- 명확한 경고 메시지
|
||||
|
||||
#### 10. 코드 품질
|
||||
|
||||
**SAM API Development Rules 준수:**
|
||||
- ✅ 명확한 메서드 분리 (단일 책임)
|
||||
- ✅ 주석으로 의도 명시
|
||||
- ✅ 에러 처리 철저
|
||||
- ✅ 검증 가능한 결과
|
||||
|
||||
**Laravel 컨벤션:**
|
||||
- ✅ Artisan Command 표준 패턴
|
||||
- ✅ ReflectionClass 안전한 사용
|
||||
- ✅ File Facade 활용
|
||||
|
||||
### 향후 개선 가능 사항:
|
||||
|
||||
- [ ] 관계 FK 자동 추출 (현재는 null)
|
||||
- [ ] 피벗 테이블 정보 추가 (belongsToMany)
|
||||
- [ ] 관계 조건 추출 (where 절 등)
|
||||
- [ ] 성능 최적화 (캐싱)
|
||||
|
||||
---
|
||||
|
||||
## 2025-10-14 (화) - role 컬럼 타입 변경 (ENUM → VARCHAR)
|
||||
|
||||
### 주요 작업
|
||||
|
||||
Reference in New Issue
Block a user