diff --git a/src/Repository/CardRepository.php b/src/Repository/CardRepository.php index 3b6414f..8576d39 100644 --- a/src/Repository/CardRepository.php +++ b/src/Repository/CardRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -112,7 +113,7 @@ final class CardRepository */ public function update(int $id, array $fields): array { - $sets = ['updated_at = ' . $this->nowExpr()]; + $sets = ['updated_at = ' . Database::nowExpr()]; $params = ['id' => $id]; if (array_key_exists('text', $fields)) { @@ -185,7 +186,7 @@ final class CardRepository $place = $this->pdo->prepare( 'UPDATE cards SET project_id = :project, status_id = :status, position = :position, - updated_at = ' . $this->nowExpr() . ' + updated_at = ' . Database::nowExpr() . ' WHERE id = :id AND owner_id = :owner' ); foreach ($orderedIds as $position => $id) { @@ -229,7 +230,7 @@ final class CardRepository $next = $this->nextPositionInColumn($ownerId, $projectId, $toStatusId); $stmt = $this->pdo->prepare( - 'UPDATE cards SET status_id = :status, position = :position, updated_at = ' . $this->nowExpr() . ' + 'UPDATE cards SET status_id = :status, position = :position, updated_at = ' . Database::nowExpr() . ' WHERE id = :id AND owner_id = :owner' ); foreach ($ids as $i => $id) { @@ -362,11 +363,6 @@ final class CardRepository return $row === false ? null : $this->cast($row); } - private function nowExpr(): string - { - return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')"; - } - /** * @param array $row * @return CardRow diff --git a/src/Repository/CardStatusRepository.php b/src/Repository/CardStatusRepository.php index 647ab7a..38e411e 100644 --- a/src/Repository/CardStatusRepository.php +++ b/src/Repository/CardStatusRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -100,7 +101,7 @@ final class CardStatusRepository public function reorder(int $projectId, array $orderedIds): array { $stmt = $this->pdo->prepare( - 'UPDATE card_statuses SET position = :position, updated_at = ' . $this->nowExpr() . ' + 'UPDATE card_statuses SET position = :position, updated_at = ' . Database::nowExpr() . ' WHERE id = :id AND project_id = :project' ); foreach (array_values($orderedIds) as $position => $id) { @@ -160,11 +161,6 @@ final class CardStatusRepository return $row === false ? null : $this->cast($row); } - private function nowExpr(): string - { - return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')"; - } - /** * @param array $row * @return CardStatusRow diff --git a/src/Repository/EmailVerificationRepository.php b/src/Repository/EmailVerificationRepository.php index fcf520d..b3fc4d0 100644 --- a/src/Repository/EmailVerificationRepository.php +++ b/src/Repository/EmailVerificationRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -70,8 +71,8 @@ final class EmailVerificationRepository public function consume(int $id): bool { $stmt = $this->pdo->prepare( - "UPDATE email_verifications SET consumed_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - WHERE id = :id AND consumed_at IS NULL" + 'UPDATE email_verifications SET consumed_at = ' . Database::nowExpr() . ' + WHERE id = :id AND consumed_at IS NULL' ); $stmt->execute(['id' => $id]); @@ -84,10 +85,10 @@ final class EmailVerificationRepository public function pendingEmailFor(int $userId): ?string { $stmt = $this->pdo->prepare( - "SELECT new_email FROM email_verifications + 'SELECT new_email FROM email_verifications WHERE user_id = :user AND new_email IS NOT NULL AND consumed_at IS NULL - AND expires_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - ORDER BY id DESC LIMIT 1" + AND expires_at > ' . Database::nowExpr() . ' + ORDER BY id DESC LIMIT 1' ); $stmt->execute(['user' => $userId]); diff --git a/src/Repository/PasskeyRepository.php b/src/Repository/PasskeyRepository.php index dff4b11..35d4963 100644 --- a/src/Repository/PasskeyRepository.php +++ b/src/Repository/PasskeyRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -94,8 +95,8 @@ final class PasskeyRepository public function markUsed(int $id, int $signCount): void { $this->pdo->prepare( - "UPDATE passkeys SET sign_count = :count, last_used_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - WHERE id = :id" + 'UPDATE passkeys SET sign_count = :count, last_used_at = ' . Database::nowExpr() . ' + WHERE id = :id' )->execute(['count' => $signCount, 'id' => $id]); } diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 60b3d28..ffce5a7 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -90,7 +91,7 @@ final class ProjectRepository */ public function update(int $id, int $ownerId, array $fields): array { - $sets = ['updated_at = ' . $this->nowExpr()]; + $sets = ['updated_at = ' . Database::nowExpr()]; $params = ['id' => $id]; foreach (['title', 'description'] as $column) { @@ -117,15 +118,10 @@ final class ProjectRepository /** Bump updated_at, e.g. when the project's cards change. */ public function touch(int $id): void { - $this->pdo->prepare('UPDATE projects SET updated_at = ' . $this->nowExpr() . ' WHERE id = :id') + $this->pdo->prepare('UPDATE projects SET updated_at = ' . Database::nowExpr() . ' WHERE id = :id') ->execute(['id' => $id]); } - private function nowExpr(): string - { - return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')"; - } - /** * @param array $row * @return ProjectRow diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index bd6192d..7df2646 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; use PDOException; @@ -88,10 +89,9 @@ final class UserRepository public function markEmailVerified(int $id): void { + $now = Database::nowExpr(); $this->pdo->prepare( - "UPDATE users SET email_verified_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), - updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - WHERE id = :id" + "UPDATE users SET email_verified_at = {$now}, updated_at = {$now} WHERE id = :id" )->execute(['id' => $id]); } @@ -100,18 +100,16 @@ final class UserRepository */ public function updateEmail(int $id, string $email): void { + $now = Database::nowExpr(); $this->pdo->prepare( - "UPDATE users SET email = :email, - email_verified_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), - updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - WHERE id = :id" + "UPDATE users SET email = :email, email_verified_at = {$now}, updated_at = {$now} WHERE id = :id" )->execute(['email' => $email, 'id' => $id]); } public function markVerificationEmailSent(int $id): void { $this->pdo->prepare( - "UPDATE users SET verification_email_sent_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = :id" + 'UPDATE users SET verification_email_sent_at = ' . Database::nowExpr() . ' WHERE id = :id' )->execute(['id' => $id]); } diff --git a/src/Repository/WebAuthnChallengeRepository.php b/src/Repository/WebAuthnChallengeRepository.php index eac9595..94dbd27 100644 --- a/src/Repository/WebAuthnChallengeRepository.php +++ b/src/Repository/WebAuthnChallengeRepository.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Repository; +use App\Support\Database; use PDO; /** @@ -66,8 +67,8 @@ final class WebAuthnChallengeRepository } $update = $this->pdo->prepare( - "UPDATE webauthn_challenges SET consumed_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') - WHERE id = :id AND consumed_at IS NULL" + 'UPDATE webauthn_challenges SET consumed_at = ' . Database::nowExpr() . ' + WHERE id = :id AND consumed_at IS NULL' ); $update->execute(['id' => $id]); diff --git a/src/Support/Database.php b/src/Support/Database.php index cca1624..5638fd6 100644 --- a/src/Support/Database.php +++ b/src/Support/Database.php @@ -29,4 +29,14 @@ final class Database { return $this->pdo; } + + /** + * SQL expression for the current UTC time, in the format every + * created_at/updated_at/*_at column uses (both as their DEFAULT in the + * migrations and wherever a repository sets one explicitly). + */ + public static function nowExpr(): string + { + return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')"; + } }