<?php
abstract class FormModel
{
private array $attributes;
private array $attributesLabels;
private array $attributesErrors = [];
public function __construct()
{
$this->attributes = $this->attributes();
$this->attributesLabels = $this->attributeLabels();
}
public function getAttributeValue(string $attribute)
{
return $this->readProperty($attribute);
}
public function attributeHint(string $attribute): string
{
$hints = $this->attributeHints();
return $hints[$attribute] ?? '';
}
public function attributeHints(): array
{
return [];
}
public function attributeLabel(string $attribute): string
{
return $this->attributesLabels[$attribute] ?? $this->generateAttributeLabel($attribute);
}
public function formName(): string
{
return '';
}
public function hasAttribute(string $attribute): bool
{
return \array_key_exists($attribute, $this->attributes);
}
public function error(string $attribute): array
{
return $this->attributesErrors[$attribute] ?? [];
}
public function errors(): array
{
return $this->attributesErrors ?? [];
}
public function errorSummary(bool $showAllErrors): array
{
$lines = [];
$errors = $showAllErrors ? $this->errors() : [$this->firstErrors()];
foreach ($errors as $error) {
$lines = \array_merge($lines, $error);
}
return $lines;
}
public function firstError(string $attribute): string
{
return isset($this->attributesErrors[$attribute]) ? reset($this->attributesErrors[$attribute]) : '';
}
public function firstErrors(): array
{
if (empty($this->attributesErrors)) {
return [];
}
$errors = [];
foreach ($this->attributesErrors as $name => $es) {
if (!empty($es)) {
$errors[$name] = \reset($es);
}
}
return $errors;
}
public function hasErrors(?string $attribute = null): bool
{
return $attribute === null ? !empty($this->attributesErrors) : isset($this->attributesErrors[$attribute]);
}
public function load(array $data): bool
{
$result = false;
$scope = $this->formName();
if ($scope === '' && !empty($data)) {
$this->setAttributes($data);
$result = true;
} elseif (isset($data[$scope])) {
$this->setAttributes($data[$scope]);
$result = true;
}
return $result;
}
public function setAttributes(array $values): void
{
foreach ($values as $name => $value) {
if (isset($this->attributes[$name])) {
switch ($this->attributes[$name]) {
case 'bool':
$this->writeProperty($name, (bool) $value);
break;
case 'float':
$this->writeProperty($name, (float) $value);
break;
case 'int':
$this->writeProperty($name, (int) $value);
break;
default:
$this->writeProperty($name, $value);
break;
}
}
}
}
protected function addError(string $attribute, string $error): void
{
$this->attributesErrors[$attribute][] = $error;
}
private function addErrors(array $items): void
{
foreach ($items as $attribute => $errors) {
foreach ((array)$errors as $error) {
$this->attributesErrors[$attribute][] = $error;
}
}
}
private function attributes(): array
{
$class = new \ReflectionClass($this);
foreach ($class->getProperties() as $property) {
if (!$property->isStatic()) {
$type = (new \ReflectionProperty($property->class, $property->name))->getType();
if ($type !== null) {
$this->attributes[$property->getName()] = $type->getName();
} else {
throw new \InvalidArgumentException(
"You must specify the type hint for \"$property->class\" class."
);
}
}
}
return $this->attributes;
}
private function clearErrors(?string $attribute = null): void
{
if ($attribute === null) {
$this->attributesErrors = [];
} else {
unset($this->attributesErrors[$attribute]);
}
}
private function readProperty(string $attribute)
{
$class = get_class($this);
if (!property_exists($class, $attribute)) {
throw new InvalidArgumentException("Undefined property: \"$class::$attribute\".");
}
$getter = fn ($class, $attribute) => $class->$attribute;
$getter = \Closure::bind($getter, null, $this);
return $getter($this, $attribute);
}
private function writeProperty(string $attribute, $value): void
{
$setter = fn ($class, $attribute, $value) => $class->$attribute = $value;
$setter = \Closure::bind($setter, null, $this);
$setter($this, $attribute, $value);
}
}
class LoginForm extends FormModel
{
private ?string $login = null;
private ?string $password = null;
private bool $rememberMe = false;
public function getLogin(): ?string
{
return $this->login;
}
public function getPassword(): ?string
{
return $this->password;
}
public function getRememberMe(): bool
{
return $this->rememberMe;
}
public function login(string $value): void
{
$this->login = $value;
}
public function password(string $value): void
{
$this->password = $value;
}
public function rememberMe(bool $value): void
{
$this->rememberMe = $value;
}
public function attributeHints(): array
{
return [
'login' => 'Write your id or email.',
'password' => 'Write your password.'
];
}
public function attributeLabels(): array
{
return [
'login' => 'Login:',
'password' => 'Password:',
'rememberMe' => 'remember Me:'
];
}
public function formName(): string
{
return 'LoginForm';
}
public function addError(string $attribute, string $error): void
{
parent::addError($attribute, $error);
}
}
$form = new LoginForm();
$form->login('yiiliveext');
$form->addError('login', 'Login incorrect');
var_dump($form->errors());
preferences:
28.88 ms | 406 KiB | 5 Q