diff --git a/CHANGELOG.md b/CHANGELOG.md index dc22905e9..520d5b8a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ The format is based on Keep a Changelog. - Added `Response::back()` and the `back()` helper, which redirect to the referrer and fall back to `base_url()` when the request has none, or when the referrer is not on the application origin (same scheme, host and port), which is compared with `base_url()`: behind a proxy that terminates TLS, or where the `Host` header is not pinned, set `app.base_url`. They take the old input to keep for the next request, read with `old()` as after `redirectWith()`, and an optional status code (`back($data, $code)`) (#540) ### Changed +- **BREAKING:** Converted the constant classes `ReservedKeys`, `AuthKeys`, `Pagination` and the database `Type`, `Key` and `Relation` to PHP enums, and moved the table actions of `Table` into the new `TableAction` enum (#612). An enum case is not its value, so code that needs the text or number reads `->value`, and text that comes from outside is turned into a case with `::from()` or `::tryFrom()`: + - `ReservedKeys`, `AuthKeys`, `Pagination`, `Type`, `Key` and `Relation` are string-backed enums with the same case names and values, so `Relation::BELONGS_TO`, `Type::INT` and `Key::INDEX` are written as before. An application that uses a case as an array key, compares it with text, or passes it to a `string` parameter must add `->value`, for example `$this->keyFields[AuthKeys::USERNAME->value]`. Code that reads the constants with `ReflectionClass::getConstants()` uses `AuthKeys::cases()`. + - `Table::addIndex()` takes `Key` and `Table::setAction()` takes `TableAction`, and no longer accept text: `addIndex('name', 'unique')` throws a `TypeError`; write `Key::UNIQUE`. A subclass of `Table` that overrides these methods must declare the enum types. + - `Table::addColumn()`, `Table::modifyColumn()` and `Column::__construct()` accept a `Type` case or the type as text. A text type is used as before, in upper case, so any SQL type name still works (`'integer'`, `'bigint'`, `'mediumtext'`), including those that `Type` has no case for. A subclass that overrides them must declare `Type|string`. + - `Table::CREATE`, `Table::ALTER`, `Table::DROP` and `Table::RENAME` are removed: use `TableAction::CREATE`, `TableAction::ALTER`, `TableAction::DROP` and `TableAction::RENAME`. + - The `type` of a model relation must be a `Relation` case. The text `'hasOne'` is rejected with the message "Relation type `hasOne` is not supported"; write `Relation::HAS_ONE`. `ModelException::unsupportedRelationType()` accepts a `Relation` or text. + - `Pagination` keeps the four names (`PAGINATION_CLASS`, `PAGINATION_CLASS_ACTIVE`, `PER_PAGE`, `PAGE`) as a string-backed enum, read with `->value`. The numbers `FIRST_PAGE_NUMBER`, `MINIMUM_PAGE_ITEMS_COUNT` and `EDGE_PADDING` moved to `PaginationLimit`, an enum that holds them as constants, read without `->value`. + - The adapter types, `StatusCode`, `ContentType`, `Env`, `ExitCode` and the `ExceptionMessages` classes stay constant classes. - The `DemoWeb` and `Toolkit` module templates redirect back with `back()` instead of `redirect(get_referrer() ?? base_url())` and `redirectWith(get_referrer() ?? base_url(), $data)`, so generated code gets the same-origin check. Modules generated before keep their code (#541) - Twig is a required dependency again: `twig/twig` `^3.27` is in `require` and no longer only suggested, which reverses the 3.0.3 change that made it optional while the framework supported PHP 8.0 (#532). Twig `^3.27` needs PHP 8.1, the minimum since 3.1.0. An application that pins an older Twig must allow `^3.27`. Removed `RendererException::twigNotInstalled()` and the check in `TwigAdapter` that threw it when Twig was missing, and the Twig tests no longer skip (#626) - The test suite runs on PHPUnit `^10.5` (a development dependency) and `phpunit.xml` uses its schema. It now sets `error_reporting` to `-1`: it was the text `E_ALL & ~E_DEPRECATED`, which PHP reads as `0`, so every PHP warning was hidden in the tests (#622) diff --git a/src/App/Enums/ReservedKeys.php b/src/App/Enums/ReservedKeys.php index 0ad3be297..e8ff32be3 100644 --- a/src/App/Enums/ReservedKeys.php +++ b/src/App/Enums/ReservedKeys.php @@ -11,19 +11,18 @@ namespace Quantum\App\Enums; /** - * Class ReservedKeys + * Enum ReservedKeys * @package Quantum\App */ -final class ReservedKeys +enum ReservedKeys: string { /** * Internal response key for rendered view */ - public const RENDERED_VIEW = '_qt_rendered_view'; + case RENDERED_VIEW = '_qt_rendered_view'; /** * Internal session key for previous request */ - public const PREV_REQUEST = '_qt_prev_request'; - + case PREV_REQUEST = '_qt_prev_request'; } diff --git a/src/Auth/Adapters/JwtAuthAdapter.php b/src/Auth/Adapters/JwtAuthAdapter.php index 434e9ab8d..c0caa4ed0 100644 --- a/src/Auth/Adapters/JwtAuthAdapter.php +++ b/src/Auth/Adapters/JwtAuthAdapter.php @@ -71,18 +71,18 @@ public function signin(string $username, string $password): string|array */ public function signout(): bool { - $refreshToken = request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); + $refreshToken = request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value]); - $user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN], $refreshToken); + $user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN->value], $refreshToken); if ($user) { $this->authService->update( - $this->keyFields[AuthKeys::REFRESH_TOKEN], + $this->keyFields[AuthKeys::REFRESH_TOKEN->value], $refreshToken, - array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => '']) + array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN->value] => '']) ); - request()->deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); + request()->deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value]); request()->deleteHeader('Authorization'); response()->delete('tokens'); @@ -142,8 +142,8 @@ protected function getUpdatedTokens(User $user): array { return [ - $this->keyFields[AuthKeys::REFRESH_TOKEN] => $this->generateToken(), - $this->keyFields[AuthKeys::ACCESS_TOKEN] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose()), + $this->keyFields[AuthKeys::REFRESH_TOKEN->value] => $this->generateToken(), + $this->keyFields[AuthKeys::ACCESS_TOKEN->value] => base64_encode($this->jwt->setData($this->getVisibleFields($user))->compose()), ]; } @@ -157,13 +157,13 @@ protected function setUpdatedTokens(User $user): array $tokens = $this->getUpdatedTokens($user); $this->authService->update( - $this->keyFields[AuthKeys::USERNAME], - $user->getFieldValue($this->keyFields[AuthKeys::USERNAME]), - array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]]) + $this->keyFields[AuthKeys::USERNAME->value], + $user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]), + array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN->value] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN->value]]]) ); - request()->setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]); - request()->setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN]]); + request()->setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN->value]]); + request()->setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN->value]]); response()->set('tokens', $tokens); return $tokens; @@ -175,8 +175,8 @@ protected function setUpdatedTokens(User $user): array protected function checkRefreshToken(): ?User { return $this->authService->get( - $this->keyFields[AuthKeys::REFRESH_TOKEN], - request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]) + $this->keyFields[AuthKeys::REFRESH_TOKEN->value], + request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value]) ); } @@ -203,7 +203,7 @@ private function getUserFromAccessToken(): ?User */ private function getUserFromRefreshToken(): ?User { - if (!request()->hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])) { + if (!request()->hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN->value])) { return null; } diff --git a/src/Auth/Adapters/SessionAuthAdapter.php b/src/Auth/Adapters/SessionAuthAdapter.php index b12be1881..2c7abcc5b 100644 --- a/src/Auth/Adapters/SessionAuthAdapter.php +++ b/src/Auth/Adapters/SessionAuthAdapter.php @@ -111,7 +111,7 @@ public function user(): ?User return (new User())->setData(session()->get(self::AUTH_USER)); } - if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN])) { + if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])) { $user = $this->checkRememberToken(); if ($user) { @@ -171,15 +171,15 @@ public function verifyOtp(int $otp, string $otpToken): bool private function checkRememberToken(): false|\Quantum\Auth\User { $user = $this->authService->get( - $this->keyFields[AuthKeys::REMEMBER_TOKEN], - cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN]) + $this->keyFields[AuthKeys::REMEMBER_TOKEN->value], + cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]) ); if (!$user) { return false; } - if ($this->isTwoFactorEnabled() && !empty($user->getFieldValue($this->keyFields[AuthKeys::OTP_TOKEN]))) { + if ($this->isTwoFactorEnabled() && !empty($user->getFieldValue($this->keyFields[AuthKeys::OTP_TOKEN->value]))) { return false; } @@ -195,15 +195,15 @@ private function setRememberToken(User $user): void $rememberToken = $this->generateToken(); $this->authService->update( - $this->keyFields[AuthKeys::USERNAME], - $user->getFieldValue($this->keyFields[AuthKeys::USERNAME]), - [$this->keyFields[AuthKeys::REMEMBER_TOKEN] => $rememberToken] + $this->keyFields[AuthKeys::USERNAME->value], + $user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]), + [$this->keyFields[AuthKeys::REMEMBER_TOKEN->value] => $rememberToken] ); $rememberLifetime = (int) ($this->config['session']['remember_lifetime'] ?? self::DEFAULT_REMEMBER_LIFETIME); cookie()->set( - $this->keyFields[AuthKeys::REMEMBER_TOKEN], + $this->keyFields[AuthKeys::REMEMBER_TOKEN->value], $rememberToken, $rememberLifetime, '/', @@ -218,21 +218,21 @@ private function setRememberToken(User $user): void */ private function removeRememberToken(): void { - if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN])) { + if (cookie()->has($this->keyFields[AuthKeys::REMEMBER_TOKEN->value])) { $user = $this->authService->get( - $this->keyFields[AuthKeys::REMEMBER_TOKEN], - cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN]) + $this->keyFields[AuthKeys::REMEMBER_TOKEN->value], + cookie()->get($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]) ); if ($user) { $this->authService->update( - $this->keyFields[AuthKeys::REMEMBER_TOKEN], - $user->getFieldValue($this->keyFields[AuthKeys::REMEMBER_TOKEN]), - [$this->keyFields[AuthKeys::REMEMBER_TOKEN] => ''] + $this->keyFields[AuthKeys::REMEMBER_TOKEN->value], + $user->getFieldValue($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]), + [$this->keyFields[AuthKeys::REMEMBER_TOKEN->value] => ''] ); } - cookie()->delete($this->keyFields[AuthKeys::REMEMBER_TOKEN]); + cookie()->delete($this->keyFields[AuthKeys::REMEMBER_TOKEN->value]); } } } diff --git a/src/Auth/Enums/AuthKeys.php b/src/Auth/Enums/AuthKeys.php index 3c6ee165d..e7e9477a1 100644 --- a/src/Auth/Enums/AuthKeys.php +++ b/src/Auth/Enums/AuthKeys.php @@ -11,63 +11,59 @@ namespace Quantum\Auth\Enums; /** - * Class AuthKeys + * Enum AuthKeys * @package Quantum\Auth * @codeCoverageIgnore */ -final class AuthKeys +enum AuthKeys: string { /** * One time password key */ - public const OTP = 'otp'; + case OTP = 'otp'; /** * One time password expiry key */ - public const OTP_EXPIRY = 'otpExpiry'; + case OTP_EXPIRY = 'otpExpiry'; /** * One time password token key */ - public const OTP_TOKEN = 'otpToken'; + case OTP_TOKEN = 'otpToken'; /** * Username key */ - public const USERNAME = 'username'; + case USERNAME = 'username'; /** * Password key */ - public const PASSWORD = 'password'; + case PASSWORD = 'password'; /** * Access token key */ - public const ACCESS_TOKEN = 'accessToken'; + case ACCESS_TOKEN = 'accessToken'; /** * Refresh token key */ - public const REFRESH_TOKEN = 'refreshToken'; + case REFRESH_TOKEN = 'refreshToken'; /** * Activation token key */ - public const ACTIVATION_TOKEN = 'activationToken'; + case ACTIVATION_TOKEN = 'activationToken'; /** * Reset token key */ - public const RESET_TOKEN = 'resetToken'; + case RESET_TOKEN = 'resetToken'; /** * Remember token key */ - public const REMEMBER_TOKEN = 'rememberToken'; - - private function __construct() - { - } + case REMEMBER_TOKEN = 'rememberToken'; } diff --git a/src/Auth/Traits/AuthTrait.php b/src/Auth/Traits/AuthTrait.php index 7355222fe..6b1254eb7 100644 --- a/src/Auth/Traits/AuthTrait.php +++ b/src/Auth/Traits/AuthTrait.php @@ -21,7 +21,6 @@ use Quantum\Hasher\Hasher; use ReflectionException; use Quantum\Auth\User; -use ReflectionClass; use DateInterval; use Exception; use DateTime; @@ -77,8 +76,8 @@ public function signup(array $userData, ?array $customData = null): User { $activationToken = $this->generateToken(); - $userData[$this->keyFields[AuthKeys::PASSWORD]] = $this->hasher->hash($userData[$this->keyFields[AuthKeys::PASSWORD]]); - $userData[$this->keyFields[AuthKeys::ACTIVATION_TOKEN]] = $activationToken; + $userData[$this->keyFields[AuthKeys::PASSWORD->value]] = $this->hasher->hash($userData[$this->keyFields[AuthKeys::PASSWORD->value]]); + $userData[$this->keyFields[AuthKeys::ACTIVATION_TOKEN->value]] = $activationToken; $user = $this->authService->add($userData); @@ -105,9 +104,9 @@ public function signup(array $userData, ?array $customData = null): User public function activate(string $token): void { $this->authService->update( - $this->keyFields[AuthKeys::ACTIVATION_TOKEN], + $this->keyFields[AuthKeys::ACTIVATION_TOKEN->value], $token, - [$this->keyFields[AuthKeys::ACTIVATION_TOKEN] => ''] + [$this->keyFields[AuthKeys::ACTIVATION_TOKEN->value] => ''] ); } @@ -116,7 +115,7 @@ public function activate(string $token): void */ public function forget(string $username): ?string { - $user = $this->authService->get($this->keyFields[AuthKeys::USERNAME], $username); + $user = $this->authService->get($this->keyFields[AuthKeys::USERNAME->value], $username); if (!$user) { return null; @@ -125,9 +124,9 @@ public function forget(string $username): ?string $resetToken = $this->generateToken(); $this->authService->update( - $this->keyFields[AuthKeys::USERNAME], + $this->keyFields[AuthKeys::USERNAME->value], $username, - [$this->keyFields[AuthKeys::RESET_TOKEN] => $resetToken] + [$this->keyFields[AuthKeys::RESET_TOKEN->value] => $resetToken] ); $body = [ @@ -148,7 +147,7 @@ public function forget(string $username): ?string */ public function reset(string $token, string $password): void { - $user = $this->authService->get($this->keyFields[AuthKeys::RESET_TOKEN], $token); + $user = $this->authService->get($this->keyFields[AuthKeys::RESET_TOKEN->value], $token); if ($user) { if (!$this->isActivated($user)) { @@ -156,11 +155,11 @@ public function reset(string $token, string $password): void } $this->authService->update( - $this->keyFields[AuthKeys::RESET_TOKEN], + $this->keyFields[AuthKeys::RESET_TOKEN->value], $token, [ - $this->keyFields[AuthKeys::PASSWORD] => $this->hasher->hash($password), - $this->keyFields[AuthKeys::RESET_TOKEN] => '', + $this->keyFields[AuthKeys::PASSWORD->value] => $this->hasher->hash($password), + $this->keyFields[AuthKeys::RESET_TOKEN->value] => '', ] ); } @@ -173,7 +172,7 @@ public function reset(string $token, string $password): void */ public function resendOtp(string $otpToken): string { - $user = $this->authService->get($this->keyFields[AuthKeys::OTP_TOKEN], $otpToken); + $user = $this->authService->get($this->keyFields[AuthKeys::OTP_TOKEN->value], $otpToken); if (!$user) { throw AuthException::incorrectCredentials(); @@ -189,13 +188,13 @@ public function resendOtp(string $otpToken): string */ protected function getUser(string $username, string $password): User { - $user = $this->authService->get($this->keyFields[AuthKeys::USERNAME], $username); + $user = $this->authService->get($this->keyFields[AuthKeys::USERNAME->value], $username); if (!$user) { throw AuthException::incorrectCredentials(); } - if (!$this->hasher->check($password, $user->getFieldValue($this->keyFields[AuthKeys::PASSWORD]) ?? '')) { + if (!$this->hasher->check($password, $user->getFieldValue($this->keyFields[AuthKeys::PASSWORD->value]) ?? '')) { throw AuthException::incorrectCredentials(); } @@ -214,19 +213,19 @@ protected function twoStepVerification(User $user): string { $otp = random_number($this->otpLength); - $otpToken = $this->generateToken($user->getFieldValue($this->keyFields[AuthKeys::USERNAME])); + $otpToken = $this->generateToken($user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value])); $time = new DateTime(); $time->add(new DateInterval('PT' . ($this->config['otp_expires'] ?? 2) . 'M')); $this->authService->update( - $this->keyFields[AuthKeys::USERNAME], - $user->getFieldValue($this->keyFields[AuthKeys::USERNAME]), + $this->keyFields[AuthKeys::USERNAME->value], + $user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]), [ - $this->keyFields[AuthKeys::OTP] => $otp, - $this->keyFields[AuthKeys::OTP_EXPIRY] => $time->format('Y-m-d H:i'), - $this->keyFields[AuthKeys::OTP_TOKEN] => $otpToken, + $this->keyFields[AuthKeys::OTP->value] => $otp, + $this->keyFields[AuthKeys::OTP_EXPIRY->value] => $time->format('Y-m-d H:i'), + $this->keyFields[AuthKeys::OTP_TOKEN->value] => $otpToken, ] ); @@ -250,25 +249,25 @@ protected function twoStepVerification(User $user): string */ protected function verifyAndUpdateOtp(int $otp, string $otpToken): User { - $user = $this->authService->get($this->keyFields[AuthKeys::OTP_TOKEN], $otpToken); + $user = $this->authService->get($this->keyFields[AuthKeys::OTP_TOKEN->value], $otpToken); - if (!$user || $otp != $user->getFieldValue($this->keyFields[AuthKeys::OTP])) { + if (!$user || $otp != $user->getFieldValue($this->keyFields[AuthKeys::OTP->value])) { throw AuthException::incorrectVerificationCode(); } - $otpExpiry = $user->getFieldValue($this->keyFields[AuthKeys::OTP_EXPIRY]); + $otpExpiry = $user->getFieldValue($this->keyFields[AuthKeys::OTP_EXPIRY->value]); if (!$otpExpiry || new DateTime() >= new DateTime($otpExpiry)) { throw AuthException::verificationCodeExpired(); } $this->authService->update( - $this->keyFields[AuthKeys::USERNAME], - $user->getFieldValue($this->keyFields[AuthKeys::USERNAME]), + $this->keyFields[AuthKeys::USERNAME->value], + $user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]), [ - $this->keyFields[AuthKeys::OTP] => null, - $this->keyFields[AuthKeys::OTP_EXPIRY] => null, - $this->keyFields[AuthKeys::OTP_TOKEN] => null, + $this->keyFields[AuthKeys::OTP->value] => null, + $this->keyFields[AuthKeys::OTP_EXPIRY->value] => null, + $this->keyFields[AuthKeys::OTP_TOKEN->value] => null, ] ); @@ -299,7 +298,7 @@ protected function getVisibleFields(User $user): array */ protected function isActivated(User $user): bool { - return in_array($user->getFieldValue($this->keyFields[AuthKeys::ACTIVATION_TOKEN]), [null, '', '0'], true); + return in_array($user->getFieldValue($this->keyFields[AuthKeys::ACTIVATION_TOKEN->value]), [null, '', '0'], true); } /** @@ -322,7 +321,7 @@ protected function sendMail(User $user, array $body): void $appName = config()->get('app.name') ?: ''; $this->mailer->setFrom($appEmail, $appName) - ->setAddress($user->getFieldValue($this->keyFields[AuthKeys::USERNAME]) ?? '', $fullName) + ->setAddress($user->getFieldValue($this->keyFields[AuthKeys::USERNAME->value]) ?? '', $fullName) ->setBody($body) ->send(); } @@ -334,14 +333,12 @@ protected function sendMail(User $user, array $body): void */ protected function verifySchema(array $schema): void { - $constants = (new ReflectionClass(AuthKeys::class))->getConstants(); - - foreach ($constants as $constant) { - if (!isset($schema[$constant]) || !isset($schema[$constant]['name'])) { + foreach (AuthKeys::cases() as $authKey) { + if (!isset($schema[$authKey->value]) || !isset($schema[$authKey->value]['name'])) { throw AuthException::incorrectUserSchema(); } - $this->keyFields[$constant] = $schema[$constant]['name']; + $this->keyFields[$authKey->value] = $schema[$authKey->value]['name']; } foreach ($schema as $field) { diff --git a/src/Database/Enums/Key.php b/src/Database/Enums/Key.php index 9957155e3..06bcbc401 100644 --- a/src/Database/Enums/Key.php +++ b/src/Database/Enums/Key.php @@ -11,38 +11,34 @@ namespace Quantum\Database\Enums; /** - * Class Key + * Enum Key * @package Quantum\Database * @codeCoverageIgnore */ -final class Key +enum Key: string { /** * Primary key definition */ - public const PRIMARY = 'primary'; + case PRIMARY = 'primary'; /** * Index key definition */ - public const INDEX = 'index'; + case INDEX = 'index'; /** * Unique key definition */ - public const UNIQUE = 'unique'; + case UNIQUE = 'unique'; /** * Full-text key definition */ - public const FULLTEXT = 'fulltext'; + case FULLTEXT = 'fulltext'; /** * Spatial key definition */ - public const SPATIAL = 'spatial'; - - private function __construct() - { - } + case SPATIAL = 'spatial'; } diff --git a/src/Database/Enums/Relation.php b/src/Database/Enums/Relation.php index 93d877e9f..292a20648 100644 --- a/src/Database/Enums/Relation.php +++ b/src/Database/Enums/Relation.php @@ -11,18 +11,14 @@ namespace Quantum\Database\Enums; /** - * Class Relation + * Enum Relation * @package Quantum\Database * @codeCoverageIgnore */ -final class Relation +enum Relation: string { - public const HAS_ONE = 'hasOne'; - public const HAS_MANY = 'hasMany'; - public const BELONGS_TO = 'belongsTo'; - public const BELONGS_TO_MANY = 'belongsToMany'; - - private function __construct() - { - } + case HAS_ONE = 'hasOne'; + case HAS_MANY = 'hasMany'; + case BELONGS_TO = 'belongsTo'; + case BELONGS_TO_MANY = 'belongsToMany'; } diff --git a/src/Database/Enums/TableAction.php b/src/Database/Enums/TableAction.php new file mode 100644 index 000000000..acd40bcfd --- /dev/null +++ b/src/Database/Enums/TableAction.php @@ -0,0 +1,39 @@ +createInstance($name)->setAction(Table::CREATE); + return $this->createInstance($name)->setAction(TableAction::CREATE); } /** @@ -44,7 +45,7 @@ public function get(string $name): Table throw DatabaseException::tableDoesNotExists($name); } - return $this->createInstance($name)->setAction(Table::ALTER); + return $this->createInstance($name)->setAction(TableAction::ALTER); } /** @@ -57,7 +58,7 @@ public function rename(string $oldName, string $newName): bool throw DatabaseException::tableDoesNotExists($oldName); } - $this->createInstance($oldName)->setAction(Table::RENAME, ['newName' => $newName]); + $this->createInstance($oldName)->setAction(TableAction::RENAME, ['newName' => $newName]); return true; } @@ -71,7 +72,7 @@ public function drop(string $name): bool throw DatabaseException::tableDoesNotExists($name); } - $this->createInstance($name)->setAction(Table::DROP); + $this->createInstance($name)->setAction(TableAction::DROP); return true; } diff --git a/src/Database/Schemas/Column.php b/src/Database/Schemas/Column.php index e37585cdb..2d5895a68 100644 --- a/src/Database/Schemas/Column.php +++ b/src/Database/Schemas/Column.php @@ -10,6 +10,7 @@ namespace Quantum\Database\Schemas; +use Quantum\Database\Enums\Type; use Quantum\Database\Enums\Key; /** @@ -151,12 +152,12 @@ class Column * Column constructor. * @param mixed $constraint */ - public function __construct(string $name, ?string $type = null, $constraint = null) + public function __construct(string $name, Type|string|null $type = null, $constraint = null) { $this->name = $name; if ($type) { - $this->type = strtoupper($type); + $this->type = strtoupper($type instanceof Type ? $type->value : $type); $this->constraint = $constraint; } } @@ -199,7 +200,7 @@ public function autoIncrement(): void */ public function primary(): void { - $this->indexKey = Key::PRIMARY; + $this->indexKey = Key::PRIMARY->value; } /** @@ -207,7 +208,7 @@ public function primary(): void */ public function index(?string $name = null): void { - $this->indexKey = Key::INDEX; + $this->indexKey = Key::INDEX->value; if ($name) { $this->indexName = $name; @@ -219,7 +220,7 @@ public function index(?string $name = null): void */ public function unique(?string $name = null): void { - $this->indexKey = Key::UNIQUE; + $this->indexKey = Key::UNIQUE->value; if ($name) { $this->indexName = $name; @@ -231,7 +232,7 @@ public function unique(?string $name = null): void */ public function fulltext(?string $name = null): void { - $this->indexKey = Key::FULLTEXT; + $this->indexKey = Key::FULLTEXT->value; if ($name) { $this->indexName = $name; @@ -243,7 +244,7 @@ public function fulltext(?string $name = null): void */ public function spatial(?string $name = null): void { - $this->indexKey = Key::SPATIAL; + $this->indexKey = Key::SPATIAL->value; if ($name) { $this->indexName = $name; diff --git a/src/Database/Schemas/Table.php b/src/Database/Schemas/Table.php index 01ad1337e..aa1eebbca 100644 --- a/src/Database/Schemas/Table.php +++ b/src/Database/Schemas/Table.php @@ -13,7 +13,10 @@ use Quantum\Migration\Exceptions\MigrationException; use Quantum\Database\Exceptions\DatabaseException; use Quantum\App\Exceptions\BaseException; +use Quantum\Database\Enums\TableAction; use Quantum\Database\Traits\TableTrait; +use Quantum\Database\Enums\Type; +use Quantum\Database\Enums\Key; use Quantum\Database\Database; /** @@ -36,31 +39,11 @@ class Table { use TableTrait; - /** - * Action create - */ - public const CREATE = 1; - - /** - * Action alter - */ - public const ALTER = 2; - - /** - * Action drop - */ - public const DROP = 3; - - /** - * Action rename - */ - public const RENAME = 4; - private string $name; private string $newName = ''; - private ?int $action = null; + private ?TableAction $action = null; /** * @var array @@ -108,7 +91,7 @@ public function renameTo(string $newName): Table * Sets an action on a table to be performed * @param array $data */ - public function setAction(int $action, ?array $data = null): Table + public function setAction(TableAction $action, ?array $data = null): Table { $this->action = $action; @@ -124,11 +107,11 @@ public function setAction(int $action, ?array $data = null): Table * Adds column to the table * @param mixed $constraint */ - public function addColumn(string $name, string $type, $constraint = null): Table + public function addColumn(string $name, Type|string $type, $constraint = null): Table { $this->columns[] = [ 'column' => new Column($name, $type, $constraint), - 'action' => $this->action == self::ALTER ? Column::ADD : null, + 'action' => $this->action === TableAction::ALTER ? Column::ADD : null, ]; return $this; @@ -138,9 +121,9 @@ public function addColumn(string $name, string $type, $constraint = null): Table * Modifies the column * @param mixed $constraint */ - public function modifyColumn(string $name, string $type, $constraint = null): Table + public function modifyColumn(string $name, Type|string $type, $constraint = null): Table { - if ($this->action == self::ALTER) { + if ($this->action === TableAction::ALTER) { $this->columns[] = [ 'column' => new Column($name, $type, $constraint), 'action' => Column::MODIFY, @@ -155,7 +138,7 @@ public function modifyColumn(string $name, string $type, $constraint = null): Ta */ public function renameColumn(string $oldName, string $newName): void { - if ($this->action == self::ALTER) { + if ($this->action === TableAction::ALTER) { $this->columns[] = [ 'column' => (new Column($oldName))->renameTo($newName), 'action' => Column::RENAME, @@ -168,7 +151,7 @@ public function renameColumn(string $oldName, string $newName): void */ public function dropColumn(string $name): void { - if ($this->action == self::ALTER) { + if ($this->action === TableAction::ALTER) { $this->columns[] = [ 'column' => new Column($name), 'action' => Column::DROP, @@ -179,15 +162,21 @@ public function dropColumn(string $name): void /** * Adds new index to column */ - public function addIndex(string $columnName, string $indexType, ?string $indexName = null): void + public function addIndex(string $columnName, Key $indexType, ?string $indexName = null): void { - if ($this->action == self::ALTER) { + if ($this->action === TableAction::ALTER) { $this->columns[] = [ 'column' => new Column($columnName), 'action' => Column::ADD_INDEX, ]; - $this->$indexType($indexName); + match ($indexType) { + Key::PRIMARY => $this->primary(), + Key::INDEX => $this->index($indexName), + Key::UNIQUE => $this->unique($indexName), + Key::FULLTEXT => $this->fulltext($indexName), + Key::SPATIAL => $this->spatial($indexName), + }; } } @@ -196,7 +185,7 @@ public function addIndex(string $columnName, string $indexType, ?string $indexNa */ public function dropIndex(string $indexName): void { - if ($this->action == self::ALTER) { + if ($this->action === TableAction::ALTER) { $this->columns[] = [ 'column' => (new Column('dummy'))->indexDrop($indexName), 'action' => Column::DROP_INDEX, @@ -218,17 +207,13 @@ public function after(string $columnName): Table */ public function getSql(): string { - $sql = ''; - - $sql = match ($this->action) { - self::CREATE => $this->createTableSql(), - self::ALTER => $this->alterTableSql(), - self::RENAME => $this->renameTableSql(), - self::DROP => $this->dropTableSql(), - default => $sql, + return match ($this->action) { + TableAction::CREATE => $this->createTableSql(), + TableAction::ALTER => $this->alterTableSql(), + TableAction::RENAME => $this->renameTableSql(), + TableAction::DROP => $this->dropTableSql(), + null => '', }; - - return $sql; } /** diff --git a/src/Database/Traits/TableTrait.php b/src/Database/Traits/TableTrait.php index 09c96b7ad..58892c41c 100644 --- a/src/Database/Traits/TableTrait.php +++ b/src/Database/Traits/TableTrait.php @@ -10,6 +10,7 @@ namespace Quantum\Database\Traits; +use Quantum\Database\Enums\TableAction; use Quantum\Database\Schemas\Column; use Quantum\Database\Enums\Key; @@ -151,7 +152,7 @@ protected function primaryKeysSql(): string $sql = ''; if (isset($this->indexKeys['primary'])) { - $sql .= ($this->action == self::ALTER ? 'ADD ' : ''); + $sql .= ($this->action === TableAction::ALTER ? 'ADD ' : ''); $sql .= 'PRIMARY KEY ('; @@ -177,7 +178,7 @@ protected function indexKeysSql(string $type): string $indexes = []; foreach ($this->indexKeys[$type] as $indexKey) { - $indexString = ($this->action == self::ALTER ? 'ADD ' : ''); + $indexString = ($this->action === TableAction::ALTER ? 'ADD ' : ''); $indexString .= strtoupper($type); $indexString .= ($indexKey['indexName'] ? ' `' . $indexKey['indexName'] . '`' : ''); $indexString .= ' (`' . $indexKey['columnName'] . '`)'; @@ -198,10 +199,10 @@ protected function indexesSql(): string { $indexes = [ $this->primaryKeysSql(), - $this->indexKeysSql(Key::INDEX), - $this->indexKeysSql(Key::UNIQUE), - $this->indexKeysSql(Key::FULLTEXT), - $this->indexKeysSql(Key::SPATIAL), + $this->indexKeysSql(Key::INDEX->value), + $this->indexKeysSql(Key::UNIQUE->value), + $this->indexKeysSql(Key::FULLTEXT->value), + $this->indexKeysSql(Key::SPATIAL->value), ]; return implode(', ', array_filter($indexes)); diff --git a/src/Http/Helpers/http.php b/src/Http/Helpers/http.php index 417bd8be1..10d31c4fa 100644 --- a/src/Http/Helpers/http.php +++ b/src/Http/Helpers/http.php @@ -74,7 +74,7 @@ function redirect(string $url, int $code = StatusCode::FOUND): Response */ function redirectWith(string $url, array $data, int $code = StatusCode::FOUND): Response { - session()->set(ReservedKeys::PREV_REQUEST, $data); + session()->set(ReservedKeys::PREV_REQUEST->value, $data); return response()->redirect($url, $code); } @@ -95,11 +95,11 @@ function back(array $data = [], int $code = StatusCode::FOUND): Response */ function old(string $key) { - if (!session()->has(ReservedKeys::PREV_REQUEST)) { + if (!session()->has(ReservedKeys::PREV_REQUEST->value)) { return null; } - $prevRequest = session()->get(ReservedKeys::PREV_REQUEST); + $prevRequest = session()->get(ReservedKeys::PREV_REQUEST->value); if (!is_array($prevRequest) || !isset($prevRequest[$key])) { return null; @@ -108,7 +108,7 @@ function old(string $key) $value = $prevRequest[$key]; unset($prevRequest[$key]); - session()->set(ReservedKeys::PREV_REQUEST, $prevRequest); + session()->set(ReservedKeys::PREV_REQUEST->value, $prevRequest); return $value; } diff --git a/src/Http/Traits/Request/Body.php b/src/Http/Traits/Request/Body.php index c39995265..0eb7dab65 100644 --- a/src/Http/Traits/Request/Body.php +++ b/src/Http/Traits/Request/Body.php @@ -60,7 +60,7 @@ public function get(string $key, ?string $default = null, bool $raw = false) */ public function set(string $key, $value): void { - if ($key === ReservedKeys::RENDERED_VIEW) { + if ($key === ReservedKeys::RENDERED_VIEW->value) { throw new InvalidArgumentException("Cannot set reserved key: `$key`"); } diff --git a/src/Http/Traits/Response/Body.php b/src/Http/Traits/Response/Body.php index c2294c462..33d67f260 100644 --- a/src/Http/Traits/Response/Body.php +++ b/src/Http/Traits/Response/Body.php @@ -167,7 +167,7 @@ public function html(string $html, ?int $code = null): self $this->setStatusCode($code); } - $this->__response[ReservedKeys::RENDERED_VIEW] = $html; + $this->__response[ReservedKeys::RENDERED_VIEW->value] = $html; return $this; } @@ -273,7 +273,7 @@ private function formatXml(): string */ private function formatHtml(): string { - return $this->get(ReservedKeys::RENDERED_VIEW) ?? ''; + return $this->get(ReservedKeys::RENDERED_VIEW->value) ?? ''; } /** diff --git a/src/Http/Traits/Response/Header.php b/src/Http/Traits/Response/Header.php index 5e835295a..a6e5d0b5b 100644 --- a/src/Http/Traits/Response/Header.php +++ b/src/Http/Traits/Response/Header.php @@ -111,7 +111,7 @@ public function redirect(string $url, int $code = 302): self public function back(array $data = [], int $code = 302): self { if ($data !== []) { - session()->set(ReservedKeys::PREV_REQUEST, $data); + session()->set(ReservedKeys::PREV_REQUEST->value, $data); } $referrer = get_referrer(); diff --git a/src/Model/Exceptions/ModelException.php b/src/Model/Exceptions/ModelException.php index 4a6b5f675..d1f3a3b38 100644 --- a/src/Model/Exceptions/ModelException.php +++ b/src/Model/Exceptions/ModelException.php @@ -12,6 +12,7 @@ use Quantum\Model\Enums\ExceptionMessages; use Quantum\App\Exceptions\BaseException; +use Quantum\Database\Enums\Relation; /** * Class ModelException @@ -59,10 +60,10 @@ public static function missingForeignKeyValue(string $modelName, string $foreign ); } - public static function unsupportedRelationType(string $relationType): self + public static function unsupportedRelationType(Relation|string $relationType): self { return new self( - _message(ExceptionMessages::UNSUPPORTED_RELATION, [$relationType]), + _message(ExceptionMessages::UNSUPPORTED_RELATION, [$relationType instanceof Relation ? $relationType->value : $relationType]), E_ERROR ); } diff --git a/src/Paginator/Enums/Pagination.php b/src/Paginator/Enums/Pagination.php index b20faa17c..b9db097c7 100644 --- a/src/Paginator/Enums/Pagination.php +++ b/src/Paginator/Enums/Pagination.php @@ -11,48 +11,29 @@ namespace Quantum\Paginator\Enums; /** - * Class Pagination + * Enum Pagination * @package Quantum\Paginator * @codeCoverageIgnore */ -final class Pagination +enum Pagination: string { /** * Pagination class name */ - public const PAGINATION_CLASS = 'pagination'; + case PAGINATION_CLASS = 'pagination'; /** * Active class name */ - public const PAGINATION_CLASS_ACTIVE = 'active'; + case PAGINATION_CLASS_ACTIVE = 'active'; /** * Parameter name for per page */ - public const PER_PAGE = 'per_page'; + case PER_PAGE = 'per_page'; /** * Parameter name for page number */ - public const PAGE = 'page'; - - /** - * First page number - */ - public const FIRST_PAGE_NUMBER = 1; - - /** - * Minimum page items count - */ - public const MINIMUM_PAGE_ITEMS_COUNT = 3; - - /** - * Edge padding - */ - public const EDGE_PADDING = 3; - - private function __construct() - { - } + case PAGE = 'page'; } diff --git a/src/Paginator/Enums/PaginationLimit.php b/src/Paginator/Enums/PaginationLimit.php new file mode 100644 index 000000000..f5a74408c --- /dev/null +++ b/src/Paginator/Enums/PaginationLimit.php @@ -0,0 +1,34 @@ +getPageLink(Pagination::FIRST_PAGE_NUMBER, $withBaseUrl); + return $this->getPageLink(PaginationLimit::FIRST_PAGE_NUMBER, $withBaseUrl); } /** @@ -179,9 +180,9 @@ public function getPagination(bool $withBaseUrl = false, ?int $pageItemsCount = return null; } - $pageItemsCount = max(Pagination::MINIMUM_PAGE_ITEMS_COUNT, $pageItemsCount ?? Pagination::MINIMUM_PAGE_ITEMS_COUNT); + $pageItemsCount = max(PaginationLimit::MINIMUM_PAGE_ITEMS_COUNT, $pageItemsCount ?? PaginationLimit::MINIMUM_PAGE_ITEMS_COUNT); - $pagination = ['