188 lines
6.8 KiB
PHP
188 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace Lean\Api\Form;
|
|
|
|
use CSLSJ\Lean\Form\Session\EmailContext;
|
|
use Picea\Ui\Method\{FormContext, FormContextInterface};
|
|
|
|
use Lean\Api\Attribute\ContextField;
|
|
use Lean\Api\Exception\MandatoryFieldException;
|
|
use Notes\Common\ReflectedProperty;
|
|
use Notes\ObjectReflection;
|
|
use Ulmus\Attribute\Property\Field;
|
|
use Lean\Api\Attribute\EntityField;
|
|
|
|
use Ulmus\Entity\EntityInterface;
|
|
use Ulmus\Entity\Field\Datetime;
|
|
use Ulmus\EntityCollection;
|
|
|
|
abstract class Save extends Form implements \Picea\Ui\Method\FormInterface {
|
|
|
|
protected bool $skipEntityCreatedAt = false;
|
|
|
|
protected bool $skipEntityLastModified = false;
|
|
|
|
protected string $messageSuccessSave = 'lean.api.form.save.success.save';
|
|
|
|
protected string $messageWarningSave = 'lean.api.form.save.warning.entity';
|
|
|
|
protected array $fields = [
|
|
'updatedAt' => 'updatedAt',
|
|
'createdAt' => 'createdAt'
|
|
];
|
|
|
|
protected array $reflectedProperties;
|
|
|
|
protected string $contextClass = FormContext::class;
|
|
|
|
public function initialize(FormContextInterface $context) : void
|
|
{
|
|
if ( $this->getEntity() instanceof EntityInterface && ! $this->getEntity()->isLoaded() ) {
|
|
if (method_exists($context, 'initializeEntity')) {
|
|
$context->initializeEntity($this->getEntity());
|
|
}
|
|
|
|
$this->assignContextToEntity($context);
|
|
}
|
|
|
|
parent::initialize($context);
|
|
}
|
|
|
|
public function validate(FormContextInterface $context) : bool
|
|
{
|
|
# Context validates ContextField attributes containing properties on empty entity
|
|
$this->validateContextFields($context);
|
|
|
|
return $context->valid($this->getEntity()->isLoaded() ? $this->getEntity() : null);
|
|
}
|
|
|
|
public function execute(FormContextInterface $context) : mixed
|
|
{
|
|
$entity = $this->getEntity();
|
|
|
|
if ($entity->isLoaded() && ! $this->skipEntityLastModified) {
|
|
if (property_exists($entity, 'updatedAt') && $entity->repository()->generateDatasetDiff($entity) ) {
|
|
if ($field = $entity::resolveEntity()->field('updatedAt')) {
|
|
$cls = $field->type->type;
|
|
$entity->updatedAt = new $cls();
|
|
}
|
|
}
|
|
}
|
|
elseif (! $this->skipEntityCreatedAt) {
|
|
if (property_exists($entity, 'createdAt') && empty($entity->createdAt)) {
|
|
if ($field = $entity::resolveEntity()->field('createdAt')) {
|
|
$cls = $field->type->type;
|
|
$entity->createdAt = new $cls();
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->handleTimestamp($entity);
|
|
|
|
try {
|
|
$this->assignContextToEntity($context);
|
|
|
|
if ( $saved = $entity::repository()->save($entity) ) {
|
|
method_exists($context, 'pushSuccessMessage') && $context->pushSuccessMessage($this->messageSuccessSave);
|
|
}
|
|
else {
|
|
method_exists($context, 'pushWarningMessage') && $context->pushWarningMessage($this->messageWarningSave, [ 'entity' => substr($this->entity::class, strrpos($this->entity::class, '\\') + 1) ]);
|
|
}
|
|
}
|
|
catch(\PDOException $ex) {
|
|
throw new \PDOException($this->lang('lean.api.form.save.error.pdo', [ 'error' => $ex->getMessage() ]));
|
|
}
|
|
catch(\Throwable $ex) {
|
|
method_exists($context, 'pushErrorMessage') && $context->pushErrorMessage($ex->getMessage());
|
|
}
|
|
|
|
return $saved ?? false;
|
|
}
|
|
|
|
protected function validateContextFields(FormContextInterface $context) : void
|
|
{
|
|
foreach ($this->reflectedProperties($context) as $name => $property) {
|
|
$attribute = $property->getAttribute(ContextField::class);
|
|
|
|
if ($attribute) {
|
|
try {
|
|
$attribute->object->assertValueSpecs($context->$name ?? null, ! $this->getEntity()->isLoaded());
|
|
}
|
|
catch(\Throwable $ex) {
|
|
throw new MandatoryFieldException("An error occured with field '$name': " . $ex->getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function assignContextToEntity(FormContextInterface $context) : void
|
|
{
|
|
$entity = $this->getEntity();
|
|
|
|
foreach($entity::resolveEntity()->getPropertyHavingAttribute(EntityField::class) as $key => $entityField) {
|
|
$property = $entity::resolveEntity()->reflectedClass->getProperties(true)[$key];
|
|
|
|
if ($property) {
|
|
if (($property->getAttribute(Field::class)->object->readonly ?? false) && $entity->isLoaded()) {
|
|
continue;
|
|
}
|
|
|
|
$apiField = $entityField[0]->object ;
|
|
$var = $apiField->field ?: $key;
|
|
|
|
if ( property_exists($context, $var) && ( new \ReflectionProperty($context, $var) )->isInitialized($context) ) {
|
|
if ($apiField->setterMethod) {
|
|
# Use a setter method
|
|
call_user_func([ $entity, $apiField->setterMethod ], $context->{$var});
|
|
}
|
|
else {
|
|
# Direct property set
|
|
$entity->$key = $context->{$var};
|
|
}
|
|
}
|
|
elseif (! $entity->isLoaded()) {
|
|
# Applies 'default' value assigned to ContextField property
|
|
$attr = $this->reflectedProperties($context)[$var] ?? null;
|
|
|
|
if ($attr) {
|
|
$contextField = $attr->getAttribute(ContextField::class);
|
|
|
|
if ($contextField) {
|
|
if (isset($contextField->object->default)) {
|
|
|
|
$entity->$key = $contextField->object->default;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function handleTimestamp(EntityInterface $entity) : void
|
|
{
|
|
if ($entity->isLoaded()) {
|
|
if (property_exists($entity, $this->fields['updatedAt']) && $entity->repository()->generateDatasetDiff($entity) ) {
|
|
$field = $this->fields['updatedAt'];
|
|
}
|
|
}
|
|
else {
|
|
if (property_exists($entity, $this->fields['createdAt']) && empty($entity->{$this->fields['createdAt']})) {
|
|
$field = $this->fields['createdAt'];
|
|
}
|
|
}
|
|
|
|
if ($field ?? false) {
|
|
$cls = $entity::resolveEntity()->field($field)->type->type;
|
|
$entity->{$field} = new $cls();
|
|
}
|
|
}
|
|
|
|
private function reflectedProperties(FormContextInterface $context) : array
|
|
{
|
|
$this->reflectedProperties ??= ObjectReflection::fromClass($context::class)->reflectProperties(\ReflectionProperty::IS_PUBLIC);
|
|
|
|
return $this->reflectedProperties;
|
|
}
|
|
}
|