Техническое задание: Каталог рецептов с рейтингом
1. Описание проекта
Кулинарная платформа для публикации рецептов, комментирования и оценки блюд. Проект закрепляет навыки CRUD, отношений многие-ко-многим (рецепт ↔ ингредиенты), расчёта среднего рейтинга, разграничения прав и совместной работы через Git.
Команда: 2–3 человека
2. Функциональные требования
2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
2.2. Каталог рецептов
- Просмотр списка рецептов с пагинацией
- Фильтрация по категориям (завтрак, обед, ужин, выпечка, десерты, напитки)
- Поиск по названию и ингредиентам
- Сортировка по дате добавления, рейтингу, времени приготовления
- Детальная страница рецепта
2.3. Управление рецептами (автор/админ)
- CRUD рецептов: название, описание, категория, время приготовления, количество порций, изображение, сложность
- Пошаговое приготовление (несколько шагов, каждое — отдельная запись)
- Привязка ингредиентов с количеством и единицей измерения
- Загрузка изображения рецепта (валидация: jpg/png, макс. 2MB)
2.4. Ингредиенты
- Справочник ингредиентов (название, единица измерения по умолчанию)
- CRUD ингредиентов (админ)
- Привязка к рецептам (многие-ко-многим) с указанием количества и единицы
2.5. Комментарии
- Добавление комментариев к рецепту
- Удаление своего комментария
- Удаление любого комментария (админ/автор рецепта)
- Пагинация комментариев
2.6. Оценки и рейтинг
- Оценка рецепта (1–5 звёзд) — один голос на пользователя
- Отображение среднего рейтинга на карточке и странице рецепта
- Автоматический пересчёт среднего рейтинга
2.7. Избранное
- Добавление рецепта в избранное
- Список избранных рецептов (личный кабинет)
2.8. Личный кабинет автора
- Мои рецепты
- Статистика: количество рецептов, средний рейтинг, количество комментариев
- Избранные рецепты
2.9. Панель администратора
- Управление пользователями и ролями
- CRUD ингредиентов (справочник)
- Модерация рецептов и комментариев
- Общая статистика
3. Структура базы данных
Таблица users
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| name |
VARCHAR(255) |
Имя |
| email |
VARCHAR(255) |
Email (unique) |
| password |
VARCHAR(255) |
Хеш пароля |
| role |
ENUM('admin', 'author', 'user') |
Роль |
| avatar |
VARCHAR(255) | NULL |
Аватар |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица categories
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| name |
VARCHAR(255) |
Название категории |
| slug |
VARCHAR(255) |
URL-идентификатор (unique) |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица recipes
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| title |
VARCHAR(255) |
Название рецепта |
| slug |
VARCHAR(255) |
URL-идентификатор (unique) |
| description |
TEXT |
Краткое описание |
| image |
VARCHAR(255) | NULL |
Изображение |
| category_id |
BIGINT UNSIGNED |
Категория (FK → categories) |
| author_id |
BIGINT UNSIGNED |
Автор (FK → users) |
| cooking_time |
INT |
Время приготовления (минуты) |
| servings |
INT |
Количество порций |
| difficulty |
ENUM('easy', 'medium', 'hard') |
Сложность |
| rating |
DECIMAL(3, 2) | NULL |
Средний рейтинг (0.00–5.00) |
| rating_count |
INT |
Количество оценок |
| is_published |
BOOLEAN |
Опубликован ли |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица recipe_steps
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| recipe_id |
BIGINT UNSIGNED |
Рецепт (FK → recipes) |
| step_number |
INT |
Порядковый номер шага |
| description |
TEXT |
Описание шага |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица ingredients
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| name |
VARCHAR(255) |
Название ингредиента (unique) |
| default_unit |
VARCHAR(50) |
Единица измерения по умолчанию (г, мл, шт, ст.л.) |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица ingredient_recipe (многие-ко-многим)
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| recipe_id |
BIGINT UNSIGNED |
FK → recipes |
| ingredient_id |
BIGINT UNSIGNED |
FK → ingredients |
| quantity |
DECIMAL(10, 2) |
Количество |
| unit |
VARCHAR(50) |
Единица измерения |
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| body |
TEXT |
Текст комментария |
| user_id |
BIGINT UNSIGNED |
Автор (FK → users) |
| recipe_id |
BIGINT UNSIGNED |
Рецепт (FK → recipes) |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица ratings
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| user_id |
BIGINT UNSIGNED |
FK → users |
| recipe_id |
BIGINT UNSIGNED |
FK → recipes |
| rating |
TINYINT |
Оценка (1–5) |
| created_at |
TIMESTAMP |
|
| updated_at |
TIMESTAMP |
|
Таблица recipe_user (избранное)
| Поле |
Тип |
Описание |
| id |
BIGINT UNSIGNED |
Первичный ключ |
| user_id |
BIGINT UNSIGNED |
FK → users |
| recipe_id |
BIGINT UNSIGNED |
FK → recipes |
| added_at |
TIMESTAMP |
|
4. Маршруты и контроллеры
// Публичные маршруты
Route::get('/', [RecipeController::class, 'index'])->name('recipes.index');
Route::get('/recipes/{recipe:slug}', [RecipeController::class, 'show'])->name('recipes.show');
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Оценки
Route::post('/recipes/{recipe}/rate', [RatingController::class, 'store'])->name('recipes.rate');
// Избранное
Route::get('/favorites', [FavoriteController::class, 'index'])->name('favorites.index');
Route::post('/recipes/{recipe}/favorite', [FavoriteController::class, 'toggle'])->name('recipes.favorite');
// Комментарии
Route::resource('recipes.comments', CommentController::class)->scoped(['recipes' => 'recipe'])->only(['store', 'destroy']);
// Мои рецепты (автор)
Route::prefix('my-recipes')->name('my-recipes.')->middleware('can:be-author')->group(function () {
Route::resource('recipes', AuthorRecipeController::class);
Route::get('dashboard', [AuthorDashboardController::class, 'index'])->name('dashboard');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::resource('ingredients', AdminIngredientController::class);
Route::patch('recipes/{recipe}/toggle-publish', [AdminRecipeController::class, 'togglePublish'])->name('recipes.publish');
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
Контроллеры
RecipeController — публичный каталог и детальный просмотр
AuthorRecipeController — CRUD рецептов автором
RatingController — добавление/обновление оценки
FavoriteController — управление избранным
CommentController — создание/удаление комментариев
AuthorDashboardController — панель автора
AdminUserController, AdminIngredientController, AdminRecipeController, AdminDashboardController — админка
5. Роли, Gates и Policies
Роли
| Роль |
Описание |
admin |
Полный доступ, модерация, CRUD ингредиентов, управление пользователями |
author |
Создание/редактирование своих рецептов, управление комментариями к своим рецептам |
user |
Просмотр каталога, комментирование, оценка, добавление в избранное |
Gates
Gate::define('be-author', function (User $user) {
return in_array($user->role, ['admin', 'author']);
});
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
Policies
RecipePolicy:
view — все (если опубликован), автор/admin (если не опубликован)
create — author или admin
update, delete — автор рецепта или admin
rate — user (ещё не оценивал этот рецепт)
CommentPolicy:
create — все аутентифицированные
delete — автор комментария, автор рецепта или admin
IngredientPolicy:
view — все
create, update, delete — только admin
6. Требования к интерфейсу (Bootstrap 5)
Компоненты
- Навбар — логотип, каталог, «Избранное», «Мои рецепты», профиль
- Карточки рецептов — изображение, название, категория (бейдж), время (иконка + текст), рейтинг (звёзды)
- Страница рецепта — большое изображение, описание, ингредиенты (таблица), шаги приготовления (нумерованный список), комментарии, форма оценки
- Звёздный рейтинг — интерактивные звёзды (radio buttons + стилизация)
- Форма рецепта — Bootstrap-форма с загрузкой изображения, динамическое добавление шагов и ингредиентов
- Таблица ингредиентов — название, количество, единица измерения
- Дашборд автора — статистика (карточки), список моих рецептов
- Адаптивная сетка —
col-md-4 для карточек, col-md-8 + col-md-4 для основного контента и сайдбара
Цветовая схема сложности
- Лёгкий —
bg-success
- Средний —
bg-warning
- Сложный —
bg-danger
Цветовая схема категорий
- Завтрак —
bg-info
- Обед —
bg-primary
- Ужин —
bg-dark
- Выпечка —
bg-warning
- Десерты —
bg-pink
- Напитки —
bg-light text-dark
7. Git-workflow для команды
Распределение модулей (для 2 человек)
| Участник |
Модуль |
Ветки |
| Участник A |
Рецепты, категории, шаги, ингредиенты, каталог |
feature/recipes-crud, feature/categories, feature-recipe-steps |
| Участник B |
Оценки, комментарии, избранное, дашборд, UI |
feature-ingredients, feature-recipe-ingredients-m2m, feature-ratings |
Распределение модулей (для 3 человек)
| Участник |
Модуль |
Ветки |
| Участник A |
Рецепты, категории, шаги приготовления, загрузка изображений |
feature/recipes-crud, feature/categories, feature-recipe-steps |
| Участник B |
Ингредиенты, многие-ко-многим, оценки, расчёт рейтинга |
feature-ingredients, feature-recipe-ingredients-m2m, feature-ratings |
| Участник C |
Комментарии, избранное, каталог, UI, дашборд |
feature-comments, feature-favorites, feature-recipe-catalog-ui |
Правила
- Ветка
develop — основная ветка разработки
- Каждый участник создаёт фич-ветки от
develop
- Минимум 3 PR на участника
- Обязательный код-ревью перед мёржем
- Conventional Commits
Визуализация workflow (2 участника)
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/recipes-crud
checkout develop
branch feature-ingredients
checkout feature/recipes-crud
commit id: "feat: add recipes CRUD"
commit id: "feat: add recipe steps"
checkout feature-ingredients
commit id: "feat: add ingredients m2m"
commit id: "feat: add ratings system"
checkout develop
merge feature/recipes-crud tag: "PR + review"
merge feature-ingredients tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
Визуализация workflow (3 участника)
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/recipes-crud
checkout develop
branch feature-ingredients
checkout develop
branch feature-comments
checkout feature/recipes-crud
commit id: "feat: add recipes CRUD"
commit id: "feat: add recipe categories"
checkout feature-ingredients
commit id: "feat: add ingredients m2m"
commit id: "feat: add ratings system"
checkout feature-comments
commit id: "feat: add recipe comments"
commit id: "feat: add favorites system"
checkout develop
merge feature/recipes-crud tag: "PR + review"
merge feature-ingredients tag: "PR + review"
merge feature-comments tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
8. Критерии приёмки
Обязательно
Дополнительно (бонусные баллы)
9. Дополнительные задания (для продвинутых)
- Масштабирование рецепта: калькулятор для пересчёта ингредиентов на другое количество порций
- Планировщик питания: календарь с рецептами на неделю
- Коллекции: пользовательские подборки рецептов (как плейлисты)
- Тесты: PHPUnit Feature-тесты для RecipeController и RatingController
- API: RESTful API для каталога и создания рецептов
- Queue: асинхронный пересчёт рейтинга при большом количестве оценок
- Events & Listeners: событие «новая оценка» → пересчёт рейтинга
10. Рекомендуемые материалы