Embeds.php 6.8 KB

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