Skip to content

Commit

Permalink
Merge pull request #73 from adelf/patch-1 (resolves #71)
Browse files Browse the repository at this point in the history
IDE Builder and Blueprint completions with ide.json
  • Loading branch information
tpetry authored Feb 26, 2024
2 parents 1576a07 + f7cfcb0 commit 80066a2
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 116 deletions.
31 changes: 31 additions & 0 deletions ide.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://laravel-ide.com/schema/laravel-ide-v2.json",
"helperCode": {
"classMixins": [
{
"classFqn": "Illuminate\\Contracts\\Database\\Query\\Builder",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Query\\Builder"
},
{
"classFqn": "Illuminate\\Database\\Query\\Builder",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Query\\Builder"
},
{
"classFqn": "Illuminate\\Database\\Schema\\Blueprint",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Schema\\Blueprint"
},
{
"classFqn": "Illuminate\\Database\\Schema\\ColumnDefinition",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Schema\\ColumnDefinition"
},
{
"classFqn": "Illuminate\\Database\\Schema\\IndexDefinition",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Schema\\IndexDefinition"
},
{
"classFqn": "Illuminate\\Support\\Facades\\Schema",
"mixinFqn": "Tpetry\\PostgresqlEnhanced\\Support\\Facades\\Schema"
}
]
}
}
38 changes: 38 additions & 0 deletions src/Schema/ColumnDefinition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Tpetry\PostgresqlEnhanced\Schema;

use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Schema\ColumnDefinition as BaseColumnDefinition;

/**
* @internal This class is not used. It only exists to teach Laravel projects using PHPStan or IDEs supporting auto-suggest about added functionality.
*/
class ColumnDefinition extends BaseColumnDefinition
{
/**
* Specify the compression method for TOASTed values (PostgreSQL).
*/
public function compression(string $algorithm): self
{
return $this;
}

/**
* Sets an initial value to the column (PostgreSQL).
*/
public function initial(mixed $value): self
{
return $this;
}

/**
* Specify casting expression when changing the column type (PostgreSQL).
*/
public function using(string|Expression $expression): self
{
return $this;
}
}
61 changes: 61 additions & 0 deletions src/Schema/IndexDefinition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Tpetry\PostgresqlEnhanced\Schema;

use Illuminate\Database\Schema\IndexDefinition as BaseIndexDefinition;

/**
* @internal This class is not used. It only exists to teach Laravel projects using PHPStan or IDEs supporting auto-suggest about added functionality.
*/
class IndexDefinition extends BaseIndexDefinition
{
/**
* Include non-key columns in the index (PostgreSQL).
*
* @param string|array<int, string> $columns
*/
public function include(string|array $columns): self
{
return $this;
}

/**
* Mark NULLs as not distinct values (PostgreSQL).
*/
public function nullsNotDistinct(): self
{
return $this;
}

/**
* Specify fulltext index weight for columns (PostgreSQL).
*
* @param array<int, string> $labels
*/
public function weight(array $labels): self
{
return $this;
}

/**
* Build a partial index by specifying the rows that should be included (PostgreSQL).
*
* @param string|(callable(\Illuminate\Database\Query\Builder):mixed)|(callable(\Illuminate\Contracts\Database\Query\Builder):mixed) $columns
*/
public function where(string|callable $columns): self
{
return $this;
}

/**
* Specify index parameters to fine-tune its configuration (PostgreSQL).
*
* @param array<string, bool|float|int|string> $options
*/
public function with(array $options): self
{
return $this;
}
}
74 changes: 11 additions & 63 deletions src/Support/Phpstan/SchemaColumnDefinitionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,31 @@

namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;

use Illuminate\Contracts\Database\Query\Expression as ExpressionContract;
use Illuminate\Database\Schema\ColumnDefinition;
use Illuminate\Database\Schema\ColumnDefinition as BaseColumnDefinition;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionVariant;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\MethodsClassReflectionExtension;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedMethod;
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedParameter;
use PHPStan\Reflection\ReflectionProvider;
use Tpetry\PostgresqlEnhanced\Schema\ColumnDefinition;

class SchemaColumnDefinitionExtension implements MethodsClassReflectionExtension
{
/**
* @param 'compression'|'initial'|'using' $methodName
*/
public function __construct(
private ReflectionProvider $reflectionProvider,
) {
}

public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
{
return match ($methodName) {
'initial' => $this->getInitialMethod($classReflection),
'compression' => $this->getCompressionMethod($classReflection),
'using' => $this->getUsingMethod($classReflection),
};
return $this->reflectionProvider->getClass(ColumnDefinition::class)->getNativeMethod($methodName);
}

public function hasMethod(ClassReflection $classReflection, string $methodName): bool
{
if (ColumnDefinition::class !== $classReflection->getName()) {
if (BaseColumnDefinition::class !== $classReflection->getName()) {
return false;
}

return \in_array($methodName, ['compression', 'initial', 'using']);
}

private function getCompressionMethod(ClassReflection $classReflection): MethodReflection
{
$parameters = [new ReflectedParameter('algorithm', new StringType())];
$returnType = new ObjectType(ColumnDefinition::class);

return new ReflectedMethod(
classReflection: $classReflection,
name: 'compression',
variants: [
new FunctionVariant(TemplateTypeMap::createEmpty(), null, $parameters, false, $returnType),
],
);
}

private function getInitialMethod(ClassReflection $classReflection): MethodReflection
{
$parameters = [new ReflectedParameter('value', new MixedType())];
$returnType = new ObjectType(ColumnDefinition::class);

return new ReflectedMethod(
classReflection: $classReflection,
name: 'initial',
variants: [
new FunctionVariant(TemplateTypeMap::createEmpty(), null, $parameters, false, $returnType),
],
);
}

private function getUsingMethod(ClassReflection $classReflection): MethodReflection
{
$parametersExpression = [new ReflectedParameter('expression', new ObjectType(ExpressionContract::class))];
$parametersString = [new ReflectedParameter('expression', new StringType())];
$returnType = new ObjectType(ColumnDefinition::class);

return new ReflectedMethod(
classReflection: $classReflection,
name: 'using',
variants: [
new FunctionVariant(TemplateTypeMap::createEmpty(), null, $parametersExpression, false, $returnType),
new FunctionVariant(TemplateTypeMap::createEmpty(), null, $parametersString, false, $returnType),
],
);
return $this->reflectionProvider->getClass(ColumnDefinition::class)->hasNativeMethod($methodName);
}
}
64 changes: 11 additions & 53 deletions src/Support/Phpstan/SchemaIndexDefinitionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,73 +4,31 @@

namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;

use Illuminate\Contracts\Database\Query\Builder as BuilderContract;
use Illuminate\Database\Query\Builder as BuilderQuery;
use Illuminate\Database\Schema\IndexDefinition;
use Illuminate\Database\Schema\IndexDefinition as BaseIndexDefinition;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionVariant;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\MethodsClassReflectionExtension;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\CallableType;
use PHPStan\Type\FloatType;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\TypeCombinator;
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedMethod;
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedParameter;
use UnexpectedValueException;
use PHPStan\Reflection\ReflectionProvider;
use Tpetry\PostgresqlEnhanced\Schema\IndexDefinition;

class SchemaIndexDefinitionExtension implements MethodsClassReflectionExtension
{
public function __construct(
private ReflectionProvider $reflectionProvider,
) {
}

public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
{
return match ($methodName) {
'include' => new ReflectedMethod($classReflection, $methodName, [
$this->createFunctionVariant([new ReflectedParameter('columns', new StringType())]),
$this->createFunctionVariant([new ReflectedParameter('columns', new ArrayType(new IntegerType(), new StringType()))]),
]),
'nullsNotDistinct' => new ReflectedMethod($classReflection, $methodName, [
$this->createFunctionVariant([]),
]),
'weight' => new ReflectedMethod($classReflection, $methodName, [
$this->createFunctionVariant([new ReflectedParameter('labels', new ArrayType(new IntegerType(), new StringType()))]),
]),
'where' => new ReflectedMethod($classReflection, $methodName, [
$this->createFunctionVariant([new ReflectedParameter('columns', new StringType())]),
$this->createFunctionVariant([new ReflectedParameter('columns', new CallableType([new ReflectedParameter('builder', new ObjectType(BuilderContract::class))], new ObjectType(BuilderContract::class), false))]),
$this->createFunctionVariant([new ReflectedParameter('columns', new CallableType([new ReflectedParameter('builder', new ObjectType(BuilderQuery::class))], new ObjectType(BuilderContract::class), false))]),
]),
'with' => new ReflectedMethod($classReflection, $methodName, [
$this->createFunctionVariant([new ReflectedParameter('options', new ArrayType(new StringType(), TypeCombinator::union(new BooleanType(), new FloatType(), new IntegerType(), new StringType())))]),
]),
default => throw new UnexpectedValueException("'{$methodName}' is not defined for IndexDefinition."),
};
return $this->reflectionProvider->getClass(IndexDefinition::class)->getNativeMethod($methodName);
}

public function hasMethod(ClassReflection $classReflection, string $methodName): bool
{
if (IndexDefinition::class !== $classReflection->getName()) {
if (BaseIndexDefinition::class !== $classReflection->getName()) {
return false;
}

return \in_array($methodName, ['include', 'nullsNotDistinct', 'weight', 'where', 'with']);
}

/**
* @param array<int, \PHPStan\Reflection\ParameterReflection> $parameters
*/
private function createFunctionVariant(array $parameters): FunctionVariant
{
return new FunctionVariant(
templateTypeMap: TemplateTypeMap::createEmpty(),
resolvedTemplateTypeMap: null,
parameters: $parameters,
isVariadic: false,
returnType: new ObjectType(IndexDefinition::class),
);
return $this->reflectionProvider->getClass(IndexDefinition::class)->hasNativeMethod($methodName);
}
}

0 comments on commit 80066a2

Please sign in to comment.