Model.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <?php
  2. namespace App\Core;
  3. /*
  4. * Clase base para todos los
  5. * modelos de la aplicación.
  6. */
  7. abstract class Model
  8. {
  9. public const RULE_REQUIRED = 'required';
  10. public const RULE_EMAIL = 'email';
  11. public const RULE_MIN = 'min';
  12. public const RULE_MAX = 'max';
  13. public const RULE_MATCH = 'match';
  14. public const RULE_UNIQUE = 'unique';
  15. /*
  16. * Almacena errores de validación.
  17. */
  18. public array $errors = [];
  19. /*
  20. * Inicializa las propiedades de un modelo.
  21. */
  22. public function loadData(array $data)
  23. {
  24. foreach ($data as $key => $value) {
  25. if (property_exists($this, $key)) {
  26. $this->{$key} = $value;
  27. }
  28. }
  29. }
  30. /*
  31. * Define las reglas de validación de un modelo.
  32. */
  33. abstract public function rules(): array;
  34. /*
  35. * Agrega un mensaje de validación
  36. * desde una regla de validación.
  37. */
  38. protected function addErrorForRule(string $attribute, string $rule, array $params = [])
  39. {
  40. $message = $this->errorMessages()[$rule] ?? '';
  41. foreach ($params as $key => $value) {
  42. $message = str_replace("{{$key}}", mb_strtolower($value), $message);
  43. }
  44. $this->addError($attribute, $message);
  45. }
  46. /*
  47. * Agrega un mensaje de validación.
  48. */
  49. public function addError(string $attribute, string $message)
  50. {
  51. $this->errors[$attribute][] = $message;
  52. }
  53. /*
  54. * Define todos los mensajes de validación.
  55. */
  56. public function errorMessages()
  57. {
  58. return [
  59. self::RULE_REQUIRED => 'Este campo es obligatorio.',
  60. self::RULE_EMAIL => 'Este campo debe ser una dirección de correo electrónico válida.',
  61. self::RULE_MIN => 'La longitud mínima de este campo debe ser {min}.',
  62. self::RULE_MAX => 'La longitud máxima de este campo debe ser {max}.',
  63. self::RULE_MATCH => 'Este campo no coincide con el campo {match}.',
  64. self::RULE_UNIQUE => 'El campo {field} debe contener un valor único.',
  65. ];
  66. }
  67. /*
  68. * Valida los atributos de un modelo.
  69. */
  70. public function validate()
  71. {
  72. foreach ($this->rules() as $attribute => $rules) {
  73. $value = $this->{$attribute};
  74. foreach ($rules as $rule) {
  75. $ruleName = $rule;
  76. if (! is_string($ruleName)) {
  77. $ruleName = $rule[0];
  78. }
  79. if ($ruleName === self::RULE_REQUIRED && ! $value) {
  80. $this->addErrorForRule($attribute, self::RULE_REQUIRED);
  81. }
  82. if ($ruleName === self::RULE_EMAIL && ! filter_var($value, FILTER_VALIDATE_EMAIL)) {
  83. $this->addErrorForRule($attribute, self::RULE_EMAIL);
  84. }
  85. if ($ruleName === self::RULE_MIN && array_key_exists('min', $rule) && strlen($value) < $rule['min']) {
  86. $this->addErrorForRule($attribute, self::RULE_MIN, $rule);
  87. }
  88. if ($ruleName === self::RULE_MAX && array_key_exists('max', $rule) && strlen($value) > $rule['max']) {
  89. $this->addErrorForRule($attribute, self::RULE_MAX, $rule);
  90. }
  91. if ($ruleName === self::RULE_MATCH && array_key_exists('match', $rule) && $value !== $this->{$rule['match']}) {
  92. $rule['match'] = $this->getLabel($rule['match']);
  93. $this->addErrorForRule($attribute, self::RULE_MATCH, $rule);
  94. }
  95. if ($ruleName == self::RULE_UNIQUE) {
  96. $className = $rule['class'];
  97. $uniqueAttr = $rule['attribute'] ?? $attribute;
  98. $tableName = $className::tableName();
  99. $statement = Application::$app->db->prepare("SELECT * FROM {$tableName} WHERE {$uniqueAttr} = :attr LIMIT 1");
  100. $statement->bindValue(':attr', $value);
  101. $statement->execute();
  102. if ($statement->fetchObject()) {
  103. $this->addErrorForRule($attribute, self::RULE_UNIQUE, ['field' => $this->getLabel($attribute)]);
  104. }
  105. }
  106. }
  107. }
  108. return empty($this->errors);
  109. }
  110. /*
  111. * Valida si existe un error de validación.
  112. */
  113. public function hasError(string $attribute)
  114. {
  115. return array_key_exists($attribute, $this->errors);
  116. }
  117. /*
  118. * Obtiene un mensaje de error de validación.
  119. */
  120. public function getFirstError(string $attribute)
  121. {
  122. return $this->errors[$attribute][0] ?? null;
  123. }
  124. /*
  125. * Establece las etiquetas de los campos
  126. * de un formulario desde un modelo.
  127. */
  128. public function labels(): array
  129. {
  130. return [];
  131. }
  132. /*
  133. * Obtiene la etiqueta de un campo
  134. * del formulario desde un modelo.
  135. */
  136. public function getLabel(string $attribute)
  137. {
  138. return $this->labels()[$attribute] ?? $attribute;
  139. }
  140. }