Повторюваний код — це не просто “некрасиво”. Це прямий ризик регресій: ви виправили баг в одному місці, а в іншому забули. Через місяць у вас два різні “правильні” варіанти однієї логіки. Тому винос повторюваних шматків — це навик, який переводить розробку з “латання” у системність. Але робити це треба акуратно: не переписувати ядро, а виносити рівно те, що дублюється, залишаючи поведінку незмінною.
Починаємо з головного правила: винос робиться після того, як ви чітко бачите повтор. Не “наперед”, не “можливо колись знадобиться”, а коли у вас є 2–3 однакові шматки. Причина проста: завчасні абстракції майже завжди неправильні — ви не знаєте, що саме буде спільним. А от коли дублювання вже є, ви можете винести “мінімальний спільний знаменник” і не зламати поведінку.
Далі — як вибрати інструмент: partial, component чи trait. Partial — це просто повторюваний шматок Blade, який ви вставляєте через @include. Він ідеальний для простих фрагментів розмітки: однакові кнопки, однакова шапка форми, блок флеш-повідомлень, таблиця фільтрів, панель дій. Component — це “розумніший” UI-блок (Blade component), який має чіткі параметри, може мати слоти, і дозволяє будувати бібліотеку UI-елементів. Trait — це PHP-механізм для повторюваної логіки в класах (контролери, моделі, сервіси), але його треба застосовувати обережно, щоб не зробити “міксин-кашу”.
Почнемо з Blade partials. Це найпростіший і найбезпечніший рівень. Типові кандидати: блок показу помилок валідації, блок flash success/error, заголовок сторінки з breadcrumbs, таблиця фільтрів (date_from/date_to/status), кнопки “Створити/Зберегти/Скасувати”, row-actions (Edit/Delete), повторювані “карточки” з однаковою структурою. Винос у partial дає вам один файл, де змінюється UI-деталь, і вона відразу змінюється скрізь. Головний плюс partial — мінімальна магія: це той самий Blade, тільки в окремому файлі.
Але partialи часто перетворюються на “мішок параметрів”, якщо ви не дисципліновані. Щоб цього уникнути, partial має приймати чіткий набір даних: наприклад, ['filters' => $filters, 'url' => $actionUrl]. Не передавайте туди “весь $refs і весь $doc і весь $request” просто тому, що лінь. Це робить partial залежним від контексту і важко переносимим. Правило: partial отримує рівно те, що йому треба для відображення.
Далі — Blade components. Компоненти потрібні тоді, коли partial уже не тягне: у вас багато місць, де потрібен один і той самий елемент з варіаціями. Наприклад: універсальна кнопка з іконкою, input з помилкою, select з пошуком, бейдж статусу (draft/published/cancelled), компонент таблиці з шапкою/порожнім станом/пагінацією, компонент “фільтри” з чіткими слотами. Компонент виграє тим, що він має контракт: атрибути (props), слоти, дефолти. Це робить UI передбачуваним і зменшує “хаос верстки”.
Найважливіша дисципліна для компонентів: вони не повинні лізти в бізнес-логіку. Компонент — це представлення. Він може форматувати (дати, бейджі, класи), але не повинен вирішувати “кому можна бачити”. Доступи вирішуються у контролері/policy і вже готові прапори передаються в компонент (наприклад, canEdit, canDelete). Інакше ви отримаєте розмазану авторизацію по Blade-файлах, що дуже небезпечно.
Тепер про traits у PHP. Traits доречні, коли у вас реально повторюється код у контролерах/сервісах, і це не просто “два рядки”, а повторюваний алгоритм. Типові приклади: однакове будування фільтрів для index-сторінок (нормалізація query-параметрів), однакові helper-методи для відповіді (redirect з флешем), повторювані правила для scoping по account_id/company_id, повторюваний код для логування activity. Але тут важливий ризик: trait може сховати складну поведінку і зробити клас нечитабельним. Тому правило: trait має бути маленьким, з конкретною темою, і не мати прихованих побічних ефектів.
Правильний підхід у навчанні: якщо у вас повторюється логіка “записати activity log”, не робіть trait, який “сам все зробить з магією”. Краще зробити маленький ActivityLogger service і викликати його. Trait доречний, коли він спрощує, а не ховає. Наприклад, AppliesIndexFilters може містити метод applyDateRange($q, $from, $to, $column) і applyStatus($q, $status), але не повинен сам вирішувати, який колонкою фільтрувати “сьогодні”.
Як знайти, що саме виносити. Ви дивитесь на три рівні: UI, контролер/запити, доменна логіка. Якщо повтор — у верстці, виносите в partial/component. Якщо повтор — у підготовці даних для views (наприклад, один і той самий список довідників, однакові фільтри), це кандидат у helper-метод або в окремий “QueryFilter” клас, а не в Blade. Якщо повтор — у бізнес-дії (create/update + транзакція + activity), це кандидат у service. Виносити бізнес-логіку в Blade — помилка.
Найпоширеніша практична схема для вашого типу проєкту така. В UI ви виносите: flash/errors, панель фільтрів, бейджі статусів, кнопки row-actions, пусті стани (empty state), пагінацію. У PHP ви виносите: клас фільтрів для списку (набори when(...)), спільні методи формування query, і service-методи create/update з транзакцією. А доступи ви тримаєте централізовано: policy/gate + один “базовий scope” видимості. Це дає мінімум ризику.
Щоб рефакторинг був “без ризику”, є один залізний порядок: спочатку тести/перевірка поведінки, потім винесення, потім повторна перевірка. Якщо тестів немає — хоча б ручний чек-лист: фільтри працюють, доступи не змінилися, пагінація не губить query, форма показує errors. Винос повторюваного коду не має змінити результат. Якщо результат змінився — ви не “рефакторили”, ви “переписали”.
Висновок: partials — для простих повторів у Blade, components — для повторюваних UI-елементів з чітким контрактом, traits — для невеликих повторів у PHP, але обережно і без прихованої логіки. Головна мета — прибрати дублювання так, щоб поведінка лишилася тією самою. Якщо ви збережете це правило, ви зможете масштабувати інтерфейс і модулі без “копіпасту”, який потім неможливо підтримувати.