Skip to content

Commit

Permalink
[9.x] Fix ViewErrorBag for JSON session serialization (#42090)
Browse files Browse the repository at this point in the history
* Fix ViewErrorBag for JSON session serialization

* Fix style

* formatting

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
Krisell and taylorotwell authored Apr 22, 2022
1 parent 2fb344e commit 216a808
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 3 deletions.
51 changes: 51 additions & 0 deletions src/Illuminate/Session/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use Closure;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Arr;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Str;
use Illuminate\Support\ViewErrorBag;
use SessionHandlerInterface;
use stdClass;

Expand Down Expand Up @@ -94,6 +96,8 @@ public function start()
protected function loadSession()
{
$this->attributes = array_merge($this->attributes, $this->readFromHandler());

$this->marshalErrorBag();
}

/**
Expand Down Expand Up @@ -129,6 +133,28 @@ protected function prepareForUnserialize($data)
return $data;
}

/**
* Marshal the ViewErrorBag when using JSON serialization for sessions.
*
* @return void
*/
protected function marshalErrorBag()
{
if ($this->serialization !== 'json' || $this->missing('errors')) {
return;
}

$errorBag = new ViewErrorBag;

foreach ($this->get('errors') as $key => $value) {
$messageBag = new MessageBag($value['messages']);

$errorBag->put($key, $messageBag->setFormat($value['format']));
}

$this->put('errors', $errorBag);
}

/**
* Save the session data to storage.
*
Expand All @@ -138,13 +164,38 @@ public function save()
{
$this->ageFlashData();

$this->prepareErrorBagForSerialization();

$this->handler->write($this->getId(), $this->prepareForStorage(
$this->serialization === 'json' ? json_encode($this->attributes) : serialize($this->attributes)
));

$this->started = false;
}

/**
* Prepare the ViewErrorBag instance for JSON serialization.
*
* @return void
*/
protected function prepareErrorBagForSerialization()
{
if ($this->serialization !== 'json' || $this->missing('errors')) {
return;
}

$errors = [];

foreach ($this->attributes['errors']->getBags() as $key => $value) {
$errors[$key] = [
'format' => $value->getFormat(),
'messages' => $value->getMessages()
];
}

$this->attributes['errors'] = $errors;
}

/**
* Prepare the serialized session data for storage.
*
Expand Down
79 changes: 76 additions & 3 deletions tests/Session/SessionStoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use Illuminate\Cookie\CookieJar;
use Illuminate\Session\CookieSessionHandler;
use Illuminate\Session\Store;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Str;
use Illuminate\Support\ViewErrorBag;
use Mockery as m;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
Expand Down Expand Up @@ -493,19 +495,90 @@ public function testRememberMethodReturnsPreviousValueIfItAlreadySets()
$this->assertSame('foo', $result);
}

public function getSession()
public function testValidationErrorsCanBeSerializedAsJson()
{
$session = $this->getSession('json');
$session->getHandler()->shouldReceive('read')->once()->andReturn(serialize([]));
$session->start();
$session->put('errors', $errorBag = new ViewErrorBag);
$messageBag = new MessageBag([
'first_name' => [
'Your first name is required',
'Your first name must be at least 1 character',
],
]);
$messageBag->setFormat('<p>:message</p>');
$errorBag->put('default', $messageBag);

$session->getHandler()->shouldReceive('write')->once()->with(
$this->getSessionId(),
json_encode([
'_token' => $session->token(),
'errors' => [
'default' => [
'format' => '<p>:message</p>',
'messages' => [
'first_name' => [
'Your first name is required',
'Your first name must be at least 1 character',
],
],
],
],
'_flash' => [
'old' => [],
'new' => [],
],
])
);
$session->save();

$this->assertFalse($session->isStarted());
}

public function testValidationErrorsCanBeReadAsJson()
{
$session = $this->getSession('json');
$session->getHandler()->shouldReceive('read')->once()->with($this->getSessionId())->andReturn(json_encode([
'errors' => [
'default' => [
'format' => '<p>:message</p>',
'messages' => [
'first_name' => [
'Your first name is required',
'Your first name must be at least 1 character',
],
],
],
],
]));
$session->start();

$errors = $session->get('errors');

$this->assertInstanceOf(ViewErrorBag::class, $errors);
$this->assertInstanceOf(MessageBag::class, $errors->getBags()['default']);
$this->assertEquals('<p>:message</p>', $errors->getBags()['default']->getFormat());
$this->assertEquals(['first_name' => [
'Your first name is required',
'Your first name must be at least 1 character',
]], $errors->getBags()['default']->getMessages());
}

public function getSession($serialization = 'php')
{
$reflection = new ReflectionClass(Store::class);

return $reflection->newInstanceArgs($this->getMocks());
return $reflection->newInstanceArgs($this->getMocks($serialization));
}

public function getMocks()
public function getMocks($serialization = 'json')
{
return [
$this->getSessionName(),
m::mock(SessionHandlerInterface::class),
$this->getSessionId(),
$serialization,
];
}

Expand Down

0 comments on commit 216a808

Please sign in to comment.