|
@@ -2,6 +2,7 @@
|
|
|
|
|
|
namespace Knuckles\Camel\Extraction;
|
|
namespace Knuckles\Camel\Extraction;
|
|
|
|
|
|
|
|
+use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Routing\Route;
|
|
use Illuminate\Routing\Route;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Support\Str;
|
|
use Knuckles\Camel\BaseDTO;
|
|
use Knuckles\Camel\BaseDTO;
|
|
@@ -81,11 +82,12 @@ class ExtractedEndpointData extends BaseDTO
|
|
|
|
|
|
public function __construct(array $parameters = [])
|
|
public function __construct(array $parameters = [])
|
|
{
|
|
{
|
|
- $parameters['uri'] = $this->normalizeResourceParamName($parameters['uri'], $parameters['route']);
|
|
|
|
$parameters['metadata'] = $parameters['metadata'] ?? new Metadata([]);
|
|
$parameters['metadata'] = $parameters['metadata'] ?? new Metadata([]);
|
|
$parameters['responses'] = $parameters['responses'] ?? new ResponseCollection([]);
|
|
$parameters['responses'] = $parameters['responses'] ?? new ResponseCollection([]);
|
|
|
|
|
|
parent::__construct($parameters);
|
|
parent::__construct($parameters);
|
|
|
|
+
|
|
|
|
+ $this->uri = $this->normalizeResourceParamName($this->uri, $this->route, $this->getTypeHintedArguments());
|
|
}
|
|
}
|
|
|
|
|
|
public static function fromRoute(Route $route, array $extras = []): self
|
|
public static function fromRoute(Route $route, array $extras = []): self
|
|
@@ -131,7 +133,7 @@ class ExtractedEndpointData extends BaseDTO
|
|
return $this->httpMethods[0] . str_replace(['/', '?', '{', '}', ':', '\\', '+', '|'], '-', $this->uri);
|
|
return $this->httpMethods[0] . str_replace(['/', '?', '{', '}', ':', '\\', '+', '|'], '-', $this->uri);
|
|
}
|
|
}
|
|
|
|
|
|
- public function normalizeResourceParamName(string $uri, Route $route): string
|
|
|
|
|
|
+ public function normalizeResourceParamName(string $uri, Route $route, array $typeHintedArguments): string
|
|
{
|
|
{
|
|
$params = [];
|
|
$params = [];
|
|
preg_match_all('#\{(\w+?)}#', $uri, $params);
|
|
preg_match_all('#\{(\w+?)}#', $uri, $params);
|
|
@@ -157,9 +159,11 @@ class ExtractedEndpointData extends BaseDTO
|
|
"{$pluralResource}/{{$singularResource}?}"
|
|
"{$pluralResource}/{{$singularResource}?}"
|
|
];
|
|
];
|
|
|
|
|
|
- // We'll replace with {id} by default, but if the user is using a different key,
|
|
|
|
- // like /users/{user:uuid}, use that instead
|
|
|
|
- $binding = static::getFieldBindingForUrlParam($route, $singularResource, 'id');
|
|
|
|
|
|
+ // If there is an inline binding in the route, like /users/{user:uuid}, use that key,
|
|
|
|
+ // Else, search for a type-hinted variable in the action, whose name matches the route segment name,
|
|
|
|
+ // If there is such variable (like User $user), call getRouteKeyName() on the model,
|
|
|
|
+ // Otherwise, use the id
|
|
|
|
+ $binding = static::getFieldBindingForUrlParam($route, $singularResource, $typeHintedArguments, 'id');
|
|
|
|
|
|
if (!$foundResourceParam) {
|
|
if (!$foundResourceParam) {
|
|
// Only the last resource param should be {id}
|
|
// Only the last resource param should be {id}
|
|
@@ -179,8 +183,8 @@ class ExtractedEndpointData extends BaseDTO
|
|
}
|
|
}
|
|
|
|
|
|
foreach ($params[1] as $param) {
|
|
foreach ($params[1] as $param) {
|
|
- // For non-resource parameters, if there's a field binding, replace that too:
|
|
|
|
- if ($binding = static::getFieldBindingForUrlParam($route, $param)) {
|
|
|
|
|
|
+ // For non-resource parameters, if there's a field binding/type-hinted variable, replace that too:
|
|
|
|
+ if ($binding = static::getFieldBindingForUrlParam($route, $param, $typeHintedArguments)) {
|
|
$search = ["{{$param}}", "{{$param}?}"];
|
|
$search = ["{{$param}}", "{{$param}?}"];
|
|
$replace = ["{{$param}_{$binding}}", "{{$param}_{$binding}?}"];
|
|
$replace = ["{{$param}_{$binding}}", "{{$param}_{$binding}?}"];
|
|
$uri = str_replace($search, $replace, $uri);
|
|
$uri = str_replace($search, $replace, $uri);
|
|
@@ -207,7 +211,8 @@ class ExtractedEndpointData extends BaseDTO
|
|
return $copy;
|
|
return $copy;
|
|
}
|
|
}
|
|
|
|
|
|
- public static function getFieldBindingForUrlParam(Route $route, string $paramName, string $default = null): ?string
|
|
|
|
|
|
+ public static function getFieldBindingForUrlParam(Route $route, string $paramName, array $typeHintedArguments = [],
|
|
|
|
+ string $default = null): ?string
|
|
{
|
|
{
|
|
$binding = null;
|
|
$binding = null;
|
|
// Was added in Laravel 7.x
|
|
// Was added in Laravel 7.x
|
|
@@ -215,6 +220,48 @@ class ExtractedEndpointData extends BaseDTO
|
|
$binding = $route->bindingFieldFor($paramName);
|
|
$binding = $route->bindingFieldFor($paramName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Search for a type-hinted variable whose name matches the route segment name
|
|
|
|
+ if (is_null($binding) && array_key_exists($paramName, $typeHintedArguments)) {
|
|
|
|
+ $argumentType = $typeHintedArguments[$paramName]->getType();
|
|
|
|
+ $argumentClassName = $argumentType->getName();
|
|
|
|
+ $argumentInstance = new $argumentClassName;
|
|
|
|
+ $binding = $argumentInstance->getRouteKeyName();
|
|
|
|
+ }
|
|
|
|
+
|
|
return $binding ?: $default;
|
|
return $binding ?: $default;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Return the type-hinted method arguments in the action that have a Model type,
|
|
|
|
+ * The arguments will be returned as an array of the form: $arguments[<variable_name>] = $argument
|
|
|
|
+ */
|
|
|
|
+ protected function getTypeHintedArguments(): array
|
|
|
|
+ {
|
|
|
|
+ $arguments = [];
|
|
|
|
+ if ($this->method) {
|
|
|
|
+ foreach ($this->method->getParameters() as $argument) {
|
|
|
|
+ if ($this->argumentHasModelType($argument)) {
|
|
|
|
+ $arguments[$argument->getName()] = $argument;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return $arguments;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Determine whether the argument has a Model type
|
|
|
|
+ */
|
|
|
|
+ protected function argumentHasModelType(\ReflectionParameter $argument): bool
|
|
|
|
+ {
|
|
|
|
+ $argumentType = $argument->getType();
|
|
|
|
+ if (!$argumentType) {
|
|
|
|
+ // The argument does not have a type-hint
|
|
|
|
+ return false;
|
|
|
|
+ } else {
|
|
|
|
+ $argumentClassName = $argumentType->getName();
|
|
|
|
+ $argumentInstance = new $argumentClassName;
|
|
|
|
+ return ($argumentInstance instanceof Model);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|