img
scroll
#

Реализация структуры база продуктов с сервиса sql-ex.ru с помощью Doctrine2

Uk Ru En
Статья
#0002

Особенности использования Doctrine2 для реализации наследования

Часто некоторые сущности обладают общими свойствами, которые имеет смысл оставить в базовой сущности (таблице). Хорошим примером этого может служить база продуктов с сервиса sql-ex.ru, с которым многие сталкивались при изучении SQL. Попробуем реализовать структуру предлагаемой базы данных с помощью Symfony и Doctrine2.

База эта интересна тем, что служит хорошим примером наследования. В её основе лежит сущность "продукт", у которой есть поля Производитель, Модель и Тип. В оригинальной базе данных в качестве внешнего ключа использовалось поле Модель, но в Doctrine дублирование этого поля в таблицах будет излишним, кроме того имеет смысл создать отдельное поле Id для некоторой систематизированности и расширяемости. Для описанного выше создадим Entity Product.

Implementation of the structure of the product database from the sql-ex.ru service using Doctrine2
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({"product" = "Product", "pc" = "PC"})
 */
class Product
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $maker;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $model;

    public function getId(): ?int {
        return $this->id;
    }

    public function getMaker(): ?string {
        return $this->maker;
    }

    public function setMaker(string $maker): self {
        $this->maker = $maker;
        return $this;
    }

    public function getModel(): ?string {
        return $this->model;
    }

    public function setModel(string $model): self {
        $this->model = $model;
        return $this;
    }
}

Кроме того создадим дочерний класс PC

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 */

class PC extends Product
{
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $code;

    /**
     * @ORM\Column(type="integer")
     */
    private $speed;

    /**
     * @ORM\Column(type="integer")
     */
    private $ram;

    /**
     * @ORM\Column(type="integer")
     */
    private $hd;

    /**
     * @ORM\Column(type="integer")
     */
    private $cd;

    /**
     * @ORM\Column(type="float", scale=10, precision=2)
     */
    private $price;

    public function getCode() {
        return $this->code;
    }

    public function getSpeed() {
        return $this->speed;
    }

    public function getRam() {
        return $this->ram;
    }

    public function getHd() {
        return $this->hd;
    }

    public function getCd() {
        return $this->cd;
    }

    public function getPrice() {
        return $this->price;
    }

    public function setCode($value) {
        $this->code = $value;
        return $this;
    }

    public function setSpeed($value) {
        $this->speed = $value;
        return $this;
    }

    public function setRam($value) {
        $this->ram = $value;
        return $this;
    }

    public function setHd($value) {
        $this->hd = $value;
        return $this;
    }

    public function setCd($value) {
        $this->cd = $value;
        return $this;
    }

    public function setPrice($value) {
        $this->price = $value;
        return $this;
    }
}

Теперь обратим внимание на три последних аннотации для класса Product
@ORM\InheritanceType("JOINED") - здесь мы указываем тип наследования JOINED, тем самым указывая, что хранить каждый Entity нужно в отдельной таблице со своими колонками, не перегружая, например, Laptop колонками Printer и т.д.

@ORM\DiscriminatorColumn(name="type", type="string") - здесь мы указываем, что именно поле type будет использоваться для определения нужной таблицы, а значения для колонки type берутся из последующей аннотации.

@ORM\DiscriminatorMap({"product" = "Product", "pc" = "PC"}) - здесь видно, что для класса Product в свойстве type будет храниться значение product, а для PC - pc. Позднее для Laptop и Printer мы добавим их значения.
После настройки подключения к базе данных можно средствами Doctrine создать миграцию следующей командой php bin/console doctrine:migrations:diff и, если всё будет хорошо ;), то мигрировать, - php bin/console doctrine:migrations:migrate

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

Для реализации остальных таблиц достаточно добавить классы:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 */

class Laptop extends Product
{
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $code;

    /**
     * @ORM\Column(type="integer")
     */
    private $speed;

    /**
     * @ORM\Column(type="integer")
     */
    private $ram;

    /**
     * @ORM\Column(type="integer")
     */
    private $hd;

    /**
     * @ORM\Column(type="integer")
     */
    private $screen;

    /**
     * @ORM\Column(type="float", scale=10, precision=2)
     */
    private $price;

    public function getCode() {
        return $this->code;
    }

    public function getSpeed() {
        return $this->speed;
    }

    public function getRam() {
        return $this->ram;
    }

    public function getHd() {
        return $this->hd;
    }

    public function getScreen() {
        return $this->screen;
    }

    public function getPrice() {
        return $this->price;
    }

    public function setCode($value) {
        $this->code = $value;
        return $this;
    }

    public function setSpeed($value) {
        $this->speed = $value;
        return $this;
    }

    public function setRam($value) {
        $this->ram = $value;
        return $this;
    }

    public function setHd($value) {
        $this->hd = $value;
        return $this;
    }

    public function setScreen($value) {
        $this->screen = $value;
        return $this;
    }

    public function setPrice($value) {
        $this->price = $value;
        return $this;
    }
}

and

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 */

class Printer extends Product
{
    /**
     * @ORM\Column(type="boolean")
     */
    private $color;

    /**
     * @ORM\Column(type="string")
     */
    private $printerType;

    /**
     * @ORM\Column(type="float", scale=10, precision=2)
     */
    private $price;

    public function getColor() {
        return $this->color;
    }

    public function setColor($value) {
        $this->color = $value;
    return $this;
    }

    public function getPrinterType() {
        return $this->printerType;
    }

    public function setPrinterType($value) {
        $this->printerType = $value;
        return $this;
    }

    public function getPrice() {
        return $this->price;
    }

    public function setPrice($value) {
        $this->price = $value;
        return $this;
    }
}

Остается изменить строчку

@ORM\DiscriminatorMap({"product" = "Product", "pc" = "PC"})

на

@ORM\DiscriminatorMap({"product" = "Product", "pc" = "PC", "laptop" = "Laptop", "printer" = "Printer"})

После этого снова мигрируем и получаем четыре таблицы: product - базовая, и три производных, объединение которых с базовой происходит по внешнему ключу Id.

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

   /**
     * @Route("/printer/list")
     */
    public function printerListAction() {

        $em = $this->getDoctrine()->getManager();

        /** @var QueryBuilder $qb */
        $qb = $em
            ->getRepository(Printer::class)
            ->createQueryBuilder('p')
        ;

        $results = $qb
            ->setMaxResults(20)
            ->getQuery()
            ->getArrayResult()
        ;

        return new JsonResponse(['status' => 'success', 'data' => [$results]]);
    }

где все необходимые классы уже импортированы:

use App\Entity\Printer;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

Теперь расширение проекта на прочной основе с использованием встроенного функционала Doctrine2 будет происходить быстрее и легче, и вы можете попробовать добавить свои классы и таблицы.

Блог
#0002
Статьи, которые могут вас также заинтересовать
Передача данных в Amazon несколько отличается от получения
Рассмотрим передачу данных на примере присвоения заказу трекномера.
Подключение API EasyPost
Подключение API EasyPost
Особенности подключения API EasyPost
Особенности подключения Iiko API
Особенности подключения Iiko API
Iiko API это JSON API. Каждый запрос нужно подписывать специальным временным токеном доступа. Получить временный токен можно используя имя и пароль Вашего Iiko аккаунта, предоставленного Вам Iiko.
Реализация структуры база продуктов с сервиса sql-ex.ru с помощью Doctrine2
Расширяйте проект на прочной основе с использованием встроенного функционала Doctrine2, попробуйте добавить свои классы и таблицы. Характеристика базы продуктов
Свяжитесь с нами
#0013
Готовы Начать? Дайте нам знать!
Телефон:
Адрес:

Украина, Житомир
ул. Витрука 9в

Пн-Пт 9.00 - 19.00

Свяжитесь с нами
#0000
Остались вопросы?
Опишите свою проблему, заполните форму ниже и наши специалисты помогут Вам!
Обязательное поле
Обязательное поле
Обязательное поле
Обязательное поле