Кращі практики MVC ================== Недивлячись на те, що MVC відомий практично кожному веб-розробнику, його використання у реальних проектах часто викликає труднощі. Головна ідея MVC — **повторне використання коду та розділення проблем**. У даному розділі будуть описані загальні принципи, які допоможуть слідувати MVC у вашому додатку. Допустимо, що веб-додаток складається із декількох піддодатків, таких як * front end: та частина сайту, яку бачать користувачі; * back end: адміністративна частина сайту, яка дозволяє керувати додатком. Доступ до неї зазвичай обмежений; * консоль: додаток, який складається із набору консольних команд та запускається у вікні терміналу або як завдання cron-у; * API: надає стороннім додаткам інтерфейси для інтеграції з вашим додатком. Піддодатки можуть бути реалізовані у вигляді [модулів](/doc/guide/basics.module) або як додаток, який містить код, загальний для декількох піддодатків. Модель ------ [Моделі](/doc/guide/basics.model) представляють структури даних, які використовуться у додатку, і часто є спільними для кількох піддодатків. Наприклад, модель `LoginForm` може бути використана як у користувальницькій, так і у адміністративній частині додатку. Модель `News` може використовуватися консольними командами, API та front/back частинами додатку. Тому, моделі * повинні містити властивості, які представляють конкретні дані; * повинні включати у себе бізнес-логіку (наприклад, правила валідації) для того, щоб переконатися, що дані відповідають вимогам; * можуть містити код для роботи із даними. Наприклад, модель `SearchForm`, окрім даних для пошуку, може містити метод `search`, який цей пошук здійснює. Іноді слідування останньому перерахованому правилу робить модель дуже товстою, тобто вона містить дуже багато коду в одному класі. Це може призвести до труднощів підтримки коду у тому випадку, якщо модель використовується декількома способами. Приміром, модель `News` може містити метод `getLatestNews`, який використовується тільки користувальницькою частиною і метод `getDeletedNews`, який використовується тільки адміністративною частиною. Для невеликих та середніх додатків це припустимо. Для великих додатків з метою поліпшення підтримуваності коду можна зробити наступне: * Створити модель `NewsBase`, яка містить тільки код, загальний для піддодатків (користувальницької та административної частин); * У кожному піддодатку створити модель `News`, наслідувану від `NewsBase` та визначити у ній специфічні для піддодатку методи. Таким чином, якщо застосувати це до розглянутому вище прикладу, необхідно додати модель `News` із методом `getLatestNews` у користувальницьку частину та ще одну модель `News` із методом `getDeletedNews` у адміністративну частину. У загальному випадку моделі не повинні безпосередньо взаємодіяти з користувачем. тобто: * не повинні використовувати `$_GET`, `$_POST` або інші подібні змінні, які напряму отримуємо із запиту користувача, так як моделі можуть використовуватися у зовсім інших піддодатках (наприклад, у модульних тестах або API), у яких ці змінні недоступні. Всі змінні, які відносяться до запиту користувача, повинні оброблятися у контролері; * не повинні генерувати HTML або інший код представлення, так як він може змінюватися незалежно від потреб користувача (тобто, користувальницька частина та адміністративна частина можуть відображати новини у абсолютно різному форматі). Такий код повинен оброблятися у представленнях. Представлення ------------- [Представлення](/doc/guide/basics.view) відповідають за відображення моделей у потрібному користувачу форматі. У загальному випадку представлення * повинні, головним чином, містити розмітку, таку як HTML, або простий PHP код, який використовується для обходу, форматування та відображення даних; * не повинні безпосередньо звертатися до бази даних. Цим повинні займатися моделі; * не повинні безпосередньо звертатися до `$_GET`, `$_POST` та іншим змінним, які отримуються із запиту користувача. Цю задачу повинен виконувати контролер. Представлення повинно використовуватися для задання зовнішнього вигляду даних, отриманих із контролера та моделі; * може безпосередньо звертатися до властивостей та методів контролера або моделей. При цьому дані властивості та методи повинні відповідати за відображення. Представлення можна використовувати повторно кількома способами: * Загальний шаблон: у нього можна винести розмітку, загальну для всіх сторінок. Наприклад, шапку та підвал; * Частини шаблону: використовуються всередині інших шаблонів та, як правило, не використовуються із загальним шаблоном. Приміром, частину шаблону `_form.php` можна використовувати для відображення форми вводу моделі, яка буде використовуватися як при її створенні, так і при редагуванні; * Віджети: використовуються у тому випадку, коли для частини шаблону потрібно занадто багато логіки. При цьому логіка переноситься у клас віджета. Віджет, який генерує велику кількість розмітки, може використовувати свої шаблони представлень; * Хелпери: у шаблонах часто потрібно виконувати невеликі завдання, такі як форматування даних або генерація HTML-тегів. Замість того, щоб вставляти код безпосередньоу у шаблони, можна помістити його в клас-хелпер та використовувати цей клас у шаблонах. Наприклад, такий підхід можна знайти у класі [CHtml], який допомогає генерувати часто використовуваний HTML код. Для того, щоб уникнути явного підключення класів, хелпери можна помістити у [окрему директорію, яка прописана у import](/doc/guide/basics.namespace). Контролер --------- [Контролери](/doc/guide/basics.controller) — сполучна ланка, яка поєднує моделі, представлення та інші компоненти у робочий додаток. Контролер відповідає за обробку запитів користувача. Тому контролер * може звертатися до `$_GET`, `$_POST` та інших змінних PHP, які отримуються із запиту користувача; * може створювати екземпляри моделей та керувати ними. Приміром, у типової дії оновлення моделі контролер може спочатку створити екземпляр моделі, потім заповнити його даними із `$_POST` та, після вдалого збереження моделі, перенаправити браузер користувача на сторінку створеної моделі. Слід відмітити, що саме зберігання моделі повинно бути реалізоване у моделі, а не в контролері; * не повинен містити SQL-запитів. Їх краще тримати у моделях; * не повинен містити HTML та іншої розмітки. Її варто винести у представлення. У добре спроектованому MVC-додатку контролери зазвичай дуже тонкі та містять тільки декілька десятків рядків коду. У той же час, моделі дуже товсті та містять більшу частину коду, повʼязану із обробкою даних, так як структура даних та бізнес-логіка, які там міститься, зазвичай доволі специфічні для конкретного додатку. Логіка контролеру, навпаки, доволі типова та може бути винесена у базові класи.