Меню

Реализация структуры база продуктов с сервиса 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;
    }
}

и

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

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

Советы по подключению к сервису API ShipStation 17.09.2019 Советы по подключению к сервису API ShipStation
Используйте API ShipStation в качестве агрегатора для вашего сервиса и как отличный инструмент для экономии средств на отправках посылок. Описание особенностей сервиса

Возврат к списку