413 lines
12 KiB
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(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
|