Added HasUserNotifications trait to show messages to the user

This commit is contained in:
Igor Scheller 2020-03-01 18:21:16 +01:00 committed by msquare
parent e42284deca
commit 72f4839130
10 changed files with 154 additions and 30 deletions

View File

@ -0,0 +1,19 @@
{% import 'macros/base.twig' as m %}
{{ msg() }}
{% for message in errors|default([]) %}
{{ m.alert(__(message), 'danger') }}
{% endfor %}
{% for message in warnings|default([]) %}
{{ m.alert(__(message), 'warning') }}
{% endfor %}
{% for message in information|default([]) %}
{{ m.alert(__(message), 'info') }}
{% endfor %}
{% for message in messages|default([]) %}
{{ m.alert(__(message), 'success') }}
{% endfor %}

View File

@ -32,10 +32,7 @@
<div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4"> <div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<div class="panel panel-primary first"> <div class="panel panel-primary first">
<div class="panel-body"> <div class="panel-body">
{{ msg() }} {% include 'layouts/parts/messages.twig' %}
{% for message in errors|default([]) %}
{{ m.alert(__(message), 'danger') }}
{% endfor %}
<form action="" enctype="multipart/form-data" method="post"> <form action="" enctype="multipart/form-data" method="post">
{{ csrf() }} {{ csrf() }}

View File

@ -8,9 +8,7 @@
<div class="container"> <div class="container">
<h1>{{ __('Password recovery') }}</h1> <h1>{{ __('Password recovery') }}</h1>
{% for message in errors|default([]) %} {% include 'layouts/parts/messages.twig' %}
{{ m.alert(__(message), 'danger') }}
{% endfor %}
<div class="row"> <div class="row">
{% block row_content %} {% block row_content %}

View File

@ -9,12 +9,12 @@ use Engelsystem\Http\Request;
use Engelsystem\Http\Response; use Engelsystem\Http\Response;
use Engelsystem\Http\UrlGeneratorInterface; use Engelsystem\Http\UrlGeneratorInterface;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
class AuthController extends BaseController class AuthController extends BaseController
{ {
use HasUserNotifications;
/** @var Response */ /** @var Response */
protected $response; protected $response;
@ -70,12 +70,9 @@ class AuthController extends BaseController
*/ */
protected function showLogin(): Response protected function showLogin(): Response
{ {
$errors = Collection::make(Arr::flatten($this->session->get('errors', [])));
$this->session->remove('errors');
return $this->response->withView( return $this->response->withView(
'pages/login', 'pages/login',
['errors' => $errors] $this->getNotifications()
); );
} }
@ -95,7 +92,7 @@ class AuthController extends BaseController
$user = $this->auth->authenticate($data['login'], $data['password']); $user = $this->auth->authenticate($data['login'], $data['password']);
if (!$user instanceof User) { if (!$user instanceof User) {
$this->session->set('errors', array_merge($this->session->get('errors', []), ['auth.not-found'])); $this->addNotification('auth.not-found', 'errors');
return $this->showLogin(); return $this->showLogin();
} }

View File

@ -0,0 +1,35 @@
<?php
namespace Engelsystem\Controllers;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
trait HasUserNotifications
{
/**
* @param string|array $value
* @param string $type
*/
protected function addNotification($value, $type = 'messages')
{
session()->set(
$type,
array_merge(session()->get($type, []), [$value])
);
}
/**
* @return array
*/
protected function getNotifications(): array
{
$return = [];
foreach (['errors', 'warnings', 'information', 'messages'] as $type) {
$return[$type] = Collection::make(Arr::flatten(session()->get($type, [])));
session()->remove($type);
}
return $return;
}
}

View File

@ -8,13 +8,13 @@ use Engelsystem\Http\Response;
use Engelsystem\Mail\EngelsystemMailer; use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface;
class PasswordResetController extends BaseController class PasswordResetController extends BaseController
{ {
use HasUserNotifications;
/** @var LoggerInterface */ /** @var LoggerInterface */
protected $log; protected $log;
@ -120,10 +120,7 @@ class PasswordResetController extends BaseController
]); ]);
if ($data['password'] !== $data['password_confirmation']) { if ($data['password'] !== $data['password_confirmation']) {
$this->session->set( $this->addNotification('validation.password.confirmed', 'errors');
'errors',
array_merge($this->session->get('errors', []), ['validation.password.confirmed'])
);
return $this->showView('pages/password/reset-form'); return $this->showView('pages/password/reset-form');
} }
@ -141,12 +138,9 @@ class PasswordResetController extends BaseController
*/ */
protected function showView($view = 'pages/password/reset', $data = []): Response protected function showView($view = 'pages/password/reset', $data = []): Response
{ {
$errors = Collection::make(Arr::flatten($this->session->get('errors', [])));
$this->session->remove('errors');
return $this->response->withView( return $this->response->withView(
$view, $view,
array_merge_recursive(['errors' => $errors], $data) array_merge_recursive($this->getNotifications(), $data)
); );
} }

View File

@ -2,6 +2,7 @@
namespace Engelsystem\Test\Unit\Controllers; namespace Engelsystem\Test\Unit\Controllers;
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Controllers\AuthController; use Engelsystem\Controllers\AuthController;
use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Authenticator;
@ -21,6 +22,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class AuthControllerTest extends TestCase class AuthControllerTest extends TestCase
{ {
use ArraySubsetAsserts;
use HasDatabase; use HasDatabase;
/** /**
@ -38,10 +40,11 @@ class AuthControllerTest extends TestCase
/** @var Authenticator|MockObject $auth */ /** @var Authenticator|MockObject $auth */
list(, $session, $url, $config, $auth) = $this->getMocks(); list(, $session, $url, $config, $auth) = $this->getMocks();
$session->expects($this->once()) $session->expects($this->atLeastOnce())
->method('get') ->method('get')
->with('errors', []) ->willReturnCallback(function ($type) {
->willReturn(['foo' => 'bar']); return $type == 'errors' ? ['foo' => 'bar'] : [];
});
$response->expects($this->once()) $response->expects($this->once())
->method('withView') ->method('withView')
->with('pages/login') ->with('pages/login')
@ -69,6 +72,7 @@ class AuthControllerTest extends TestCase
/** @var Validator|MockObject $validator */ /** @var Validator|MockObject $validator */
$validator = new Validator(); $validator = new Validator();
$session->set('errors', [['bar' => 'some.bar.error']]); $session->set('errors', [['bar' => 'some.bar.error']]);
$this->app->instance('session', $session);
$user = new User([ $user = new User([
'name' => 'foo', 'name' => 'foo',
@ -92,8 +96,11 @@ class AuthControllerTest extends TestCase
$response->expects($this->once()) $response->expects($this->once())
->method('withView') ->method('withView')
->with('pages/login', ['errors' => collect(['some.bar.error', 'auth.not-found'])]) ->willReturnCallback(function ($view, $data = []) use ($response) {
->willReturn($response); $this->assertEquals('pages/login', $view);
$this->assertArraySubset(['errors' => collect(['some.bar.error', 'auth.not-found'])], $data);
return $response;
});
$response->expects($this->once()) $response->expects($this->once())
->method('redirectTo') ->method('redirectTo')
->with('news') ->with('news')
@ -168,6 +175,8 @@ class AuthControllerTest extends TestCase
/** @var Authenticator|MockObject $auth */ /** @var Authenticator|MockObject $auth */
$auth = $this->createMock(Authenticator::class); $auth = $this->createMock(Authenticator::class);
$this->app->instance('session', $session);
return [$response, $session, $url, $config, $auth]; return [$response, $session, $url, $config, $auth];
} }
} }

View File

@ -0,0 +1,35 @@
<?php
namespace Engelsystem\Test\Unit\Controllers;
use Engelsystem\Test\Unit\Controllers\Stub\HasUserNotificationsImplementation;
use Engelsystem\Test\Unit\TestCase;
use Illuminate\Support\Collection;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class HasUserNotificationsTest extends TestCase
{
/**
* @covers \Engelsystem\Controllers\HasUserNotifications::getNotifications
* @covers \Engelsystem\Controllers\HasUserNotifications::addNotification
*/
public function testNotifications()
{
$session = new Session(new MockArraySessionStorage());
$this->app->instance('session', $session);
$notify = new HasUserNotificationsImplementation();
$notify->add('Foo', 'errors');
$notify->add('Bar', 'warnings');
$notify->add(['Baz', 'Lorem'], 'information');
$notify->add(['Hm', ['Uff', 'sum']], 'messages');
$this->assertEquals([
'errors' => new Collection(['Foo']),
'warnings' => new Collection(['Bar']),
'information' => new Collection(['Baz', 'Lorem']),
'messages' => new Collection(['Hm', 'Uff', 'sum']),
], $notify->get());
}
}

View File

@ -2,6 +2,7 @@
namespace Engelsystem\Test\Unit\Controllers; namespace Engelsystem\Test\Unit\Controllers;
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Controllers\PasswordResetController; use Engelsystem\Controllers\PasswordResetController;
use Engelsystem\Helpers\Authenticator; use Engelsystem\Helpers\Authenticator;
@ -23,6 +24,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class PasswordResetControllerTest extends TestCase class PasswordResetControllerTest extends TestCase
{ {
use ArraySubsetAsserts;
use HasDatabase; use HasDatabase;
/** @var array */ /** @var array */
@ -199,6 +201,8 @@ class PasswordResetControllerTest extends TestCase
$renderer = $this->createMock(Renderer::class); $renderer = $this->createMock(Renderer::class);
$response->setRenderer($renderer); $response->setRenderer($renderer);
$this->app->instance('session', $session);
return $this->args = [ return $this->args = [
'response' => $response, 'response' => $response,
'session' => $session, 'session' => $session,
@ -230,7 +234,16 @@ class PasswordResetControllerTest extends TestCase
$args[] = $data; $args[] = $data;
} }
$this->setExpects($renderer, 'render', $args, 'Foo'); $renderer->expects($this->atLeastOnce())
->method('render')
->willReturnCallback(function ($template, $data = []) use ($args) {
$this->assertEquals($args[0], $template);
if (isset($args[1])) {
$this->assertArraySubset($args[1], $data);
}
return 'Foo';
});
} }
return $controller; return $controller;

View File

@ -0,0 +1,27 @@
<?php
namespace Engelsystem\Test\Unit\Controllers\Stub;
use Engelsystem\Controllers\HasUserNotifications;
class HasUserNotificationsImplementation
{
use HasUserNotifications;
/**
* @param string|array $value
* @param string $type
*/
public function add($value, $type = 'messages')
{
$this->addNotification($value, $type);
}
/**
* @return array
*/
public function get(): array
{
return $this->getNotifications();
}
}