Доступ к базам данных в Symfony
Введение
В Symfony доступ к базам данных осуществляется с помощью Doctrine ORM (Object-Relational Mapping). Doctrine позволяет работать с базой данных как с набором объектов, что облегчает взаимодействие с базой данных и делает код более читабельным и поддерживаемым. Ниже приведены основные шаги для работы с базой данных в Symfony с помощью Doctrine ORM.
Установка Doctrine ORM
Для начала необходимо установить Doctrine ORM в проект Symfony. Это можно сделать с помощью Composer, добавив следующую строку в файл composer.json:
Настройка подключения к базе данных
После установки Doctrine необходимо настроить подключение к базе данных. Это можно сделать в файле конфигурации .env, добавив следующие строки:
Здесь db_user, db_password и db_name - имя пользователя, пароль и имя базы данных соответственно.
Создание сущностей
Для работы с базой данных необходимо создать сущности, которые будут представлять таблицы в базе данных. Для создания сущностей можно использовать консольную утилиту Doctrine. Например, для создания сущности User необходимо выполнить следующую команду:
В процессе создания сущности необходимо указать поля, которые будут соответствовать столбцам в таблице базы данных.
Создание таблиц в базе данных
После создания сущностей необходимо создать таблицы в базе данных. Для этого можно использовать консольную утилиту Doctrine. Например, для создания таблиц необходимо выполнить следующую команду:
Добавление, изменение и удаление данных
Для добавления, изменения и удаления данных в базе данных можно использовать методы сущностей и 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 для сущности User и вызывается метод findAll(), который возвращает все записи из таблицы пользователей.
Отношения между сущностями
В Symfony, при работе с ORM Doctrine, отношения между сущностями можно устанавливать с помощью аннотаций или YAML-файлов. Существует несколько типов отношений между сущностями:
- 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;
// ...
}
- 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;
// ...
}
- 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-файлов. Существует несколько типов наследования:
- 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.
- 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 хранится в своей таблице, при этом используется внешний ключ для связи таблиц.
- 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 для сущности User и вызывается метод findAll(), который возвращает все записи из таблицы пользователей, включая администраторов.
События жизненного цикла
В Symfony, при работе с ORM Doctrine, существует несколько событий жизненного цикла сущностей, которые можно использовать для выполнения дополнительных действий при создании, обновлении, удалении и других операциях над сущностями.
Существует два типа событий жизненного цикла:
- События, связанные с операциями над сущностями:
- prePersist - вызывается перед сохранением новой сущности в базе данных.
- postPersist - вызывается после сохранения новой сущности в базе данных.
- preUpdate - вызывается перед обновлением существующей сущности в базе данных.
- postUpdate - вызывается после обновления существующей сущности в базе данных.
- preRemove - вызывается перед удалением сущности из базы данных.
- postRemove - вызывается после удаления сущности из базы данных.
- События, связанные с транзакциями:
- 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 в базе данных.