Session
Session
Dado que las aplicaciones que utilizan HTTP no tienen estado, las sesiones proveen una forma de guardar información acerca del usuario a través de varias peticiones.
Laravel viene con una api que soporta el acceso de diferentes bases de datos, como Memcached y Redis.
Tu archivo de configuración de sesiones se encuentra en config/session.php. Laravel ****esta configurado para usar un driver de archivo (guarda las sesiones en storage/framework/sessions).
file - sessions are stored in storage/framework/sessions.
cookie - sessions are stored in secure, encrypted cookies.
database - sessions are stored in a relational database.
memcached / redis - sessions are stored in one of these fast, cache based stores.
dynamodb - sessions are stored in AWS DynamoDB.
array - sessions are stored in a PHP array and will not be persisted.
The array driver is primarily used during testing and prevents the data stored in the session from being persisted.
Driver requisitos
Database
Cuando estamos usando el driver session database, vas a tener que crear una tabla en la que se almacene los registros de la sesión.
Ejemplo de esquema:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity')->index();
});
Para generar un schema automaticamente usar php artisan session:table
Redis
Antes de usar redis para guardar las sesiones, vas a necesitar una de estas cosas. Instalar phpRedis extension via PECL o instalar predis/predis package via composer.
Interactuar con las sesiones
Obtener un elemento de la sesion
Usando Request
Existen dos formas principales de trabajar con sesiones en laravel: usando el helper global session o a través de una instancia de Request, el cual puede ser inyectado automáticamente en un método de un controlador o en una función anónima mediante tipo insinuado.
Ejemplo usando Request:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
// ...
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
Se puede pasar un segundo parámetro a la función get para obtener un valor por defecto, si esa key no existe en la sesión. Si se pasa una función anónima como segundo parámetro, se ejecutará si no existe la key.
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
Usando session helper
Se puede usar también el helper de session para recuperar o guardar información en la sesión. Cuando la función es llamada con un único parámetro de tipo string, devolvera el valor de la key guardada en la sesión. Cuando la función es llamada con un array asociativo, se guardara la key con su valor.
Route::get('/home', function () {
//devuelve la key de la sesion
$value = session('key');
// usando un valor por defecto, sino existe la key
$value = session('key', 'default');
// Guarda key comoo la clave y value como el valor
session(['key' => 'value']);
});
Obtener todos los elementos de la sesión
Si quieres recuperar toda la información de la sesión, usar data
$data = $request->session()->all();
Obtener una porción de datos
// solamente esas claves
$data = $request->session()->only(['username', 'email']);
// todas excepto esas claves
$data = $request->session()->except(['username', 'email']);
Determinar si un elemento existe
has
Para determinar si un elemento esta presente en la sesión, si esta presente devuelve el elemento, sino devuelve null
if ($request->session()->has('users')) {
// ...
}
exists
Para determinar si un elemento esta presente aunque su valor sea null, usar exists
if ($request->session()->exists('users')) {
// ...
}
missing
Para determinar si un elemento no esta en la sesión, devuelve true si el item no esta en la sesión.
if ($request->session()->missing('users')) {
// ...
}
Guardar elementos
Como guardar usando las dos formas vistas
// Via a request instance...
$request->session()->put('key', 'value');
// Via the global "session" helper...
session(['key' => 'value']);
Usar push para los arrays guardados en sesión
Si user.teams fuera un array guardado en la sesión, realiza un push al array, agregando un nuevo elemento.
$request->session()->push('user.teams', 'developers');
Usar pull para recuperar elemento
Al usar pull, se recupera dicho elemento y se borra de la sesión. También se puede poner valor por defecto.
$value = $request->session()->pull('key', 'default');
increment y decrement para valores tipo contador
Si la sesión esta guardando un valor que puede ir incrementando o decrementando, se puede usar increment o decrement.
$request->session()->increment('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);
Flashear la información
Algunas veces querrás guardar elementos dentro de la sesión para la siguiente petición. Lo puedes hacer usando el método flash. La información guardada usando este método va a estar. disponible inmediatamente y durante la siguiente petición (solo la siguiente). Después de la siguiente petición la información flasheada sera eliminada. Es útil para mensajes de corto periodo de vida
$request->session()->flash('status', 'Task was successful!');
Si necesitas flashear la información para varias peticiones, podrías usar el método reflash, el cual mantendrá la información para la siguiente petición. Si solamente se quiere conservar una parte de la información flasheada para la siguiente petición, se usa keep
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
Para persistir la información para la actual petición, se puede usar now
$request->session()->now('status', 'Task was successful!');
Borrar elemento o elementos
Si se quiere borrar un elemento de la sesión se puede usar el método forget. En cambio si se quiere borrar toda la información de la sesión usar el método flush.
// Forget a single key...
$request->session()->forget('name');
// Forget multiple keys...
$request->session()->forget(['name', 'status']);
$request->session()->flush();
Regeneración de la sesión
Regenerar la sesión se suele realizar para prevenir que usuarios maliciosos realicen un ataque de session fixation.
Laravel automaticamente regenera la sesión durante la autenticación. Sin embargo si se quiere regenerar la sesión se puede hacer con el metodo regenerate
$request->session()->regenerate();
Si se quiere regenerar la sesión y borrar la información, se puede hacer con un único método. Usando el método invalidate.
$request->session()->invalidate();
Bloqueo de sesión
Para usar un bloqueo de sesión, tu aplicación debe usar un driver que soporte los atomic locks. Actualmente los drives incluidos son memcached, dynamodb, redis, database, file y array driver.
No deberías usar cookies driver sesión
Por defecto, Laravel permite que varias peticiones que usen la misma sesión se ejecuten concurrentemente. Por ejemplo, si usas una librería HTTP de Javascript para realizar dos peticiones HTTP, las dos van a ser ejecutadas al mismo tiempo. Para la mayoría de aplicaciones esto no es un problema, a pesar de esto, una perdida de información puede ocurrir si por ejemplo dos peticiones simultaneas, dirigidas a diferentes endpoints y en la que las dos intentan guardar información en la sesión.
Para mitigar esto, Laravel provee una funcionalidad para limitar las peticiones concurrentes para una sesión dada.
Para empezar podemos agregar block en la definición de la ruta.
En este ejemplo una petición a /profile va a adquirir un bloqueo de sesión. Cualquier siguiente petición a /profile o /order que tenga el mismo SessionId va a esperar mientras que la primera petición siga teniendo el candado, para continuar su ejecución
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10)
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10)
El método block acepta dos argumentos opcionales. El primer argumento es la cantidad de segundos que va a durar el bloqueo de session. Si por ejemplo la petición tarda mas que esos segundos, el candado sera soltado antes cuando finalice el tiempo.
El segundo parámetro es la cantidad de segundos que una petición debe esperar para intentar volver a obtener el candado. Una Illuminate\Contracts\Cache\LockTimeoutException sera lanzada si no es posible obtener el candado dentro de ese numero de segundos.
Si ninguno de esos parámetros es pasado al método block, por defecto el tiempo de bloqueo es de 10 segundos y se intenta obtener el candado pasado 10 segundos.
Añadir un driver personalizado
Implementar el driver
Si ninguno de los existentes driver se ajusta a lo que necesita tu aplicación. Laravel permite crear tu propio manejador de sesión. Para ello tu driver personalizado debe implementar la interfaz de PHP`s built-in SessionHandlerInterface, esta interfaz contiene unos pocos de métodos, una implementación de mongo podria verse asi:
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Laravel no viene con un directorio donde guardar tus extensiones. Eres libre de guardarlos donde quieras, en el ejemplo se ha creado un directorio llamado Extensions.
Vamos a ver que hace cada metodo:
- The
openmethod would typically be used in file based session store systems. Since Laravel ships with afilesession driver, you will rarely need to put anything in this method. You can simply leave this method empty. - The
closemethod, like theopenmethod, can also usually be disregarded. For most drivers, it is not needed. - The
readmethod should return the string version of the session data associated with the given$sessionId. There is no need to do any serialization or other encoding when retrieving or storing session data in your driver, as Laravel will perform the serialization for you. - The
writemethod should write the given$datastring associated with the$sessionIdto some persistent storage system, such as MongoDB or another storage system of your choice. Again, you should not perform any serialization - Laravel will have already handled that for you. - The
destroymethod should remove the data associated with the$sessionIdfrom persistent storage. - The
gcmethod should destroy all session data that is older than the given$lifetime, which is a UNIX timestamp. For self-expiring systems like Memcached and Redis, this method may be left empty.
Registrar el driver
Una vez creada la implementación del driver es necesario incluirlo dentro de Laravel. Para ello dentro de un provider.
To add additional drivers to Laravel’s session backend, you may use the extend method provided by the Session facade. You should call the extend method from the boot method of a service provider. You may do this from the existing App\Providers\AppServiceProvider or create an entirely new provider:
<?php
namespace App\Providers;
use App\Extensions\MongoSessionHandler;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Return an implementation of SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}
Una vez el driver de sesión ha sido registrado, puedes usar tu driver de mongo en el archivo de configuración config/session.php