vueloenglobo.mx/app/Http/Controllers/Page/VuelosController.php

413 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Page;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Collection;
/**
* Class VuelosController
*
* Controlador encargado de la gestión de vuelos en la web.
*
* Funcionalidades principales:
* - Listado de vuelos visibles en la web
* - Vuelos para la página principal (home)
* - Detalle de un vuelo específico
* - Soporte multilingual (ES / EN)
* - Agregación optimizada de información relacionada:
* precios, categorías, imágenes e inclusiones
*
* Optimización:
* - Queries en bloque (evita N+1)
* - Uso de keyBy / groupBy para acceso rápido
* - Transformación centralizada de datos
*/
class VuelosController extends Controller
{
/**
* ID del sitio web actual.
*
* @var int
*/
protected int $sitio_web;
public function __construct()
{
$this->sitio_web = config('sitioweb.sitio_web_erp_id', 0);
}
/**
* Muestra el detalle de un vuelo específico.
*
* @param Request $request
* @param int $id ID del vuelo
*
* @return \Illuminate\Http\JsonResponse
*
* Respuesta:
* - 200: Objeto del vuelo
* - 404: Vuelo no encontrado
*/
public function show(Request $request, int $id)
{
$lang = $request->get('lang', 'en');
$vuelo = $this->getVuelosData(collect([$id]), $lang)->first();
if (!$vuelo) {
return response()->json(
[
'message' => 'Vuelo no encontrado',
],
404,
);
}
return response()->json($vuelo);
}
/**
* Obtiene colección de vuelos visibles en la web.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getVuelosCollection()
{
return $this->buildCollectionResponse(function ($query) {
$query->where('visibilidad_pagina_web', 1);
});
}
/**
* Obtiene colección de vuelos visibles en la home.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getVuelosCollectionHome()
{
return $this->buildCollectionResponse(function ($query) {
$query->where('visibilidad_pagina_web', 1)->where('show_flight_in_home_view', 0);
});
}
/**
* Builder reutilizable para colecciones de vuelos.
*
* @param callable $callback Filtros dinámicos sobre la query base
*
* @return \Illuminate\Http\JsonResponse
*/
private function buildCollectionResponse(callable $callback)
{
$lang = app()->getLocale();
$query = DB::table('vuelos')->where('id_sitio_web', $this->sitio_web)->where('id_estatus', 1);
$callback($query);
$ids = $query->pluck('id_vuelo');
return response()->json([
'vuelos' => $this->getVuelosData($ids, $lang)->values(),
]);
}
/**
* Motor principal de transformación de vuelos.
*
* @param Collection $ids IDs de vuelos
* @param string $lang Idioma actual
*
* @return Collection
*/
private function getVuelosData(Collection $ids, string $lang): Collection
{
if ($ids->isEmpty()) {
return collect();
}
$data = $this->fetchAllData($ids, $lang);
return $ids->map(fn($id) => $this->transformVuelo($id, $data, $lang))->filter();
}
/**
* Obtiene todos los datos relacionados en bloque.
*
* @param Collection $ids
* @param string $lang
*
* @return array
*/
private function fetchAllData(Collection $ids, string $lang): array
{
$vuelos = DB::table('vuelos')->whereIn('id_vuelo', $ids)->get()->keyBy('id_vuelo');
return [
'vuelos' => $vuelos,
'vuelos_en' => $lang === 'en' ? DB::table('vuelos_usd')->whereIn('id_vuelo', $ids)->get()->keyBy('id_vuelo') : collect(),
'categorias' => DB::table('categorias_vuelos')->whereIn('id_categoria', $vuelos->pluck('id_categoria'))->get()->keyBy('id_categoria'),
'precios' => DB::table('vuelos_precios')->whereIn('id_vuelo', $ids)->where('id_estatus', 1)->get()->keyBy('id_vuelo'),
'imagenes' => DB::table('vuelos_imagenes_galeria')->whereIn('id_vuelo', $ids)->where('id_estatus', 1)->orderBy('orden')->get()->groupBy('id_vuelo'),
'incluye' => DB::table('vuelos_items_incluye')->whereIn('id_vuelo', $ids)->where('id_estatus', 1)->get()->groupBy('id_vuelo'),
];
}
/**
* Transforma un vuelo a su estructura final.
*
* @param int $id
* @param array $data
* @param string $lang
*
* @return array|null
*/
private function transformVuelo(int $id, array $data, string $lang): ?array
{
$vuelo = $data['vuelos'][$id] ?? null;
if (!$vuelo) {
return null;
}
$vuelo = $this->mergeIdioma($vuelo, $data['vuelos_en'][$id] ?? null, $lang);
$vuelo = $this->normalizeVuelo($vuelo);
$categoria = $data['categorias'][$vuelo->id_categoria] ?? null;
return [
'vuelo' => $vuelo,
'categoria' => [
'nombre' => $lang === 'en' ? $categoria->nombre_categoria_usd ?? null : $categoria->nombre_categoria ?? null,
],
'precios' => $data['precios'][$id] ?? null,
'imagenes' => $data['imagenes'][$id] ?? collect(),
'incluye' => $data['incluye'][$id] ?? collect(),
];
}
/**
* Aplica traducción al vuelo si el idioma es inglés.
*
* @param mixed $vuelo
* @param mixed $vuelo_en
* @param string $lang
*
* @return mixed
*/
private function mergeIdioma($vuelo, $vuelo_en, string $lang)
{
if ($lang !== 'en' || !$vuelo_en) {
return $vuelo;
}
$map = [
'nombre_vuelo' => 'titulo_vuelo',
'descripcion_corta' => 'descripcion_corta',
'descripcion_larga' => 'descripcion_larga',
'json_incluye' => 'json_incluye',
'servicios_adicionales' => 'servicios_adicionales',
'precio_publico' => 'precio_usd',
'precio_especial' => 'precio_especial_usd',
];
foreach ($map as $target => $source) {
if (isset($vuelo_en->$source)) {
$vuelo->$target = $vuelo_en->$source;
}
}
return $vuelo;
}
/**
* Normaliza campos JSON del vuelo.
*
* @param mixed $vuelo
*
* @return mixed
*/
private function normalizeVuelo($vuelo)
{
$vuelo->json_incluye = $this->safeJsonDecode($vuelo->json_incluye);
$vuelo->precios_adicionales = $this->safeJsonDecode($vuelo->precios_adicionales);
return $vuelo;
}
/**
* Decodifica JSON de forma segura.
*
* @param mixed $value
*
* @return array
*/
private function safeJsonDecode($value)
{
if (!$value || $value === 'null') {
return [];
}
return json_decode($value, true) ?? [];
}
public static function obtenerServiciosAdicionales()
{
return DB::table('servicios_adicionales as sa')
->leftJoin('unidades as u', 'u.id_unidad', '=', 'sa.id_unidad')
->leftJoin('categorias as c', 'c.id_categoria', '=', 'sa.additional_service_category_id')
->select(
DB::raw('COALESCE(c.nombre_categoria, "Sin categoría") as nombre_categoria'),
DB::raw("
JSON_ARRAYAGG(
JSON_OBJECT(
'id', sa.id_servicio_adicional,
'descripcion', TRIM(sa.descripcion),
'precio', CAST(sa.precio AS DECIMAL(10,2)),
'observaciones', TRIM(sa.observaciones),
'unidad', u.nombre_unidad,
'imagenes', COALESCE((
SELECT JSON_ARRAYAGG(
JSON_UNQUOTE(
JSON_EXTRACT(sag2.json_rutas_imagenes, '$[0]')
)
)
FROM servicios_adicionales_galeria sag2
WHERE sag2.id_servicio_adicional = sa.id_servicio_adicional
AND sag2.id_estatus = 1
), JSON_ARRAY())
)
) as servicios
"),
)
->where('sa.id_estatus', 1)
->where('sa.visibilidad_web', 1)
->groupBy('nombre_categoria')
->orderBy('nombre_categoria') // ordena categorías
->get()
->map(function ($item) {
return [
'nombre_categoria' => $item->nombre_categoria,
'servicios' => json_decode($item->servicios, true), // 🔥 JSON → array real
];
})
->values();
}
/**
* Busca el ID del vuelo a partir del slug (multilenguaje).
*
* @param string $slug
* @param string $lang
* @return int|null
*/
private function findVueloIdBySlug(string $slug, string $lang): ?int
{
// Buscar en tabla base (ES)
$query = DB::table('vuelos')->where('id_sitio_web', $this->sitio_web)->where('id_estatus', 1);
if ($lang === 'en') {
// Buscar en tabla traducida
return DB::table('vuelos_usd')->where('slug', $slug)->value('id_vuelo');
}
return $query->where('slug', $slug)->value('id_vuelo');
}
/**
* Muestra el detalle de un vuelo por su enlace_permanente (slug permanente)
*
* Usa el idioma actual de la aplicación (sesión)
*
* @param Request $request
* @param string $enlace_permanente
* @return \Illuminate\Http\JsonResponse
*/
public function showByPermanentLink(Request $request, string $enlace_permanente)
{
$lang = app()->getLocale(); // 'es' o 'en'
$id = $this->findVueloIdByPermanentLink($enlace_permanente, $lang);
if (!$id) {
return response()->json([
'message' => $lang === 'en'
? 'Flight not found'
: 'Vuelo no encontrado'
], 404);
}
$vuelo = $this->getVuelosData(collect([$id]), $lang)->first();
if (!$vuelo) {
return response()->json([
'message' => $lang === 'en'
? 'Flight not found'
: 'Vuelo no encontrado'
], 404);
}
return response()->json($vuelo);
}
/**
* Busca el ID del vuelo a partir del enlace permanente (slug canónico).
*
* @param string $enlace_permanente
* @param string $lang
* @return int|null
*/
private function findVueloIdByPermanentLink(string $enlace_permanente, string $lang): ?int
{
// Siempre buscar primero en la tabla base (canónica)
$id = DB::table('vuelos')
->where('id_sitio_web', $this->sitio_web)
->where('id_estatus', 1)
->where('enlace_permanente', $enlace_permanente)
->value('id_vuelo');
if ($id) {
return $id;
}
// Fallback opcional: si estás en inglés y existe en tabla traducida
if ($lang === 'en') {
return DB::table('vuelos_usd')
->where('enlace_permanente', $enlace_permanente)
->value('id_vuelo');
}
return null;
}
public function getVuelosFoot()
{
}
public static function cotizaVuelo($slug, Request $request)
{
//Insertar en NNewsletter
\Log::info('Cotización recibida:', $request->all());
// return $request;
DB::table('newsletter_skyballoons')->insert([
'correo_electronico' => $request->email,
'origin_url' => 'https://' . DB::table('sitios_web')->where('id_sitio_web', 2)->value('url_web'),
'id_sitio_web' => 2,
'fecha_alta' => now(),
]);
}
}