Skip to content

Commit b029e07

Browse files
authored
Merge pull request #487 from N1ebieski/Add-scope-parameters-to-the-repository-and-docblock-generator-#66
Add scope parameters to the repository and docblock generator
2 parents 14c12f1 + 0a10eaa commit b029e07

File tree

4 files changed

+206
-12
lines changed

4 files changed

+206
-12
lines changed

php-templates/models.php

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,11 @@ protected function getInfo($className)
159159
->toArray();
160160

161161
$data['scopes'] = collect($reflection->getMethods())
162-
->filter(fn($method) =>!$method->isStatic() && ($method->getAttributes(\Illuminate\Database\Eloquent\Attributes\Scope::class) || ($method->isPublic() && str_starts_with($method->name, 'scope'))))
163-
->map(fn($method) => str($method->name)->replace('scope', '')->lcfirst()->toString())
162+
->filter(fn(\ReflectionMethod $method) => !$method->isStatic() && ($method->getAttributes(\Illuminate\Database\Eloquent\Attributes\Scope::class) || ($method->isPublic() && str_starts_with($method->name, 'scope'))))
163+
->map(fn(\ReflectionMethod $method) => [
164+
"name" => str($method->name)->replace('scope', '')->lcfirst()->toString(),
165+
"parameters" => collect($method->getParameters())->map($this->getScopeParameterInfo(...)),
166+
])
164167
->values()
165168
->toArray();
166169

@@ -170,6 +173,84 @@ protected function getInfo($className)
170173
$className => $data,
171174
];
172175
}
176+
177+
protected function getScopeParameterInfo(\ReflectionParameter $parameter): array
178+
{
179+
$result = [
180+
"name" => $parameter->getName(),
181+
"type" => $this->typeToString($parameter->getType()),
182+
"hasDefault" => $parameter->isDefaultValueAvailable(),
183+
"isVariadic" => $parameter->isVariadic(),
184+
"isPassedByReference" => $parameter->isPassedByReference(),
185+
];
186+
187+
if ($parameter->isDefaultValueAvailable()) {
188+
$result['default'] = $this->defaultValueToString($parameter);
189+
}
190+
191+
return $result;
192+
}
193+
194+
protected function typeToString(?\ReflectionType $type): string
195+
{
196+
return match (true) {
197+
$type instanceof \ReflectionNamedType => $this->namedTypeToString($type),
198+
$type instanceof \ReflectionUnionType => $this->unionTypeToString($type),
199+
$type instanceof \ReflectionIntersectionType => $this->intersectionTypeToString($type),
200+
default => 'mixed',
201+
};
202+
}
203+
204+
protected function namedTypeToString(\ReflectionNamedType $type): string
205+
{
206+
$name = $type->getName();
207+
208+
if (! $type->isBuiltin() && ! in_array($name, ['self', 'parent', 'static'])) {
209+
$name = '\\'.$name;
210+
}
211+
212+
if ($type->allowsNull() && ! in_array($name, ['null', 'mixed', 'void'])) {
213+
$name = '?'.$name;
214+
}
215+
216+
return $name;
217+
}
218+
219+
protected function unionTypeToString(\ReflectionUnionType $type): string
220+
{
221+
return implode('|', array_map(function (\ReflectionType $type) {
222+
$result = $this->typeToString($type);
223+
224+
if ($type instanceof \ReflectionIntersectionType) {
225+
return "({$result})";
226+
}
227+
228+
return $result;
229+
}, $type->getTypes()));
230+
}
231+
232+
protected function intersectionTypeToString(\ReflectionIntersectionType $type): string
233+
{
234+
return implode('&', array_map($this->typeToString(...), $type->getTypes()));
235+
}
236+
237+
protected function defaultValueToString(\ReflectionParameter $param): string
238+
{
239+
if ($param->isDefaultValueConstant()) {
240+
return '\\'.$param->getDefaultValueConstantName();
241+
}
242+
243+
$value = $param->getDefaultValue();
244+
245+
return match (true) {
246+
is_null($value) => 'null',
247+
is_numeric($value) => $value,
248+
is_bool($value) => $value ? 'true' : 'false',
249+
is_array($value) => '[]',
250+
is_object($value) => 'new \\'.get_class($value),
251+
default => "'{$value}'",
252+
};
253+
}
173254
};
174255

175256
$builder = new class($docblocks) {

src/index.d.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ declare namespace Eloquent {
8181
relations: Relation[];
8282
events: Event[];
8383
observers: Observer[];
84-
scopes: string[];
84+
scopes: Scope[];
8585
extends: string | null;
8686
}
8787

@@ -115,4 +115,18 @@ declare namespace Eloquent {
115115
event: string;
116116
observer: string[];
117117
}
118+
119+
interface Scope {
120+
name: string;
121+
parameters: ScopeParameter[];
122+
}
123+
124+
interface ScopeParameter {
125+
name: string;
126+
type: string;
127+
default?: string | null;
128+
isOptional: boolean;
129+
isVariadic: boolean;
130+
isPassedByReference: boolean;
131+
}
118132
}

src/support/docblocks.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,13 @@ const getBlocks = (
114114
return model.attributes
115115
.map((attr) => getAttributeBlocks(attr, className))
116116
.concat(
117-
[...model.scopes, "newModelQuery", "newQuery", "query"].map(
118-
(method) => {
119-
return `@method static ${modelBuilderType(
120-
className,
121-
)} ${method}()`;
122-
},
123-
),
117+
["newModelQuery", "newQuery", "query"].map((method) => {
118+
return `@method static ${modelBuilderType(
119+
className,
120+
)} ${method}()`;
121+
}),
124122
)
123+
.concat(model.scopes.map((scope) => getScopeBlock(className, scope)))
125124
.concat(model.relations.map((relation) => getRelationBlocks(relation)))
126125
.flat()
127126
.map((block) => ` * ${block}`)
@@ -175,6 +174,25 @@ const getRelationBlocks = (relation: Eloquent.Relation): string[] => {
175174
return [`@property-read \\${relation.related} $${relation.name}`];
176175
};
177176

177+
const getScopeBlock = (className: string, scope: Eloquent.Scope): string => {
178+
const parameters = scope.parameters
179+
.slice(1)
180+
.map((param) => {
181+
return [
182+
param.type,
183+
param.isVariadic ? " ..." : " ",
184+
param.isPassedByReference ? "&" : "",
185+
`$${param.name}`,
186+
param.default ? ` = ${param.default}` : "",
187+
].join("");
188+
})
189+
.join(", ");
190+
191+
return `@method static ${modelBuilderType(
192+
className,
193+
)} ${scope.name}(${parameters})`;
194+
};
195+
178196
const classToDocBlock = (block: ClassBlock, namespace: string) => {
179197
return [
180198
`/**`,

src/templates/models.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,11 @@ $models = new class($factory) {
159159
->toArray();
160160
161161
$data['scopes'] = collect($reflection->getMethods())
162-
->filter(fn($method) =>!$method->isStatic() && ($method->getAttributes(\\Illuminate\\Database\\Eloquent\\Attributes\\Scope::class) || ($method->isPublic() && str_starts_with($method->name, 'scope'))))
163-
->map(fn($method) => str($method->name)->replace('scope', '')->lcfirst()->toString())
162+
->filter(fn(\\ReflectionMethod $method) => !$method->isStatic() && ($method->getAttributes(\\Illuminate\\Database\\Eloquent\\Attributes\\Scope::class) || ($method->isPublic() && str_starts_with($method->name, 'scope'))))
163+
->map(fn(\\ReflectionMethod $method) => [
164+
"name" => str($method->name)->replace('scope', '')->lcfirst()->toString(),
165+
"parameters" => collect($method->getParameters())->map($this->getScopeParameterInfo(...)),
166+
])
164167
->values()
165168
->toArray();
166169
@@ -170,6 +173,84 @@ $models = new class($factory) {
170173
$className => $data,
171174
];
172175
}
176+
177+
protected function getScopeParameterInfo(\\ReflectionParameter $parameter): array
178+
{
179+
$result = [
180+
"name" => $parameter->getName(),
181+
"type" => $this->typeToString($parameter->getType()),
182+
"hasDefault" => $parameter->isDefaultValueAvailable(),
183+
"isVariadic" => $parameter->isVariadic(),
184+
"isPassedByReference" => $parameter->isPassedByReference(),
185+
];
186+
187+
if ($parameter->isDefaultValueAvailable()) {
188+
$result['default'] = $this->defaultValueToString($parameter);
189+
}
190+
191+
return $result;
192+
}
193+
194+
protected function typeToString(?\\ReflectionType $type): string
195+
{
196+
return match (true) {
197+
$type instanceof \\ReflectionNamedType => $this->namedTypeToString($type),
198+
$type instanceof \\ReflectionUnionType => $this->unionTypeToString($type),
199+
$type instanceof \\ReflectionIntersectionType => $this->intersectionTypeToString($type),
200+
default => 'mixed',
201+
};
202+
}
203+
204+
protected function namedTypeToString(\\ReflectionNamedType $type): string
205+
{
206+
$name = $type->getName();
207+
208+
if (! $type->isBuiltin() && ! in_array($name, ['self', 'parent', 'static'])) {
209+
$name = '\\\\'.$name;
210+
}
211+
212+
if ($type->allowsNull() && ! in_array($name, ['null', 'mixed', 'void'])) {
213+
$name = '?'.$name;
214+
}
215+
216+
return $name;
217+
}
218+
219+
protected function unionTypeToString(\\ReflectionUnionType $type): string
220+
{
221+
return implode('|', array_map(function (\\ReflectionType $type) {
222+
$result = $this->typeToString($type);
223+
224+
if ($type instanceof \\ReflectionIntersectionType) {
225+
return "({$result})";
226+
}
227+
228+
return $result;
229+
}, $type->getTypes()));
230+
}
231+
232+
protected function intersectionTypeToString(\\ReflectionIntersectionType $type): string
233+
{
234+
return implode('&', array_map($this->typeToString(...), $type->getTypes()));
235+
}
236+
237+
protected function defaultValueToString(\\ReflectionParameter $param): string
238+
{
239+
if ($param->isDefaultValueConstant()) {
240+
return '\\\\'.$param->getDefaultValueConstantName();
241+
}
242+
243+
$value = $param->getDefaultValue();
244+
245+
return match (true) {
246+
is_null($value) => 'null',
247+
is_numeric($value) => $value,
248+
is_bool($value) => $value ? 'true' : 'false',
249+
is_array($value) => '[]',
250+
is_object($value) => 'new \\\\'.get_class($value),
251+
default => "'{$value}'",
252+
};
253+
}
173254
};
174255
175256
$builder = new class($docblocks) {

0 commit comments

Comments
 (0)