/src/Traits/ErrorMessageTrait.php

https://github.com/inhere/php-validate · PHP · 461 lines · 223 code · 55 blank · 183 comment · 19 complexity · af6b3e2446a68b5f58c709182500f24b MD5 · raw file

  1. <?php declare(strict_types=1);
  2. namespace Inhere\Validate\Traits;
  3. use Closure;
  4. use Inhere\Validate\Helper;
  5. use Inhere\Validate\Validator\GlobalMessage;
  6. use Inhere\Validate\Validators;
  7. use function array_merge;
  8. use function array_pop;
  9. use function array_shift;
  10. use function count;
  11. use function implode;
  12. use function is_array;
  13. use function is_int;
  14. use function is_string;
  15. use function strpos;
  16. use function strtr;
  17. /**
  18. * trait ErrorMessageTrait
  19. *
  20. * @author inhere
  21. * @package Inhere\Validate\Traits
  22. */
  23. trait ErrorMessageTrait
  24. {
  25. /**
  26. * error messages map
  27. *
  28. * @var array
  29. */
  30. private $_messages = [];
  31. /**
  32. * attribute field translate list
  33. *
  34. * @var array
  35. */
  36. private $_translates = [];
  37. /**
  38. * Save all validation error messages
  39. *
  40. * @var array[]
  41. * [
  42. * ['name' => 'field', 'msg' => 'error Message1' ],
  43. * ['name' => 'field2', 'msg' => 'error Message2' ],
  44. * ]
  45. */
  46. private $_errors = [];
  47. /**
  48. * Whether there is error stop validation 是否出现验证失败就立即停止验证
  49. * True -- 出现一个验证失败即停止验证,并退出
  50. * False -- 全部验证并将错误信息保存到 {@see $_errors}
  51. *
  52. * @var boolean
  53. */
  54. private $_stopOnError = true;
  55. /**
  56. * Prettify field name on get error message
  57. *
  58. * @var bool
  59. */
  60. private $_prettifyName = true;
  61. protected function prepareValidation(): void
  62. {
  63. // error message
  64. $this->_messages = array_merge($this->messages(), $this->_messages);
  65. // field translate
  66. $this->_translates = array_merge($this->translates(), $this->_translates);
  67. }
  68. /*******************************************************************************
  69. * Errors Information
  70. ******************************************************************************/
  71. /**
  72. * @return bool
  73. */
  74. protected function shouldStop(): bool
  75. {
  76. return $this->isFail() && $this->_stopOnError;
  77. }
  78. /**
  79. * Is there an error?
  80. *
  81. * @return boolean
  82. */
  83. public function hasError(): bool
  84. {
  85. return $this->isFail();
  86. }
  87. /**
  88. * @return bool
  89. */
  90. public function isFail(): bool
  91. {
  92. return count($this->_errors) > 0;
  93. }
  94. /**
  95. * @return bool
  96. */
  97. public function failed(): bool
  98. {
  99. return $this->isFail();
  100. }
  101. /**
  102. * @return bool
  103. * @deprecated will delete, please use isOk() or isPassed() instead
  104. */
  105. public function ok(): bool
  106. {
  107. return !$this->isFail();
  108. }
  109. /**
  110. * @return bool
  111. */
  112. public function isOk(): bool
  113. {
  114. return !$this->isFail();
  115. }
  116. /**
  117. * @return bool
  118. * @deprecated will delete, please use isOk() or isPassed() instead
  119. */
  120. public function passed(): bool
  121. {
  122. return !$this->isFail();
  123. }
  124. /**
  125. * @return bool
  126. */
  127. public function isPassed(): bool
  128. {
  129. return !$this->isFail();
  130. }
  131. /**
  132. * check field whether in the errors
  133. *
  134. * @param string $field
  135. *
  136. * @return bool
  137. */
  138. public function inError(string $field): bool
  139. {
  140. foreach ($this->_errors as $item) {
  141. if ($field === $item['name']) {
  142. return true;
  143. }
  144. }
  145. return false;
  146. }
  147. /**
  148. * @param string $field
  149. * @param string $msg
  150. */
  151. public function addError(string $field, string $msg): void
  152. {
  153. $this->_errors[] = [
  154. 'name' => $field,
  155. 'msg' => $msg,
  156. ];
  157. }
  158. /**
  159. * @param string $field Only get errors of the field.
  160. *
  161. * @return array
  162. */
  163. public function getErrors(string $field = ''): array
  164. {
  165. if ($field) {
  166. $errors = [];
  167. foreach ($this->_errors as $item) {
  168. if ($field === $item['name']) {
  169. $errors[] = $item['msg'];
  170. }
  171. }
  172. return $errors;
  173. }
  174. return $this->_errors;
  175. }
  176. /**
  177. * clear errors
  178. */
  179. public function clearErrors(): void
  180. {
  181. $this->_errors = [];
  182. }
  183. /**
  184. * Get the first error message
  185. *
  186. * @param bool $onlyMsg
  187. *
  188. * @return array|string
  189. */
  190. public function firstError(bool $onlyMsg = true)
  191. {
  192. if (!$errors = $this->_errors) {
  193. return $onlyMsg ? '' : [];
  194. }
  195. $first = array_shift($errors);
  196. return $onlyMsg ? $first['msg'] : $first;
  197. }
  198. /**
  199. * Get the last error message
  200. *
  201. * @param bool $onlyMsg
  202. *
  203. * @return array|string
  204. */
  205. public function lastError(bool $onlyMsg = true)
  206. {
  207. if (!$errors = $this->_errors) {
  208. return $onlyMsg ? '' : [];
  209. }
  210. $last = array_pop($errors);
  211. return $onlyMsg ? $last['msg'] : $last;
  212. }
  213. /**
  214. * @param bool|null $_stopOnError
  215. *
  216. * @return $this
  217. */
  218. public function setStopOnError($_stopOnError = null): self
  219. {
  220. if (null !== $_stopOnError) {
  221. $this->_stopOnError = (bool)$_stopOnError;
  222. }
  223. return $this;
  224. }
  225. /**
  226. * @return bool
  227. */
  228. public function isStopOnError(): bool
  229. {
  230. return $this->_stopOnError;
  231. }
  232. /*******************************************************************************
  233. * Error Messages
  234. ******************************************************************************/
  235. /**
  236. * @param string $key
  237. * @param string|array $message
  238. */
  239. public function setMessage(string $key, $message): void
  240. {
  241. if ($key && $message) {
  242. $this->_messages[$key] = $message;
  243. }
  244. }
  245. /**
  246. * @return array
  247. */
  248. public function getMessages(): array
  249. {
  250. return $this->_messages;
  251. }
  252. /**
  253. * @param array $messages
  254. *
  255. * @return $this
  256. */
  257. public function setMessages(array $messages): self
  258. {
  259. foreach ($messages as $key => $value) {
  260. $this->setMessage($key, $value);
  261. }
  262. return $this;
  263. }
  264. /**
  265. * 各个验证器的提示消息
  266. *
  267. * @param string|Closure $validator 验证器
  268. * @param string $field
  269. * @param array $args
  270. * @param string|array $message 自定义提示消息
  271. *
  272. * @return string
  273. */
  274. public function getMessage($validator, string $field, array $args = [], $message = null): string
  275. {
  276. $rawName = is_string($validator) ? $validator : 'callback';
  277. $params = [
  278. '{attr}' => $this->getTranslate($field)
  279. ];
  280. // get message from built in dict.
  281. if (!$message) {
  282. $message = $this->findMessage($field, $rawName) ?: GlobalMessage::getDefault();
  283. // is array. It's defined multi error messages
  284. } elseif (is_array($message)) {
  285. $message = $message[$rawName] ?? $this->findMessage($field, $rawName);
  286. if (!$message) { // use default
  287. return strtr(GlobalMessage::getDefault(), $params);
  288. }
  289. } else {
  290. $message = (string)$message;
  291. }
  292. /** @see GlobalMessage::$messages['size'] */
  293. if (is_array($message)) {
  294. $msgKey = count($args);
  295. $message = $message[$msgKey] ?? $message[0];
  296. }
  297. if (false === strpos($message, '{')) {
  298. return $message;
  299. }
  300. foreach ($args as $key => $value) {
  301. $key = is_int($key) ? "value{$key}" : $key;
  302. // build params
  303. $params['{' . $key . '}'] = is_array($value) ? implode(',', $value) : $value;
  304. }
  305. return strtr($message, $params);
  306. }
  307. /**
  308. * @param string $field
  309. * @param string $rawName
  310. *
  311. * @return string|array
  312. */
  313. protected function findMessage(string $field, string $rawName)
  314. {
  315. // allow define a message for a validator.
  316. // eg: 'username.required' => 'some message ...'
  317. $fullKey = $field . '.' . $rawName;
  318. $realName = Validators::realName($rawName);
  319. // get from default
  320. if (!$this->_messages) {
  321. return GlobalMessage::get($realName);
  322. }
  323. if (isset($this->_messages[$fullKey])) {
  324. $message = $this->_messages[$fullKey];
  325. // eg 'required' => 'some message ...'
  326. } elseif (isset($this->_messages[$rawName])) {
  327. $message = $this->_messages[$rawName];
  328. } elseif (isset($this->_messages[$realName])) {
  329. $message = $this->_messages[$realName];
  330. } else { // get from default
  331. $message = GlobalMessage::get($realName);
  332. }
  333. return $message;
  334. }
  335. /**
  336. * set the attrs translation data
  337. *
  338. * @param array $fieldTrans
  339. *
  340. * @return $this
  341. */
  342. public function setTranslates(array $fieldTrans): self
  343. {
  344. return $this->addTranslates($fieldTrans);
  345. }
  346. /**
  347. * add the attrs translation data
  348. *
  349. * @param array $fieldTrans
  350. *
  351. * @return $this
  352. */
  353. public function addTranslates(array $fieldTrans): self
  354. {
  355. foreach ($fieldTrans as $field => $tran) {
  356. $this->_translates[$field] = $tran;
  357. }
  358. return $this;
  359. }
  360. /**
  361. * @return array
  362. */
  363. public function getTranslates(): array
  364. {
  365. return $this->_translates;
  366. }
  367. /**
  368. * get field translate string.
  369. *
  370. * @param string $field
  371. *
  372. * @return string
  373. */
  374. public function getTranslate(string $field): string
  375. {
  376. $trans = $this->getTranslates();
  377. if (isset($trans[$field])) {
  378. return $trans[$field];
  379. }
  380. if ($this->_prettifyName) {
  381. return Helper::prettifyFieldName($field);
  382. }
  383. return $field;
  384. }
  385. /**
  386. * @return array
  387. */
  388. public function clearTranslates(): array
  389. {
  390. return $this->_translates = [];
  391. }
  392. /**
  393. * @return bool
  394. */
  395. public function isPrettifyName(): bool
  396. {
  397. return $this->_prettifyName;
  398. }
  399. /**
  400. * @param bool $prettifyName
  401. */
  402. public function setPrettifyName(bool $prettifyName = true): void
  403. {
  404. $this->_prettifyName = $prettifyName;
  405. }
  406. }