Skip to content

Commit 36c4406

Browse files
Alias will grab macros from \Illuminate\Database\Eloquent\Builder too (#1118)
* `Alias` will grab macros from `\Illuminate\Database\Eloquent\Builder` too. * `@return` will contains `|static` for Eloquent Builder macros which return Eloquent Builder instance. * `Macro` tests. * `Alias::detectMethods()` tests. * Mock classes rename.
1 parent 9ba2f5c commit 36c4406

File tree

4 files changed

+194
-3
lines changed

4 files changed

+194
-3
lines changed

src/Alias.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Barryvdh\Reflection\DocBlock\Tag\MethodTag;
1818
use Closure;
1919
use Illuminate\Config\Repository as ConfigRepository;
20+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
2021
use Illuminate\Support\Facades\Facade;
2122
use ReflectionClass;
2223

@@ -368,8 +369,9 @@ protected function detectMethods()
368369
}
369370

370371
// Check if the class is macroable
372+
// (Eloquent\Builder is also macroable but doesn't use Macroable trait)
371373
$traits = collect($reflection->getTraitNames());
372-
if ($traits->contains('Illuminate\Support\Traits\Macroable')) {
374+
if ($traits->contains('Illuminate\Support\Traits\Macroable') || $class === EloquentBuilder::class) {
373375
$properties = $reflection->getStaticProperties();
374376
$macros = isset($properties['macros']) ? $properties['macros'] : [];
375377
foreach ($macros as $macro_name => $macro_func) {

src/Macro.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Barryvdh\Reflection\DocBlock;
66
use Barryvdh\Reflection\DocBlock\Tag;
7+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
78
use Illuminate\Support\Collection;
89

910
class Macro extends Method
@@ -49,8 +50,12 @@ protected function initPhpDoc($method)
4950

5051
// Add macro return type
5152
if ($method->hasReturnType()) {
52-
$type = $method->getReturnType()->getName();
53-
$type .= $method->getReturnType()->allowsNull() ? '|null' : '';
53+
$builder = EloquentBuilder::class;
54+
$return = $method->getReturnType();
55+
56+
$type = $return->getName();
57+
$type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : '';
58+
$type .= $return->allowsNull() ? '|null' : '';
5459

5560
$this->phpdoc->appendTag(Tag::createInstance("@return {$type}"));
5661
}

tests/AliasTest.php

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests;
6+
7+
use Barryvdh\LaravelIdeHelper\Alias;
8+
use Barryvdh\LaravelIdeHelper\Macro;
9+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
10+
use Illuminate\Database\Query\Builder;
11+
use Illuminate\Support\Arr;
12+
13+
/**
14+
* @internal
15+
* @coversDefaultClass \Barryvdh\LaravelIdeHelper\Alias
16+
*/
17+
class AliasTest extends TestCase
18+
{
19+
/**
20+
* @covers ::detectMethods
21+
*/
22+
public function testDetectMethodsMacroableMacros(): void
23+
{
24+
// Mock
25+
$macro = __FUNCTION__;
26+
$alias = new AliasMock();
27+
28+
// Macros
29+
Builder::macro(
30+
$macro,
31+
function () {
32+
// empty
33+
}
34+
);
35+
36+
// Prepare
37+
$alias->setClasses([Builder::class]);
38+
$alias->detectMethods();
39+
40+
// Test
41+
$this->assertNotNull($this->getAliasMacro($alias, Builder::class, $macro));
42+
}
43+
44+
/**
45+
* @covers ::detectMethods
46+
*/
47+
public function testDetectMethodsEloquentBuilderMacros(): void
48+
{
49+
// Mock
50+
$macro = __FUNCTION__;
51+
$alias = new AliasMock();
52+
53+
// Macros
54+
EloquentBuilder::macro(
55+
$macro,
56+
function () {
57+
// empty
58+
}
59+
);
60+
61+
// Prepare
62+
$alias->setClasses([EloquentBuilder::class]);
63+
$alias->detectMethods();
64+
65+
// Test
66+
$this->assertNotNull($this->getAliasMacro($alias, EloquentBuilder::class, $macro));
67+
}
68+
69+
protected function getAliasMacro(Alias $alias, string $class, string $method): ?Macro
70+
{
71+
return Arr::first(
72+
$alias->getMethods(),
73+
function ($macro) use ($class, $method) {
74+
return $macro instanceof Macro
75+
&& $macro->getDeclaringClass() === "\\{$class}"
76+
&& $macro->getName() === $method;
77+
}
78+
);
79+
}
80+
}
81+
82+
/**
83+
* @internal
84+
* @noinspection PhpMultipleClassesDeclarationsInOneFile
85+
*/
86+
class AliasMock extends Alias
87+
{
88+
public function __construct()
89+
{
90+
// no need to call parent
91+
}
92+
93+
/**
94+
* @param string[] $classes
95+
*/
96+
public function setClasses(array $classes)
97+
{
98+
$this->classes = $classes;
99+
}
100+
101+
public function detectMethods()
102+
{
103+
parent::detectMethods();
104+
}
105+
}

tests/MacroTest.php

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests;
6+
7+
use Barryvdh\LaravelIdeHelper\Macro;
8+
use Barryvdh\Reflection\DocBlock;
9+
use Barryvdh\Reflection\DocBlock\Tag;
10+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
11+
use ReflectionClass;
12+
use ReflectionFunction;
13+
use ReflectionFunctionAbstract;
14+
15+
use function array_map;
16+
use function implode;
17+
18+
use const PHP_EOL;
19+
20+
/**
21+
* @internal
22+
* @coversDefaultClass \Barryvdh\LaravelIdeHelper\Macro
23+
*/
24+
class MacroTest extends TestCase
25+
{
26+
/**
27+
* @covers ::initPhpDoc
28+
* @throws \ReflectionException
29+
*/
30+
public function testInitPhpDocEloquentBuilderHasStaticInReturnType(): void
31+
{
32+
$class = new ReflectionClass(EloquentBuilder::class);
33+
$phpdoc = (new MacroMock())->getPhpDoc(
34+
new ReflectionFunction(
35+
function (): EloquentBuilder {
36+
return $this;
37+
}
38+
),
39+
$class
40+
);
41+
42+
$this->assertNotNull($phpdoc);
43+
$this->assertEquals(
44+
'@return \Illuminate\Database\Eloquent\Builder|static',
45+
$this->tagsToString($phpdoc, 'return')
46+
);
47+
}
48+
49+
protected function tagsToString(DocBlock $docBlock, string $name)
50+
{
51+
$tags = $docBlock->getTagsByName($name);
52+
$tags = array_map(
53+
function (Tag $tag) {
54+
return trim((string)$tag);
55+
},
56+
$tags
57+
);
58+
$tags = implode(PHP_EOL, $tags);
59+
60+
return $tags;
61+
}
62+
}
63+
64+
/**
65+
* @internal
66+
* @noinspection PhpMultipleClassesDeclarationsInOneFile
67+
*/
68+
class MacroMock extends Macro
69+
{
70+
public function __construct()
71+
{
72+
// no need to call parent
73+
}
74+
75+
public function getPhpDoc(ReflectionFunctionAbstract $method, ReflectionClass $class = null): DocBlock
76+
{
77+
return (new Macro($method, '', $class ?? $method->getClosureScopeClass()))->phpdoc;
78+
}
79+
}

0 commit comments

Comments
 (0)