Меню

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

logo
Розширюйте проект на міцній основі з використанням вбудованого функціоналу Doctrine2, спробуйте додати свої класи і таблиці. Характеристика бази продуктів
Реалізація структури бази продуктів з сервісу sql-ex.ru з підтримкою Doctrine2

Особливості використання Doctrine2 для реалізації успадкування

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

Ця база цікава тим, що служить добрим прикладом успадкування. В її основі лежить сутність "продукт", у якій є поля Виробник, Модель і Тип. В оригінальній базі даних як зовнішній ключ використовувалося поле Модель, але в Doctrine дублювання цього поля в таблицях буде зайвим. Крім того, має сенс створити окреме поле Id для деякої систематизированности і розширюваності. Для описаного вище створимо Entity Product.


<?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;
    }
}

i


<?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 відбуватиметься швидше і легше, і ви можете спробувати додати свої класи і таблиці.

Поради щодо підключення до сервісу API ShipStation 17.09.2019 Поради щодо підключення до сервісу API ShipStation
Використовуйте API ShipStation як агрегатор для вашого сервісу, а такж як відмінний інструмент для економії коштів на відправленнях посилок. Опис особливостей сервісу

Ключові відмінності між Laravel і Symfony 07.09.2019 Ключові відмінності між Laravel і Symfony
Який з двох фреймворків Laravel чи Symfony вибрати для реалізації проекту? Під які конкретні потреби використовувати той чи інший фреймворк? Характеристика функціональних можливостей фреймворків

Повернення до списку