File manager - Edit - /var/www/html/portalHomolog/app/Http/Controllers/Api/ParqueTecMarController.php
Back
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Resources\ParqueTecnologico\ParqueTecResource; use App\Models\ParqueTecnologico\DecretoLei; use App\Models\ParqueTecnologico\Edital; use App\Models\ParqueTecnologico\Startup; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\StreamedResponse; /** * API para consumo pelo portal Parquetecmar (projeto separado). * Expõe Editais e Decretos/Leis gerenciados no admin do Angra. */ class ParqueTecMarController extends Controller { private const LIMIT_HOME = 6; /** * Segmentos de startups e suas cores. */ private function segmentosConfig(): array { return [ 'tecnologia' => [ 'label' => 'tecnologia', 'color' => '#43D2F5', ], 'energia' => [ 'label' => 'energia', 'color' => '#F06C4C', ], 'logistica' => [ 'label' => 'logística', 'color' => '#F0C12D', ], 'sustentabilidade' => [ 'label' => 'sustentabilidade', 'color' => '#11E09E', ], 'nautica_naval' => [ 'label' => 'náutica e naval', 'color' => '#426AF4', ], ]; } private function segmentoInfo(string $key): array { $all = $this->segmentosConfig(); return $all[$key] ?? ['label' => $key, 'color' => '#426AF4']; } /** * Base URL do portal Parquetecmar (para links nas respostas). */ private function portalBaseUrl(): string { return rtrim(config('services.parquetecmar.url', 'https://parquetecmar.angra.rj.gov.br'), '/'); } /** * URL para download de arquivo (Angra) - rota que faz stream (registros antigos). * URL absoluta para o parquetecmar abrir no domínio do Angra. */ private function downloadUrl(string $tipo, string $slug): string { return $this->angraBaseUrl() . '/api/parquetecmar/' . $tipo . '/' . $slug . '/download'; } /** * Base URL do Angra (para links de arquivos/imagens consumidos pelo parquetecmar em outro domínio). */ private function angraBaseUrl(): string { return rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/'); } /** * URL da foto do edital via link simbólico (pmar/assets/img/editais-ptmar-imagens ou legado parque_tec_editais). * Sempre retorna URL absoluta do Angra para o parquetecmar. */ private function editalFotoUrl(Edital $e): ?string { if (empty($e->foto)) { return null; } $path = str_replace('\\', '/', $e->foto); if (str_contains($path, ParqueTecResource::EDITAIS_PTMAR_IMAGENS) || str_contains($path, 'parque_tec_editais')) { return $this->angraBaseUrl() . '/' . ltrim($path, '/'); } return $this->angraBaseUrl() . '/api/parquetecmar/editais/' . $e->slug . '/foto'; } /** * URL do arquivo: link público simbólico (pmar/assets/files/editais-ptmar-documentos/... ou legado editais-ptmar/...). * Path no banco pode ser "editais-ptmar-documentos/xxx.pdf" ou "editais-ptmar/xxx.pdf" ou só "xxx.pdf". */ private function editalArquivoUrl(Edital $e): ?string { if (empty($e->arquivo)) { return null; } $path = str_replace('\\', '/', trim($e->arquivo, '/\\')); $prefixes = [ParqueTecResource::EDITAIS_PTMAR_DOCUMENTOS . '/', 'editais-ptmar/']; $ok = false; foreach ($prefixes as $prefix) { if (str_starts_with($path, $prefix)) { $ok = true; break; } } if (!$ok) { $path = ParqueTecResource::EDITAIS_PTMAR_DOCUMENTOS . '/' . $path; } return $this->angraBaseUrl() . '/pmar/assets/files/' . $path; } /** * URL do arquivo: sempre link público simbólico direto (pmar/assets/files/decretos-leis-ptmar/...). * Nunca rota com slug; path no banco pode ser "decretos-leis-ptmar/xxx.pdf" ou só "xxx.pdf". */ private function decretoLeiArquivoUrl(DecretoLei $d): ?string { if (empty($d->arquivo)) { return null; } $path = str_replace('\\', '/', trim($d->arquivo, '/\\')); $prefix = ParqueTecResource::DECRETOS_LEIS_PTMAR_FOLDER . '/'; if (!str_starts_with($path, $prefix)) { $path = ParqueTecResource::DECRETOS_LEIS_PTMAR_FOLDER . '/' . $path; } return $this->angraBaseUrl() . '/pmar/assets/files/' . $path; } private function startupLogoUrl(Startup $s): ?string { if (empty($s->logo)) { return null; } $path = str_replace('\\', '/', $s->logo); // Preferir link público institucional do PTMar (pmar/assets/img/...) ao invés de /storage, // pois o Parquetecmar consome em outro domínio e o symlink /storage pode variar por ambiente. $path = ltrim($path, '/'); if (str_starts_with($path, 'pmar/assets/img/')) { return $this->angraBaseUrl() . '/' . $path; } $relative = ltrim($path, '/'); return $this->angraBaseUrl() . '/pmar/assets/img/' . $relative; } private function startupBackgroundUrl(Startup $s): ?string { if (empty($s->background)) { return null; } $path = str_replace('\\', '/', $s->background); $path = ltrim($path, '/'); if (str_starts_with($path, 'pmar/assets/img/')) { return $this->angraBaseUrl() . '/' . $path; } $relative = ltrim($path, '/'); return $this->angraBaseUrl() . '/pmar/assets/img/' . $relative; } /** * Lista editais (para home com limit ou para página "Ver Todos" com paginação). * GET /api/parquetecmar/editais?limit=6 * GET /api/parquetecmar/editais?page=1&per_page=12 */ public function editais(Request $request): JsonResponse { $query = Edital::ativos()->orderBy('ordem')->orderByDesc('data_publicacao'); $limit = $request->integer('limit', 0); if ($limit > 0) { $items = $query->limit($limit)->get(); return response()->json([ 'data' => $this->formatEditaisList($items), ]); } $perPage = min($request->integer('per_page', 12), 50); $paginator = $query->paginate($perPage); return response()->json([ 'data' => $this->formatEditaisList($paginator->items()), 'meta' => [ 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ], ]); } /** * Detalhe de um edital por slug. * GET /api/parquetecmar/editais/{slug} */ public function edital(string $slug): JsonResponse { $edital = Edital::ativos()->where('slug', $slug)->firstOrFail(); return response()->json($this->formatEditalSingle($edital)); } /** * Download do arquivo do edital. * GET /api/parquetecmar/editais/{slug}/download * Se o arquivo estiver em storage editais-ptmar-documentos (ou legado editais-ptmar), redireciona para o link público. */ public function editalDownload(string $slug): StreamedResponse|JsonResponse|\Illuminate\Http\RedirectResponse { $edital = Edital::ativos()->where('slug', $slug)->firstOrFail(); if (empty($edital->arquivo)) { return response()->json(['error' => 'Arquivo não disponível'], 404); } $path = str_replace('\\', '/', $edital->arquivo); if (str_starts_with($path, ParqueTecResource::EDITAIS_PTMAR_DOCUMENTOS . '/') || str_starts_with($path, 'editais-ptmar/')) { return redirect()->away($this->angraBaseUrl() . '/pmar/assets/files/' . ltrim($path, '/')); } return $this->streamArquivo($edital->arquivo, basename($edital->arquivo ?? 'edital.pdf')); } /** * Exibe a foto do edital. Se estiver em editais-ptmar-imagens (ou legado parque_tec_editais), redireciona para o link simbólico. * GET /api/parquetecmar/editais/{slug}/foto */ public function editalFoto(string $slug): StreamedResponse|BinaryFileResponse|JsonResponse|\Illuminate\Http\RedirectResponse { $edital = Edital::ativos()->where('slug', $slug)->firstOrFail(); if (empty($edital->foto)) { return response()->json(['error' => 'Foto não disponível'], 404); } $path = str_replace('\\', '/', $edital->foto); if (str_contains($path, ParqueTecResource::EDITAIS_PTMAR_IMAGENS) || str_contains($path, 'parque_tec_editais')) { return redirect()->away($this->editalFotoUrl($edital)); } return $this->streamArquivo($edital->foto, basename($edital->foto)); } /** * Lista decretos e leis (para home com limit ou página com paginação). * GET /api/parquetecmar/decretos-leis?limit=6 * GET /api/parquetecmar/decretos-leis?page=1&per_page=12 */ public function decretosLeis(Request $request): JsonResponse { $query = DecretoLei::ativos()->orderBy('ordem')->orderByDesc('data_publicacao'); $limit = $request->integer('limit', 0); if ($limit > 0) { $items = $query->limit($limit)->get(); return response()->json([ 'data' => $this->formatDecretosLeisList($items), ]); } $perPage = min($request->integer('per_page', 12), 50); $paginator = $query->paginate($perPage); return response()->json([ 'data' => $this->formatDecretosLeisList($paginator->items()), 'meta' => [ 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ], ]); } /** * Lista startups ativas (com filtro opcional por segmento). * GET /api/parquetecmar/startups?segmento=tecnologia */ public function startups(Request $request): JsonResponse { $query = Startup::ativos()->orderBy('ordem')->orderBy('nome'); if ($request->filled('segmento')) { $query->where('segmento', $request->input('segmento')); } $items = $query->get(); $base = $this->portalBaseUrl(); $data = $items->map(function (Startup $s) use ($base) { $segmento = $this->segmentoInfo($s->segmento); return [ 'id' => $s->id, 'nome' => $s->nome, 'slug' => $s->slug, 'descricao' => $s->descricao, 'segmento' => [ 'key' => $s->segmento, 'label' => $segmento['label'], 'color' => $segmento['color'], ], 'logo_url' => $this->startupLogoUrl($s), 'background_url' => $this->startupBackgroundUrl($s), 'email' => $s->email, 'telefone' => $s->telefone, 'rede_social' => $s->rede_social, 'link_site' => $s->link_site, 'url' => "{$base}/startups/{$s->slug}", ]; }); return response()->json(['data' => $data]); } /** * Detalhe de uma startup por slug ou ID. * GET /api/parquetecmar/startups/{slugOrId} */ public function startup(string $slugOrId): JsonResponse { $startup = Startup::ativos() ->where(function ($q) use ($slugOrId) { $q->where('slug', $slugOrId) ->orWhere('id', $slugOrId); }) ->firstOrFail(); $segmento = $this->segmentoInfo($startup->segmento); $base = $this->portalBaseUrl(); return response()->json([ 'id' => $startup->id, 'nome' => $startup->nome, 'slug' => $startup->slug, 'descricao' => $startup->descricao, 'segmento' => [ 'key' => $startup->segmento, 'label' => $segmento['label'], 'color' => $segmento['color'], ], 'logo_url' => $this->startupLogoUrl($startup), 'background_url' => $this->startupBackgroundUrl($startup), 'email' => $startup->email, 'telefone' => $startup->telefone, 'rede_social' => $startup->rede_social, 'link_site' => $startup->link_site, 'url' => "{$base}/startups/{$startup->slug}", ]); } /** * Lista perguntas frequentes do Parque Tecnológico do Mar. * GET /api/parquetecmar/faqs?limit=10 */ public function faqs(Request $request): JsonResponse { $query = \App\Models\ParqueTecnologico\Faq::ativas() ->orderBy('ordem') ->orderByDesc('created_at'); $limit = $request->integer('limit', 0); if ($limit > 0) { $items = $query->limit($limit)->get(); return response()->json([ 'data' => $items->map(function (\App\Models\ParqueTecnologico\Faq $faq) { return [ 'id' => $faq->id, 'pergunta' => $faq->pergunta, 'resposta' => $faq->resposta, 'ordem' => $faq->ordem, ]; }), ]); } $items = $query->get(); return response()->json([ 'data' => $items->map(function (\App\Models\ParqueTecnologico\Faq $faq) { return [ 'id' => $faq->id, 'pergunta' => $faq->pergunta, 'resposta' => $faq->resposta, 'ordem' => $faq->ordem, ]; }), ]); } /** * Detalhe de um decreto/lei por slug. * GET /api/parquetecmar/decretos-leis/{slug} */ public function decretoLei(string $slug): JsonResponse { $decretoLei = DecretoLei::ativos()->where('slug', $slug)->firstOrFail(); return response()->json($this->formatDecretoLeiSingle($decretoLei)); } /** * Download do arquivo do decreto/lei. * GET /api/parquetecmar/decretos-leis/{slug}/download * Se o arquivo estiver em storage decretos-leis-ptmar, redireciona para o link público. */ public function decretoLeiDownload(string $slug): StreamedResponse|JsonResponse|\Illuminate\Http\RedirectResponse { $decretoLei = DecretoLei::ativos()->where('slug', $slug)->firstOrFail(); if (empty($decretoLei->arquivo)) { return response()->json(['error' => 'Arquivo não disponível'], 404); } $path = str_replace('\\', '/', $decretoLei->arquivo); if (str_starts_with($path, ParqueTecResource::DECRETOS_LEIS_PTMAR_FOLDER . '/')) { return redirect()->away($this->angraBaseUrl() . '/pmar/assets/files/' . ltrim($path, '/')); } return $this->streamArquivo($decretoLei->arquivo, basename($decretoLei->arquivo ?? 'decreto-lei.pdf')); } private function formatEditaisList($items): array { $items = $items instanceof \Illuminate\Support\Collection ? $items->all() : (array) $items; $base = $this->portalBaseUrl(); return array_map(function (Edital $e) use ($base) { return [ 'id' => $e->id, 'titulo' => $e->titulo, 'slug' => $e->slug, 'descricao' => $e->descricao, 'descricao_resumo' => $e->descricao ? \Illuminate\Support\Str::limit(strip_tags($e->descricao), 160) : null, 'foto_url' => $this->editalFotoUrl($e), 'link' => $e->link ?: null, 'data_publicacao' => $e->data_publicacao?->format('Y-m-d'), 'data_publicacao_formatada' => $e->data_publicacao?->format('d/m/Y'), 'tem_arquivo' => !empty($e->arquivo), 'arquivo_url' => $this->editalArquivoUrl($e), 'url' => "{$base}/editais/{$e->slug}", ]; }, $items); } private function formatEditalSingle(Edital $e): array { $base = $this->portalBaseUrl(); return [ 'id' => $e->id, 'titulo' => $e->titulo, 'slug' => $e->slug, 'descricao' => $e->descricao, 'foto_url' => $this->editalFotoUrl($e), 'link' => $e->link ?: null, 'data_publicacao' => $e->data_publicacao?->format('Y-m-d'), 'data_publicacao_formatada' => $e->data_publicacao?->format('d/m/Y'), 'tem_arquivo' => !empty($e->arquivo), 'arquivo_url' => $this->editalArquivoUrl($e), 'url' => "{$base}/editais/{$e->slug}", ]; } private function formatDecretosLeisList($items): array { $items = $items instanceof \Illuminate\Support\Collection ? $items->all() : (array) $items; $base = $this->portalBaseUrl(); return array_map(function (DecretoLei $d) use ($base) { return [ 'id' => $d->id, 'titulo' => $d->titulo, 'slug' => $d->slug, 'numero_lei' => $d->numero_lei, 'descricao' => $d->descricao, 'descricao_resumo' => $d->descricao ? \Illuminate\Support\Str::limit(strip_tags($d->descricao), 160) : null, 'data_publicacao' => $d->data_publicacao?->format('Y-m-d'), 'data_publicacao_formatada' => $d->data_publicacao?->format('d/m/Y'), 'tem_arquivo' => !empty($d->arquivo), 'arquivo_url' => $this->decretoLeiArquivoUrl($d), 'url' => "{$base}/decretos-leis/{$d->slug}", ]; }, $items); } private function formatDecretoLeiSingle(DecretoLei $d): array { $base = $this->portalBaseUrl(); return [ 'id' => $d->id, 'titulo' => $d->titulo, 'slug' => $d->slug, 'numero_lei' => $d->numero_lei, 'descricao' => $d->descricao, 'data_publicacao' => $d->data_publicacao?->format('Y-m-d'), 'data_publicacao_formatada' => $d->data_publicacao?->format('d/m/Y'), 'tem_arquivo' => !empty($d->arquivo), 'arquivo_url' => $this->decretoLeiArquivoUrl($d), 'url' => "{$base}/decretos-leis/{$d->slug}", ]; } private function streamArquivo(?string $path, string $filename): StreamedResponse|BinaryFileResponse|JsonResponse|\Illuminate\Http\RedirectResponse { if (!$path) { return response()->json(['error' => 'Arquivo não disponível'], 404); } $pathNorm = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path); $ds = DIRECTORY_SEPARATOR; $search = "pmar{$ds}assets{$ds}img"; $filePath = storage_path() . str_replace($search, "{$ds}app{$ds}public", $pathNorm); if (!is_file($filePath)) { return response()->json(['error' => 'Arquivo não encontrado'], 404); } return response()->download($filePath, $filename, [ 'Content-Type' => mime_content_type($filePath), ]); } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings