В 7-ом уроке мы изучим одну из ключевых тем разработки на Symfony, это CRUD-операции. Если вы хотите создавать полноценные веб-приложения, без понимания CRUD (Create, Read, Update, Delete) не обойтись. В этом уроке я покажу, как быстро генерировать CRUD-интерфейсы с помощью команды make:crud, реализовывать пагинацию, добавлять поиск и фильтрацию данных, а также дам практические задания для закрепления материала.
Создание CRUD через make:crud
Symfony предоставляет мощный инструмент для автоматической генерации CRUD-интерфейсов, команду make:crud. Давайте разберемся, как она работает.
Шаг 1: Подготовка сущности
Предположим, у нас есть сущность Product (продукт) с полями name, price и description. Если вы еще не создали ее, выполните:
php bin/console make:entity Product
Заполните поля:
name(string, 255)price(float)description(text)
После этого сгенерируйте миграцию и выполните ее:
php bin/console make:migration php bin/console doctrine:migrations:migrate
Шаг 2: Генерация CRUD
Теперь запустите команду:
php bin/console make:crud Product
Symfony автоматически создаст:
- Контроллер
ProductControllerс методами для CRUD. - Шаблоны Twig в папке
templates/product/. - Форму для создания и редактирования продукта в
Form/ProductType.php.
Что внутри контроллера?
Давайте посмотрим на метод index, который отвечает за отображение списка продуктов:
// src/Controller/ProductController.php public function index(ProductRepository $productRepository): Response { return $this->render('product/index.html.twig', [ 'products' => $productRepository->findAll(), ]); }
Этот код получает все продукты из базы и передает их в шаблон index.html.twig.
Реализация CRUD-операций
Хотя make:crud уже создал базовый функционал, важно понимать, как работают методы в контроллере.
Создание записи (Create)
Метод new в ProductController обрабатывает создание продукта:
public function new(Request $request, ProductRepository $productRepository): Response { $product = new Product(); $form = $this->createForm(ProductType::class, $product); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $productRepository->save($product, true); return $this->redirectToRoute('app_product_index'); } return $this->render('product/new.html.twig', [ 'form' => $form->createView(), ]); }
ProductType— форма, связанная с сущностьюProduct.- После валидации данные сохраняются через репозиторий.
Чтение данных (Read)
Метод show отображает детали продукта:
public function show(Product $product): Response { return $this->render('product/show.html.twig', [ 'product' => $product, ]); }
Обратите внимание: Symfony автоматически находит продукт по его ID благодаря параметру Product $product.
Обновление (Update)
Метод edit работает аналогично new, но заполняет форму существующими данными:
public function edit(Request $request, Product $product, ProductRepository $productRepository): Response { $form = $this->createForm(ProductType::class, $product); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $productRepository->save($product, true); return $this->redirectToRoute('app_product_index'); } return $this->render('product/edit.html.twig', [ 'product' => $product, 'form' => $form->createView(), ]); }
Удаление (Delete)
Метод delete удаляет запись:
public function delete(Request $request, Product $product, ProductRepository $productRepository): Response { if ($this->isCsrfTokenValid('delete'.$product->getId(), $request->request->get('_token'))) { $productRepository->remove($product, true); } return $this->redirectToRoute('app_product_index'); }
Symfony проверяет CSRF-токен для защиты от подделки запросов.
Пагинация данных
Если в базе тысячи записей, вывод всех данных на одной странице неэффективен. Реализуем пагинацию с помощью библиотеки KnpPaginatorBundle.
Установка бандла
Выполните команду:
composer require knplabs/knp-paginator-bundle
Модификация контроллера
Изменим метод index в ProductController:
use Knp\Component\Pager\PaginatorInterface; public function index(Request $request, ProductRepository $productRepository, PaginatorInterface $paginator): Response { $query = $productRepository->createQueryBuilder('p')->getQuery(); $pagination = $paginator->paginate( $query, $request->query->getInt('page', 1), // Номер страницы 10 // Количество элементов на странице ); return $this->render('product/index.html.twig', [ 'pagination' => $pagination, ]); }
Обновление шаблона
В templates/product/index.html.twig замените вывод данных на:
{% for product in pagination %} <!-- Вывод продукта --> {% endfor %} {{ knp_pagination_render(pagination) }}
Теперь на странице будут отображаться по 10 продуктов с навигацией между страницами.
Поиск и фильтрация
Добавим возможность фильтровать продукты по названию и цене.
Шаг 1: Создание формы фильтрации
Создайте форму ProductFilterType:
// src/Form/ProductFilterType.php use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; class ProductFilterType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('name', TextType::class, ['required' => false]) ->add('minPrice', NumberType::class, ['required' => false]) ->add('maxPrice', NumberType::class, ['required' => false]); } }
Шаг 2: Модификация репозитория
Добавьте метод search в ProductRepository:
public function search(?string $name, ?float $minPrice, ?float $maxPrice): QueryBuilder { $qb = $this->createQueryBuilder('p'); if ($name) { $qb->andWhere('p.name LIKE :name') ->setParameter('name', '%' . $name . '%'); } if ($minPrice) { $qb->andWhere('p.price >= :minPrice') ->setParameter('minPrice', $minPrice); } if ($maxPrice) { $qb->andWhere('p.price <= :maxPrice') ->setParameter('maxPrice', $maxPrice); } return $qb; }
Шаг 3: Обновление контроллера
Внесите изменения в метод index:
public function index(Request $request, ProductRepository $productRepository, PaginatorInterface $paginator): Response { $filterForm = $this->createForm(ProductFilterType::class); $filterForm->handleRequest($request); $name = $filterForm->get('name')->getData(); $minPrice = $filterForm->get('minPrice')->getData(); $maxPrice = $filterForm->get('maxPrice')->getData(); $query = $productRepository->search($name, $minPrice, $maxPrice)->getQuery(); $pagination = $paginator->paginate($query, $request->query->getInt('page', 1), 10); return $this->render('product/index.html.twig', [ 'pagination' => $pagination, 'filterForm' => $filterForm->createView(), ]); }
Шаг 4: Добавление формы в шаблон
Вставьте форму в index.html.twig:
{{ form_start(filterForm) }} {{ form_widget(filterForm) }} <button type="submit" class="btn btn-primary">Фильтровать</button> {{ form_end(filterForm) }}
Теперь пользователи могут искать продукты по названию и диапазону цен.
Практические задания
Чтобы закрепить материал, выполните следующие задачи:
- Добавьте поле
categoryв сущностьProductи обновите CRUD.- Создайте сущность
Category. - Добавьте отношение ManyToOne между
ProductиCategory. - Обновите формы и шаблоны.
- Создайте сущность
- Реализуйте пагинацию для сущности
Category.- Установите KnpPaginatorBundle, если еще не сделали этого.
- Модифицируйте контроллер и шаблон.
- Создайте фильтр для категорий в списке продуктов.
- Добавьте поле выбора категории в
ProductFilterType. - Измените метод
searchв репозитории.
- Добавьте поле выбора категории в
Сегодня мы разобрали, как создавать CRUD-интерфейсы в Symfony, работать с пагинацией и фильтрами. Эти навыки критически важны для любого backend-разработчика. Если что-то осталось непонятным, перечитайте урок еще раз или перейдите к практическим заданиям, они помогут лучше усвоить материал.
Полный курс по Symfony для начинающих по ссылке: https://max-gabov.ru/symphony-dlya-nachinaushih
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


