102 lines
4.2 KiB
PHP
102 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Repositories;
|
|
|
|
use App\Database;
|
|
use PDO;
|
|
|
|
final class PostRepository
|
|
{
|
|
private PDO $db;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->db = Database::connection();
|
|
}
|
|
|
|
public function latest(int $limit = 3): array
|
|
{
|
|
$statement = $this->db->prepare(
|
|
"SELECT posts.id, posts.title, posts.slug, posts.category, posts.excerpt, posts.published_at,
|
|
(SELECT file_path FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_path,
|
|
(SELECT alt_text FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_alt
|
|
FROM posts
|
|
WHERE posts.status = 'published' AND posts.deleted_at IS NULL AND posts.published_at <= NOW()
|
|
ORDER BY published_at DESC, id DESC
|
|
LIMIT :limit"
|
|
);
|
|
$statement->bindValue(':limit', $limit, PDO::PARAM_INT);
|
|
$statement->execute();
|
|
return $statement->fetchAll();
|
|
}
|
|
|
|
public function paginate(int $page, int $perPage, ?string $category = null): array
|
|
{
|
|
$where = "posts.status = 'published' AND posts.deleted_at IS NULL AND posts.published_at <= NOW()";
|
|
$params = [];
|
|
if ($category !== null && $category !== '') {
|
|
$where .= ' AND posts.category = :category';
|
|
$params['category'] = $category;
|
|
}
|
|
|
|
$count = $this->db->prepare("SELECT COUNT(*) FROM posts WHERE {$where}");
|
|
$count->execute($params);
|
|
$total = (int) $count->fetchColumn();
|
|
|
|
$offset = max(0, ($page - 1) * $perPage);
|
|
$query = $this->db->prepare(
|
|
"SELECT posts.id, posts.title, posts.slug, posts.category, posts.excerpt, posts.published_at,
|
|
(SELECT file_path FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_path,
|
|
(SELECT alt_text FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_alt
|
|
FROM posts WHERE {$where}
|
|
ORDER BY published_at DESC, id DESC
|
|
LIMIT :limit OFFSET :offset"
|
|
);
|
|
foreach ($params as $key => $value) {
|
|
$query->bindValue(':' . $key, $value);
|
|
}
|
|
$query->bindValue(':limit', $perPage, PDO::PARAM_INT);
|
|
$query->bindValue(':offset', $offset, PDO::PARAM_INT);
|
|
$query->execute();
|
|
|
|
return ['items' => $query->fetchAll(), 'total' => $total];
|
|
}
|
|
|
|
public function findPublishedBySlug(string $slug): ?array
|
|
{
|
|
$statement = $this->db->prepare(
|
|
"SELECT posts.id, posts.title, posts.slug, posts.category, posts.excerpt, posts.body, posts.author, posts.published_at,
|
|
(SELECT file_path FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_path,
|
|
(SELECT alt_text FROM post_images WHERE post_id = posts.id AND caption NOT LIKE '@gtsit:inline:%' ORDER BY sort_order, id LIMIT 1) AS image_alt
|
|
FROM posts
|
|
WHERE posts.slug = :slug AND posts.status = 'published' AND posts.deleted_at IS NULL AND posts.published_at <= NOW()
|
|
LIMIT 1"
|
|
);
|
|
$statement->execute(['slug' => $slug]);
|
|
$post = $statement->fetch();
|
|
return $post ? $this->withMedia($post) : null;
|
|
}
|
|
|
|
public function withMedia(array $post): array
|
|
{
|
|
$statement = $this->db->prepare(
|
|
'SELECT id, file_path, alt_text, caption, sort_order
|
|
FROM post_images WHERE post_id = :post_id ORDER BY sort_order, id'
|
|
);
|
|
$statement->execute(['post_id' => $post['id']]);
|
|
$post['gallery_images'] = [];
|
|
$post['inline_images'] = [];
|
|
foreach ($statement->fetchAll() as $image) {
|
|
if (preg_match('/^@gtsit:inline:(\d+)$/', (string) $image['caption'], $matches) === 1) {
|
|
$image['token'] = (int) $matches[1];
|
|
$post['inline_images'][(int) $matches[1]] = $image;
|
|
continue;
|
|
}
|
|
$post['gallery_images'][] = $image;
|
|
}
|
|
return $post;
|
|
}
|
|
}
|