lean-api/src/Form/Save.php

150 lines
5.2 KiB
PHP

<?php
namespace Lean\Api\Form;
use CSLSJ\Lean\Form\Session\EmailContext;
use Picea\Ui\Method\{ 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;
abstract class Save extends Form implements \Picea\Ui\Method\FormInterface {
protected array $reflectedProperties;
protected string $contextClass;
public function initialize(FormContextInterface $context) : void
{
if ( ! $this->getEntity()->isLoaded() || $context->formSent() ) {
if (method_exists($context, 'initializeEntity')) {
$context->initializeEntity($this->getEntity());
}
else {
$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()) {
if (property_exists($entity, 'updatedAt') && $entity->repository()->generateDatasetDiff($entity) ) {
$entity->updatedAt = new Datetime();
}
}
else {
if (property_exists($entity, 'createdAt') && empty($entity->createdAt)) {
$entity->createdAt = new Datetime();
}
}
try {
$this->assignContextToEntity($context);
if ( $saved = $entity::repository()->save($entity) ) {
$context->pushMessage($this->message::generateSuccess(
$this->lang('lean.api.form.save.success.save')
));
}
else {
$context->pushMessage($this->message::generateWarning(
$this->lang('lean.api.form.save.warning.entity', [ '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) {
throw $ex;
}
return $saved;
}
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 (MandatoryFieldException $e) {
throw new MandatoryFieldException("An error occured with field '$name': " . $e->getMessage());
}
}
}
}
protected function assignContextToEntity(FormContextInterface $context) : void
{
$entity = $this->getEntity();
foreach($entity::resolveEntity()->fieldList() as $key => $property) {
$field = $property->getAttribute(Field::class)->object;
if (! $field->readonly || ! $entity->isLoaded()) {
$apiField = $property->getAttribute(EntityField::class)->object ?? null;
if ($apiField) {
$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;
}
}
}
}
}
}
}
}
private function reflectedProperties(FormContextInterface $context) : array
{
$this->reflectedProperties ??= ObjectReflection::fromClass($context::class)->reflectProperties(\ReflectionProperty::IS_PUBLIC);
return $this->reflectedProperties;
}
}