Практические примеры моделей Home в архитектуре MVC
Модели Home в PHP: варианты реализации
Модель Home в MVC отвечает за бизнес-логику главной страницы: получение последних новостей, слайдера, популярных товаров. Рассмотрим различные подходы к её построению.
Как создать базовую модель Home с использованием PDO?
Простой и эффективный способ – написать класс HomeModel с методами, использующими PDO для запросов к базе данных. Это даёт полный контроль над SQL и производительностью.
class HomeModel {
private PDO $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function getRecentNews(int $limit = 5): array {
$stmt = $this->db->prepare('SELECT id, title, created_at FROM news ORDER BY created_at DESC LIMIT :limit');
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function getSliderImages(): array {
$stmt = $this->db->query('SELECT url, alt FROM slider WHERE active = 1');
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}Типичная ошибка:
Использование глобального состояния (singleton для PDO) или повторное создание соединения в каждом методе. Лучше внедрять PDO через конструктор.
Ещё одна проблема – отсутствие обработки ошибок. Всегда оборачивайте запросы в try-catch и логируйте исключения.
Как использовать Eloquent для модели Home?
Если проект использует Laravel, модель Home может наследовать Eloquent Model. Это удобно для связей и красивого синтаксиса.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Home extends Model {
protected $table = 'home_data';
public $timestamps = false;
public function items() {
return $this->hasMany(HomeItem::class);
}
}Возможная проблема:
N+1 запросов при обращении к связанным данным. Решается жадной загрузкой (with()).
Как организовать модель Home через Repository?
Паттерн Repository абстрагирует источник данных. Создаём интерфейс и реализацию для Home.
interface HomeRepositoryInterface {
public function getFeaturedNews(): array;
public function getSlider(): array;
}
class PdoHomeRepository implements HomeRepositoryInterface {
private PDO $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function getFeaturedNews(): array {
// реализация
}
public function getSlider(): array {
// реализация
}
}Проблема:
Избыточность для простых страниц. Repository оправдан, если несколько источников данных или сложная логика.
Как реализовать модель Home без базы данных?
Для статического контента (например, информация о компании) модель может возвращать массивы.
class HomeModel {
public function getCompanyInfo(): array {
return [
'name' => 'Example Corp',
'slogan' => 'Best solutions',
'founded' => 2005
];
}
}Ошибка:
Смешивание конфигурации и бизнес-логики. Лучше выносить статические данные в конфигурационные файлы.
Как разделить логику Home через сервисы?
Service Layer инкапсулирует сложную логику, используя модель или репозиторий.
class HomeService {
private HomeRepositoryInterface $homeRepo;
private CacheService $cache;
public function __construct(HomeRepositoryInterface $homeRepo, CacheService $cache) {
$this->homeRepo = $homeRepo;
$this->cache = $cache;
}
public function getHomeData(): array {
$cacheKey = 'home_data';
if ($cached = $this->cache->get($cacheKey)) {
return $cached;
}
$data = [
'news' => $this->homeRepo->getFeaturedNews(),
'slider' => $this->homeRepo->getSlider(),
];
$this->cache->set($cacheKey, $data, 3600);
return $data;
}
}Проблема:
Чрезмерное усложнение для тривиальных страниц. Service полезен при наличии кеширования, бизнес-правил.
Расширенные примеры моделей Home
Рассмотрим полную реализацию HomeModel с расширенными возможностями: пагинация, фильтрация, транзакции, кеширование.
Пример 1: Модель Home с пагинацией и подготовленными запросами
class HomeModel {
private PDO $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function getArticlesByCategory(string $category, int $page = 1, int $perPage = 10): array {
$offset = ($page - 1) * $perPage;
$stmt = $this->db->prepare('SELECT id, title, content, created_at FROM articles WHERE category = :cat ORDER BY created_at DESC LIMIT :limit OFFSET :offset');
$stmt->bindValue(':cat', $category, PDO::PARAM_STR);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
// получаем общее количество для пагинации
$countStmt = $this->db->prepare('SELECT COUNT(*) FROM articles WHERE category = :cat');
$countStmt->bindValue(':cat', $category, PDO::PARAM_STR);
$countStmt->execute();
$total = $countStmt->fetchColumn();
return ['articles' => $articles, 'total' => $total, 'page' => $page, 'perPage' => $perPage];
}
}Результат: массив с данными статей и мета-информацией для постраничной навигации.
Пояснение:
Используется LIMIT и OFFSET для выборки одной страницы. Отдельный запрос на COUNT() для пагинации. Все параметры передаются через bindValue для защиты от SQL-инъекций.
Пример 2: Модель с кешированием через файлы (без внешних библиотек)
class CachedHomeModel {
private HomeModel $model;
private string $cacheDir;
public function __construct(HomeModel $model, string $cacheDir = '/tmp/cache') {
$this->model = $model;
$this->cacheDir = $cacheDir;
}
public function getSliderImages($ttl = 300): array {
$cacheFile = $this->cacheDir . '/slider.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $ttl)) {
return json_decode(file_get_contents($cacheFile), true);
}
$data = $this->model->getSliderImages();
file_put_contents($cacheFile, json_encode($data));
return $data;
}
}Результат: при первом вызове данные берутся из БД и сохраняются в файл. Последующие вызовы в течение TTL читают из файла.
Пояснение:
Декоратор добавляет кеширование без изменения исходного класса. Хорошо для редко обновляемых данных, но не подходит для высоконагруженных систем – лучше использовать Redis или Memcached.
Пример 3: Использование транзакций в модели Home (обновление нескольких таблиц)
class HomeModelTransaction {
private PDO $db;
public function updateHomeData(array $news, array $slider): bool {
try {
$this->db->beginTransaction();
// очищаем старые данные
$this->db->exec('DELETE FROM home_news');
$this->db->exec('DELETE FROM home_slider');
$insertNews = $this->db->prepare('INSERT INTO home_news (id, title) VALUES (:id, :title)');
foreach ($news as $item) {
$insertNews->execute([':id' => $item['id'], ':title' => $item['title']]);
}
$insertSlider = $this->db->prepare('INSERT INTO home_slider (url, alt) VALUES (:url, :alt)');
foreach ($slider as $item) {
$insertSlider->execute([':url' => $item['url'], ':alt' => $item['alt']]);
}
$this->db->commit();
return true;
} catch (\PDOException $e) {
$this->db->rollBack();
// логирование ошибки
return false;
}
}
}Результат: обе таблицы обновляются атомарно. При ошибке все изменения откатываются.
Пояснение:
Транзакции гарантируют целостность данных при множественных записях. Обязательно использовать try-catch и rollBack.
Пример 4: Модель Home с использованием JOIN для связанных данных
class HomeModelJoin {
private PDO $db;
public function getHomePageWithRelations(): array {
$sql = 'SELECT n.id AS news_id, n.title AS news_title, c.name AS category_name, s.url AS slider_url
FROM news n
JOIN categories c ON n.category_id = c.id
LEFT JOIN sliders s ON s.id = n.featured_slider_id
WHERE n.published = 1
ORDER BY n.publish_date DESC
LIMIT 10';
$stmt = $this->db->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}Результат: каждая строка содержит поля из нескольких таблиц.
Пояснение:
JOIN уменьшает количество запросов, но может быть сложным для поддержки. Для больших объёмов данных нужны индексы.
Пример 5: Модель Home с гидратацией объектов (Data Mapper)
class HomeArticle {
public int $id;
public string $title;
public string $content;
}
class HomeMapper {
private PDO $db;
public function find(int $id): ?HomeArticle {
$stmt = $this->db->prepare('SELECT id, title, content FROM articles WHERE id = :id');
$stmt->execute([':id' => $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) return null;
$article = new HomeArticle();
$article->id = $row['id'];
$article->title = $row['title'];
$article->content = $row['content'];
return $article;
}
}Результат: объект HomeArticle, а не массив.
Пояснение:
Преобразование строк БД в объекты (гидратация) улучшает читаемость и даёт типизацию. Можно использовать автоматические гидраторы (например, PDO FETCH_CLASS).