• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RonasIT / laravel-swagger / 29081315241

10 Jul 2026 08:55AM UTC coverage: 99.239% (-0.4%) from 99.671%
29081315241

Pull #193

github

web-flow
Merge 3fdeeb0cb into ead648e99
Pull Request #193: feat: name response object by resource class

247 of 252 new or added lines in 12 files covered. (98.02%)

1043 of 1051 relevant lines covered (99.24%)

25.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.61
/src/Support/Resolvers/ResourceSchemaResolver.php
1
<?php
2

3
namespace RonasIT\AutoDoc\Support\Resolvers;
4

5
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
6
use Illuminate\Http\Resources\Json\JsonResource;
7
use Illuminate\Http\Resources\Json\ResourceCollection;
8
use Illuminate\Support\Str;
9
use ReflectionFunctionAbstract;
10
use ReflectionNamedType;
11
use ReflectionUnionType;
12
use RonasIT\AutoDoc\DTO\ResourceSchema;
13

14
class ResourceSchemaResolver
15
{
16
    public function resolve(ReflectionFunctionAbstract $reflection): ?ResourceSchema
17
    {
18
        $returnType = $reflection->getReturnType();
40✔
19

20
        $result = (!empty($returnType))
40✔
21
            ? $this->resolveFromReturnType($returnType)
6✔
22
            : null;
34✔
23

24
        return $result ?? $this->resolveFromSource($reflection);
40✔
25
    }
26

27
    protected function resolveFromReturnType(object $returnType): ?ResourceSchema
28
    {
29
        $types = match (get_class($returnType)) {
6✔
30
            ReflectionNamedType::class => [$returnType],
5✔
31
            ReflectionUnionType::class => $returnType->getTypes(),
1✔
NEW
32
            default => [],
×
33
        };
34

35
        foreach ($types as $type) {
6✔
36
            $isResourceClass = $type instanceof ReflectionNamedType
6✔
37
                && !$type->isBuiltin()
6✔
38
                && $this->isResourceClass($type->getName());
6✔
39

40
            if ($isResourceClass) {
6✔
41
                $className = $type->getName();
4✔
42

43
                return new ResourceSchema(
4✔
44
                    className: $className,
4✔
45
                    isCollection: is_subclass_of($className, ResourceCollection::class),
4✔
46
                );
4✔
47
            }
48
        }
49

50
        return null;
2✔
51
    }
52

53
    protected function resolveFromSource(ReflectionFunctionAbstract $reflection): ?ResourceSchema
54
    {
55
        $fileContent = $this->getFileContent($reflection);
36✔
56
        $code = $this->getFunctionCode($reflection, $fileContent);
36✔
57

58
        $patterns = [
36✔
59
            'make' => '/(?:return\s+|=>\s+)([^\s(]+)::make/',
36✔
60
            'collection' => '/(?:return\s+|=>\s+)([^\s(]+)::collection/',
36✔
61
            'class' => '/(?:return\s+|=>\s+)new\s+([^\s(]+)/',
36✔
62
        ];
36✔
63

64
        foreach ($patterns as $type => $pattern) {
36✔
65
            preg_match($pattern, $code, $matches);
36✔
66

67
            if (empty($matches[1])) {
36✔
68
                continue;
31✔
69
            }
70

71
            $resourceName = class_exists($matches[1])
7✔
72
                ? $matches[1]
1✔
73
                : $this->getClassNameFromImports($matches[1], $fileContent);
6✔
74

75
            if (is_subclass_of($resourceName, JsonResource::class)) {
7✔
76
                return new ResourceSchema(
7✔
77
                    className: $resourceName,
7✔
78
                    isCollection: $type === 'collection',
7✔
79
                );
7✔
80
            }
81
        }
82

83
        return null;
29✔
84
    }
85

86
    protected function isResourceClass(string $className): bool
87
    {
88
        return is_subclass_of($className, JsonResource::class)
6✔
89
            && $className !== AnonymousResourceCollection::class;
6✔
90
    }
91

92
    protected function getFileContent(ReflectionFunctionAbstract $reflection): array
93
    {
94
        $fileName = $reflection->getFileName();
36✔
95

96
        return (empty($fileName) || !is_readable($fileName)) ? [] : file($fileName) ?? [];
36✔
97
    }
98

99
    protected function getFunctionCode(ReflectionFunctionAbstract $reflection, array $fileContent): string
100
    {
101
        $startLineIndex = $reflection->getStartLine() - 1;
36✔
102
        $methodSlice = array_slice($fileContent, $startLineIndex, $reflection->getEndLine() - $startLineIndex);
36✔
103

104
        return implode('', $methodSlice);
36✔
105
    }
106

107
    protected function getClassNameFromImports(string $resourceName, array $fileContent): string
108
    {
109
        foreach ($fileContent as $line) {
6✔
110
            $line = trim($line);
6✔
111

112
            if (!Str::startsWith($line, 'use ')) {
6✔
113
                continue;
6✔
114
            }
115

116
            preg_match('/^use\s+(?<namespace>[^;]+?)(?:\s+as\s+(?<alias>\w+))?;$/', $line, $matches);
6✔
117

118
            $nameSpace = $matches['namespace'] ?? '';
6✔
119
            $alias = $matches['alias'] ?? '';
6✔
120

121
            $isFoundNamespace = ($alias === $resourceName)
6✔
122
                || (empty($alias) && Str::afterLast($nameSpace, '\\') === $resourceName);
6✔
123

124
            if ($isFoundNamespace) {
6✔
125
                return $nameSpace;
6✔
126
            }
127
        }
128

NEW
129
        return '';
×
130
    }
131
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc