Skip to content

Instantly share code, notes, and snippets.

@juampi92
Created December 4, 2022 13:46
Show Gist options
  • Save juampi92/487dd66c5b2507679dd4c76863e6c01c to your computer and use it in GitHub Desktop.
Save juampi92/487dd66c5b2507679dd4c76863e6c01c to your computer and use it in GitHub Desktop.
KeyedSession

Keyed Session


This simple class attempts to bind a Store to a specific key. The benefit is quite simple: you don't have to repeat the key on every session call.

It's not much, but it can save you having to make a constant for each Session key.

Usage

$store = resolve(\Illuminate\Session\Store::class)

$fooSession = new KeyedSession($store, 'foo');
$fooSession->get();
$fooSession->put('bar');

// Is equivalent to doing:

$store->get('foo');
$store->put('foo', 'bar');

Notes

Since the contract Illuminate\Contracts\Session does not implement methods like push or remember, I decided to use the Illuminate\Session\Store, which does implement those methods. Feel free to use the Contract instead.

<?php
namespace App\Support;
use Illuminate\Session\Store;
use RuntimeException;
/**
* Helper class that transforms all sessions into a specific key.
*
* @method bool exists()
* @method bool has()
* @method bool missing()
* @method mixed get(mixed $default = null)
* @method mixed pull(mixed $default = null)
* @method void put(mixed $value = null)
* @method void push(mixed $value)
* @method mixed remove()
* @method void forget()
* @method mixed remember(Closure|callable $callback)
*
* @example
* $fooSession = new KeyedSession($store, 'foo');
* $fooSession->get();
* $fooSession->put('bar');
*
* is equivalent to doing:
*
* $store->get('foo');
* $store->put('foo', 'bar');
*/
class KeyedSession
{
/**
* List of methods that the KeyedSession supports. These are Store
* methods, and they all require the key to be the first parameter.
*
* @var array|array<string>
*/
private array $supportedMethods = [
'exists',
'has',
'missing',
'get',
'pull',
'push',
'put',
'remove',
'forget',
'remember',
];
public function __construct(
private Store $store,
private string $key,
) {}
public function __call(string $method, array $arguments): mixed
{
if (!in_array($method, $this->supportedMethods)) {
throw new RuntimeException("The method [{$method}] is not supported by the KeyedSession");
}
return $this->store->{$method}($this->key, ...$arguments);
}
}
<?php
namespace Tests\Support;
use App\Support\KeyedSession;
use Illuminate\Foundation\Testing\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Session\Store;
class KeyedSessionTest extends TestCase
{
use WithFaker;
public function testShouldWorkWithSimpleGetSetOperations(): void
{
$this->startSession();
// Arrange
$key = $this->faker->bothify('test_session_###???');
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$this->assertFalse($session->has(), 'Has should be empty initially');
$session->put('foo');
$this->assertTrue($session->has(), 'Has should not be empty after putting');
$this->assertEquals('foo', $session->get(), 'Get should match the previous inserted value');
$this->assertEquals('foo', $this->app['session']->get($key), 'The session must have the value assigned to the key');
$this->assertEquals('foo', $session->pull('bar'), 'Pulling the value returns it');
$this->assertFalse($session->has(), 'Pulling the value removed it');
}
public function testShouldWorkWithExistingData(): void
{
// Arrange
$key = $this->faker->bothify('test_session_###???');
$this->withSession([
$key => 'foobar',
]);
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$this->assertTrue($session->has(), 'Has should not be empty');
$this->assertEquals('foobar', $session->get(), 'Get should match the existing value');
$this->assertEquals('foobar', $this->app['session']->get($key), 'The session must have the value assigned to the key');
}
/**
* @dataProvider keyedSessionMethodsDataProvider
*/
public function testVariousMethods(mixed $initialValue, callable $method, mixed $expected): void
{
// Arrange
$this->startSession();
$key = $this->faker->bothify('test_session_###???');
if (!is_null($initialValue)) {
$this->withSession([
$key => $initialValue,
]);
}
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act
$value = $method($session);
// Assert
$this->assertEquals($expected, $value);
}
public function keyedSessionMethodsDataProvider(): array
{
return [
'exists on existing key' => [
'initialValue' => '',
'method' => fn (KeyedSession $session) => $session->exists(),
'expected' => true,
],
'exists on new key' => [
'initialValue' => null,
'method' => fn (KeyedSession $session) => $session->exists(),
'expected' => false,
],
'has on empty key' => [
'initialValue' => '',
'method' => fn (KeyedSession $session) => $session->has(),
'expected' => true,
],
'has on non-existing key' => [
'initialValue' => null,
'method' => fn (KeyedSession $session) => $session->has(),
'expected' => false,
],
'has on set key' => [
'initialValue' => 'foo',
'method' => fn (KeyedSession $session) => $session->has(),
'expected' => true,
],
'missing on existing key' => [
'initialValue' => '',
'method' => fn (KeyedSession $session) => $session->missing(),
'expected' => false,
],
'missing on new key' => [
'initialValue' => null,
'method' => fn (KeyedSession $session) => $session->missing(),
'expected' => true,
],
'get' => [
'initialValue' => ['foo' => 'bar'],
'method' => fn (KeyedSession $session) => $session->get(),
'expected' => ['foo' => 'bar'],
],
'remember on new value' => [
'initialValue' => null,
'method' => fn (KeyedSession $session) => $session->remember(fn () => 'foo'),
'expected' => 'foo',
],
'remember on existing value' => [
'initialValue' => 'bar',
'method' => fn (KeyedSession $session) => $session->remember(fn () => 'foo'),
'expected' => 'bar',
],
];
}
public function testShouldPullItems(): void
{
// Arrange
$key = $this->faker->bothify('test_session_###???');
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$session->put('foo');
$this->assertEquals('foo', $session->pull());
$this->assertEmpty($session->get());
$session->put(['foo', 'bar']);
$this->assertEquals(['foo', 'bar'], $session->pull());
$this->assertEmpty($session->get());
}
public function testShouldPushItems(): void
{
// Arrange
$key = $this->faker->bothify('test_session_###???');
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$session->put(['foo', 'bar']);
$session->push('foobar');
$this->assertEquals(['foo', 'bar', 'foobar'], $session->get());
}
public function testShouldRemoveItems(): void
{
// Arrange
$key = $this->faker->bothify('test_session_###???');
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$session->put('foo');
$this->assertEquals('foo', $session->get());
$this->assertEquals('foo', $session->remove());
$this->assertNotEquals('foo', $session->get());
$this->assertFalse($session->has());
}
public function testShouldForgetItems(): void
{
// Arrange
$key = $this->faker->bothify('test_session_###???');
$session = new KeyedSession($this->app->make(Store::class), $key);
// Act + Assert
$session->put('foo');
$this->assertEquals('foo', $session->get());
$this->assertNull($session->forget());
$this->assertNotEquals('foo', $session->get());
$this->assertFalse($session->has());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment