Files
gtsit/app/Repositories/InquiryRepository.php
2026-08-10 21:19:10 +09:00

112 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Repositories;
use App\Database;
use PDO;
final class InquiryRepository
{
private PDO $db;
public function __construct()
{
$this->db = Database::connection();
}
public function create(array $data): string
{
do {
$ticket = 'GTS-' . date('Ymd') . '-' . strtoupper(bin2hex(random_bytes(3)));
$check = $this->db->prepare('SELECT COUNT(*) FROM inquiries WHERE ticket = :ticket');
$check->execute(['ticket' => $ticket]);
} while ((int) $check->fetchColumn() > 0);
$statement = $this->db->prepare(
"INSERT INTO inquiries
(ticket, name, phone, email, inquiry_type, title, body, password_hash, status, consent_at)
VALUES (:ticket, :name, :phone, :email, :inquiry_type, :title, :body, :password_hash, 'pending', NOW())"
);
$statement->execute([
'ticket' => $ticket,
'name' => $data['name'],
'phone' => $data['phone'],
'email' => $data['email'] ?: null,
'inquiry_type' => $data['type'],
'title' => $data['title'],
'body' => $data['body'],
'password_hash' => password_hash($data['password'], PASSWORD_DEFAULT),
]);
return $ticket;
}
public function findForLookup(string $ticket): ?array
{
$statement = $this->db->prepare(
'SELECT id, ticket, inquiry_type, title, body, status, reply, replied_at, created_at,
password_hash, lookup_failed_attempts, lookup_locked_until
FROM inquiries WHERE ticket = :ticket AND deleted_at IS NULL LIMIT 1'
);
$statement->execute(['ticket' => strtoupper($ticket)]);
$inquiry = $statement->fetch();
return $inquiry ?: null;
}
public function resetLookupLimit(int $id): void
{
$statement = $this->db->prepare(
'UPDATE inquiries
SET lookup_failed_attempts = 0, lookup_locked_until = NULL
WHERE id = :id AND deleted_at IS NULL'
);
$statement->execute(['id' => $id]);
}
public function recordLookupFailure(int $id): bool
{
$this->db->beginTransaction();
try {
$select = $this->db->prepare(
'SELECT lookup_failed_attempts, lookup_locked_until
FROM inquiries WHERE id = :id AND deleted_at IS NULL FOR UPDATE'
);
$select->execute(['id' => $id]);
$limit = $select->fetch();
if (!$limit) {
$this->db->rollBack();
return false;
}
$attempts = (int) $limit['lookup_failed_attempts'];
$lockedUntil = $limit['lookup_locked_until'];
if (is_string($lockedUntil) && strtotime($lockedUntil) <= time()) {
$attempts = 0;
}
$attempts++;
$locked = $attempts >= 5;
$update = $this->db->prepare(
'UPDATE inquiries
SET lookup_failed_attempts = :attempts,
lookup_locked_until = CASE WHEN :locked = 1 THEN DATE_ADD(NOW(), INTERVAL 15 MINUTE) ELSE NULL END
WHERE id = :id'
);
$update->execute([
'attempts' => $attempts,
'locked' => $locked ? 1 : 0,
'id' => $id,
]);
$this->db->commit();
return $locked;
} catch (\Throwable $error) {
if ($this->db->inTransaction()) {
$this->db->rollBack();
}
throw $error;
}
}
}