Embeds.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. namespace Dcat\Admin\Form\Field;
  3. use Dcat\Admin\Form\EmbeddedForm;
  4. use Dcat\Admin\Form\Field;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Facades\Validator;
  7. use Illuminate\Support\Str;
  8. class Embeds extends Field
  9. {
  10. /**
  11. * @var \Closure
  12. */
  13. protected $builder = null;
  14. /**
  15. * Create a new HasMany field instance.
  16. *
  17. * @param string $column
  18. * @param array $arguments
  19. */
  20. public function __construct($column, $arguments = [])
  21. {
  22. $this->column = $column;
  23. if (count($arguments) == 1) {
  24. $this->label = $this->formatLabel();
  25. $this->builder = $arguments[0];
  26. }
  27. if (count($arguments) == 2) {
  28. [$this->label, $this->builder] = $arguments;
  29. }
  30. }
  31. /**
  32. * Prepare input data for insert or update.
  33. *
  34. * @param array $input
  35. *
  36. * @return array
  37. */
  38. protected function prepareInputValue($input)
  39. {
  40. $form = $this->buildEmbeddedForm();
  41. return $form->setOriginal($this->original)->prepare($input);
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function getValidator(array $input)
  47. {
  48. if (! array_key_exists($this->column, $input)) {
  49. return false;
  50. }
  51. $input = Arr::only($input, $this->column);
  52. $rules = $attributes = $messages = [];
  53. /** @var Field $field */
  54. foreach ($this->buildEmbeddedForm()->fields() as $field) {
  55. if (! $fieldRules = $field->getRules()) {
  56. continue;
  57. }
  58. $column = $field->column();
  59. /*
  60. *
  61. * For single column field format rules to:
  62. * [
  63. * 'extra.name' => 'required'
  64. * 'extra.email' => 'required'
  65. * ]
  66. *
  67. * For multiple column field with rules like 'required':
  68. * 'extra' => [
  69. * 'start' => 'start_at'
  70. * 'end' => 'end_at',
  71. * ]
  72. *
  73. * format rules to:
  74. * [
  75. * 'extra.start_atstart' => 'required'
  76. * 'extra.end_atend' => 'required'
  77. * ]
  78. */
  79. if (is_array($column)) {
  80. foreach ($column as $key => $name) {
  81. $rules["{$this->column}.$name$key"] = $fieldRules;
  82. }
  83. $this->resetInputKey($input, $column);
  84. } else {
  85. $rules["{$this->column}.$column"] = $fieldRules;
  86. }
  87. /**
  88. * For single column field format attributes to:
  89. * [
  90. * 'extra.name' => $label
  91. * 'extra.email' => $label
  92. * ].
  93. *
  94. * For multiple column field with rules like 'required':
  95. * 'extra' => [
  96. * 'start' => 'start_at'
  97. * 'end' => 'end_at',
  98. * ]
  99. *
  100. * format rules to:
  101. * [
  102. * 'extra.start_atstart' => "$label[start_at]"
  103. * 'extra.end_atend' => "$label[end_at]"
  104. * ]
  105. */
  106. $attributes = array_merge(
  107. $attributes,
  108. $this->formatValidationAttribute($input, $field->label(), $column)
  109. );
  110. $messages = array_merge(
  111. $messages,
  112. $this->formatValidationMessages($input, $field->getValidationMessages())
  113. );
  114. }
  115. if (empty($rules)) {
  116. return false;
  117. }
  118. return Validator::make($input, $rules, array_merge($this->getValidationMessages(), $messages), $attributes);
  119. }
  120. /**
  121. * Format validation messages.
  122. *
  123. * @param array $input
  124. * @param array $messages
  125. *
  126. * @return array
  127. */
  128. protected function formatValidationMessages(array $input, array $messages)
  129. {
  130. $result = [];
  131. foreach ($messages as $k => $message) {
  132. $result[$this->column.'.'.$k] = $message;
  133. }
  134. return $result;
  135. }
  136. /**
  137. * Format validation attributes.
  138. *
  139. * @param array $input
  140. * @param string $label
  141. * @param string $column
  142. *
  143. * @return array
  144. */
  145. protected function formatValidationAttribute($input, $label, $column)
  146. {
  147. $new = $attributes = [];
  148. if (is_array($column)) {
  149. foreach ($column as $index => $col) {
  150. $new[$col.$index] = $col;
  151. }
  152. }
  153. foreach (array_keys(Arr::dot($input)) as $key) {
  154. if (is_string($column)) {
  155. if (Str::endsWith($key, ".$column")) {
  156. $attributes[$key] = $label;
  157. }
  158. } else {
  159. foreach ($new as $k => $val) {
  160. if (Str::endsWith($key, ".$k")) {
  161. $attributes[$key] = $label."[$val]";
  162. }
  163. }
  164. }
  165. }
  166. return $attributes;
  167. }
  168. /**
  169. * Reset input key for validation.
  170. *
  171. * @param array $input
  172. * @param array $column $column is the column name array set
  173. *
  174. * @return void.
  175. */
  176. public function resetInputKey(array &$input, array $column)
  177. {
  178. $column = array_flip($column);
  179. foreach ($input[$this->column] as $key => $value) {
  180. if (! array_key_exists($key, $column)) {
  181. continue;
  182. }
  183. $newKey = $key.$column[$key];
  184. /*
  185. * set new key
  186. */
  187. Arr::set($input, "{$this->column}.$newKey", $value);
  188. /*
  189. * forget the old key and value
  190. */
  191. Arr::forget($input, "{$this->column}.$key");
  192. }
  193. }
  194. /**
  195. * Get data for Embedded form.
  196. *
  197. * Normally, data is obtained from the database.
  198. *
  199. * When the data validation errors, data is obtained from session flash.
  200. *
  201. * @return array
  202. */
  203. protected function getEmbeddedData()
  204. {
  205. if ($old = old($this->column)) {
  206. return $old;
  207. }
  208. if (empty($this->value)) {
  209. return [];
  210. }
  211. if (is_string($this->value)) {
  212. return json_decode($this->value, true);
  213. }
  214. return (array) $this->value;
  215. }
  216. /**
  217. * Build a Embedded Form and fill data.
  218. *
  219. * @return EmbeddedForm
  220. */
  221. protected function buildEmbeddedForm()
  222. {
  223. $form = new EmbeddedForm($this->column);
  224. $form->setParent($this->form);
  225. call_user_func($this->builder, $form);
  226. $form->fill($this->getEmbeddedData());
  227. return $form;
  228. }
  229. /**
  230. * Render the form.
  231. *
  232. * @return \Illuminate\View\View
  233. */
  234. public function render()
  235. {
  236. return parent::render()->with(['form' => $this->buildEmbeddedForm()]);
  237. }
  238. }