'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; } }