Урок 7: CRUD-операции в Symfony

В 7-ом уроке мы изучим одну из ключевых тем разработки на Symfony, это CRUD-операции. Если вы хотите создавать полноценные веб-приложения, без понимания CRUD (Create, Read, Update, Delete) не обойтись. В этом уроке я покажу, как быстро генерировать CRUD-интерфейсы с помощью команды make:crud, реализовывать пагинацию, добавлять поиск и фильтрацию данных, а также дам практические задания для закрепления материала.

Создание CRUD через make:crud

Symfony предоставляет мощный инструмент для автоматической генерации CRUD-интерфейсов, команду make:crud. Давайте разберемся, как она работает.

Шаг 1: Подготовка сущности

Предположим, у нас есть сущность Product (продукт) с полями nameprice и description. Если вы еще не создали ее, выполните:

bash
php bin/console make:entity Product

Заполните поля:

  • name (string, 255)
  • price (float)
  • description (text)

После этого сгенерируйте миграцию и выполните ее:

bash
php bin/console make:migration
php bin/console doctrine:migrations:migrate

Шаг 2: Генерация CRUD

Теперь запустите команду:

bash
php bin/console make:crud Product

Symfony автоматически создаст:

  • Контроллер ProductController с методами для CRUD.
  • Шаблоны Twig в папке templates/product/.
  • Форму для создания и редактирования продукта в Form/ProductType.php.

Что внутри контроллера?
Давайте посмотрим на метод index, который отвечает за отображение списка продуктов:

php
// 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 обрабатывает создание продукта:

php
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 отображает детали продукта:

php
public function show(Product $product): Response
{
    return $this->render('product/show.html.twig', [
        'product' => $product,
    ]);
}

Обратите внимание: Symfony автоматически находит продукт по его ID благодаря параметру Product $product.

Обновление (Update)

Метод edit работает аналогично new, но заполняет форму существующими данными:

php
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 удаляет запись:

php
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.

Установка бандла

Выполните команду:

bash
composer require knplabs/knp-paginator-bundle

Модификация контроллера

Изменим метод index в ProductController:

php
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 замените вывод данных на:

twig
{% for product in pagination %}
    <!-- Вывод продукта -->
{% endfor %}

{{ knp_pagination_render(pagination) }}

Теперь на странице будут отображаться по 10 продуктов с навигацией между страницами.

Поиск и фильтрация

Добавим возможность фильтровать продукты по названию и цене.

Шаг 1: Создание формы фильтрации

Создайте форму ProductFilterType:

php
// 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:

php
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:

php
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:

twig
{{ form_start(filterForm) }}
    {{ form_widget(filterForm) }}
    <button type="submit" class="btn btn-primary">Фильтровать</button>
{{ form_end(filterForm) }}

Теперь пользователи могут искать продукты по названию и диапазону цен.

Практические задания

Чтобы закрепить материал, выполните следующие задачи:

  1. Добавьте поле category в сущность Product и обновите CRUD.
    • Создайте сущность Category.
    • Добавьте отношение ManyToOne между Product и Category.
    • Обновите формы и шаблоны.
  2. Реализуйте пагинацию для сущности Category.
    • Установите KnpPaginatorBundle, если еще не сделали этого.
    • Модифицируйте контроллер и шаблон.
  3. Создайте фильтр для категорий в списке продуктов.
    • Добавьте поле выбора категории в ProductFilterType.
    • Измените метод search в репозитории.

Сегодня мы разобрали, как создавать CRUD-интерфейсы в Symfony, работать с пагинацией и фильтрами. Эти навыки критически важны для любого backend-разработчика. Если что-то осталось непонятным, перечитайте урок еще раз или перейдите к практическим заданиям, они помогут лучше усвоить материал.

Полный курс по Symfony для начинающих по ссылке: https://max-gabov.ru/symphony-dlya-nachinaushih

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий