Validator.php 29KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. <?php
  2. namespace Illuminate\Validation;
  3. use RuntimeException;
  4. use BadMethodCallException;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Str;
  7. use Illuminate\Support\Fluent;
  8. use Illuminate\Support\MessageBag;
  9. use Illuminate\Contracts\Container\Container;
  10. use Illuminate\Contracts\Translation\Translator;
  11. use Illuminate\Contracts\Validation\ImplicitRule;
  12. use Symfony\Component\HttpFoundation\File\UploadedFile;
  13. use Illuminate\Contracts\Validation\Rule as RuleContract;
  14. use Illuminate\Contracts\Validation\Validator as ValidatorContract;
  15. class Validator implements ValidatorContract
  16. {
  17. use Concerns\FormatsMessages,
  18. Concerns\ValidatesAttributes;
  19. /**
  20. * The Translator implementation.
  21. *
  22. * @var \Illuminate\Contracts\Translation\Translator
  23. */
  24. protected $translator;
  25. /**
  26. * The container instance.
  27. *
  28. * @var \Illuminate\Contracts\Container\Container
  29. */
  30. protected $container;
  31. /**
  32. * The Presence Verifier implementation.
  33. *
  34. * @var \Illuminate\Validation\PresenceVerifierInterface
  35. */
  36. protected $presenceVerifier;
  37. /**
  38. * The failed validation rules.
  39. *
  40. * @var array
  41. */
  42. protected $failedRules = [];
  43. /**
  44. * The message bag instance.
  45. *
  46. * @var \Illuminate\Support\MessageBag
  47. */
  48. protected $messages;
  49. /**
  50. * The data under validation.
  51. *
  52. * @var array
  53. */
  54. protected $data;
  55. /**
  56. * The initial rules provided.
  57. *
  58. * @var array
  59. */
  60. protected $initialRules;
  61. /**
  62. * The rules to be applied to the data.
  63. *
  64. * @var array
  65. */
  66. protected $rules;
  67. /**
  68. * The current rule that is validating.
  69. *
  70. * @var string
  71. */
  72. protected $currentRule;
  73. /**
  74. * The array of wildcard attributes with their asterisks expanded.
  75. *
  76. * @var array
  77. */
  78. protected $implicitAttributes = [];
  79. /**
  80. * All of the registered "after" callbacks.
  81. *
  82. * @var array
  83. */
  84. protected $after = [];
  85. /**
  86. * The array of custom error messages.
  87. *
  88. * @var array
  89. */
  90. public $customMessages = [];
  91. /**
  92. * The array of fallback error messages.
  93. *
  94. * @var array
  95. */
  96. public $fallbackMessages = [];
  97. /**
  98. * The array of custom attribute names.
  99. *
  100. * @var array
  101. */
  102. public $customAttributes = [];
  103. /**
  104. * The array of custom displayable values.
  105. *
  106. * @var array
  107. */
  108. public $customValues = [];
  109. /**
  110. * All of the custom validator extensions.
  111. *
  112. * @var array
  113. */
  114. public $extensions = [];
  115. /**
  116. * All of the custom replacer extensions.
  117. *
  118. * @var array
  119. */
  120. public $replacers = [];
  121. /**
  122. * The validation rules that may be applied to files.
  123. *
  124. * @var array
  125. */
  126. protected $fileRules = [
  127. 'File', 'Image', 'Mimes', 'Mimetypes', 'Min',
  128. 'Max', 'Size', 'Between', 'Dimensions',
  129. ];
  130. /**
  131. * The validation rules that imply the field is required.
  132. *
  133. * @var array
  134. */
  135. protected $implicitRules = [
  136. 'Required', 'Filled', 'RequiredWith', 'RequiredWithAll', 'RequiredWithout',
  137. 'RequiredWithoutAll', 'RequiredIf', 'RequiredUnless', 'Accepted', 'Present',
  138. ];
  139. /**
  140. * The validation rules which depend on other fields as parameters.
  141. *
  142. * @var array
  143. */
  144. protected $dependentRules = [
  145. 'RequiredWith', 'RequiredWithAll', 'RequiredWithout', 'RequiredWithoutAll',
  146. 'RequiredIf', 'RequiredUnless', 'Confirmed', 'Same', 'Different', 'Unique',
  147. 'Before', 'After', 'BeforeOrEqual', 'AfterOrEqual', 'Gt', 'Lt', 'Gte', 'Lte',
  148. ];
  149. /**
  150. * The size related validation rules.
  151. *
  152. * @var array
  153. */
  154. protected $sizeRules = ['Size', 'Between', 'Min', 'Max', 'Gt', 'Lt', 'Gte', 'Lte'];
  155. /**
  156. * The numeric related validation rules.
  157. *
  158. * @var array
  159. */
  160. protected $numericRules = ['Numeric', 'Integer'];
  161. /**
  162. * Create a new Validator instance.
  163. *
  164. * @param \Illuminate\Contracts\Translation\Translator $translator
  165. * @param array $data
  166. * @param array $rules
  167. * @param array $messages
  168. * @param array $customAttributes
  169. * @return void
  170. */
  171. public function __construct(Translator $translator, array $data, array $rules,
  172. array $messages = [], array $customAttributes = [])
  173. {
  174. $this->initialRules = $rules;
  175. $this->translator = $translator;
  176. $this->customMessages = $messages;
  177. $this->data = $this->parseData($data);
  178. $this->customAttributes = $customAttributes;
  179. $this->setRules($rules);
  180. }
  181. /**
  182. * Parse the data array, converting dots to ->.
  183. *
  184. * @param array $data
  185. * @return array
  186. */
  187. public function parseData(array $data)
  188. {
  189. $newData = [];
  190. foreach ($data as $key => $value) {
  191. if (is_array($value)) {
  192. $value = $this->parseData($value);
  193. }
  194. // If the data key contains a dot, we will replace it with another character
  195. // sequence so it doesn't interfere with dot processing when working with
  196. // array based validation rules and array_dot later in the validations.
  197. if (Str::contains($key, '.')) {
  198. $newData[str_replace('.', '->', $key)] = $value;
  199. } else {
  200. $newData[$key] = $value;
  201. }
  202. }
  203. return $newData;
  204. }
  205. /**
  206. * Add an after validation callback.
  207. *
  208. * @param callable|string $callback
  209. * @return $this
  210. */
  211. public function after($callback)
  212. {
  213. $this->after[] = function () use ($callback) {
  214. return call_user_func_array($callback, [$this]);
  215. };
  216. return $this;
  217. }
  218. /**
  219. * Determine if the data passes the validation rules.
  220. *
  221. * @return bool
  222. */
  223. public function passes()
  224. {
  225. $this->messages = new MessageBag;
  226. // We'll spin through each rule, validating the attributes attached to that
  227. // rule. Any error messages will be added to the containers with each of
  228. // the other error messages, returning true if we don't have messages.
  229. foreach ($this->rules as $attribute => $rules) {
  230. $attribute = str_replace('\.', '->', $attribute);
  231. foreach ($rules as $rule) {
  232. $this->validateAttribute($attribute, $rule);
  233. if ($this->shouldStopValidating($attribute)) {
  234. break;
  235. }
  236. }
  237. }
  238. // Here we will spin through all of the "after" hooks on this validator and
  239. // fire them off. This gives the callbacks a chance to perform all kinds
  240. // of other validation that needs to get wrapped up in this operation.
  241. foreach ($this->after as $after) {
  242. call_user_func($after);
  243. }
  244. return $this->messages->isEmpty();
  245. }
  246. /**
  247. * Determine if the data fails the validation rules.
  248. *
  249. * @return bool
  250. */
  251. public function fails()
  252. {
  253. return ! $this->passes();
  254. }
  255. /**
  256. * Run the validator's rules against its data.
  257. *
  258. * @return array
  259. *
  260. * @throws \Illuminate\Validation\ValidationException
  261. */
  262. public function validate()
  263. {
  264. if ($this->fails()) {
  265. throw new ValidationException($this);
  266. }
  267. $data = collect($this->getData());
  268. return $data->only(collect($this->getRules())->keys()->map(function ($rule) {
  269. return explode('.', $rule)[0];
  270. })->unique())->toArray();
  271. }
  272. /**
  273. * Validate a given attribute against a rule.
  274. *
  275. * @param string $attribute
  276. * @param string $rule
  277. * @return void
  278. */
  279. protected function validateAttribute($attribute, $rule)
  280. {
  281. $this->currentRule = $rule;
  282. list($rule, $parameters) = ValidationRuleParser::parse($rule);
  283. if ($rule == '') {
  284. return;
  285. }
  286. // First we will get the correct keys for the given attribute in case the field is nested in
  287. // an array. Then we determine if the given rule accepts other field names as parameters.
  288. // If so, we will replace any asterisks found in the parameters with the correct keys.
  289. if (($keys = $this->getExplicitKeys($attribute)) &&
  290. $this->dependsOnOtherFields($rule)) {
  291. $parameters = $this->replaceAsterisksInParameters($parameters, $keys);
  292. }
  293. $value = $this->getValue($attribute);
  294. // If the attribute is a file, we will verify that the file upload was actually successful
  295. // and if it wasn't we will add a failure for the attribute. Files may not successfully
  296. // upload if they are too large based on PHP's settings so we will bail in this case.
  297. if ($value instanceof UploadedFile && ! $value->isValid() &&
  298. $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
  299. ) {
  300. return $this->addFailure($attribute, 'uploaded', []);
  301. }
  302. // If we have made it this far we will make sure the attribute is validatable and if it is
  303. // we will call the validation method with the attribute. If a method returns false the
  304. // attribute is invalid and we will add a failure message for this failing attribute.
  305. $validatable = $this->isValidatable($rule, $attribute, $value);
  306. if ($rule instanceof RuleContract) {
  307. return $validatable
  308. ? $this->validateUsingCustomRule($attribute, $value, $rule)
  309. : null;
  310. }
  311. $method = "validate{$rule}";
  312. if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
  313. $this->addFailure($attribute, $rule, $parameters);
  314. }
  315. }
  316. /**
  317. * Determine if the given rule depends on other fields.
  318. *
  319. * @param string $rule
  320. * @return bool
  321. */
  322. protected function dependsOnOtherFields($rule)
  323. {
  324. return in_array($rule, $this->dependentRules);
  325. }
  326. /**
  327. * Get the explicit keys from an attribute flattened with dot notation.
  328. *
  329. * E.g. 'foo.1.bar.spark.baz' -> [1, 'spark'] for 'foo.*.bar.*.baz'
  330. *
  331. * @param string $attribute
  332. * @return array
  333. */
  334. protected function getExplicitKeys($attribute)
  335. {
  336. $pattern = str_replace('\*', '([^\.]+)', preg_quote($this->getPrimaryAttribute($attribute), '/'));
  337. if (preg_match('/^'.$pattern.'/', $attribute, $keys)) {
  338. array_shift($keys);
  339. return $keys;
  340. }
  341. return [];
  342. }
  343. /**
  344. * Get the primary attribute name.
  345. *
  346. * For example, if "name.0" is given, "name.*" will be returned.
  347. *
  348. * @param string $attribute
  349. * @return string
  350. */
  351. protected function getPrimaryAttribute($attribute)
  352. {
  353. foreach ($this->implicitAttributes as $unparsed => $parsed) {
  354. if (in_array($attribute, $parsed)) {
  355. return $unparsed;
  356. }
  357. }
  358. return $attribute;
  359. }
  360. /**
  361. * Replace each field parameter which has asterisks with the given keys.
  362. *
  363. * @param array $parameters
  364. * @param array $keys
  365. * @return array
  366. */
  367. protected function replaceAsterisksInParameters(array $parameters, array $keys)
  368. {
  369. return array_map(function ($field) use ($keys) {
  370. return vsprintf(str_replace('*', '%s', $field), $keys);
  371. }, $parameters);
  372. }
  373. /**
  374. * Determine if the attribute is validatable.
  375. *
  376. * @param object|string $rule
  377. * @param string $attribute
  378. * @param mixed $value
  379. * @return bool
  380. */
  381. protected function isValidatable($rule, $attribute, $value)
  382. {
  383. return $this->presentOrRuleIsImplicit($rule, $attribute, $value) &&
  384. $this->passesOptionalCheck($attribute) &&
  385. $this->isNotNullIfMarkedAsNullable($rule, $attribute) &&
  386. $this->hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute);
  387. }
  388. /**
  389. * Determine if the field is present, or the rule implies required.
  390. *
  391. * @param object|string $rule
  392. * @param string $attribute
  393. * @param mixed $value
  394. * @return bool
  395. */
  396. protected function presentOrRuleIsImplicit($rule, $attribute, $value)
  397. {
  398. if (is_string($value) && trim($value) === '') {
  399. return $this->isImplicit($rule);
  400. }
  401. return $this->validatePresent($attribute, $value) ||
  402. $this->isImplicit($rule);
  403. }
  404. /**
  405. * Determine if a given rule implies the attribute is required.
  406. *
  407. * @param object|string $rule
  408. * @return bool
  409. */
  410. protected function isImplicit($rule)
  411. {
  412. return $rule instanceof ImplicitRule ||
  413. in_array($rule, $this->implicitRules);
  414. }
  415. /**
  416. * Determine if the attribute passes any optional check.
  417. *
  418. * @param string $attribute
  419. * @return bool
  420. */
  421. protected function passesOptionalCheck($attribute)
  422. {
  423. if (! $this->hasRule($attribute, ['Sometimes'])) {
  424. return true;
  425. }
  426. $data = ValidationData::initializeAndGatherData($attribute, $this->data);
  427. return array_key_exists($attribute, $data)
  428. || array_key_exists($attribute, $this->data);
  429. }
  430. /**
  431. * Determine if the attribute fails the nullable check.
  432. *
  433. * @param string $rule
  434. * @param string $attribute
  435. * @return bool
  436. */
  437. protected function isNotNullIfMarkedAsNullable($rule, $attribute)
  438. {
  439. if ($this->isImplicit($rule) || ! $this->hasRule($attribute, ['Nullable'])) {
  440. return true;
  441. }
  442. return ! is_null(Arr::get($this->data, $attribute, 0));
  443. }
  444. /**
  445. * Determine if it's a necessary presence validation.
  446. *
  447. * This is to avoid possible database type comparison errors.
  448. *
  449. * @param string $rule
  450. * @param string $attribute
  451. * @return bool
  452. */
  453. protected function hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute)
  454. {
  455. return in_array($rule, ['Unique', 'Exists']) ? ! $this->messages->has($attribute) : true;
  456. }
  457. /**
  458. * Validate an attribute using a custom rule object.
  459. *
  460. * @param string $attribute
  461. * @param mixed $value
  462. * @param \Illuminate\Contracts\Validation\Rule $rule
  463. * @return void
  464. */
  465. protected function validateUsingCustomRule($attribute, $value, $rule)
  466. {
  467. if (! $rule->passes($attribute, $value)) {
  468. $this->failedRules[$attribute][get_class($rule)] = [];
  469. $this->messages->add($attribute, $this->makeReplacements(
  470. $rule->message(), $attribute, get_class($rule), []
  471. ));
  472. }
  473. }
  474. /**
  475. * Check if we should stop further validations on a given attribute.
  476. *
  477. * @param string $attribute
  478. * @return bool
  479. */
  480. protected function shouldStopValidating($attribute)
  481. {
  482. if ($this->hasRule($attribute, ['Bail'])) {
  483. return $this->messages->has($attribute);
  484. }
  485. if (isset($this->failedRules[$attribute]) &&
  486. array_key_exists('uploaded', $this->failedRules[$attribute])) {
  487. return true;
  488. }
  489. // In case the attribute has any rule that indicates that the field is required
  490. // and that rule already failed then we should stop validation at this point
  491. // as now there is no point in calling other rules with this field empty.
  492. return $this->hasRule($attribute, $this->implicitRules) &&
  493. isset($this->failedRules[$attribute]) &&
  494. array_intersect(array_keys($this->failedRules[$attribute]), $this->implicitRules);
  495. }
  496. /**
  497. * Add a failed rule and error message to the collection.
  498. *
  499. * @param string $attribute
  500. * @param string $rule
  501. * @param array $parameters
  502. * @return void
  503. */
  504. protected function addFailure($attribute, $rule, $parameters)
  505. {
  506. $this->messages->add($attribute, $this->makeReplacements(
  507. $this->getMessage($attribute, $rule), $attribute, $rule, $parameters
  508. ));
  509. $this->failedRules[$attribute][$rule] = $parameters;
  510. }
  511. /**
  512. * Returns the data which was valid.
  513. *
  514. * @return array
  515. */
  516. public function valid()
  517. {
  518. if (! $this->messages) {
  519. $this->passes();
  520. }
  521. return array_diff_key(
  522. $this->data, $this->attributesThatHaveMessages()
  523. );
  524. }
  525. /**
  526. * Returns the data which was invalid.
  527. *
  528. * @return array
  529. */
  530. public function invalid()
  531. {
  532. if (! $this->messages) {
  533. $this->passes();
  534. }
  535. return array_intersect_key(
  536. $this->data, $this->attributesThatHaveMessages()
  537. );
  538. }
  539. /**
  540. * Generate an array of all attributes that have messages.
  541. *
  542. * @return array
  543. */
  544. protected function attributesThatHaveMessages()
  545. {
  546. return collect($this->messages()->toArray())->map(function ($message, $key) {
  547. return explode('.', $key)[0];
  548. })->unique()->flip()->all();
  549. }
  550. /**
  551. * Get the failed validation rules.
  552. *
  553. * @return array
  554. */
  555. public function failed()
  556. {
  557. return $this->failedRules;
  558. }
  559. /**
  560. * Get the message container for the validator.
  561. *
  562. * @return \Illuminate\Support\MessageBag
  563. */
  564. public function messages()
  565. {
  566. if (! $this->messages) {
  567. $this->passes();
  568. }
  569. return $this->messages;
  570. }
  571. /**
  572. * An alternative more semantic shortcut to the message container.
  573. *
  574. * @return \Illuminate\Support\MessageBag
  575. */
  576. public function errors()
  577. {
  578. return $this->messages();
  579. }
  580. /**
  581. * Get the messages for the instance.
  582. *
  583. * @return \Illuminate\Support\MessageBag
  584. */
  585. public function getMessageBag()
  586. {
  587. return $this->messages();
  588. }
  589. /**
  590. * Determine if the given attribute has a rule in the given set.
  591. *
  592. * @param string $attribute
  593. * @param string|array $rules
  594. * @return bool
  595. */
  596. public function hasRule($attribute, $rules)
  597. {
  598. return ! is_null($this->getRule($attribute, $rules));
  599. }
  600. /**
  601. * Get a rule and its parameters for a given attribute.
  602. *
  603. * @param string $attribute
  604. * @param string|array $rules
  605. * @return array|null
  606. */
  607. protected function getRule($attribute, $rules)
  608. {
  609. if (! array_key_exists($attribute, $this->rules)) {
  610. return;
  611. }
  612. $rules = (array) $rules;
  613. foreach ($this->rules[$attribute] as $rule) {
  614. list($rule, $parameters) = ValidationRuleParser::parse($rule);
  615. if (in_array($rule, $rules)) {
  616. return [$rule, $parameters];
  617. }
  618. }
  619. }
  620. /**
  621. * Get the data under validation.
  622. *
  623. * @return array
  624. */
  625. public function attributes()
  626. {
  627. return $this->getData();
  628. }
  629. /**
  630. * Get the data under validation.
  631. *
  632. * @return array
  633. */
  634. public function getData()
  635. {
  636. return $this->data;
  637. }
  638. /**
  639. * Set the data under validation.
  640. *
  641. * @param array $data
  642. * @return $this
  643. */
  644. public function setData(array $data)
  645. {
  646. $this->data = $this->parseData($data);
  647. $this->setRules($this->initialRules);
  648. return $this;
  649. }
  650. /**
  651. * Get the value of a given attribute.
  652. *
  653. * @param string $attribute
  654. * @return mixed
  655. */
  656. protected function getValue($attribute)
  657. {
  658. return Arr::get($this->data, $attribute);
  659. }
  660. /**
  661. * Get the validation rules.
  662. *
  663. * @return array
  664. */
  665. public function getRules()
  666. {
  667. return $this->rules;
  668. }
  669. /**
  670. * Set the validation rules.
  671. *
  672. * @param array $rules
  673. * @return $this
  674. */
  675. public function setRules(array $rules)
  676. {
  677. $this->initialRules = $rules;
  678. $this->rules = [];
  679. $this->addRules($rules);
  680. return $this;
  681. }
  682. /**
  683. * Parse the given rules and merge them into current rules.
  684. *
  685. * @param array $rules
  686. * @return void
  687. */
  688. public function addRules($rules)
  689. {
  690. // The primary purpose of this parser is to expand any "*" rules to the all
  691. // of the explicit rules needed for the given data. For example the rule
  692. // names.* would get expanded to names.0, names.1, etc. for this data.
  693. $response = (new ValidationRuleParser($this->data))
  694. ->explode($rules);
  695. $this->rules = array_merge_recursive(
  696. $this->rules, $response->rules
  697. );
  698. $this->implicitAttributes = array_merge(
  699. $this->implicitAttributes, $response->implicitAttributes
  700. );
  701. }
  702. /**
  703. * Add conditions to a given field based on a Closure.
  704. *
  705. * @param string|array $attribute
  706. * @param string|array $rules
  707. * @param callable $callback
  708. * @return $this
  709. */
  710. public function sometimes($attribute, $rules, callable $callback)
  711. {
  712. $payload = new Fluent($this->getData());
  713. if (call_user_func($callback, $payload)) {
  714. foreach ((array) $attribute as $key) {
  715. $this->addRules([$key => $rules]);
  716. }
  717. }
  718. return $this;
  719. }
  720. /**
  721. * Register an array of custom validator extensions.
  722. *
  723. * @param array $extensions
  724. * @return void
  725. */
  726. public function addExtensions(array $extensions)
  727. {
  728. if ($extensions) {
  729. $keys = array_map('\Illuminate\Support\Str::snake', array_keys($extensions));
  730. $extensions = array_combine($keys, array_values($extensions));
  731. }
  732. $this->extensions = array_merge($this->extensions, $extensions);
  733. }
  734. /**
  735. * Register an array of custom implicit validator extensions.
  736. *
  737. * @param array $extensions
  738. * @return void
  739. */
  740. public function addImplicitExtensions(array $extensions)
  741. {
  742. $this->addExtensions($extensions);
  743. foreach ($extensions as $rule => $extension) {
  744. $this->implicitRules[] = Str::studly($rule);
  745. }
  746. }
  747. /**
  748. * Register an array of custom implicit validator extensions.
  749. *
  750. * @param array $extensions
  751. * @return void
  752. */
  753. public function addDependentExtensions(array $extensions)
  754. {
  755. $this->addExtensions($extensions);
  756. foreach ($extensions as $rule => $extension) {
  757. $this->dependentRules[] = Str::studly($rule);
  758. }
  759. }
  760. /**
  761. * Register a custom validator extension.
  762. *
  763. * @param string $rule
  764. * @param \Closure|string $extension
  765. * @return void
  766. */
  767. public function addExtension($rule, $extension)
  768. {
  769. $this->extensions[Str::snake($rule)] = $extension;
  770. }
  771. /**
  772. * Register a custom implicit validator extension.
  773. *
  774. * @param string $rule
  775. * @param \Closure|string $extension
  776. * @return void
  777. */
  778. public function addImplicitExtension($rule, $extension)
  779. {
  780. $this->addExtension($rule, $extension);
  781. $this->implicitRules[] = Str::studly($rule);
  782. }
  783. /**
  784. * Register a custom dependent validator extension.
  785. *
  786. * @param string $rule
  787. * @param \Closure|string $extension
  788. * @return void
  789. */
  790. public function addDependentExtension($rule, $extension)
  791. {
  792. $this->addExtension($rule, $extension);
  793. $this->dependentRules[] = Str::studly($rule);
  794. }
  795. /**
  796. * Register an array of custom validator message replacers.
  797. *
  798. * @param array $replacers
  799. * @return void
  800. */
  801. public function addReplacers(array $replacers)
  802. {
  803. if ($replacers) {
  804. $keys = array_map('\Illuminate\Support\Str::snake', array_keys($replacers));
  805. $replacers = array_combine($keys, array_values($replacers));
  806. }
  807. $this->replacers = array_merge($this->replacers, $replacers);
  808. }
  809. /**
  810. * Register a custom validator message replacer.
  811. *
  812. * @param string $rule
  813. * @param \Closure|string $replacer
  814. * @return void
  815. */
  816. public function addReplacer($rule, $replacer)
  817. {
  818. $this->replacers[Str::snake($rule)] = $replacer;
  819. }
  820. /**
  821. * Set the custom messages for the validator.
  822. *
  823. * @param array $messages
  824. * @return $this
  825. */
  826. public function setCustomMessages(array $messages)
  827. {
  828. $this->customMessages = array_merge($this->customMessages, $messages);
  829. return $this;
  830. }
  831. /**
  832. * Set the custom attributes on the validator.
  833. *
  834. * @param array $attributes
  835. * @return $this
  836. */
  837. public function setAttributeNames(array $attributes)
  838. {
  839. $this->customAttributes = $attributes;
  840. return $this;
  841. }
  842. /**
  843. * Add custom attributes to the validator.
  844. *
  845. * @param array $customAttributes
  846. * @return $this
  847. */
  848. public function addCustomAttributes(array $customAttributes)
  849. {
  850. $this->customAttributes = array_merge($this->customAttributes, $customAttributes);
  851. return $this;
  852. }
  853. /**
  854. * Set the custom values on the validator.
  855. *
  856. * @param array $values
  857. * @return $this
  858. */
  859. public function setValueNames(array $values)
  860. {
  861. $this->customValues = $values;
  862. return $this;
  863. }
  864. /**
  865. * Add the custom values for the validator.
  866. *
  867. * @param array $customValues
  868. * @return $this
  869. */
  870. public function addCustomValues(array $customValues)
  871. {
  872. $this->customValues = array_merge($this->customValues, $customValues);
  873. return $this;
  874. }
  875. /**
  876. * Set the fallback messages for the validator.
  877. *
  878. * @param array $messages
  879. * @return void
  880. */
  881. public function setFallbackMessages(array $messages)
  882. {
  883. $this->fallbackMessages = $messages;
  884. }
  885. /**
  886. * Get the Presence Verifier implementation.
  887. *
  888. * @return \Illuminate\Validation\PresenceVerifierInterface
  889. *
  890. * @throws \RuntimeException
  891. */
  892. public function getPresenceVerifier()
  893. {
  894. if (! isset($this->presenceVerifier)) {
  895. throw new RuntimeException('Presence verifier has not been set.');
  896. }
  897. return $this->presenceVerifier;
  898. }
  899. /**
  900. * Get the Presence Verifier implementation.
  901. *
  902. * @param string $connection
  903. * @return \Illuminate\Validation\PresenceVerifierInterface
  904. *
  905. * @throws \RuntimeException
  906. */
  907. protected function getPresenceVerifierFor($connection)
  908. {
  909. return tap($this->getPresenceVerifier(), function ($verifier) use ($connection) {
  910. $verifier->setConnection($connection);
  911. });
  912. }
  913. /**
  914. * Set the Presence Verifier implementation.
  915. *
  916. * @param \Illuminate\Validation\PresenceVerifierInterface $presenceVerifier
  917. * @return void
  918. */
  919. public function setPresenceVerifier(PresenceVerifierInterface $presenceVerifier)
  920. {
  921. $this->presenceVerifier = $presenceVerifier;
  922. }
  923. /**
  924. * Get the Translator implementation.
  925. *
  926. * @return \Illuminate\Contracts\Translation\Translator
  927. */
  928. public function getTranslator()
  929. {
  930. return $this->translator;
  931. }
  932. /**
  933. * Set the Translator implementation.
  934. *
  935. * @param \Illuminate\Contracts\Translation\Translator $translator
  936. * @return void
  937. */
  938. public function setTranslator(Translator $translator)
  939. {
  940. $this->translator = $translator;
  941. }
  942. /**
  943. * Set the IoC container instance.
  944. *
  945. * @param \Illuminate\Contracts\Container\Container $container
  946. * @return void
  947. */
  948. public function setContainer(Container $container)
  949. {
  950. $this->container = $container;
  951. }
  952. /**
  953. * Call a custom validator extension.
  954. *
  955. * @param string $rule
  956. * @param array $parameters
  957. * @return bool|null
  958. */
  959. protected function callExtension($rule, $parameters)
  960. {
  961. $callback = $this->extensions[$rule];
  962. if (is_callable($callback)) {
  963. return call_user_func_array($callback, $parameters);
  964. } elseif (is_string($callback)) {
  965. return $this->callClassBasedExtension($callback, $parameters);
  966. }
  967. }
  968. /**
  969. * Call a class based validator extension.
  970. *
  971. * @param string $callback
  972. * @param array $parameters
  973. * @return bool
  974. */
  975. protected function callClassBasedExtension($callback, $parameters)
  976. {
  977. list($class, $method) = Str::parseCallback($callback, 'validate');
  978. return call_user_func_array([$this->container->make($class), $method], $parameters);
  979. }
  980. /**
  981. * Handle dynamic calls to class methods.
  982. *
  983. * @param string $method
  984. * @param array $parameters
  985. * @return mixed
  986. *
  987. * @throws \BadMethodCallException
  988. */
  989. public function __call($method, $parameters)
  990. {
  991. $rule = Str::snake(substr($method, 8));
  992. if (isset($this->extensions[$rule])) {
  993. return $this->callExtension($rule, $parameters);
  994. }
  995. throw new BadMethodCallException(sprintf(
  996. 'Method %s::%s does not exist.', static::class, $method
  997. ));
  998. }
  999. }