Enum.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. namespace Illuminate\Validation\Rules;
  3. use Illuminate\Contracts\Support\Arrayable;
  4. use Illuminate\Contracts\Validation\Rule;
  5. use Illuminate\Contracts\Validation\ValidatorAwareRule;
  6. use Illuminate\Support\Arr;
  7. use Illuminate\Support\Traits\Conditionable;
  8. use Stringable;
  9. use TypeError;
  10. use function Illuminate\Support\enum_value;
  11. class Enum implements Rule, ValidatorAwareRule, Stringable
  12. {
  13. use Conditionable;
  14. /**
  15. * The type of the enum.
  16. *
  17. * @var class-string<\UnitEnum>
  18. */
  19. protected $type;
  20. /**
  21. * The current validator instance.
  22. *
  23. * @var \Illuminate\Validation\Validator
  24. */
  25. protected $validator;
  26. /**
  27. * The cases that should be considered valid.
  28. *
  29. * @var array
  30. */
  31. protected $only = [];
  32. /**
  33. * The cases that should be considered invalid.
  34. *
  35. * @var array
  36. */
  37. protected $except = [];
  38. /**
  39. * Create a new rule instance.
  40. *
  41. * @param class-string<\UnitEnum> $type
  42. */
  43. public function __construct($type)
  44. {
  45. $this->type = $type;
  46. }
  47. /**
  48. * Determine if the validation rule passes.
  49. *
  50. * @param string $attribute
  51. * @param mixed $value
  52. * @return bool
  53. */
  54. public function passes($attribute, $value)
  55. {
  56. if ($value instanceof $this->type) {
  57. return $this->isDesirable($value);
  58. }
  59. if (is_null($value) || ! enum_exists($this->type) || ! method_exists($this->type, 'tryFrom')) {
  60. return false;
  61. }
  62. try {
  63. $value = $this->type::tryFrom($value);
  64. return ! is_null($value) && $this->isDesirable($value);
  65. } catch (TypeError) {
  66. return false;
  67. }
  68. }
  69. /**
  70. * Specify the cases that should be considered valid.
  71. *
  72. * @param \UnitEnum[]|\UnitEnum|\Illuminate\Contracts\Support\Arrayable<array-key, \UnitEnum> $values
  73. * @return $this
  74. */
  75. public function only($values)
  76. {
  77. $this->only = $values instanceof Arrayable ? $values->toArray() : Arr::wrap($values);
  78. return $this;
  79. }
  80. /**
  81. * Specify the cases that should be considered invalid.
  82. *
  83. * @param \UnitEnum[]|\UnitEnum|\Illuminate\Contracts\Support\Arrayable<array-key, \UnitEnum> $values
  84. * @return $this
  85. */
  86. public function except($values)
  87. {
  88. $this->except = $values instanceof Arrayable ? $values->toArray() : Arr::wrap($values);
  89. return $this;
  90. }
  91. /**
  92. * Determine if the given case is a valid case based on the only / except values.
  93. *
  94. * @param mixed $value
  95. * @return bool
  96. */
  97. protected function isDesirable($value)
  98. {
  99. return match (true) {
  100. ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true),
  101. ! empty($this->except) => ! in_array(needle: $value, haystack: $this->except, strict: true),
  102. default => true,
  103. };
  104. }
  105. /**
  106. * Get the validation error message.
  107. *
  108. * @return array
  109. */
  110. public function message()
  111. {
  112. $message = $this->validator->getTranslator()->get('validation.enum');
  113. return $message === 'validation.enum'
  114. ? ['The selected :attribute is invalid.']
  115. : $message;
  116. }
  117. /**
  118. * Set the current validator.
  119. *
  120. * @param \Illuminate\Validation\Validator $validator
  121. * @return $this
  122. */
  123. public function setValidator($validator)
  124. {
  125. $this->validator = $validator;
  126. return $this;
  127. }
  128. /**
  129. * Convert the rule to a validation string.
  130. *
  131. * @return string
  132. */
  133. public function __toString()
  134. {
  135. $cases = ! empty($this->only)
  136. ? $this->only
  137. : array_filter($this->type::cases(), fn ($case) => ! in_array($case, $this->except, true));
  138. $values = array_map(function ($case) {
  139. $value = enum_value($case);
  140. return '"'.str_replace('"', '""', (string) $value).'"';
  141. }, $cases);
  142. return 'in:'.implode(',', $values);
  143. }
  144. }