Mostrando entradas con la etiqueta laravel. Mostrar todas las entradas
Mostrando entradas con la etiqueta laravel. Mostrar todas las entradas

viernes, 3 de octubre de 2025

Obtener la traducción al español de validation.php de Laravel 12

 Para obtener la traducción al español del archivo validation.php en Laravel 12, usa el paquete oficial laravel-lang/common mediante composer require laravel-lang/common --dev y luego publicando el idioma español con php artisan lang:add es. Esto copiará los archivos de traducción, incluido el de validaciones, al directorio resources/lang/es en tu proyecto. 

Pasos detallados:
  1. Instala el paquete de traducciones:
    Abre tu terminal en la raíz de tu proyecto Laravel y ejecuta el siguiente comando para instalar el paquete oficial de traducciones mantenido por la comunidad:
    bash
    composer require laravel-lang/common --dev
    
  2. Añade el idioma español:
    Después de la instalación, ejecuta el comando php artisan para añadir los archivos del idioma español:
    bash
    php artisan lang:add es
    
    Este comando copiará los archivos de traducción, incluido el directorio es con sus traducciones, al directorio lang/ dentro de tu proyecto.
  3. Configura el idioma en config/app.php (opcional):
    Para asegurarte de que Laravel use el español como idioma principal, verifica y modifica el archivo config/app.php.
    • Busca la línea APP_LOCALE.
    • Asegúrate de que APP_LOCALE=es esté configurado o reemplaza el valor con es.
    php
    // En el archivo .env o en config/app.php
    'locale' => env('APP_LOCALE', 'es'),
    
     
Con estos pasos, los mensajes de validación del archivo resources/lang/es/validation.php ahora estarán disponibles en español dentro de tu aplicación. 

lunes, 22 de septiembre de 2025

Event Listeners síncronos y asíncronos en Laravel

 Quiero que se desencadene una tarea en respuesta a lo que ocurre en un controlador, pero de manera desacoplada. ¿Es apropiado utilizar eventos y listeners? ¿A un listener se le puede hacer type-hint del request? ¿El listener puede trabajar con los global helpers request y session?

Sí, utilizar eventos y listeners es la forma más apropiada y canónica de resolver ese problema.

Es la solución ideal para desacoplar la lógica, siguiendo el patrón de diseño Observer. En este patrón, el controlador "emite" una señal (el evento) de que algo ha ocurrido, y los "observadores" (los listeners) reaccionan a esa señal de forma independiente.

Esto te permite:

  • Mantener la lógica del controlador limpia, centrada solo en manejar la solicitud HTTP.

  • Hacer que la tarea desacoplada sea más modular y reutilizable.


¿Es apropiado utilizar eventos y listeners?

Absolutamente. Este es un caso de uso de libro para el sistema de eventos de Laravel. El controlador no necesita saber qué sucede después de que se completa su acción, solo necesita notificar que se ha realizado con éxito. Los listeners se encargan del resto, lo que te permite agregar o quitar tareas en el futuro sin modificar el controlador.

¿A un listener se le puede hacer type-hint del request?

No, no se puede. Esta es una limitación intencional y una de las reglas clave de este patrón. Un listener no recibe el objeto Request directamente, ya que su método handle solo recibe una instancia del evento.

La razón es simple pero crucial: los listeners pueden ser procesados en una cola en un proceso diferente al de la solicitud web original. En ese entorno, el objeto Request ya no existe, y tratar de acceder a él causaría un error o devolvería null.

¿El listener puede trabajar con los global helpers request() y session()?

Técnicamente, sí, pero es una muy mala práctica.

Aunque podrías llamar a request() o session() dentro de un listener, esto rompe por completo el propósito del desacoplamiento. Si el listener se ejecuta en una cola, estos helpers no tendrían un contexto HTTP y no funcionarían como esperas, lo que llevaría a errores difíciles de depurar.

La forma correcta de pasar datos

La solución correcta es pasar toda la información que el listener necesita desde el controlador a través del objeto Evento.

  1. En tu Evento: Define las propiedades que necesitas, como la información de la solicitud, el usuario o los datos del modelo.

  2. Evento Personalizado
    <?php

    namespace App\Events;

    use App\Models\User;
    use Illuminate\Foundation\Events\Dispatchable;

    class UserRegistered
    {
    use Dispatchable;

    public User $user;
    public string $ipAddress;

    public function __construct(User $user, string $ipAddress)
    {
    $this->user = $user;
    $this->ipAddress = $ipAddress;
    }
    }

2. En tu Controlador: Dispara el evento, pasando los datos necesarios.

Controlador de Registro
<?php

namespace App\Http\Controllers;

use App\Events\UserRegistered;
use Illuminate\Http\Request;
use App\Models\User;

class RegisterController extends Controller
{
public function register(Request $request)
{
// ... (lógica de validación y creación del usuario)

$user = User::create($request->validated());

// Disparamos el evento, pasando el objeto Request
UserRegistered::dispatch($user, $request->ip());

return redirect()->route('dashboard');
}
}

  1. En tu Listener: El listener recibe el objeto UserRegistered y accede a las propiedades sin depender de nada del entorno global.

  2. Listener de Bienvenida
    <?php

    namespace App\Listeners;

    use App\Events\UserRegistered;
    use App\Mail\WelcomeMail;
    use Illuminate\Support\Facades\Mail;

    class SendWelcomeEmail
    {
    public function handle(UserRegistered $event)
    {
    // Accedes a la data directamente del evento, no del request
    $user = $event->user;
    $ip = $event->ipAddress;

    // Enviar un correo, loguear la actividad, etc.
    Mail::to($user->email)->send(new WelcomeMail($user));
    }
    }

Al seguir esta estrategia, tu código es robusto, modular y fácil de probar, ya que cada componente cumple su función sin depender de otros.


Necesito que en consecuencia de una acción realizada en un controller, pase otra cosa que no tiene que ver con el objetivo del controller pero que necesita operar sobre el request y la sesión.

Tu pregunta es muy específica y apunta a un dilema común. Si una tarea debe ser desacoplada del controlador pero necesita imperiosamente el objeto Request y la Session, el enfoque con eventos y listeners sigue siendo la solución correcta, pero con una restricción crucial: el listener no puede ser asíncrono.

La clave es que el listener se ejecutará de forma síncrona, es decir, dentro del mismo ciclo de vida de la solicitud HTTP.

Esto te permite:

  • Desacoplar la lógica: Tu controlador no se llena de tareas que no le corresponden. Simplemente "anuncia" que una acción ha ocurrido.

  • Acceder al Request y Session: Como el listener se ejecuta en el mismo proceso de la solicitud, el estado del Request y la Session aún están disponibles y son accesibles.

El enfoque correcto

  1. Crea un Evento: Un simple objeto de datos (POPO - Plain Old PHP Object) que contiene las referencias necesarias, incluyendo el objeto Request.

  2. Dispara el Evento en el Controlador: En el momento adecuado, disparas el evento.

  3. Crea un Listener Síncrono: Este listener recibirá el evento y podrá operar sobre el Request y la Session sin problemas. No lo marques como ShouldQueue.

Este es el único método seguro para mantener el acceso al estado HTTP mientras se mantiene un cierto nivel de desacoplamiento.


Ejemplo de código paso a paso

Vamos a crear un escenario donde, después de que un usuario actualiza su perfil, quieres registrar su actividad en la sesión y guardar su historial de cambios en una base de datos. La segunda tarea no es responsabilidad directa del controlador, pero requiere los datos de la solicitud.

Evento
<?php

namespace App\Events;

use Illuminate\Http\Request;
use Illuminate\Foundation\Events\Dispatchable;

class ProfileUpdated
{
use Dispatchable;

public Request $request;
public $user;

/**
* Crea una nueva instancia del evento.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function __construct(Request $request, $user)
{
// En un listener síncrono, es seguro pasar el objeto Request
$this->request = $request;
$this->user = $user;
}
}

Listener
<?php

namespace App\Listeners;

use App\Events\ProfileUpdated;
use Illuminate\Support\Facades\Log;

class LogProfileActivity
{
/**
* Maneja el evento.
*
* @param \App\Events\ProfileUpdated $event
* @return void
*/
public function handle(ProfileUpdated $event)
{
// Accedemos a la data de la solicitud y la sesión a través del evento.
$request = $event->request;
$user = $event->user;

// Tarea 1: Guardar un registro de actividad en la base de datos
// Aquí puedes usar la información del $request, como el IP, user-agent, etc.
Log::info("El usuario {$user->id} actualizó su perfil desde la IP: {$request->ip()}");

// Tarea 2: Agregar un mensaje flash a la sesión del usuario
// Accedes a la sesión directamente, ya que el listener es síncrono.
$request->session()->flash('status', 'Tu perfil ha sido actualizado con éxito.');

}
}

Controlador
<?php

namespace App\Http\Controllers;

use App\Events\ProfileUpdated;
use App\Models\User;
use Illuminate\Http\Request;

class ProfileController extends Controller
{
public function update(Request $request)
{
$user = auth()->user();

// Lógica de validación y actualización del modelo
$user->fill($request->validated())->save();

// Disparamos el evento pasando el objeto Request y el usuario.
ProfileUpdated::dispatch($request, $user);

return redirect()->back();
}
}

Resumen del enfoque

  • Tu controlador llama a una sola línea: ProfileUpdated::dispatch($request, $user);. Su lógica principal se mantiene enfocada en la actualización del perfil.

  • El Evento actúa como un simple contenedor de datos, llevando el Request y el $user al listener.

  • El Listener realiza las tareas secundarias (logging, manipulación de sesión), operando con todos los datos del Request que necesita.

Este patrón te permite mantener el código limpio y organizado. Si en el futuro necesitas agregar otra tarea, simplemente creas otro listener y lo asocias al mismo evento, sin tocar el controlador original.

lunes, 15 de septiembre de 2025

¿Es lo mismo auth()->user() y request()->user()?

 Aunque en la mayoría de los casos prácticos el resultado de auth()->user() y request()->user() es el mismo, no son idénticos y se derivan de procesos diferentes. Entender la sutil diferencia es clave para comprender cómo funciona el sistema de autenticación de Laravel.

auth()->user()

  • ¿Qué es? auth() es un helper global que te da acceso a la instancia del guard de autenticación por defecto.

  • ¿Qué hace? Cuando llamas a user(), el guard de autenticación comprueba la sesión o el token (dependiendo de la configuración del guard) para encontrar y recuperar el modelo User autenticado de la base de datos.

  • Cuándo usarlo: Este es el método estándar y recomendado para acceder al usuario autenticado en cualquier lugar de tu aplicación (controladores, vistas, servicios, etc.). Es la forma más declarativa de decir: "dame el usuario que está logueado en este momento".

request()->user()

  • ¿Qué es? request() es un helper global que te da acceso a la instancia del objeto Request de la solicitud HTTP actual.

  • ¿Qué hace? El método user() en el objeto Request es un atajo (o proxy) que delega la llamada al Auth facade subyacente. Básicamente, es una conveniencia que hace exactamente lo mismo que auth()->user(). Su existencia se debe a que a menudo tienes el objeto Request inyectado en un método y es una forma rápida de acceder al usuario sin tener que llamar a auth().

  • Cuándo usarlo: Cuando ya tienes la instancia de Request a mano (por ejemplo, como un parámetro de método en tu controlador), es una forma legible de acceder al usuario autenticado.


¿Son lo mismo?

A nivel de resultado final, sí, ambos te darán el mismo modelo User si el usuario está autenticado. La diferencia es semántica y de cómo se llega a ese resultado:

  • auth()->user() va directamente a la fuente (el guard de autenticación).

  • request()->user() pasa por el objeto de la solicitud, que luego delega la llamada a la misma fuente.

Para la mayoría de los desarrolladores, la diferencia es insignificante. La única vez que podría importar es en escenarios muy avanzados, como cuando se manejan múltiples guards de autenticación o se modifican los guards en tiempo de ejecución.

Conclusión

Para el 99% de las situaciones, puedes usar auth()->user() o request()->user() indistintamente. Sin embargo, por claridad y para seguir las mejores prácticas de la comunidad, se prefiere usar auth()->user() cuando el único propósito es obtener al usuario, ya que es más explícito sobre lo que estás tratando de lograr.

Si estás en un controlador y ya tienes $request inyectado, usar $request->user() es totalmente aceptable por conveniencia. Lo importante es que entiendas que ambos métodos te llevan al mismo lugar.