112 lines
3.6 KiB
PHP
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;
|
|
}
|
|
}
|
|
}
|