components ?? []; $materialSummary = $this->material_summary; if (empty($materialSummary) && ! empty($components)) { $materialSummary = $this->calcMaterialSummary($components); } return [ 'id' => $this->id, 'code' => $this->code, 'name' => $this->name, 'is_active' => $this->is_active, // MNG2 호환 'item_type' => 'FG', 'item_category' => $this->model_type, // 모델 속성 (정규 컬럼) 'model_name' => $this->model_name, 'check_type' => $this->check_type, 'rail_width' => $this->rail_width ? (int) $this->rail_width : null, 'rail_length' => $this->rail_length ? (int) $this->rail_length : null, 'finishing_type' => $this->finishing_type, 'item_sep' => $this->item_sep, 'model_UA' => $this->model_UA, 'search_keyword' => $this->search_keyword, 'author' => $this->author, 'memo' => $this->getOption('memo'), 'registration_date' => $this->registration_date?->format('Y-m-d'), // 케이스 전용 'exit_direction' => $this->exit_direction, 'front_bottom_width' => $this->front_bottom_width ? (int) $this->front_bottom_width : null, 'box_width' => $this->box_width ? (int) $this->box_width : null, 'box_height' => $this->box_height ? (int) $this->box_height : null, // 하단마감재 전용 'bar_width' => $this->bar_width ? (int) $this->bar_width : null, 'bar_height' => $this->bar_height ? (int) $this->bar_height : null, // 수정자 'modified_by' => $this->getOption('modified_by'), // 이미지 'image_file_id' => $this->getImageFileId(), 'image_url' => $this->getImageUrl(), // 부품 조합 'components' => $this->enrichComponentsWithImageUrls($components), 'material_summary' => $materialSummary, 'component_count' => count($components), // 메타 'created_at' => $this->created_at?->format('Y-m-d H:i:s'), 'updated_at' => $this->updated_at?->format('Y-m-d H:i:s'), ]; } private function getImageFile(): ?\App\Models\Commons\File { $file = \App\Models\Commons\File::where('document_id', $this->id) ->where('document_type', 'bending_model') ->where('field_key', 'assembly_image') ->whereNull('deleted_at') ->orderByDesc('id') ->first(); if (! $file) { $file = $this->files() ->where('field_key', 'bending_diagram') ->orderByDesc('id') ->first(); } return $file; } private function getImageFileId(): ?int { return $this->getImageFile()?->id; } private function getImageUrl(): ?string { return $this->getImageFile()?->presignedUrl(); } private function enrichComponentsWithImageUrls(array $components): array { $fileIds = array_filter(array_column($components, 'image_file_id')); if (empty($fileIds)) { return $components; } $files = \App\Models\Commons\File::whereIn('id', $fileIds) ->whereNull('deleted_at') ->get() ->keyBy('id'); foreach ($components as &$comp) { $fileId = $comp['image_file_id'] ?? null; $comp['image_url'] = $fileId && isset($files[$fileId]) ? $files[$fileId]->presignedUrl() : null; } unset($comp); return $components; } private function calcMaterialSummary(array $components): array { $summary = []; foreach ($components as $comp) { $material = $comp['material'] ?? null; $widthSum = $comp['widthsum'] ?? $comp['width_sum'] ?? 0; $qty = $comp['quantity'] ?? 1; if ($material && $widthSum) { $summary[$material] = ($summary[$material] ?? 0) + ($widthSum * $qty); } } return $summary; } }