Практические примеры моделей Home в архитектуре MVC

Раздел: Разработка веб-приложений -> 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).

Модели home в PHP - comments

En
Models home php (php)