mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-17 17:47:13 +00:00
Improve replacement of return type for methods from Query\Builder (#1575)
* Add return type `|static` to Query\Builder methods (#1574) * Replace return type Query\Builder with Eloquent\Builder (#1574) * Replace return type Query\Builder only for facade Eloquent (#1574) * Add special return type replacement for Macros of \Eloquent * Restrict special return type to methods from Eloquent\Builder and Query\Builder * Do not overwrite return type in normalizeReturn() with conditional call of setType() * Use generic return type for builder methods in \Eloquent * composer fix-style --------- Co-authored-by: laravel-ide-helper <[email protected]>
This commit is contained in:
co-authored by
laravel-ide-helper
parent
64588afe8b
commit
e3ec773374
+29
-3
@@ -18,6 +18,7 @@ use Barryvdh\Reflection\DocBlock\Tag\MethodTag;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Config\Repository as ConfigRepository;
|
use Illuminate\Config\Repository as ConfigRepository;
|
||||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||||
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
use Illuminate\Support\Facades\Facade;
|
use Illuminate\Support\Facades\Facade;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@@ -333,7 +334,15 @@ class Alias
|
|||||||
|
|
||||||
if (!in_array($magic, $this->usedMethods)) {
|
if (!in_array($magic, $this->usedMethods)) {
|
||||||
if ($class !== $this->root) {
|
if ($class !== $this->root) {
|
||||||
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces, $this->classAliases);
|
$this->methods[] = new Method(
|
||||||
|
$method,
|
||||||
|
$this->alias,
|
||||||
|
$class,
|
||||||
|
$magic,
|
||||||
|
$this->interfaces,
|
||||||
|
$this->classAliases,
|
||||||
|
$this->getReturnTypeNormalizers($class)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$this->usedMethods[] = $magic;
|
$this->usedMethods[] = $magic;
|
||||||
}
|
}
|
||||||
@@ -363,7 +372,8 @@ class Alias
|
|||||||
$reflection,
|
$reflection,
|
||||||
$method->name,
|
$method->name,
|
||||||
$this->interfaces,
|
$this->interfaces,
|
||||||
$this->classAliases
|
$this->classAliases,
|
||||||
|
$this->getReturnTypeNormalizers($reflection)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->usedMethods[] = $method->name;
|
$this->usedMethods[] = $method->name;
|
||||||
@@ -386,7 +396,8 @@ class Alias
|
|||||||
$reflection,
|
$reflection,
|
||||||
$macro_name,
|
$macro_name,
|
||||||
$this->interfaces,
|
$this->interfaces,
|
||||||
$this->classAliases
|
$this->classAliases,
|
||||||
|
$this->getReturnTypeNormalizers($reflection)
|
||||||
);
|
);
|
||||||
$this->usedMethods[] = $macro_name;
|
$this->usedMethods[] = $macro_name;
|
||||||
}
|
}
|
||||||
@@ -395,6 +406,21 @@ class Alias
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ReflectionClass $class
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function getReturnTypeNormalizers($class)
|
||||||
|
{
|
||||||
|
if ($this->alias === 'Eloquent' && in_array($class->getName(), [EloquentBuilder::class, QueryBuilder::class])) {
|
||||||
|
return [
|
||||||
|
'$this' => '\\' . EloquentBuilder::class . ($this->config->get('ide-helper.use_generics_annotations') ? '<static>' : '|static'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $macro_func
|
* @param $macro_func
|
||||||
*
|
*
|
||||||
|
|||||||
+4
-2
@@ -18,6 +18,7 @@ class Macro extends Method
|
|||||||
* @param null $methodName
|
* @param null $methodName
|
||||||
* @param array $interfaces
|
* @param array $interfaces
|
||||||
* @param array $classAliases
|
* @param array $classAliases
|
||||||
|
* @param array $returnTypeNormalizers
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$method,
|
$method,
|
||||||
@@ -25,9 +26,10 @@ class Macro extends Method
|
|||||||
$class,
|
$class,
|
||||||
$methodName = null,
|
$methodName = null,
|
||||||
$interfaces = [],
|
$interfaces = [],
|
||||||
$classAliases = []
|
$classAliases = [],
|
||||||
|
$returnTypeNormalizers = []
|
||||||
) {
|
) {
|
||||||
parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases);
|
parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases, $returnTypeNormalizers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+34
-14
@@ -17,8 +17,6 @@ use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
|
|||||||
use Barryvdh\Reflection\DocBlock\Tag;
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
use Barryvdh\Reflection\DocBlock\Tag\ParamTag;
|
use Barryvdh\Reflection\DocBlock\Tag\ParamTag;
|
||||||
use Barryvdh\Reflection\DocBlock\Tag\ReturnTag;
|
use Barryvdh\Reflection\DocBlock\Tag\ReturnTag;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class Method
|
class Method
|
||||||
{
|
{
|
||||||
@@ -39,6 +37,7 @@ class Method
|
|||||||
protected $return = null;
|
protected $return = null;
|
||||||
protected $root;
|
protected $root;
|
||||||
protected $classAliases;
|
protected $classAliases;
|
||||||
|
protected $returnTypeNormalizers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
||||||
@@ -47,12 +46,14 @@ class Method
|
|||||||
* @param string|null $methodName
|
* @param string|null $methodName
|
||||||
* @param array $interfaces
|
* @param array $interfaces
|
||||||
* @param array $classAliases
|
* @param array $classAliases
|
||||||
|
* @param array $returnTypeNormalizers
|
||||||
*/
|
*/
|
||||||
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [])
|
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [], array $returnTypeNormalizers = [])
|
||||||
{
|
{
|
||||||
$this->method = $method;
|
$this->method = $method;
|
||||||
$this->interfaces = $interfaces;
|
$this->interfaces = $interfaces;
|
||||||
$this->classAliases = $classAliases;
|
$this->classAliases = $classAliases;
|
||||||
|
$this->returnTypeNormalizers = $returnTypeNormalizers;
|
||||||
$this->name = $methodName ?: $method->name;
|
$this->name = $methodName ?: $method->name;
|
||||||
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
||||||
$this->initClassDefinedProperties($method, $class);
|
$this->initClassDefinedProperties($method, $class);
|
||||||
@@ -180,6 +181,25 @@ class Method
|
|||||||
return $implode ? implode(', ', $this->params) : $this->params;
|
return $implode ? implode(', ', $this->params) : $this->params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DocBlock|null $phpdoc
|
||||||
|
* @return ReturnTag|null
|
||||||
|
*/
|
||||||
|
public function getReturnTag($phpdoc = null)
|
||||||
|
{
|
||||||
|
if ($phpdoc === null) {
|
||||||
|
$phpdoc = $this->phpdoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
$returnTags = $phpdoc->getTagsByName('return');
|
||||||
|
|
||||||
|
if (count($returnTags) === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reset($returnTags);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parameters for this method including default values
|
* Get the parameters for this method including default values
|
||||||
*
|
*
|
||||||
@@ -248,25 +268,31 @@ class Method
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize the return tag (make full namespace, replace interfaces)
|
* Normalize the return tag (make full namespace, replace interfaces, resolve $this)
|
||||||
*
|
*
|
||||||
* @param DocBlock $phpdoc
|
* @param DocBlock $phpdoc
|
||||||
*/
|
*/
|
||||||
protected function normalizeReturn(DocBlock $phpdoc)
|
protected function normalizeReturn(DocBlock $phpdoc)
|
||||||
{
|
{
|
||||||
//Get the return type and adjust them for better autocomplete
|
//Get the return type and adjust them for better autocomplete
|
||||||
$returnTags = $phpdoc->getTagsByName('return');
|
$tag = $this->getReturnTag($phpdoc);
|
||||||
|
|
||||||
if (count($returnTags) === 0) {
|
if ($tag === null) {
|
||||||
$this->return = null;
|
$this->return = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var ReturnTag $tag */
|
|
||||||
$tag = reset($returnTags);
|
|
||||||
// Get the expanded type
|
// Get the expanded type
|
||||||
$returnValue = $tag->getType();
|
$returnValue = $tag->getType();
|
||||||
|
|
||||||
|
if (array_key_exists($returnValue, $this->returnTypeNormalizers)) {
|
||||||
|
$returnValue = $this->returnTypeNormalizers[$returnValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($returnValue === '$this') {
|
||||||
|
$returnValue = $this->root;
|
||||||
|
}
|
||||||
|
|
||||||
// Replace the interfaces
|
// Replace the interfaces
|
||||||
foreach ($this->interfaces as $interface => $real) {
|
foreach ($this->interfaces as $interface => $real) {
|
||||||
$returnValue = str_replace($interface, $real, $returnValue);
|
$returnValue = str_replace($interface, $real, $returnValue);
|
||||||
@@ -275,12 +301,6 @@ class Method
|
|||||||
// Set the changed content
|
// Set the changed content
|
||||||
$tag->setContent($returnValue . ' ' . $tag->getDescription());
|
$tag->setContent($returnValue . ' ' . $tag->getDescription());
|
||||||
$this->return = $returnValue;
|
$this->return = $returnValue;
|
||||||
|
|
||||||
if ($tag->getType() === '$this') {
|
|
||||||
Str::contains($this->root, Builder::class)
|
|
||||||
? $tag->setType($this->root . '|static')
|
|
||||||
: $tag->setType($this->root);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+68
-4
@@ -5,7 +5,8 @@ declare(strict_types=1);
|
|||||||
namespace Barryvdh\LaravelIdeHelper\Tests;
|
namespace Barryvdh\LaravelIdeHelper\Tests;
|
||||||
|
|
||||||
use Barryvdh\LaravelIdeHelper\Method;
|
use Barryvdh\LaravelIdeHelper\Method;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||||
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class MethodTest extends TestCase
|
class MethodTest extends TestCase
|
||||||
@@ -54,11 +55,11 @@ DOC;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the output of a class
|
* Test the output of Illuminate\Database\Eloquent\Builder
|
||||||
*/
|
*/
|
||||||
public function testEloquentBuilderOutput()
|
public function testEloquentBuilderOutput()
|
||||||
{
|
{
|
||||||
$reflectionClass = new \ReflectionClass(Builder::class);
|
$reflectionClass = new \ReflectionClass(EloquentBuilder::class);
|
||||||
$reflectionMethod = $reflectionClass->getMethod('upsert');
|
$reflectionMethod = $reflectionClass->getMethod('upsert');
|
||||||
|
|
||||||
$method = new Method($reflectionMethod, 'Builder', $reflectionClass);
|
$method = new Method($reflectionMethod, 'Builder', $reflectionClass);
|
||||||
@@ -76,12 +77,75 @@ DOC;
|
|||||||
DOC;
|
DOC;
|
||||||
$this->assertSame($output, $method->getDocComment(''));
|
$this->assertSame($output, $method->getDocComment(''));
|
||||||
$this->assertSame('upsert', $method->getName());
|
$this->assertSame('upsert', $method->getName());
|
||||||
$this->assertSame('\\' . Builder::class, $method->getDeclaringClass());
|
$this->assertSame('\\' . EloquentBuilder::class, $method->getDeclaringClass());
|
||||||
$this->assertSame('$values, $uniqueBy, $update', $method->getParams(true));
|
$this->assertSame('$values, $uniqueBy, $update', $method->getParams(true));
|
||||||
$this->assertSame(['$values', '$uniqueBy', '$update'], $method->getParams(false));
|
$this->assertSame(['$values', '$uniqueBy', '$update'], $method->getParams(false));
|
||||||
$this->assertSame('$values, $uniqueBy, $update = null', $method->getParamsWithDefault(true));
|
$this->assertSame('$values, $uniqueBy, $update = null', $method->getParamsWithDefault(true));
|
||||||
$this->assertSame(['$values', '$uniqueBy', '$update = null'], $method->getParamsWithDefault(false));
|
$this->assertSame(['$values', '$uniqueBy', '$update = null'], $method->getParamsWithDefault(false));
|
||||||
$this->assertTrue($method->shouldReturn());
|
$this->assertTrue($method->shouldReturn());
|
||||||
|
$this->assertSame('int', rtrim($method->getReturnTag()->getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test normalized return type of Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
public function testEloquentBuilderNormalizedReturnType()
|
||||||
|
{
|
||||||
|
$reflectionClass = new \ReflectionClass(EloquentBuilder::class);
|
||||||
|
$reflectionMethod = $reflectionClass->getMethod('where');
|
||||||
|
|
||||||
|
$method = new Method($reflectionMethod, 'Builder', $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '<static>']);
|
||||||
|
|
||||||
|
$output = <<<'DOC'
|
||||||
|
/**
|
||||||
|
* Add a basic where clause to the query.
|
||||||
|
*
|
||||||
|
* @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
|
||||||
|
* @param mixed $operator
|
||||||
|
* @param mixed $value
|
||||||
|
* @param string $boolean
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder<static>
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
DOC;
|
||||||
|
$this->assertSame($output, $method->getDocComment(''));
|
||||||
|
$this->assertSame('where', $method->getName());
|
||||||
|
$this->assertSame('\\' . EloquentBuilder::class, $method->getDeclaringClass());
|
||||||
|
$this->assertSame(['$column', '$operator', '$value', '$boolean'], $method->getParams(false));
|
||||||
|
$this->assertSame(['$column', '$operator = null', '$value = null', "\$boolean = 'and'"], $method->getParamsWithDefault(false));
|
||||||
|
$this->assertTrue($method->shouldReturn());
|
||||||
|
$this->assertSame('\Illuminate\Database\Eloquent\Builder<static>', rtrim($method->getReturnTag()->getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test normalized return type of Illuminate\Database\Query\Builder
|
||||||
|
*/
|
||||||
|
public function testQueryBuilderNormalizedReturnType()
|
||||||
|
{
|
||||||
|
$reflectionClass = new \ReflectionClass(QueryBuilder::class);
|
||||||
|
$reflectionMethod = $reflectionClass->getMethod('whereNull');
|
||||||
|
|
||||||
|
$method = new Method($reflectionMethod, 'Builder', $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '<static>']);
|
||||||
|
|
||||||
|
$output = <<<'DOC'
|
||||||
|
/**
|
||||||
|
* Add a "where null" clause to the query.
|
||||||
|
*
|
||||||
|
* @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns
|
||||||
|
* @param string $boolean
|
||||||
|
* @param bool $not
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder<static>
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
DOC;
|
||||||
|
|
||||||
|
$this->assertSame($output, $method->getDocComment(''));
|
||||||
|
$this->assertSame('whereNull', $method->getName());
|
||||||
|
$this->assertSame('\\' . QueryBuilder::class, $method->getDeclaringClass());
|
||||||
|
$this->assertSame(['$columns', '$boolean', '$not'], $method->getParams(false));
|
||||||
|
$this->assertSame(['$columns', "\$boolean = 'and'", '$not = false'], $method->getParamsWithDefault(false));
|
||||||
|
$this->assertTrue($method->shouldReturn());
|
||||||
|
$this->assertSame('\Illuminate\Database\Eloquent\Builder<static>', rtrim($method->getReturnTag()->getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user