- Some work done on entity generation
This commit is contained in:
parent
05975089e5
commit
2dc71cd350
@ -14,13 +14,13 @@ class %CLASSNAME% {
|
|||||||
use Lib\ApiTrait;
|
use Lib\ApiTrait;
|
||||||
|
|
||||||
#[Route(route: "/documentation", name: "api.%CLASSNAME_LC%:docs", method: "GET")]
|
#[Route(route: "/documentation", name: "api.%CLASSNAME_LC%:docs", method: "GET")]
|
||||||
public function index(ServerRequestInterface $request, array $attributes): ResponseInterface
|
public function index(ServerRequestInterface $request, array $arguments): ResponseInterface
|
||||||
{
|
{
|
||||||
return $this->renderMarkdown(getenv("PROJECT_PATH") . "/meta/docs/%CLASSNAME_LC%.md", [ %ENTITY_NS%\%CLASSNAME%::class, ], [ %FORM_NS%\%SAVE_FORM_CONTEXT_CLASSNAME%::class, %FORM_NS%\%DELETE_FORM_CONTEXT_CLASSNAME%::class ]);
|
return $this->renderMarkdown(getenv("PROJECT_PATH") . "/meta/docs/%CLASSNAME_LC%.md", [ %ENTITY_NS%\%CLASSNAME%::class, ], [ %FORM_NS%\%SAVE_FORM_CONTEXT_CLASSNAME%::class, %FORM_NS%\%DELETE_FORM_CONTEXT_CLASSNAME%::class ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route("/", name: "api.%CLASSNAME_LC%:list", method: "GET")]
|
#[Route("/", name: "api.%CLASSNAME_LC%:list", method: "GET")]
|
||||||
public function list(ServerRequestInterface $request, array $attributes) : ResponseInterface
|
public function list(ServerRequestInterface $request, array $arguments) : ResponseInterface
|
||||||
{
|
{
|
||||||
$request = $this->searchEntitiesFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
$request = $this->searchEntitiesFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
||||||
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
||||||
@ -29,7 +29,7 @@ class %CLASSNAME% {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route("/", name: "api.%CLASSNAME_LC%:add", method: "POST")]
|
#[Route("/", name: "api.%CLASSNAME_LC%:add", method: "POST")]
|
||||||
public function add(ServerRequestInterface $request, array $attributes) : ResponseInterface
|
public function add(ServerRequestInterface $request, array $arguments) : ResponseInterface
|
||||||
{
|
{
|
||||||
$entity = new %ENTITY_NS%\%CLASSNAME%();
|
$entity = new %ENTITY_NS%\%CLASSNAME%();
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ class %CLASSNAME% {
|
|||||||
|
|
||||||
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:single", method: "GET")]
|
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:single", method: "GET")]
|
||||||
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:edit", method: "PATCH")]
|
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:edit", method: "PATCH")]
|
||||||
public function single(ServerRequestInterface $request, array $attributes) : ResponseInterface
|
public function single(ServerRequestInterface $request, array $arguments) : ResponseInterface
|
||||||
{
|
{
|
||||||
$request = $this->searchEntityFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
$request = $this->searchEntityFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
||||||
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
||||||
@ -51,7 +51,7 @@ class %CLASSNAME% {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:delete", method: "DELETE")]
|
#[Route("/{id:\d+}", name: "api.%CLASSNAME_LC%:delete", method: "DELETE")]
|
||||||
public function delete(ServerRequestInterface $request, array $attributes) : ResponseInterface
|
public function delete(ServerRequestInterface $request, array $arguments) : ResponseInterface
|
||||||
{
|
{
|
||||||
$request = $this->searchEntityFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
$request = $this->searchEntityFromRequest($request, %ENTITY_NS%\%CLASSNAME%::class);
|
||||||
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
$result = $request->getAttribute("lean.searchRequest")[%ENTITY_NS%\%CLASSNAME%::class];
|
||||||
|
|||||||
@ -11,6 +11,10 @@ class %SAVE_FORM_CONTEXT_CLASSNAME% extends \Lean\Api\Lib\FormContext
|
|||||||
# #[ContextField]
|
# #[ContextField]
|
||||||
# public string $name;
|
# public string $name;
|
||||||
|
|
||||||
|
public function initializeEntity(%ENTITY_NS%\%CLASSNAME% $entity): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function valid(? %ENTITY_NS%\%CLASSNAME% $entity = null) : bool
|
public function valid(? %ENTITY_NS%\%CLASSNAME% $entity = null) : bool
|
||||||
{
|
{
|
||||||
return parent::valid();
|
return parent::valid();
|
||||||
|
|||||||
@ -567,14 +567,11 @@ class EntityGenerate
|
|||||||
|
|
||||||
protected function replaceClassname(string $boilerplate) : string
|
protected function replaceClassname(string $boilerplate) : string
|
||||||
{
|
{
|
||||||
return match($this->apiBase ?? false) {
|
return str_replace(
|
||||||
false => $boilerplate,
|
[ '%CLASSNAME_LC%', '%CLASSNAME%' ],
|
||||||
default => str_replace(
|
[ strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $this->name)), $this->name ],
|
||||||
[ '%CLASSNAME_LC%', '%CLASSNAME%' ],
|
$boilerplate
|
||||||
[ strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $this->name)), $this->name ],
|
);
|
||||||
$boilerplate
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function readComposerNamespace() : ? string
|
protected function readComposerNamespace() : ? string
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use Lean\Factory\HttpFactory;
|
|||||||
use Notes\ObjectReflection;
|
use Notes\ObjectReflection;
|
||||||
use Notes\Route\Attribute\Method\Route;
|
use Notes\Route\Attribute\Method\Route;
|
||||||
use Ulmus\Attribute\Obj\Table;
|
use Ulmus\Attribute\Obj\Table;
|
||||||
|
use Ulmus\Attribute\Property\ArrayOf;
|
||||||
use Ulmus\Attribute\Property\Field;
|
use Ulmus\Attribute\Property\Field;
|
||||||
use Ulmus\SearchRequest\Attribute\SearchParameter;
|
use Ulmus\SearchRequest\Attribute\SearchParameter;
|
||||||
use Ulmus\SearchRequest\Attribute\SearchRequestParameter;
|
use Ulmus\SearchRequest\Attribute\SearchRequestParameter;
|
||||||
@ -34,6 +35,7 @@ class EntityDescriptor
|
|||||||
$field = $property->getAttribute(Field::class);
|
$field = $property->getAttribute(Field::class);
|
||||||
|
|
||||||
if ($field) {
|
if ($field) {
|
||||||
|
$arrayOf = $property->getAttribute(ArrayOf::class);
|
||||||
$types = $property->getTypes();
|
$types = $property->getTypes();
|
||||||
|
|
||||||
if ($property->value ?? false) {
|
if ($property->value ?? false) {
|
||||||
@ -53,6 +55,7 @@ class EntityDescriptor
|
|||||||
'length' => $field->object->length ?? null,
|
'length' => $field->object->length ?? null,
|
||||||
'readonly' => $field->object->readonly,
|
'readonly' => $field->object->readonly,
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
|
'arrayOf' => $arrayOf ?? null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ abstract class Form
|
|||||||
{
|
{
|
||||||
if(isset($this->contextClass) && ! $context instanceof $this->contextClass ) {
|
if(isset($this->contextClass) && ! $context instanceof $this->contextClass ) {
|
||||||
throw new \LogicException(
|
throw new \LogicException(
|
||||||
sprintf("Your context type should be a %s", $this->contextClass)
|
sprintf("Your context type should be a %s, received %s", $this->contextClass, $context::class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,16 +14,21 @@ use Lean\Api\Attribute\EntityField;
|
|||||||
|
|
||||||
use Ulmus\Entity\EntityInterface;
|
use Ulmus\Entity\EntityInterface;
|
||||||
use Ulmus\Entity\Field\Datetime;
|
use Ulmus\Entity\Field\Datetime;
|
||||||
|
use Ulmus\EntityCollection;
|
||||||
|
|
||||||
abstract class Save extends Form implements \Picea\Ui\Method\FormInterface {
|
abstract class Save extends Form implements \Picea\Ui\Method\FormInterface {
|
||||||
|
|
||||||
|
protected bool $skipEntityCreatedAt = false;
|
||||||
|
|
||||||
|
protected bool $skipEntityLastModified = false;
|
||||||
|
|
||||||
protected array $reflectedProperties;
|
protected array $reflectedProperties;
|
||||||
|
|
||||||
protected string $contextClass = FormContext::class;
|
protected string $contextClass = FormContext::class;
|
||||||
|
|
||||||
public function initialize(FormContextInterface $context) : void
|
public function initialize(FormContextInterface $context) : void
|
||||||
{
|
{
|
||||||
if ( ! $this->getEntity()->isLoaded() ) {
|
if ( $this->getEntity() instanceof EntityInterface && ! $this->getEntity()->isLoaded() ) {
|
||||||
if (method_exists($context, 'initializeEntity')) {
|
if (method_exists($context, 'initializeEntity')) {
|
||||||
$context->initializeEntity($this->getEntity());
|
$context->initializeEntity($this->getEntity());
|
||||||
}
|
}
|
||||||
@ -46,13 +51,13 @@ abstract class Save extends Form implements \Picea\Ui\Method\FormInterface {
|
|||||||
{
|
{
|
||||||
$entity = $this->getEntity();
|
$entity = $this->getEntity();
|
||||||
|
|
||||||
if ($entity->isLoaded()) {
|
if ($entity->isLoaded() && ! $this->skipEntityLastModified) {
|
||||||
if (property_exists($entity, 'updatedAt') && $entity->repository()->generateDatasetDiff($entity) ) {
|
if (property_exists($entity, 'updatedAt') && $entity->repository()->generateDatasetDiff($entity) ) {
|
||||||
$cls = $entity::resolveEntity()->field('updatedAt')->type->type;
|
$cls = $entity::resolveEntity()->field('updatedAt')->type->type;
|
||||||
$entity->updatedAt = new $cls();
|
$entity->updatedAt = new $cls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
elseif (! $this->skipEntityCreatedAt) {
|
||||||
if (property_exists($entity, 'createdAt') && empty($entity->createdAt)) {
|
if (property_exists($entity, 'createdAt') && empty($entity->createdAt)) {
|
||||||
$cls = $entity::resolveEntity()->field('createdAt')->type->type;
|
$cls = $entity::resolveEntity()->field('createdAt')->type->type;
|
||||||
$entity->createdAt = new $cls();
|
$entity->createdAt = new $cls();
|
||||||
|
|||||||
@ -24,8 +24,6 @@ class ApiRenderer implements MiddlewareInterface {
|
|||||||
$response = $handler->handle($request);
|
$response = $handler->handle($request);
|
||||||
}
|
}
|
||||||
catch(\Throwable $ex) {
|
catch(\Throwable $ex) {
|
||||||
|
|
||||||
|
|
||||||
if (static::awaitingJson($request)) {
|
if (static::awaitingJson($request)) {
|
||||||
return HttpFactory::createJsonResponse([
|
return HttpFactory::createJsonResponse([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
@ -48,7 +46,7 @@ class ApiRenderer implements MiddlewareInterface {
|
|||||||
$payload = $response->getPayload();
|
$payload = $response->getPayload();
|
||||||
|
|
||||||
# For now, we match only response having a 'data' field
|
# For now, we match only response having a 'data' field
|
||||||
if (isset($payload['data'])) {
|
if ( (is_array($payload) || $payload instanceof \ArrayAccess) && isset($payload['data'])) {
|
||||||
return HttpFactory::createJsonResponse([
|
return HttpFactory::createJsonResponse([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'ts' => time(),
|
'ts' => time(),
|
||||||
@ -84,4 +82,19 @@ class ApiRenderer implements MiddlewareInterface {
|
|||||||
{
|
{
|
||||||
return str_contains(strtolower($request->getHeaderLine('content-type')), 'json');
|
return str_contains(strtolower($request->getHeaderLine('content-type')), 'json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function encodeHtml(iterable& $array) : iterable
|
||||||
|
{
|
||||||
|
foreach($array as &$item) {
|
||||||
|
if (is_array($item)) {
|
||||||
|
$item = static::encodeHtml($item);
|
||||||
|
}
|
||||||
|
elseif (is_string($item)) {
|
||||||
|
dump($item);
|
||||||
|
$item = htmlspecialchars($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,19 +35,20 @@ class RouteDescriptor
|
|||||||
|
|
||||||
$attribute = $reflector->reflectClass()->getAttribute(\Notes\Route\Attribute\Object\Route::class);
|
$attribute = $reflector->reflectClass()->getAttribute(\Notes\Route\Attribute\Object\Route::class);
|
||||||
|
|
||||||
$base = $attribute ? $attribute->object->base : "";
|
$base = $attribute ? $attribute->object->getBase() : "";
|
||||||
|
|
||||||
foreach($reflector->reflectMethods() as $method) {
|
foreach($reflector->reflectMethods() as $method) {
|
||||||
foreach(array_reverse($method->getAttributes(Route::class)) as $routeAttribute) {
|
foreach(array_reverse($method->getAttributes(Route::class)) as $routeAttribute) {
|
||||||
|
$itemBase = $routeAttribute->object->getBase() ?? $base;
|
||||||
$route = $routeAttribute->object;
|
$route = $routeAttribute->object;
|
||||||
$path = rtrim($route->route, '/');
|
$path = rtrim($route->route, '/');
|
||||||
$cleaned = $this->cleanRouteFromRegex($base.$path);
|
$cleaned = $this->cleanRouteFromRegex($itemBase.$path);
|
||||||
$url = $this->urlExtension->buildUrl($cleaned);
|
$url = $this->urlExtension->buildUrl($cleaned);
|
||||||
|
|
||||||
$routes[] = [
|
$routes[] = [
|
||||||
'name' => $route->name,
|
'name' => $route->name,
|
||||||
'route' => $url,
|
'route' => $url,
|
||||||
'path' => $base.$path,
|
'path' => $itemBase.$path,
|
||||||
'cleaned' => $cleaned,
|
'cleaned' => $cleaned,
|
||||||
'description'=> $route->description,
|
'description'=> $route->description,
|
||||||
#'methods' =>implode(', ', (array)$route->method),
|
#'methods' =>implode(', ', (array)$route->method),
|
||||||
|
|||||||
@ -1,5 +1,37 @@
|
|||||||
{% language.set "lean.api.descriptor.entity" %}
|
{% language.set "lean.api.descriptor.entity" %}
|
||||||
|
|
||||||
|
{% function describeArrayOf($arrayOf) %}
|
||||||
|
{% php
|
||||||
|
$reflection = new \ReflectionClass($arrayOf->object->type);
|
||||||
|
$constructor = $reflection->getConstructor();
|
||||||
|
|
||||||
|
if (!$constructor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
foreach ($constructor->getParameters() as $param) {
|
||||||
|
$paramStr = '';
|
||||||
|
|
||||||
|
if ($param->getType()) {
|
||||||
|
$paramStr .= (string)$param->getType() . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramStr .= '$' . $param->getName();
|
||||||
|
|
||||||
|
if ($param->isDefaultValueAvailable()) {
|
||||||
|
$default = $param->getDefaultValue();
|
||||||
|
$paramStr .= ' = ' . var_export($default, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[] = $paramStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf("%s (%s)", $reflection->getShortName(), implode(', ', $params));
|
||||||
|
%}
|
||||||
|
{% endfunction %}
|
||||||
|
|
||||||
{% function yesOrNo(bool $toggle) : void %}
|
{% function yesOrNo(bool $toggle) : void %}
|
||||||
<span style="color: {{ $toggle ? 'green' : '#ac1b1b' }}">{{ $toggle ? 'oui' : 'non' }}</span>
|
<span style="color: {{ $toggle ? 'green' : '#ac1b1b' }}">{{ $toggle ? 'oui' : 'non' }}</span>
|
||||||
{% endfunction %}
|
{% endfunction %}
|
||||||
@ -28,10 +60,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
<div class="field-desc">
|
||||||
<div class="fieldname"><u>Champ SQL</u> : {{ $field['fieldName'] }}</div>
|
<div class="fieldname"><u>Champ SQL</u> : {{ $field['fieldName'] }}</div>
|
||||||
<div class="tag"><u>Attribut</u> : {{ $field['tag'] }}</div>
|
<div class="tag"><u>Attribut</u> : {{ $field['tag'] }}</div>
|
||||||
<div class="type"><u>Type(s)</u> : {{ $field['type'] }}</div>
|
<div class="type"><u>Type(s)</u> : {{ $field['type'] }}</div>
|
||||||
|
{% if $field['arrayOf'] %}
|
||||||
|
<div class="array-of"><u>Définition</u> : {{ describeArrayOf($field['arrayOf']) }}</div>
|
||||||
|
{% endif %}
|
||||||
<div class="nullable"><u>Nullable</u> {{ yesOrNo($field['allowNulls']) }}</div>
|
<div class="nullable"><u>Nullable</u> {{ yesOrNo($field['allowNulls']) }}</div>
|
||||||
{% if $field['default'] %}
|
{% if $field['default'] %}
|
||||||
<div class="default"><u>Valeur par défaut</u> : {{ $field['default'] }}</div>
|
<div class="default"><u>Valeur par défaut</u> : {{ $field['default'] }}</div>
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
<div><u>Exemple</u> : {{ $field['example'] }}</div>
|
<div><u>Exemple</u> : {{ $field['example'] }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if $field['default'] !== null %}
|
{% if $field['default'] !== null %}
|
||||||
<div><u>Default</u> : {{ $field['default'] }}</div>
|
<div><u>Valeur par défaut</u> : {{ $field['default'] }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if $field['values'] %}
|
{% if $field['values'] %}
|
||||||
<div><u>Valeurs possibles</u> : [ <u>{{= implode('</u>, <u>', $field['values']) }}</u> ]</div>
|
<div><u>Valeurs possibles</u> : [ <u>{{= implode('</u>, <u>', $field['values']) }}</u> ]</div>
|
||||||
|
|||||||
@ -156,7 +156,6 @@
|
|||||||
|
|
||||||
launchRequest(requestMethod, input.value, editor.getValue())
|
launchRequest(requestMethod, input.value, editor.getValue())
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response);
|
|
||||||
responseHead.querySelector('.response-code').innerText = response.status;
|
responseHead.querySelector('.response-code').innerText = response.status;
|
||||||
responseHead.querySelector('.response-message').innerText = response.statusText;
|
responseHead.querySelector('.response-message').innerText = response.statusText;
|
||||||
|
|
||||||
@ -165,10 +164,10 @@
|
|||||||
return response.text();
|
return response.text();
|
||||||
})
|
})
|
||||||
.then(body => {
|
.then(body => {
|
||||||
console.log(aceMode);
|
|
||||||
if (aceMode === "json") {
|
if (aceMode === "json") {
|
||||||
body = JSON.stringify(JSON.parse(body), null, 2);
|
body = JSON.stringify(JSON.parse(body), null, 2);
|
||||||
responseEditorElement.innerHTML = body;
|
|
||||||
|
responseEditorElement.innerHTML = DOMEncode(body);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
responseEditorElement.innerText = body;
|
responseEditorElement.innerText = body;
|
||||||
@ -285,4 +284,11 @@
|
|||||||
}
|
}
|
||||||
catch(e) {}
|
catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DOMEncode(text) {
|
||||||
|
const tempElement = document.createElement('div');
|
||||||
|
tempElement.textContent = text;
|
||||||
|
|
||||||
|
return tempElement.innerHTML;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -81,6 +81,8 @@
|
|||||||
.entity-wrapper .entity-name {background:#eaa1af;color: #682828;font-size:110%}
|
.entity-wrapper .entity-name {background:#eaa1af;color: #682828;font-size:110%}
|
||||||
.entity-wrapper .fields-wrapper {border-color: #c14141}
|
.entity-wrapper .fields-wrapper {border-color: #c14141}
|
||||||
.entity-wrapper .header-fields {background:#c14141}
|
.entity-wrapper .header-fields {background:#c14141}
|
||||||
|
.entity-wrapper .field-desc {margin-top:10px;;background:#fff;padding:5px;font-size:0.9em}
|
||||||
|
.entity-wrapper .field-desc .array-of {font-weight:bold;padding-left:15px;color:#955252}
|
||||||
.entity-wrapper ol {background: #e3d0d0;}
|
.entity-wrapper ol {background: #e3d0d0;}
|
||||||
.entity-wrapper li {border-color: #ae8585;}
|
.entity-wrapper li {border-color: #ae8585;}
|
||||||
.entity-wrapper li .default {color:#bf7d4d;}
|
.entity-wrapper li .default {color:#bf7d4d;}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user