HasFieldValidator.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <?php
  2. namespace Dcat\Admin\Form\Concerns;
  3. use Dcat\Admin\Form;
  4. use Dcat\Admin\Support\Helper;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Facades\Validator;
  7. use Illuminate\Support\MessageBag;
  8. use Illuminate\Support\Str;
  9. /**
  10. * @property Form $form
  11. */
  12. trait HasFieldValidator
  13. {
  14. /**
  15. * The validation rules for creation.
  16. *
  17. * @var array|\Closure
  18. */
  19. protected $creationRules = [];
  20. /**
  21. * The validation rules for updates.
  22. *
  23. * @var array|\Closure
  24. */
  25. protected $updateRules = [];
  26. /**
  27. * Validation rules.
  28. *
  29. * @var array|\Closure
  30. */
  31. protected $rules = [];
  32. /**
  33. * @var \Closure
  34. */
  35. protected $validator;
  36. /**
  37. * Validation messages.
  38. *
  39. * @var array
  40. */
  41. protected $validationMessages = [];
  42. /**
  43. * Set the update validation rules for the field.
  44. *
  45. * @param array|callable|string $rules
  46. * @param array $messages
  47. *
  48. * @return $this
  49. */
  50. public function updateRules($rules = null, $messages = [])
  51. {
  52. $this->updateRules = $this->mergeRules($rules, $this->updateRules);
  53. if ($messages) {
  54. $this->setValidationMessages('update', $messages);
  55. }
  56. return $this;
  57. }
  58. /**
  59. * Set the creation validation rules for the field.
  60. *
  61. * @param array|callable|string $rules
  62. * @param array $messages
  63. *
  64. * @return $this
  65. */
  66. public function creationRules($rules = null, $messages = [])
  67. {
  68. $this->creationRules = $this->mergeRules($rules, $this->creationRules);
  69. if ($messages) {
  70. $this->setValidationMessages('creation', $messages);
  71. }
  72. return $this;
  73. }
  74. /**
  75. * Get or set rules.
  76. *
  77. * @param null $rules
  78. * @param array $messages
  79. *
  80. * @return $this
  81. */
  82. public function rules($rules = null, $messages = [])
  83. {
  84. if ($rules instanceof \Closure) {
  85. $this->rules = $rules;
  86. }
  87. $originalRules = $this->getRules();
  88. if (is_array($rules)) {
  89. $this->rules = array_merge($originalRules, $rules);
  90. } elseif (is_string($rules)) {
  91. $this->rules = array_merge($originalRules, array_filter(explode('|', $rules)));
  92. }
  93. if ($messages) {
  94. $this->setValidationMessages('default', $messages);
  95. }
  96. return $this;
  97. }
  98. /**
  99. * Get field validation rules.
  100. *
  101. * @return string
  102. */
  103. protected function getRules()
  104. {
  105. if ($this->isCreating()) {
  106. $rules = $this->creationRules ?: $this->rules;
  107. } elseif ($this->isEditing()) {
  108. $rules = $this->updateRules ?: $this->rules;
  109. } else {
  110. $rules = $this->rules;
  111. }
  112. if ($rules instanceof \Closure) {
  113. $rules = $rules->call($this, $this->form);
  114. }
  115. if (is_string($rules)) {
  116. $rules = array_filter(explode('|', $rules));
  117. }
  118. if (! $this->form) {
  119. return $rules;
  120. }
  121. if (method_exists($this->form, 'key') || ! $id = $this->form->getKey()) {
  122. return $rules;
  123. }
  124. if (is_array($rules)) {
  125. foreach ($rules as &$rule) {
  126. if (is_string($rule)) {
  127. $rule = str_replace('{{id}}', $id, $rule);
  128. }
  129. }
  130. }
  131. return $rules;
  132. }
  133. /**
  134. * Format validation rules.
  135. *
  136. * @param array|string $rules
  137. *
  138. * @return array
  139. */
  140. protected function formatRules($rules)
  141. {
  142. if (is_string($rules)) {
  143. $rules = array_filter(explode('|', $rules));
  144. }
  145. return array_filter((array) $rules);
  146. }
  147. /**
  148. * @param string|array|\Closure $input
  149. * @param string|array $original
  150. *
  151. * @return array|\Closure
  152. */
  153. protected function mergeRules($input, $original)
  154. {
  155. if ($input instanceof \Closure) {
  156. $rules = $input;
  157. } else {
  158. if (! empty($original)) {
  159. $original = $this->formatRules($original);
  160. }
  161. $rules = array_merge($original, $this->formatRules($input));
  162. }
  163. return $rules;
  164. }
  165. /**
  166. * @param string $rule
  167. *
  168. * @return $this
  169. */
  170. public function removeUpdateRule($rule)
  171. {
  172. $this->deleteRuleByKeyword($this->updateRules, $rule);
  173. return $this;
  174. }
  175. /**
  176. * @param string $rule
  177. *
  178. * @return $this
  179. */
  180. public function removeCreationRule($rule)
  181. {
  182. $this->deleteRuleByKeyword($this->creationRules, $rule);
  183. return $this;
  184. }
  185. /**
  186. * Remove a specific rule by keyword.
  187. *
  188. * @param string $rule
  189. *
  190. * @return $this
  191. */
  192. public function removeRule($rule)
  193. {
  194. $this->deleteRuleByKeyword($this->rules, $rule);
  195. return $this;
  196. }
  197. /**
  198. * @param $rules
  199. * @param $rule
  200. *
  201. * @return void
  202. */
  203. protected function deleteRuleByKeyword(&$rules, $rule)
  204. {
  205. if (is_array($rules)) {
  206. Helper::deleteByValue($rules, $rule);
  207. return;
  208. }
  209. if (! is_string($rules)) {
  210. return;
  211. }
  212. $pattern = "/{$rule}[^\|]?(\||$)/";
  213. $rules = preg_replace($pattern, '', $rules, -1);
  214. }
  215. /**
  216. * @param string $rule
  217. *
  218. * @return bool
  219. */
  220. public function hasUpdateRule($rule)
  221. {
  222. return $this->isRuleExists($this->updateRules, $rule);
  223. }
  224. /**
  225. * @param string $rule
  226. *
  227. * @return bool
  228. */
  229. public function hasCreationRule($rule)
  230. {
  231. return $this->isRuleExists($this->creationRules, $rule);
  232. }
  233. /**
  234. * @param string $rule
  235. *
  236. * @return bool
  237. */
  238. public function hasRule($rule)
  239. {
  240. return $this->isRuleExists($this->getRules(), $rule);
  241. }
  242. /**
  243. * @param string $rule
  244. *
  245. * @return bool|mixed
  246. */
  247. protected function getRule($rule)
  248. {
  249. $rules = $this->getRules();
  250. if (is_array($rules)) {
  251. foreach ($rules as $r) {
  252. if ($this->isRuleExists($r, $rule)) {
  253. return $r;
  254. }
  255. }
  256. return false;
  257. }
  258. if (! is_string($rules)) {
  259. return false;
  260. }
  261. foreach (explode('|', $rules) as $r) {
  262. if ($this->isRuleExists($r, $rule)) {
  263. return $r;
  264. }
  265. }
  266. return false;
  267. }
  268. /**
  269. * @param $rules
  270. * @param $rule
  271. *
  272. * @return bool
  273. */
  274. protected function isRuleExists($rules, $rule)
  275. {
  276. if (is_array($rules)) {
  277. foreach ($rules as $r) {
  278. if ($this->isRuleExists($r, $rule)) {
  279. return true;
  280. }
  281. }
  282. return false;
  283. }
  284. if (! is_string($rules)) {
  285. return false;
  286. }
  287. $rule = str_replace(['*', '/'], ['([0-9a-z-_,:=><])*', "\/"], $rule);
  288. $pattern = "/{$rule}[^\|]?(\||$)/";
  289. return (bool) preg_match($pattern, $rules);
  290. }
  291. /**
  292. * Set field validator.
  293. *
  294. * @param callable $validator
  295. *
  296. * @return $this
  297. */
  298. public function validator(callable $validator)
  299. {
  300. $this->validator = $validator;
  301. return $this;
  302. }
  303. /**
  304. * Get validator for this field.
  305. *
  306. * @param array $input
  307. *
  308. * @return bool|Validator
  309. */
  310. public function getValidator(array $input)
  311. {
  312. if ($this->validator) {
  313. return $this->validator->call($this, $input);
  314. }
  315. $rules = $attributes = [];
  316. if (! $fieldRules = $this->getRules()) {
  317. return false;
  318. }
  319. if (is_string($this->column)) {
  320. if (! Arr::has($input, $this->column)) {
  321. return false;
  322. }
  323. $input = $this->sanitizeInput($input, $this->column);
  324. $rules[$this->column] = $fieldRules;
  325. $attributes[$this->column] = $this->label;
  326. }
  327. if (is_array($this->column)) {
  328. foreach ($this->column as $key => $column) {
  329. if (! Arr::has($input, $column)) {
  330. continue;
  331. }
  332. $k = $column.$key;
  333. Arr::set($input, $k, Arr::get($input, $column));
  334. $rules[$k] = $fieldRules;
  335. $attributes[$k] = "{$this->label}[$column]";
  336. }
  337. }
  338. return Validator::make($input, $rules, $this->getValidationMessages(), $attributes);
  339. }
  340. /**
  341. * Set validation messages for column.
  342. *
  343. * @param string $key
  344. * @param array $messages
  345. *
  346. * @return $this
  347. */
  348. public function setValidationMessages($key, array $messages)
  349. {
  350. $this->validationMessages[$key] = $messages;
  351. return $this;
  352. }
  353. /**
  354. * Get validation messages for the field.
  355. *
  356. * @return array|mixed
  357. */
  358. public function getValidationMessages()
  359. {
  360. // Default validation message.
  361. $messages = $this->validationMessages['default'] ?? [];
  362. if ($this->isCreating()) {
  363. $messages = $this->validationMessages['creation'] ?? $messages;
  364. } elseif ($this->isEditing()) {
  365. $messages = $this->validationMessages['update'] ?? $messages;
  366. }
  367. $result = [];
  368. foreach ($messages as $k => $v) {
  369. if (Str::contains($k, '.')) {
  370. $result[$k] = $v;
  371. continue;
  372. }
  373. if (is_string($this->column)) {
  374. $k = $this->column.'.'.$k;
  375. $result[$k] = $v;
  376. continue;
  377. }
  378. foreach ($this->column as $column) {
  379. $result[$column.'.'.$k] = $v;
  380. }
  381. }
  382. return $result;
  383. }
  384. /**
  385. * Set error messages for individual form field.
  386. *
  387. * @see http://1000hz.github.io/bootstrap-validator/
  388. *
  389. * @param string $error
  390. * @param string $key
  391. *
  392. * @return $this
  393. */
  394. public function setClientValidationError(string $error, string $key = null)
  395. {
  396. $key = $key ? "{$key}-" : '';
  397. return $this->attribute("data-{$key}error", $error);
  398. }
  399. /**
  400. * @param MessageBag $messageBag
  401. *
  402. * @return MessageBag
  403. */
  404. public function formatValidatorMessages($messageBag)
  405. {
  406. return $messageBag;
  407. }
  408. }