Centralize the current-timestamp SQL expression
"strftime('%Y-%m-%dT%H:%M:%SZ', 'now')" was a private nowExpr()
method copy-pasted identically in CardRepository, CardStatusRepository
and ProjectRepository, and inlined as a raw literal directly in
PasskeyRepository, WebAuthnChallengeRepository, UserRepository and
EmailVerificationRepository -- 7 files, ~12 occurrences of the same
string. Now one Database::nowExpr() static method (Support/Database
already being the natural home for SQLite-specific concerns), used
everywhere a repository sets a timestamp explicitly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +113,7 @@ final class CardRepository
|
|||||||
*/
|
*/
|
||||||
public function update(int $id, array $fields): array
|
public function update(int $id, array $fields): array
|
||||||
{
|
{
|
||||||
$sets = ['updated_at = ' . $this->nowExpr()];
|
$sets = ['updated_at = ' . Database::nowExpr()];
|
||||||
$params = ['id' => $id];
|
$params = ['id' => $id];
|
||||||
|
|
||||||
if (array_key_exists('text', $fields)) {
|
if (array_key_exists('text', $fields)) {
|
||||||
@@ -185,7 +186,7 @@ final class CardRepository
|
|||||||
|
|
||||||
$place = $this->pdo->prepare(
|
$place = $this->pdo->prepare(
|
||||||
'UPDATE cards SET project_id = :project, status_id = :status, position = :position,
|
'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'
|
WHERE id = :id AND owner_id = :owner'
|
||||||
);
|
);
|
||||||
foreach ($orderedIds as $position => $id) {
|
foreach ($orderedIds as $position => $id) {
|
||||||
@@ -229,7 +230,7 @@ final class CardRepository
|
|||||||
|
|
||||||
$next = $this->nextPositionInColumn($ownerId, $projectId, $toStatusId);
|
$next = $this->nextPositionInColumn($ownerId, $projectId, $toStatusId);
|
||||||
$stmt = $this->pdo->prepare(
|
$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'
|
WHERE id = :id AND owner_id = :owner'
|
||||||
);
|
);
|
||||||
foreach ($ids as $i => $id) {
|
foreach ($ids as $i => $id) {
|
||||||
@@ -362,11 +363,6 @@ final class CardRepository
|
|||||||
return $row === false ? null : $this->cast($row);
|
return $row === false ? null : $this->cast($row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function nowExpr(): string
|
|
||||||
{
|
|
||||||
return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $row
|
* @param array<string, mixed> $row
|
||||||
* @return CardRow
|
* @return CardRow
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +101,7 @@ final class CardStatusRepository
|
|||||||
public function reorder(int $projectId, array $orderedIds): array
|
public function reorder(int $projectId, array $orderedIds): array
|
||||||
{
|
{
|
||||||
$stmt = $this->pdo->prepare(
|
$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'
|
WHERE id = :id AND project_id = :project'
|
||||||
);
|
);
|
||||||
foreach (array_values($orderedIds) as $position => $id) {
|
foreach (array_values($orderedIds) as $position => $id) {
|
||||||
@@ -160,11 +161,6 @@ final class CardStatusRepository
|
|||||||
return $row === false ? null : $this->cast($row);
|
return $row === false ? null : $this->cast($row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function nowExpr(): string
|
|
||||||
{
|
|
||||||
return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $row
|
* @param array<string, mixed> $row
|
||||||
* @return CardStatusRow
|
* @return CardStatusRow
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,8 +71,8 @@ final class EmailVerificationRepository
|
|||||||
public function consume(int $id): bool
|
public function consume(int $id): bool
|
||||||
{
|
{
|
||||||
$stmt = $this->pdo->prepare(
|
$stmt = $this->pdo->prepare(
|
||||||
"UPDATE email_verifications SET consumed_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
'UPDATE email_verifications SET consumed_at = ' . Database::nowExpr() . '
|
||||||
WHERE id = :id AND consumed_at IS NULL"
|
WHERE id = :id AND consumed_at IS NULL'
|
||||||
);
|
);
|
||||||
$stmt->execute(['id' => $id]);
|
$stmt->execute(['id' => $id]);
|
||||||
|
|
||||||
@@ -84,10 +85,10 @@ final class EmailVerificationRepository
|
|||||||
public function pendingEmailFor(int $userId): ?string
|
public function pendingEmailFor(int $userId): ?string
|
||||||
{
|
{
|
||||||
$stmt = $this->pdo->prepare(
|
$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
|
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')
|
AND expires_at > ' . Database::nowExpr() . '
|
||||||
ORDER BY id DESC LIMIT 1"
|
ORDER BY id DESC LIMIT 1'
|
||||||
);
|
);
|
||||||
$stmt->execute(['user' => $userId]);
|
$stmt->execute(['user' => $userId]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,8 +95,8 @@ final class PasskeyRepository
|
|||||||
public function markUsed(int $id, int $signCount): void
|
public function markUsed(int $id, int $signCount): void
|
||||||
{
|
{
|
||||||
$this->pdo->prepare(
|
$this->pdo->prepare(
|
||||||
"UPDATE passkeys SET sign_count = :count, last_used_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
'UPDATE passkeys SET sign_count = :count, last_used_at = ' . Database::nowExpr() . '
|
||||||
WHERE id = :id"
|
WHERE id = :id'
|
||||||
)->execute(['count' => $signCount, 'id' => $id]);
|
)->execute(['count' => $signCount, 'id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +91,7 @@ final class ProjectRepository
|
|||||||
*/
|
*/
|
||||||
public function update(int $id, int $ownerId, array $fields): array
|
public function update(int $id, int $ownerId, array $fields): array
|
||||||
{
|
{
|
||||||
$sets = ['updated_at = ' . $this->nowExpr()];
|
$sets = ['updated_at = ' . Database::nowExpr()];
|
||||||
$params = ['id' => $id];
|
$params = ['id' => $id];
|
||||||
|
|
||||||
foreach (['title', 'description'] as $column) {
|
foreach (['title', 'description'] as $column) {
|
||||||
@@ -117,15 +118,10 @@ final class ProjectRepository
|
|||||||
/** Bump updated_at, e.g. when the project's cards change. */
|
/** Bump updated_at, e.g. when the project's cards change. */
|
||||||
public function touch(int $id): void
|
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]);
|
->execute(['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function nowExpr(): string
|
|
||||||
{
|
|
||||||
return "strftime('%Y-%m-%dT%H:%M:%SZ', 'now')";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $row
|
* @param array<string, mixed> $row
|
||||||
* @return ProjectRow
|
* @return ProjectRow
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
|
||||||
@@ -88,10 +89,9 @@ final class UserRepository
|
|||||||
|
|
||||||
public function markEmailVerified(int $id): void
|
public function markEmailVerified(int $id): void
|
||||||
{
|
{
|
||||||
|
$now = Database::nowExpr();
|
||||||
$this->pdo->prepare(
|
$this->pdo->prepare(
|
||||||
"UPDATE users SET email_verified_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
"UPDATE users SET email_verified_at = {$now}, updated_at = {$now} WHERE id = :id"
|
||||||
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
|
||||||
WHERE id = :id"
|
|
||||||
)->execute(['id' => $id]);
|
)->execute(['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,18 +100,16 @@ final class UserRepository
|
|||||||
*/
|
*/
|
||||||
public function updateEmail(int $id, string $email): void
|
public function updateEmail(int $id, string $email): void
|
||||||
{
|
{
|
||||||
|
$now = Database::nowExpr();
|
||||||
$this->pdo->prepare(
|
$this->pdo->prepare(
|
||||||
"UPDATE users SET email = :email,
|
"UPDATE users SET email = :email, email_verified_at = {$now}, updated_at = {$now} WHERE id = :id"
|
||||||
email_verified_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
||||||
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
|
||||||
WHERE id = :id"
|
|
||||||
)->execute(['email' => $email, 'id' => $id]);
|
)->execute(['email' => $email, 'id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function markVerificationEmailSent(int $id): void
|
public function markVerificationEmailSent(int $id): void
|
||||||
{
|
{
|
||||||
$this->pdo->prepare(
|
$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]);
|
)->execute(['id' => $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Support\Database;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,8 +67,8 @@ final class WebAuthnChallengeRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
$update = $this->pdo->prepare(
|
$update = $this->pdo->prepare(
|
||||||
"UPDATE webauthn_challenges SET consumed_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
|
'UPDATE webauthn_challenges SET consumed_at = ' . Database::nowExpr() . '
|
||||||
WHERE id = :id AND consumed_at IS NULL"
|
WHERE id = :id AND consumed_at IS NULL'
|
||||||
);
|
);
|
||||||
$update->execute(['id' => $id]);
|
$update->execute(['id' => $id]);
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,14 @@ final class Database
|
|||||||
{
|
{
|
||||||
return $this->pdo;
|
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')";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user