Tous ceux qui ont commencé à diriger un projet d'entreprise plus ou moins normal à Laravel, face au fait que les solutions standards que Laravel propose prêtes à l'emploi ne suffisent plus.
Et si, comme moi, vous utilisez Postgres dans vos projets, vous aurez tôt ou tard besoin des avantages de ce merveilleux SGBD, tels que: divers types d'index et de contraintes, d'extensions, de nouveaux types, etc.
Aujourd'hui, comme vous l'avez déjà remarqué, nous allons parler de Postgres, des migrations Laravel, comment faire tout cela ensemble, en général, de tout ce qui nous manque dans les migrations standard de Lara.
Eh bien, pour ceux qui ne souhaitent pas se plonger dans les subtilités de la structure interne de Laravel, ils peuvent simplement télécharger un package qui étend les capacités des migrations Laravel et Postgres à partir de ce lien et l'utiliser dans leurs projets.
Mais je recommande quand même de ne pas faire défiler, mais de tout lire jusqu'au bout.
Migrations
Voici à quoi ressemble la migration standard dans Laravel:
Exemple de migration typique
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDocuments extends Migration
{
private const TABLE = 'documents';
public function up()
{
Schema::create(static::TABLE, function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->softDeletes();
$table->string('number');
$table->date('issued_date')->nullable();
$table->date('expiry_date')->nullable();
$table->string('file');
$table->bigInteger('author_id');
$table->bigInteger('type_id');
$table->foreign('author_id')->references('id')->on('users');
$table->foreign('type_id')->references('id')->on('document_types');
});
}
public function down()
{
Schema::dropIfExists(static::TABLE);
}
}
Mais, ici, vous avez lu un article sur Habré sur les nouveaux types dans Postgres, par exemple, tsrange et que vous vouliez ajouter quelque chose comme ça à la migration ...
$table->addColumn('tsrange', 'period')->nullable();
, , , Laravel, , :
$table->tsRange('period')->nullable();
, , , :
Laravel
Blueprint
<?php
Blueprint::macro('tsRange', function (string $columnName) {
return $this->addColumn('tsrange', $columnName);
});
PostgresGrammar
<?php
PostgresGrammar::macro('typeTsrange', function () {
rerurn 'tsrange';
});
- , ExtendDatabaseProvider:
<?php
use Illuminate\Support\ServiceProvider;
class DatabaseServiceProvider extends ServiceProvider
{
public function register()
{
Blueprint::macro('tsRange', function (string $columnName) {
return $this->addColumn('tsrange', $columnName);
});
PostgresGrammar::macro('typeTsrange', function () {
return 'tsrange';
});
}
}
, , ..
, Laravel MacroableTrait, .
( 1)
, , +100500 , . , CI, ...
, " " - :
Doctrine\DBAL\Driver\PDOException: SQLSTATE[08006] [7]
FATAL: sorry, too many clients already
, , .env CI GitLab, , , , , . CI - , , , - , vendor. .
, , , .
- ( ), , , , - , , .
sorry, too many clients already .
, , PostgresGrammar , , .... -, . :
Doctrine\DBAL\DBALException: Unknown database type tsrange requested,
Doctrine\DBAL\Platforms\PostgreSQL100Platform may not support it.
, , ... , . ?
Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType().
You can get a list of all the known types with \Doctrine\DBAL\Types\Type::getTypesMap().
If this error occurs during database introspection then you might have forgotten to register all database types for a Doctrine Type.
Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMappedDatabaseTypes().
If the type name is empty you might have a problem with the cache or forgot some mapping information.
, Doctrine\Dbal , Database Connection , ( , Postgres, getTypesMap Doctrine.
( 2)
, - vendor doctrine\dbal...
- , , Doctrine, , , .
, !
, , ..
..
, .
, , , , - Doctrine.
, , , , , , , , Laravel Doctrine, .
, , , . .
DatabaseProvider, Laravel, Extension- , , , Doctrine.
, , , :
Blueprint - , ,
Builder - Schema
PostgresGrammar - Blueprint- SQL-
Types -
, , Laravel , , , IDE , , .
,
<?php
namespace Umbrellio\Postgres\Extensions;
use Illuminate\Support\Traits\Macroable;
use Umbrellio\Postgres\Extensions\Exceptions\MacroableMissedException;
use Umbrellio\Postgres\Extensions\Exceptions\MixinInvalidException;
abstract class AbstractExtension extends AbstractComponent
{
abstract public static function getMixins(): array;
abstract public static function getName(): string;
public static function getTypes(): array
{
return [];
}
final public static function register(): void
{
collect(static::getMixins())->each(static function ($extension, $mixin) {
if (!is_subclass_of($mixin, AbstractComponent::class)) {
throw new MixinInvalidException(sprintf(
'Mixed class %s is not descendant of %s.',
$mixin,
AbstractComponent::class
));
}
if (!method_exists($extension, 'mixin')) {
throw new MacroableMissedException(sprintf('Class %s doesn’t use Macroable Trait.', $extension));
}
/** @var Macroable $extension */
$extension::mixin(new $mixin());
});
}
}
, , , Laravel , Doctrine.
PostgresConnection
<?php
namespace Umbrellio\Postgres;
use DateTimeInterface;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Events;
use Illuminate\Database\PostgresConnection as BasePostgresConnection;
use Illuminate\Support\Traits\Macroable;
use PDO;
use Umbrellio\Postgres\Extensions\AbstractExtension;
use Umbrellio\Postgres\Extensions\Exceptions\ExtensionInvalidException;
use Umbrellio\Postgres\Schema\Builder;
use Umbrellio\Postgres\Schema\Grammars\PostgresGrammar;
use Umbrellio\Postgres\Schema\Subscribers\SchemaAlterTableChangeColumnSubscriber;
class PostgresConnection extends BasePostgresConnection
{
use Macroable;
private static $extensions = [];
final public static function registerExtension(string $extension): void
{
if (!is_subclass_of($extension, AbstractExtension::class)) {
throw new ExtensionInvalidException(sprintf(
'Class %s must be implemented from %s',
$extension,
AbstractExtension::class
));
}
self::$extensions[$extension::getName()] = $extension;
}
public function getSchemaBuilder()
{
if ($this->schemaGrammar === null) {
$this->useDefaultSchemaGrammar();
}
return new Builder($this);
}
public function useDefaultPostProcessor(): void
{
parent::useDefaultPostProcessor();
$this->registerExtensions();
}
protected function getDefaultSchemaGrammar()
{
return $this->withTablePrefix(new PostgresGrammar());
}
private function registerExtensions(): void
{
collect(self::$extensions)->each(function ($extension) {
/** @var AbstractExtension $extension */
$extension::register();
foreach ($extension::getTypes() as $type => $typeClass) {
$this
->getSchemaBuilder()
->registerCustomDoctrineType($typeClass, $type, $type);
}
});
}
}
:
DatabaseProvider
<?php
namespace Umbrellio\Postgres;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\DatabaseServiceProvider;
use Umbrellio\Postgres\Connectors\ConnectionFactory;
class UmbrellioPostgresProvider extends DatabaseServiceProvider
{
protected function registerConnectionServices(): void
{
$this->app->singleton('db.factory', function ($app) {
return new ConnectionFactory($app);
});
$this->app->singleton('db', function ($app) {
return new DatabaseManager($app, $app['db.factory']);
});
$this->app->bind('db.connection', function ($app) {
return $app['db']->connection();
});
}
}
ConnectionFactory
<?php
namespace Umbrellio\Postgres\Connectors;
use Illuminate\Database\Connection;
use Illuminate\Database\Connectors\ConnectionFactory as ConnectionFactoryBase;
use Umbrellio\Postgres\PostgresConnection;
class ConnectionFactory extends ConnectionFactoryBase
{
protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
{
if ($resolver = Connection::getResolver($driver)) {
return $resolver($connection, $database, $prefix, $config);
}
if ($driver === 'pgsql') {
return new PostgresConnection($connection, $database, $prefix, $config);
}
return parent::createConnection($driver, $connection, $database, $prefix, $config);
}
}
tsrange . .
TsRangeExtension.php
<?php
namespace App\Extensions\TsRange;
use App\Extensions\TsRange\Schema\Grammars\TsRangeSchemaGrammar;
use App\Extensions\TsRange\Schema\TsRangeBlueprint;
use App\Extensions\TsRange\Types\TsRangeType;
use Umbrellio\Postgres\Extensions\AbstractExtension;
use Umbrellio\Postgres\Schema\Blueprint;
use Umbrellio\Postgres\Schema\Grammars\PostgresGrammar;
class TsRangeExtension extends AbstractExtension
{
public const NAME = TsRangeType::TYPE_NAME;
public static function getMixins(): array
{
return [
TsRangeBlueprint::class => Blueprint::class,
TsRangeSchemaGrammar::class => PostgresGrammar::class,
// ... Laravel
];
}
public static function getName(): string
{
return static::NAME;
}
public static function getTypes(): array
{
return [
static::NAME => TsRangeType::class,
];
}
}
TsRangeBlueprint.php
<?php
namespace App\Extensions\TsRange\Schema;
use Illuminate\Support\Fluent;
use App\Extensions\TsRange\Types\TsRangeType;
use Umbrellio\Postgres\Extensions\Schema\AbstractBlueprint;
class TsRangeBlueprint extends AbstractBlueprint
{
public function tsrange()
{
return function (string $column): Fluent {
return $this->addColumn(TsRangeType::TYPE_NAME, $column);
};
}
}
TsRangeSchemaGrammar.php
<?php
namespace App\Extensions\TsRange\Schema\Grammars;
use App\Extensions\TsRange\Types\TsRangeType;
use Umbrellio\Postgres\Extensions\Schema\Grammar\AbstractGrammar;
class TsRangeSchemaGrammar extends AbstractGrammar
{
protected function typeTsrange()
{
return function (): string {
return TsRangeType::TYPE_NAME;
};
}
}
TsRangeType.php
<?php
namespace App\Extensions\TsRange\Types;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class TsRangeType extends Type
{
public const TYPE_NAME = 'tsrange';
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return static::TYPE_NAME;
}
public function convertToPHPValue($value, AbstractPlatform $platform): ?array
{
//...
return $value;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
//...
return $value;
}
public function getName(): string
{
return self::TYPE_NAME;
}
}
TsRangeExtension :
<?php
namespace App\TsRange\Providers;
use Illuminate\Support\ServiceProvider;
use App\Extensions\TsRange\TsRangeExtension;
use Umbrellio\Postgres\PostgresConnection;
class TsRangeExtensionProvider extends ServiceProvider
{
public function register(): void
{
PostgresConnection::registerExtension(TsRangeExtension::class);
}
}
Postgres AbstractExtension, , , Laravel Doctrine.
, - PHP , Laravel / Postgres, , .
, , Issues / Pull-, , .
GitHub: laravel-pg-extensions.
.