I'm creating an authentication with the Slim 3 framework and PHP-DI 5 (PHP-DI/PHP-DI).
The project will be the base/starter template for my new projects. I want it to be easy to extend, also to larger projects later on. So I find it important that it's a really good structured project. At the moment, I'm trying to learn all the best practices of structuring projects, so all tips are welcome!
My questions are:
- Would you structure the User-model, Account-controller and Auth-class differently? Or do I have a good base here?
- Am I using dependency injection as how I should use it? Is there a better way?
- Do the container items only get instantiated when it's called or is this a performance hit? Should I implement this differently or is this correct?
- Would you prefer to place the
isActivated()
andverifyPassword()
methods from the User-model in the Auth-class? Or is it at the right place there?
My container definitions
<?php
use Slimproject\Models\User;
use Slimproject\Auth\Auth;
return [
User::class => function (ContainerInterface $c) {
return new User;
},
Auth::class => function (ContainerInterface $c) {
return new Auth($c->get(User::class));
}
];
The router
<?php
use Slimproject\Middleware\GuestMiddleware;
use Slimproject\Middleware\AuthMiddleware;
$app->get('/', ['Slimproject\Controllers\HomeController', 'index'])->setName('home');
$app->group('/account', function() {
$this->get('/login', ['Slimproject\Controllers\AccountController', 'login'])->setName('account.login');
$this->post('/login', ['Slimproject\Controllers\AccountController', 'doLogin'])->setName('account.login');
// etc..
})->add(new GuestMiddleware($container));
$app->group('/myaccount', function() {
$this->get('/', ['Slimproject\Controllers\MyAccountController', 'index'])->setName('myaccount.index');
// etc..
})->add(new AuthMiddleware($container));
The Account-controller
<?php
namespace Slimproject\Controllers;
use Slim\Views\Twig;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slimproject\Auth\Auth;
use Slimproject\Models\User;
class AccountController
{
public function doLogin(Request $request, Response $response, Twig $view, Auth $auth, User $user)
{
$user = $user->get($request->getParam('username'));
if (!$user->isActivated() {
$this->flash->addMessage('error', 'Your account is not yet activated.');
return $view->render($response, 'account/login.twig');
}
if (!$user->verifyPassword($request->getParam('password')) {
$this->flash->addMessage('error', 'Login incorrect.');
return $view->render($response, 'account/login.twig');
}
$auth->login($user);
return $view->render($response, 'myaccount/index.twig');
}
}
The User-model
<?php
namespace Slimproject\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $fillable = [
'name',
'username',
'email',
'password',
'level',
'activated'
];
public function get($username)
{
return $this->where('email', $username)->orWhere('username', $username)->first() ?: false;
}
public function isActivated()
{
return (bool) $this->activated;
}
public function verifyPassword($password)
{
return password_verify($password, $this->password);
}
}
The Auth-middleware
<?php
namespace Slimproject\Middleware;
class AuthMiddleware extends Middleware
{
public function __invoke($request, $response, $next)
{
if (!$this->auth->isLoggedIn()) {
$this->flash->addMessage('error', 'You need to be logged in to visit that page.');
return $response->withRedirect($this->router->pathFor('account.login'));
}
$response = $next($request, $response);
return $response;
}
}
The Auth-class
<?php
namespace Slimproject\Auth;
use Slimproject\Models\User;
class Auth
{
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function user()
{
return $this->user->where('id', $_SESSION['user']['id'])->first();
}
public function isLoggedin()
{
return isset($_SESSION['user']);
}
public function hasAccess(array $levels)
{
return in_array($_SESSION['user']['level'], $levels);
}
public function login(User $user)
{
$_SESSION['user'] = [
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
'level' => $user->level,
'loggedin_at' => time(),
];
}
public function logout()
{
unset($_SESSION['user']);
}
}