Doctrine, czyli dość znana i rozpowszechniona (zwłaszcza wśród entuzjastów Symfony) biblioteka do kompleksowej obsługi komunikacji z różnymi typami baz danych, nie posiada niestety wszystkich możliwych typów do zastosowania w kolumnach. Takim dość często potrzebnym typem, staje się TinyInt - oto jak można go dodać.

 

Czemu Doctrine nie posiada zaimplementowanego TinyInt ?

Odpowiedz padła we wstępie - dlatego, że jest to biblioteka, która służy do ujednolicenia interfejsu przy komunikacji z różnymi systemami bazodanowymi: a TinyInt jest typem lokalnym dla baz danych opartych na MySql (w tym MariaDB). Dla przykładu: typu TinyInt nie uświadczymy (za to uświadczymy inne) w takich bazach danych PostgreSQL czy SQLite.

 

Jest oczywiście SmallInt czy varchar - jednak ten pierwszy ma dość duży zakres, a ten drugi jest typem znakowym (a bazy danych zdecydowanie wolą liczby). Dobrze skrojona aplikacja na miarę, nie powinna mieć zbyt dużych luzów czy innych niedopowiedzeń - wpływa to na efektywność całego systemu.

 

Dodajemy nowy typ do Doctrine

W tym celu należy utworzyć nową klasę, która będzie rozszerzać klase typów Doctrine - czyli:

use Doctrine\DBAL\Types\Type;

Które to wymusi na nas, zaimplementowanie kilku wymaganych metod - cała przygotowana klasa Typu TinyInt wygląda następująco:

<?php

namespace App\Doctrine\Column\Type;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;

class TinyIntType extends Type
{
    const NAME = 'tinyint';
    const DB_TYPE = 'TINYINT';

    /** {@inheritdoc} */
    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
    {
        return self::DB_TYPE;
    }

    /** {@inheritdoc} */
    public function convertToPHPValue($value, AbstractPlatform $platform): mixed
    {
        return $value;
    }

    /** {@inheritdoc} */
    public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed
    {
        return $value;
    }

    /** {@inheritdoc} */
    public function getName(): string
    {
        return self::NAME;
    }
}

Jak widać oprócz getterów do określenia nazwy typu oraz deklaracji w SQL mamy też metody konwertujące wartości- ale w typ wypadku, nie ma takiej potrzeby.

 

Należy jeszcze tak utworzoną klasę dodać do EntityManagerInterface Doctrine w symfony - można to zrobić np. na Kernelu na metodzie boot - jak w przykładzie poniżej:

<?php

namespace App;

use App\Doctrine\Column\Type\TinyIntType;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;

/**
 * @SuppressWarnings(PHPMD.StaticAccess)
 */
class Kernel extends BaseKernel
{
    use MicroKernelTrait;

    public function boot(): void
    {
        parent::boot();

        $this->addDoctrineTypes();
    }

    private function addDoctrineTypes(): void
    {
        if (! $this->container) {
            return;
        }

        $entityManager = $this->container->get('doctrine.orm.default_entity_manager');

        if (!$entityManager) {
            return;
        }

        $this->addDoctrineTinyIntType($entityManager); // @phpstan-ignore-line
    }

    /**
     * @throws Exception
     */
    private function addDoctrineTinyIntType(EntityManagerInterface $entityManager): void
    {
        if (Type::hasType(TinyIntType::NAME)) {
            return;
        }

        Type::addType(
            TinyIntType::NAME,
            TinyIntType::class
        );

        $entityManager
            ->getConnection()
            ->getDatabasePlatform()
            ->registerDoctrineTypeMapping(
                TinyIntType::DB_TYPE,
                TinyIntType::NAME
            );
    }
}

 

I to w sumie tyle - dodaliśmy nowy typ do Doctrine. Teraz wystarczy na danych deklaracjach kolumn w Entity podmienić na nowy typ tinyint, wykonać bin/console d:s:u --force oraz sprawdzić czy zmiany faktycznie zaszły na poszczególnych tabelach bazy danych.