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

RonasIT / laravel-swagger / 27260021599

10 Jun 2026 07:19AM UTC coverage: 99.316% (-0.4%) from 99.671%
27260021599

Pull #193

github

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

221 of 225 new or added lines in 12 files covered. (98.22%)

1 existing line in 1 file now uncovered.

1017 of 1024 relevant lines covered (99.32%)

24.49 hits per line

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

97.92
/src/Support/Resolvers/ResourceSchemaNameResolver.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\Support\Arr;
8
use Illuminate\Support\Str;
9
use ReflectionFunctionAbstract;
10
use ReflectionNamedType;
11
use ReflectionUnionType;
12

13
class ResourceSchemaNameResolver
14
{
15
    public function resolve(ReflectionFunctionAbstract $reflection): ?string
16
    {
17
        $returnType = $reflection->getReturnType();
37✔
18

19
        $result = (!empty($returnType))
37✔
20
            ? $this->resolveFromReturnType($returnType)
4✔
21
            : null;
33✔
22

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

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

34
        foreach ($types as $type) {
4✔
35
            if ($type instanceof ReflectionNamedType && !$type->isBuiltin() && $this->isResourceClass($type->getName())) {
4✔
36
                return $this->toSchemaName($type->getName());
2✔
37
            }
38
        }
39

40
        return null;
2✔
41
    }
42

43
    protected function resolveFromSource(ReflectionFunctionAbstract $reflection): ?string
44
    {
45
        $fileContent = $this->getFileContent($reflection);
35✔
46
        $code = $this->getFunctionCode($reflection, $fileContent);
35✔
47

48
        $patterns = [
35✔
49
            'make' => '/(?:return\s+|=>\s+)([^\s(]+)::make/',
35✔
50
            'collection' => '/(?:return\s+|=>\s+)([^\s(]+)::collection/',
35✔
51
            'class' => '/(?:return\s+|=>\s+)new\s+([^\s(]+)/',
35✔
52
        ];
35✔
53

54
        foreach ($patterns as $type => $pattern) {
35✔
55
            preg_match($pattern, $code, $matches);
35✔
56

57
            if (empty($matches[1])) {
35✔
58
                continue;
31✔
59
            }
60

61
            $resourceName = class_exists($matches[1])
6✔
62
                ? $matches[1]
1✔
63
                : $this->getClassNameFromImports($matches[1], $fileContent);
5✔
64

65
            if (is_subclass_of($resourceName, JsonResource::class)) {
6✔
66
                return $this->toSchemaName($resourceName, $type === 'collection');
6✔
67
            }
68
        }
69

70
        return null;
29✔
71
    }
72

73
    protected function toSchemaName(string $className, bool $isCollection = false): string
74
    {
75
        $baseName = Str::replaceLast('Resource', '', class_basename($className));
8✔
76

77
        return ($isCollection && !Str::endsWith($baseName, 'Collection'))
8✔
78
            ? $baseName . 'Collection'
1✔
79
            : $baseName;
8✔
80
    }
81

82
    protected function isResourceClass(string $className): bool
83
    {
84
        return is_subclass_of($className, JsonResource::class)
4✔
85
            && $className !== AnonymousResourceCollection::class;
4✔
86
    }
87

88
    protected function getFileContent(ReflectionFunctionAbstract $reflection): array
89
    {
90
        $fileName = $reflection->getFileName();
35✔
91

92
        return (empty($fileName) || !is_readable($fileName)) ? [] : file($fileName) ?? [];
35✔
93
    }
94

95
    protected function getFunctionCode(ReflectionFunctionAbstract $reflection, array $fileContent): string
96
    {
97
        $startLineIndex = $reflection->getStartLine() - 1;
35✔
98
        $methodSlice = array_slice($fileContent, $startLineIndex, $reflection->getEndLine() - $startLineIndex);
35✔
99

100
        return implode('', $methodSlice);
35✔
101
    }
102

103
    protected function getClassNameFromImports(string $resourceName, array $fileContent): string
104
    {
105
        $resourceImport = Arr::first(
5✔
106
            array: $fileContent,
5✔
107
            callback: fn (string $line) => Str::startsWith($line, 'use')
5✔
108
                && preg_match('/\b' . preg_quote($resourceName, '/') . '\b/', $line),
5✔
109
        );
5✔
110

111
        preg_match('/^use\s+([^;]+?)(?:\s+as\s+\w+)?;$/', trim($resourceImport), $matches);
5✔
112

113
        return $matches[1] ?? '';
5✔
114
    }
115
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc