I have been working on a Yii2 project using the advanced template for the past three years. Unfortunately, it has turned into a large, messy monolith without a proper object-oriented programming (OOP) structure, and it does not adhere to the SOLID principles. Additionally, test cases have been missing up to this point.
Despite this, the project continues to grow daily, with new features and products introduced every quarter.
I’m refactoring my code from the beginning and writing test cases. What design pattern or code repository structure should I use to ensure stability, testing, and avoid technical debt?
Please share your suggestions regarding the solution I researched today.
config/web.php
'container' => [
'definitions' => [
'app\services\AuthService' => [
'class' => 'app\services\AuthService',
'constructorArgs' => [
'userRepository' => function () {
return new app\repositories\UserRepository();
},
'authUserSettingRepository' => function () {
return new app\repositories\AuthUserSettingRepository();
},
],
],
'app\services\TwoFactorAuthService' => [
'class' => 'app\services\TwoFactorAuthService',
'constructorArgs' => [
'userRepository' => function () {
return new app\repositories\UserRepository();
},
'otpRepository' => function () {
return new app\repositories\OtpRepository();
},
'emailSender' => function () {
return new app\components\EmailSender();
},
],
],
],
],
repositories/AuthUserSettingRepository.php
<?php
namespace app\repositories;
use app\models\AuthUserSetting;
class AuthUserSettingRepository
{
/**
* Find auth settings by user ID
*
* @param int $userId
* @return AuthUserSetting|null
*/
public function findByUserId($userId)
{
return AuthUserSetting::findOne(['user_id' => $userId]);
}
}
repositories/UserRepository.php
<?php
namespace app\repositories;
use app\models\User;
class UserRepository
{
/**
* Find a user by email
*
* @param string $email
* @return User|null
*/
public function findByEmail($email)
{
return User::findOne(['email' => $email]);
}
}
controllers/AuthController
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\LoginForm;
use app\services\AuthService;
use app\services\TwoFactorAuthService;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
class AuthController extends Controller
{
private $authService;
private $twoFactorAuthService;
public function __construct(
$id,
$module,
AuthService $authService,
TwoFactorAuthService $twoFactorAuthService,
$config = []
) {
$this->authService = $authService;
$this->twoFactorAuthService = $twoFactorAuthService;
parent::__construct($id, $module, $config);
}
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if (Yii::$app->request->isPost && $model->load(Yii::$app->request->post()) && $model->validate()) {
$loginResponse = $this->authService->authenticate($model->email, $model->password);
if ($loginResponse->isSuccess()) {
if ($loginResponse->requiresTwoFactor()) {
// Store email in session for the OTP verification
Yii::$app->session->set('email_for_otp', $model->email);
// Send OTP
$this->twoFactorAuthService->sendOtp($model->email);
// Redirect to OTP verification page
return $this->redirect(['auth/verify-otp']);
}
// Regular login successful
return $this->goBack();
} else {
// Set flash message
Yii::$app->session->setFlash('error', $loginResponse->getMessage());
}
}
return $this->render('login', [
'model' => $model,
]);
}
Thanks