diff --git a/.github/labeler.yml b/.github/labeler.yml index 1d5791b..9cc9011 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -3,9 +3,11 @@ type:build: - ".coveralls.yml" - ".gitignore" - "ecs.yml" - - "composer.json" - "phpcs.xml" +dependencies: + - "composer.json" + type:helpers: - "src/Helpers/**/*" @@ -43,4 +45,3 @@ theme:docs: - "CONTRIBUTING.md" - "CODE_OF_CONDUCT.md" - "src/.meta.php" - - "*.md" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index dfc3710..39ae991 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -39,24 +39,10 @@ version-resolver: major: labels: - major - - refactoring minor: labels: - - feature - minor - - type:helpers - - type:indexes - - type:routines - - type:schema - - type:compilers patch: labels: - patch - - type:build - - bug - - bugfix - - hotfix - - fix - - theme:docs - - analysis default: patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cb3165..d232bb3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,13 @@ on: - reopened - edited - synchronize + - labeled + - assigned + - unlabeled + - unlocked + - review_requested + - review_request_removed + - unassigned env: COVERAGE: '1' @@ -71,53 +78,53 @@ jobs: strategy: fail-fast: false matrix: - exclude_group: - - WithoutSchema,forPHP8 - experimental: - - false - operating_system: - - ubuntu-latest - postgres: - - '10' - - '11' - - '12' - php_versions: - - '7.3' - - '7.4' + coverage: [false] + exclude_group: ['WithoutSchema,forPHP8'] + experimental: [false] + operating_system: [ubuntu-latest] + postgres: ['10', '11', '12'] + php_versions: ['7.3', '7.4'] include: - operating_system: 'ubuntu-latest' php_versions: '7.3' postgres: '9.6' experimental: false + coverage: true exclude_group: WithoutSchema,forPHP8 - operating_system: 'ubuntu-latest' php_versions: '7.3' postgres: '13' experimental: false + coverage: false exclude_group: WithoutSchema,forPHP8 - operating_system: 'ubuntu-latest' php_versions: '7.4' postgres: '13' experimental: false + coverage: false exclude_group: WithoutSchema,forPHP8 - operating_system: 'ubuntu-latest' php_versions: '8.0' postgres: '10' experimental: false + coverage: false exclude_group: WithoutSchema,forPHP7 - operating_system: 'ubuntu-latest' php_versions: '8.0' postgres: '11' experimental: false + coverage: false exclude_group: WithoutSchema,forPHP7 - operating_system: 'ubuntu-latest' php_versions: '8.0' postgres: '12' experimental: false + coverage: true exclude_group: WithoutSchema,forPHP7 - operating_system: 'ubuntu-latest' php_versions: '8.0' postgres: '13' + coverage: false experimental: false exclude_group: WithoutSchema,forPHP7 runs-on: '${{ matrix.operating_system }}' @@ -185,7 +192,7 @@ jobs: --coverage-text working-directory: './' - name: Upload coverage results to Coveralls - if: ${{ !matrix.experimental }} + if: ${{ !matrix.experimental && matrix.coverage }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true diff --git a/README.md b/README.md index 0b2700c..0d915d9 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@ [![Code Coverage](https://scrutinizer-ci.com/g/umbrellio/laravel-pg-extensions/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/umbrellio/laravel-pg-extensions/?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/umbrellio/laravel-pg-extensions/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/umbrellio/laravel-pg-extensions/?branch=master) -This project extends Laravel`s database layer to allow use specific Postgres features without raw queries. +This project extends Laravel's database layer to allow use specific Postgres features without raw queries. ## Installation Run this command to install: ```bash -php composer.phar require umbrellio/laravel-pg-extensions +composer require umbrellio/laravel-pg-extensions ``` ## Features diff --git a/composer.json b/composer.json index dfdd6bb..2051a5c 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,10 @@ "umbrellio/laravel-ltree": "Package for working with Postgres LTree extension", "umbrellio/laravel-common-objects": "Package with helpers for common Laravel components" }, + "support": { + "issues": "https://github.com/umbrellio/laravel-pg-extensions/issues", + "source": "https://github.com/umbrellio/laravel-pg-extensions" + }, "require": { "php": "^7.2|^7.3|^7.4|^8.0", "doctrine/dbal": "2.9.*|^3.0", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index bd226b4..392c602 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,6 +9,14 @@ stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" > + + + ./src + + + ./src/.meta.php + + @@ -19,14 +27,6 @@ - - - ./src - - ./src/.meta.php - - - ./tests diff --git a/src/.meta.php b/src/.meta.php index 3765281..7b20911 100644 --- a/src/.meta.php +++ b/src/.meta.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Schema { + use Closure; use Illuminate\Support\Fluent; use Umbrellio\Postgres\Schema\Definitions\AttachPartitionDefinition; use Umbrellio\Postgres\Schema\Definitions\CheckDefinition; @@ -11,6 +12,8 @@ use Umbrellio\Postgres\Schema\Definitions\UniqueDefinition; /** + * @method __construct($table, Closure $callback = null, $prefix = '') + * * @method AttachPartitionDefinition attachPartition(string $partition) * @method void detachPartition(string $partition) * @method LikeDefinition like(string $table) @@ -22,15 +25,33 @@ * @method ColumnDefinition tsrange(string $column) * @method ExcludeDefinition exclude($columns, ?string $index = null) * @method CheckDefinition check($columns, ?string $index = null) + * @method string getTable() + * @method ColumnDefinition|Fluent addColumn($type, $name, array $parameters = []) + * + * @property bool $temporary */ class Blueprint { + protected function addCommand($name, array $parameters = []): Fluent + { + return new Fluent(); + } + + protected function createIndexName($type, array $columns): string + { + return ''; + } + + protected function dropIndexCommand($command, $type, $index): Fluent + { + return new Fluent(); + } } /** * @method ColumnDefinition using($expression) */ - class ColumnDefinition + class ColumnDefinition extends Fluent { } } diff --git a/src/Compilers/ExcludeCompiler.php b/src/Compilers/ExcludeCompiler.php index 6d1d85f..0a83b4e 100644 --- a/src/Compilers/ExcludeCompiler.php +++ b/src/Compilers/ExcludeCompiler.php @@ -72,7 +72,7 @@ private static function compileWheres(Grammar $grammar, Blueprint $blueprint, Fl { $wheres = static::build($grammar, $blueprint, $command); - if ($wheres) { + if (!empty($wheres)) { return sprintf('WHERE %s', static::removeLeadingBoolean(implode(' ', $wheres))); } diff --git a/src/Compilers/UniqueCompiler.php b/src/Compilers/UniqueCompiler.php index 4de68db..fe67013 100644 --- a/src/Compilers/UniqueCompiler.php +++ b/src/Compilers/UniqueCompiler.php @@ -26,7 +26,7 @@ public static function compile( 'CREATE UNIQUE INDEX %s ON %s (%s) WHERE %s', $fluent->get('index'), $blueprint->getTable(), - implode(',', $fluent->get('columns')), + implode(',', (array) $fluent->get('columns')), static::removeLeadingBoolean(implode(' ', $wheres)) ); } diff --git a/src/Helpers/ColumnAssertions.php b/src/Helpers/ColumnAssertions.php index e586d19..180e85f 100644 --- a/src/Helpers/ColumnAssertions.php +++ b/src/Helpers/ColumnAssertions.php @@ -6,13 +6,12 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; -use PHPUnit\Framework\TestCase; -/** - * @mixin TestCase - */ trait ColumnAssertions { + abstract public static function assertNull($actual, string $message = ''): void; + abstract public static function assertSame($expected, $actual, string $message = ''): void; + protected function assertCommentOnColumn(string $table, string $column, ?string $expected = null): void { $comment = $this->getCommentListing($table, $column); diff --git a/src/Helpers/IndexAssertions.php b/src/Helpers/IndexAssertions.php index 62d610c..7007c2e 100644 --- a/src/Helpers/IndexAssertions.php +++ b/src/Helpers/IndexAssertions.php @@ -5,13 +5,20 @@ namespace Umbrellio\Postgres\Helpers; use Illuminate\Support\Facades\DB; -use PHPUnit\Framework\TestCase; -/** - * @mixin TestCase - */ trait IndexAssertions { + abstract public static function assertNotNull($actual, string $message = ''): void; + abstract public static function assertSame($expected, $actual, string $message = ''): void; + abstract public static function assertNull($actual, string $message = ''): void; + abstract public static function assertMatchesRegularExpression( + string $pattern, + string $string, + string $message = '' + ): void; + abstract public static function assertTrue($condition, string $message = ''): void; + abstract public static function assertFalse($condition, string $message = ''): void; + protected function seeIndex(string $index): void { $this->assertNotNull($this->getIndexListing($index)); @@ -35,7 +42,7 @@ protected function assertRegExpIndex(string $index, string $expectedDef): void $definition = $this->getIndexListing($index); $this->seeIndex($index); - $this->assertMatchesRegularExpression($expectedDef, $definition); + $this->assertMatchesRegularExpression($expectedDef, $definition ?: ''); } protected function dontSeeConstraint(string $table, string $index): void @@ -57,15 +64,14 @@ private function getIndexListing($index): ?string private function existConstraintOnTable(string $table, string $index): bool { - $definition = DB::selectOne(' + $expression = ' SELECT c.conname FROM pg_constraint c LEFT JOIN pg_class t ON c.conrelid = t.oid LEFT JOIN pg_class t2 ON c.confrelid = t2.oid WHERE t.relname = ? AND c.conname = ?; - ', - [$table, $index] - ); + '; + $definition = DB::selectOne($expression, [$table, $index]); return $definition ? true : false; } } diff --git a/src/Helpers/TableAssertions.php b/src/Helpers/TableAssertions.php index 2a3c7b2..f9c1c78 100644 --- a/src/Helpers/TableAssertions.php +++ b/src/Helpers/TableAssertions.php @@ -5,13 +5,12 @@ namespace Umbrellio\Postgres\Helpers; use Illuminate\Support\Facades\Schema; -use PHPUnit\Framework\TestCase; -/** - * @mixin TestCase - */ trait TableAssertions { + abstract public static function assertSame($expected, $actual, string $message = ''): void; + abstract public static function assertTrue($condition, string $message = ''): void; + protected function assertCompareTables(string $sourceTable, string $destinationTable): void { $this->assertSame($this->getTableDefinition($sourceTable), $this->getTableDefinition($destinationTable)); diff --git a/src/Helpers/ViewAssertions.php b/src/Helpers/ViewAssertions.php index 73cd6b1..e958fcd 100644 --- a/src/Helpers/ViewAssertions.php +++ b/src/Helpers/ViewAssertions.php @@ -5,13 +5,13 @@ namespace Umbrellio\Postgres\Helpers; use Illuminate\Support\Facades\Schema; -use PHPUnit\Framework\TestCase; -/** - * @mixin TestCase - */ trait ViewAssertions { + abstract public static function assertSame($expected, $actual, string $message = ''): void; + abstract public static function assertTrue($condition, string $message = ''): void; + abstract public static function assertFalse($condition, string $message = ''): void; + protected function assertSameView(string $expectedDef, string $view): void { $definition = $this->getViewDefinition($view); diff --git a/src/PostgresConnection.php b/src/PostgresConnection.php index 96ade14..f7b78ea 100644 --- a/src/PostgresConnection.php +++ b/src/PostgresConnection.php @@ -22,6 +22,8 @@ class PostgresConnection extends BasePostgresConnection { use Macroable; + public $name; + private static $extensions = []; private $initialTypes = [ @@ -135,7 +137,8 @@ private function registerExtensions(): void /** @var AbstractExtension $extension */ $extension::register(); foreach ($extension::getTypes() as $type => $typeClass) { - $this->getSchemaBuilder() + $this + ->getSchemaBuilder() ->registerCustomDoctrineType($typeClass, $type, $type); } }); @@ -147,7 +150,8 @@ private function overrideDoctrineBehavior(Connection $connection): Connection if (!$eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { $eventManager->addEventSubscriber(new SchemaAlterTableChangeColumnSubscriber()); } - $connection->getDatabasePlatform() + $connection + ->getDatabasePlatform() ->setEventManager($eventManager); return $connection; } diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 297507f..65cb801 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -20,10 +20,12 @@ class Blueprint extends BaseBlueprint { + protected $commands = []; + /** - * @return AttachPartitionDefinition + * @return AttachPartitionDefinition|Fluent */ - public function attachPartition(string $partition) + public function attachPartition(string $partition): Fluent { return $this->addCommand('attachPartition', compact('partition')); } @@ -34,9 +36,9 @@ public function detachPartition(string $partition): void } /** - * @return LikeDefinition + * @return LikeDefinition|Fluent */ - public function like(string $table) + public function like(string $table): Fluent { return $this->addCommand('like', compact('table')); } @@ -48,9 +50,9 @@ public function ifNotExists(): Fluent /** * @param array|string $columns - * @return UniqueDefinition + * @return UniqueDefinition|UniqueBuilder */ - public function uniquePartial($columns, ?string $index = null, ?string $algorithm = null) + public function uniquePartial($columns, ?string $index = null, ?string $algorithm = null): Fluent { $columns = (array) $columns; @@ -70,9 +72,9 @@ public function dropUniquePartial($index): Fluent /** * @param array|string $columns - * @return ExcludeDefinition + * @return ExcludeDefinition|ExcludeBuilder */ - public function exclude($columns, ?string $index = null) + public function exclude($columns, ?string $index = null): Fluent { $columns = (array) $columns; @@ -83,9 +85,9 @@ public function exclude($columns, ?string $index = null) /** * @param array|string $columns - * @return CheckDefinition + * @return CheckDefinition|CheckBuilder */ - public function check($columns, ?string $index = null) + public function check($columns, ?string $index = null): Fluent { $columns = (array) $columns; @@ -128,13 +130,17 @@ public function dropView(string $view): Fluent /** * Almost like 'decimal' type, but can be with variable precision (by default) + * @return Fluent|ColumnDefinition */ - public function numeric(string $column, ?int $precision = null, ?int $scale = null): ColumnDefinition + public function numeric(string $column, ?int $precision = null, ?int $scale = null): Fluent { return $this->addColumn('numeric', $column, compact('precision', 'scale')); } - public function tsrange(string $column): ColumnDefinition + /** + * @return Fluent|ColumnDefinition + */ + public function tsrange(string $column): Fluent { return $this->addColumn('tsrange', $column); } @@ -144,7 +150,7 @@ protected function getSchemaManager() return Schema::getConnection()->getDoctrineSchemaManager(); } - private function addExtendedCommand(string $fluent, string $name, array $parameters = []) + private function addExtendedCommand(string $fluent, string $name, array $parameters = []): Fluent { $command = new $fluent(array_merge(compact('name'), $parameters)); $this->commands[] = $command; diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php index 1c35da7..f486ea3 100644 --- a/src/Schema/Builder.php +++ b/src/Schema/Builder.php @@ -12,6 +12,8 @@ class Builder extends BasePostgresBuilder { use Macroable; + public $name; + public function createView(string $view, string $select, $materialize = false): void { $blueprint = $this->createBlueprint($view); diff --git a/src/Schema/Builders/WhereBuilderTrait.php b/src/Schema/Builders/WhereBuilderTrait.php index 0fe5cfb..7962b68 100644 --- a/src/Schema/Builders/WhereBuilderTrait.php +++ b/src/Schema/Builders/WhereBuilderTrait.php @@ -7,10 +7,12 @@ use Illuminate\Support\Fluent; /** - * @mixin Fluent + * @see Fluent */ trait WhereBuilderTrait { + protected $attributes = []; + public function whereRaw(string $sql, array $bindings = [], string $boolean = 'and'): self { return $this->compileWhere('Raw', $boolean, compact('sql', 'bindings')); diff --git a/src/Schema/Grammars/PostgresGrammar.php b/src/Schema/Grammars/PostgresGrammar.php index b88639e..d634676 100644 --- a/src/Schema/Grammars/PostgresGrammar.php +++ b/src/Schema/Grammars/PostgresGrammar.php @@ -16,6 +16,8 @@ use Umbrellio\Postgres\Schema\Builders\Constraints\Exclude\ExcludeBuilder; use Umbrellio\Postgres\Schema\Builders\Indexes\Unique\UniqueBuilder; use Umbrellio\Postgres\Schema\Builders\Indexes\Unique\UniquePartialBuilder; +use Umbrellio\Postgres\Schema\Types\NumericType; +use Umbrellio\Postgres\Schema\Types\TsRangeType; class PostgresGrammar extends BasePostgresGrammar { @@ -45,17 +47,20 @@ public function compileDetachPartition(Blueprint $blueprint, Fluent $command): s ); } - public function compileCreateView(Blueprint $blueprint, Fluent $command): string + public function compileCreateView(/** @scrutinizer ignore-unused */ Blueprint $blueprint, Fluent $command): string { $materialize = $command->get('materialize') ? 'materialized' : ''; return implode(' ', array_filter([ - 'create', $materialize, 'view', + 'create', + $materialize, + 'view', $this->wrapTable($command->get('view')), - 'as', $command->get('select'), + 'as', + $command->get('select'), ])); } - public function compileDropView(Blueprint $blueprint, Fluent $command): string + public function compileDropView(/** @scrutinizer ignore-unused */ Blueprint $blueprint, Fluent $command): string { return 'drop view ' . $this->wrapTable($command->get('view')); } @@ -91,7 +96,7 @@ public function compileCheck(Blueprint $blueprint, CheckBuilder $command): strin protected function typeNumeric(Fluent $column): string { - $type = 'numeric'; + $type = NumericType::TYPE_NAME; $precision = $column->get('precision'); $scale = $column->get('scale'); @@ -106,8 +111,8 @@ protected function typeNumeric(Fluent $column): string return $type; } - protected function typeTsrange(Fluent $column): string + protected function typeTsrange(/** @scrutinizer ignore-unused */ Fluent $column): string { - return 'tsrange'; + return TsRangeType::TYPE_NAME; } } diff --git a/src/Schema/Subscribers/SchemaAlterTableChangeColumnSubscriber.php b/src/Schema/Subscribers/SchemaAlterTableChangeColumnSubscriber.php index cfd9d7e..6aee0c2 100644 --- a/src/Schema/Subscribers/SchemaAlterTableChangeColumnSubscriber.php +++ b/src/Schema/Subscribers/SchemaAlterTableChangeColumnSubscriber.php @@ -13,7 +13,6 @@ use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\BigIntType; use Doctrine\DBAL\Types\IntegerType; -use Doctrine\DBAL\Types\Type; use Illuminate\Database\Query\Expression; use Illuminate\Support\Collection; @@ -46,7 +45,8 @@ public function getAlterTableChangeColumnSQL( $quoteName = $this->quoteName($platform, $diff); - $oldColumnName = $columnDiff->getOldColumnName() + $oldColumnName = $columnDiff + ->getOldColumnName() ->getQuotedName($platform); $column = $columnDiff->column; @@ -68,7 +68,8 @@ public function getAlterTableChangeColumnSQL( 'ALTER TABLE %s ALTER %s TYPE %s', $quoteName, $oldColumnName, - $column->getType() + $column + ->getType() ->getSQLDeclaration($column->toArray(), $platform) )); @@ -205,22 +206,25 @@ public function getDefaultValueDeclarationSQL(AbstractPlatform $platform, Column public function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff): bool { - $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType()); - $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType()); + $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn); + $newTypeIsNumeric = $this->isNumericType($columnDiff->column); $isNumeric = !($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement()); return $columnDiff->hasChanged('type') && $isNumeric; } - public function isNumericType(Type $type): bool + public function isNumericType(?Column $column): bool { + $type = $column ? $column->getType() : null; + return $type instanceof IntegerType || $type instanceof BigIntType; } public function quoteName(AbstractPlatform $platform, TableDiff $diff): string { - return $diff->getName($platform) + return $diff + ->getName($platform) ->getQuotedName($platform); } diff --git a/tests.sh b/tests.sh index d34f925..0f4f623 100755 --- a/tests.sh +++ b/tests.sh @@ -11,9 +11,11 @@ composer lint if [ "x$EXCLUDE_GROUP" != "x" ]; then php -d pcov.directory='.' vendor/bin/phpunit \ --exclude-group $EXCLUDE_GROUP \ - --coverage-html build + --coverage-html build \ + --coverage-text else php -d pcov.directory='.' vendor/bin/phpunit \ - --exclude-group WithoutSchema,forPHP7,forPHP8 \ - --coverage-html build + --exclude-group WithoutSchema,forPHP7 \ + --coverage-html build \ + --coverage-text fi