Доступ к базам данных в Symfony

Бизюк Андрей

ВГТУ

2024-12-03

Введение

В Symfony доступ к базам данных осуществляется с помощью Doctrine ORM (Object-Relational Mapping). Doctrine позволяет работать с базой данных как с набором объектов, что облегчает взаимодействие с базой данных и делает код более читабельным и поддерживаемым. Ниже приведены основные шаги для работы с базой данных в Symfony с помощью Doctrine ORM.

Установка Doctrine ORM

Для начала необходимо установить Doctrine ORM в проект Symfony. Это можно сделать с помощью Composer, добавив следующую строку в файл composer.json:

composer require doctrine/orm

Настройка подключения к базе данных

После установки Doctrine необходимо настроить подключение к базе данных. Это можно сделать в файле конфигурации .env, добавив следующие строки:

DATABASE_URL=mysql://db_user:db_password@localhost:3306/db_name

Здесь db_user, db_password и db_name - имя пользователя, пароль и имя базы данных соответственно.

Создание сущностей

Для работы с базой данных необходимо создать сущности, которые будут представлять таблицы в базе данных. Для создания сущностей можно использовать консольную утилиту Doctrine. Например, для создания сущности User необходимо выполнить следующую команду:

php bin/console make:entity User

В процессе создания сущности необходимо указать поля, которые будут соответствовать столбцам в таблице базы данных.

Создание таблиц в базе данных

После создания сущностей необходимо создать таблицы в базе данных. Для этого можно использовать консольную утилиту Doctrine. Например, для создания таблиц необходимо выполнить следующую команду:

php bin/console doctrine:schema:update --force

Добавление, изменение и удаление данных

Для добавления, изменения и удаления данных в базе данных можно использовать методы сущностей и EntityManager Doctrine. Например, для добавления нового пользователя в базу данных необходимо выполнить следующий код:

$user = new User();
$user->setName('John Doe');
$user->setEmail('john.doe@example.com');

$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();

Здесь создается новый объект User, задаются значения полей и сохраняется объект в базе данных с помощью EntityManager.

Получение данных из базы данных

Для получения данных из базы данных можно использовать методы EntityManager и Repository Doctrine. Например, для получения всех пользователей из базы данных необходимо выполнить следующий код:

$repository = $this->getDoctrine()->getRepository(User::class);
$users = $repository->findAll();

Здесь получается объект Repository для сущности User и вызывается метод findAll(), который возвращает все записи из таблицы пользователей.

Отношения между сущностями

В Symfony, при работе с ORM Doctrine, отношения между сущностями можно устанавливать с помощью аннотаций или YAML-файлов. Существует несколько типов отношений между сущностями:

  1. One-To-One (Один-ко-одному) - каждая сущность соответствует только одной сущности другого типа. Например, у пользователя может быть только один профиль.
/**
 * @ORM\Entity
 */
class User
{
    // ...

    /**
     * @ORM\OneToOne(targetEntity="Profile", inversedBy="user")
     * @ORM\JoinColumn(name="profile_id", referencedColumnName="id")
     */
    private $profile;

    // ...
}

/**
 * @ORM\Entity
 */
class Profile
{
    // ...

    /**
     * @ORM\OneToOne(targetEntity="User", mappedBy="profile")
     */
    private $user;

    // ...
}
  1. One-To-Many (Один-ко-многим) - одна сущность может соответствовать многим сущностям другого типа. Например, у одного автора может быть несколько книг.
/**
 * @ORM\Entity
 */
class Author
{
    // ...

    /**
     * @ORM\OneToMany(targetEntity="Book", mappedBy="author")
     */
    private $books;

    // ...
}

/**
 * @ORM\Entity
 */
class Book
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="books")
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id")
     */
    private $author;

    // ...
}
  1. Many-To-Many (Многие-ко-многим) - несколько сущностей одного типа могут соответствовать нескольким сущностям другого типа. Например, у одного студента может быть несколько предметов, а у одного предмета может быть несколько студентов.
/**
 * @ORM\Entity
 */
class Student
{
    // ...

    /**
     * @ORM\ManyToMany(targetEntity="Subject", inversedBy="students")
     * @ORM\JoinTable(name="student_subject")
     */
    private $subjects;

    // ...
}

/**
 * @ORM\Entity
 */
class Subject
{
    // ...

    /**
     * @ORM\ManyToMany(targetEntity="Student", mappedBy="subjects")
     */
    private $students;

    // ...
}

Для работы с отношениями между сущностями можно использовать методы EntityManager и Repository, а также методы сущностей. Например, для получения всех книг конкретного автора можно выполнить следующий код:

$repository = $this->getDoctrine()->getRepository(Author::class);
$author = $repository->findOneById(1);
$books = $author->getBooks();

Здесь получается объект Repository для сущности Author, затем получается объект конкретного автора по его id и вызывается метод getBooks(), который возвращает все книги этого автора.

Наследование

В Symfony, при работе с ORM Doctrine, наследование сущностей можно реализовать с помощью аннотаций или YAML-файлов. Существует несколько типов наследования:

  1. Single Table Inheritance (STI) - все классы-наследники хранятся в одной таблице, при этом используется дискриминатор для определения типа сущности.
/**
 * @ORM\Entity
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"user" = "User", "admin" = "Admin"})
 */
class User
{
    // ...
}

/**
 * @ORM\Entity
 */
class Admin extends User
{
    // ...
}

В данном примере классы User и Admin хранятся в одной таблице, а для определения типа сущности используется дискриминатор type.

  1. Class Table Inheritance (CTI) - каждый класс-наследник хранится в своей таблице, при этом используются внешние ключи для связи таблиц.
/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"user" = "User", "admin" = "Admin"})
 */
class User
{
    // ...
}

/**
 * @ORM\Entity
 */
class Admin extends User
{
    // ...
}

В данном примере класс User хранится в своей таблице, а класс Admin хранится в своей таблице, при этом используется внешний ключ для связи таблиц.

  1. Mapped Superclass - класс-родитель не является сущностью, а только определяет общие поля для классов-наследников.
/**
 * @ORM\MappedSuperclass
 */
class BaseEntity
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    // ...
}

/**
 * @ORM\Entity
 */
class User extends BaseEntity
{
    // ...
}

В данном примере класс BaseEntity не является сущностью, а только определяет общие поля для классов-наследников, таких как User.

Для работы с наследованием сущностей можно использовать методы EntityManager и Repository, а также методы сущностей. Например, для получения всех пользователей, включая администраторов, можно выполнить следующий код:

$repository = $this->getDoctrine()->getRepository(User::class);
$users = $repository->findAll();

Здесь получается объект Repository для сущности User и вызывается метод findAll(), который возвращает все записи из таблицы пользователей, включая администраторов.

События жизненного цикла

В Symfony, при работе с ORM Doctrine, существует несколько событий жизненного цикла сущностей, которые можно использовать для выполнения дополнительных действий при создании, обновлении, удалении и других операциях над сущностями.

Существует два типа событий жизненного цикла:

  1. События, связанные с операциями над сущностями:
  • prePersist - вызывается перед сохранением новой сущности в базе данных.
  • postPersist - вызывается после сохранения новой сущности в базе данных.
  • preUpdate - вызывается перед обновлением существующей сущности в базе данных.
  • postUpdate - вызывается после обновления существующей сущности в базе данных.
  • preRemove - вызывается перед удалением сущности из базы данных.
  • postRemove - вызывается после удаления сущности из базы данных.
  1. События, связанные с транзакциями:
  • preFlush - вызывается перед выполнением операций над сущностями в базе данных.
  • postFlush - вызывается после выполнения операций над сущностями в базе данных.
  • onFlush - вызывается во время выполнения операций над сущностями в базе данных.

Для использования событий жизненного цикла необходимо создать слушателей событий (event listener) или подписчиков событий (event subscriber), которые будут реагировать на события. Слушатели событий могут быть зарегистрированы в виде сервисов Symfony, либо непосредственно в коде сущностей с помощью аннотаций или YAML-файлов.

Например, для создания слушателя события prePersist необходимо создать класс-слушатель и пометить его аннотацией @ORM\HasLifecycleCallbacks:

/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class User
{
    // ...

    /**
     * @ORM\PrePersist
     */
    public function prePersist()
    {
        // выполнение дополнительных действий перед сохранением новой сущности
    }

    // ...
}

В данном примере метод prePersist будет вызываться перед сохранением новой сущности User в базе данных.