Keine Beschreibung

class-acf-field-group.php 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. <?php
  2. if ( ! class_exists( 'acf_field__group' ) ) :
  3. class acf_field__group extends acf_field {
  4. /*
  5. * __construct
  6. *
  7. * This function will setup the field type data
  8. *
  9. * @type function
  10. * @date 5/03/2014
  11. * @since 5.0.0
  12. *
  13. * @param n/a
  14. * @return n/a
  15. */
  16. function initialize() {
  17. // vars
  18. $this->name = 'group';
  19. $this->label = __( 'Group', 'acf' );
  20. $this->category = 'layout';
  21. $this->defaults = array(
  22. 'sub_fields' => array(),
  23. 'layout' => 'block',
  24. );
  25. $this->have_rows = 'single';
  26. // field filters
  27. $this->add_field_filter( 'acf/prepare_field_for_export', array( $this, 'prepare_field_for_export' ) );
  28. $this->add_field_filter( 'acf/prepare_field_for_import', array( $this, 'prepare_field_for_import' ) );
  29. }
  30. /*
  31. * load_field()
  32. *
  33. * This filter is appied to the $field after it is loaded from the database
  34. *
  35. * @type filter
  36. * @since 3.6
  37. * @date 23/01/13
  38. *
  39. * @param $field - the field array holding all the field options
  40. *
  41. * @return $field - the field array holding all the field options
  42. */
  43. function load_field( $field ) {
  44. // vars
  45. $sub_fields = acf_get_fields( $field );
  46. // append
  47. if ( $sub_fields ) {
  48. $field['sub_fields'] = $sub_fields;
  49. }
  50. // return
  51. return $field;
  52. }
  53. /*
  54. * load_value()
  55. *
  56. * This filter is applied to the $value after it is loaded from the db
  57. *
  58. * @type filter
  59. * @since 3.6
  60. * @date 23/01/13
  61. *
  62. * @param $value (mixed) the value found in the database
  63. * @param $post_id (mixed) the $post_id from which the value was loaded
  64. * @param $field (array) the field array holding all the field options
  65. * @return $value
  66. */
  67. function load_value( $value, $post_id, $field ) {
  68. // bail early if no sub fields
  69. if ( empty( $field['sub_fields'] ) ) {
  70. return $value;
  71. }
  72. // modify names
  73. $field = $this->prepare_field_for_db( $field );
  74. // load sub fields
  75. $value = array();
  76. // loop
  77. foreach ( $field['sub_fields'] as $sub_field ) {
  78. // load
  79. $value[ $sub_field['key'] ] = acf_get_value( $post_id, $sub_field );
  80. }
  81. // return
  82. return $value;
  83. }
  84. /*
  85. * format_value()
  86. *
  87. * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
  88. *
  89. * @type filter
  90. * @since 3.6
  91. * @date 23/01/13
  92. *
  93. * @param $value (mixed) the value which was loaded from the database
  94. * @param $post_id (mixed) the $post_id from which the value was loaded
  95. * @param $field (array) the field array holding all the field options
  96. *
  97. * @return $value (mixed) the modified value
  98. */
  99. function format_value( $value, $post_id, $field ) {
  100. // bail early if no value
  101. if ( empty( $value ) ) {
  102. return false;
  103. }
  104. // modify names
  105. $field = $this->prepare_field_for_db( $field );
  106. // loop
  107. foreach ( $field['sub_fields'] as $sub_field ) {
  108. // extract value
  109. $sub_value = acf_extract_var( $value, $sub_field['key'] );
  110. // format value
  111. $sub_value = acf_format_value( $sub_value, $post_id, $sub_field );
  112. // append to $row
  113. $value[ $sub_field['_name'] ] = $sub_value;
  114. }
  115. // return
  116. return $value;
  117. }
  118. /*
  119. * update_value()
  120. *
  121. * This filter is appied to the $value before it is updated in the db
  122. *
  123. * @type filter
  124. * @since 3.6
  125. * @date 23/01/13
  126. *
  127. * @param $value - the value which will be saved in the database
  128. * @param $field - the field array holding all the field options
  129. * @param $post_id - the $post_id of which the value will be saved
  130. *
  131. * @return $value - the modified value
  132. */
  133. function update_value( $value, $post_id, $field ) {
  134. // bail early if no value
  135. if ( ! acf_is_array( $value ) ) {
  136. return null;
  137. }
  138. // bail ealry if no sub fields
  139. if ( empty( $field['sub_fields'] ) ) {
  140. return null;
  141. }
  142. // modify names
  143. $field = $this->prepare_field_for_db( $field );
  144. // loop
  145. foreach ( $field['sub_fields'] as $sub_field ) {
  146. // vars
  147. $v = false;
  148. // key (backend)
  149. if ( isset( $value[ $sub_field['key'] ] ) ) {
  150. $v = $value[ $sub_field['key'] ];
  151. // name (frontend)
  152. } elseif ( isset( $value[ $sub_field['_name'] ] ) ) {
  153. $v = $value[ $sub_field['_name'] ];
  154. // empty
  155. } else {
  156. // input is not set (hidden by conditioanl logic)
  157. continue;
  158. }
  159. // update value
  160. acf_update_value( $v, $post_id, $sub_field );
  161. }
  162. // return
  163. return '';
  164. }
  165. /*
  166. * prepare_field_for_db
  167. *
  168. * This function will modify sub fields ready for update / load
  169. *
  170. * @type function
  171. * @date 4/11/16
  172. * @since 5.5.0
  173. *
  174. * @param $field (array)
  175. * @return $field
  176. */
  177. function prepare_field_for_db( $field ) {
  178. // bail early if no sub fields
  179. if ( empty( $field['sub_fields'] ) ) {
  180. return $field;
  181. }
  182. // loop
  183. foreach ( $field['sub_fields'] as &$sub_field ) {
  184. // prefix name
  185. $sub_field['name'] = $field['name'] . '_' . $sub_field['_name'];
  186. }
  187. // return
  188. return $field;
  189. }
  190. /*
  191. * render_field()
  192. *
  193. * Create the HTML interface for your field
  194. *
  195. * @param $field - an array holding all the field's data
  196. *
  197. * @type action
  198. * @since 3.6
  199. * @date 23/01/13
  200. */
  201. function render_field( $field ) {
  202. // bail early if no sub fields
  203. if ( empty( $field['sub_fields'] ) ) {
  204. return;
  205. }
  206. // load values
  207. foreach ( $field['sub_fields'] as &$sub_field ) {
  208. // add value
  209. if ( isset( $field['value'][ $sub_field['key'] ] ) ) {
  210. // this is a normal value
  211. $sub_field['value'] = $field['value'][ $sub_field['key'] ];
  212. } elseif ( isset( $sub_field['default_value'] ) ) {
  213. // no value, but this sub field has a default value
  214. $sub_field['value'] = $sub_field['default_value'];
  215. }
  216. // update prefix to allow for nested values
  217. $sub_field['prefix'] = $field['name'];
  218. // restore required
  219. if ( $field['required'] ) {
  220. $sub_field['required'] = 0;
  221. }
  222. }
  223. // render
  224. if ( $field['layout'] == 'table' ) {
  225. $this->render_field_table( $field );
  226. } else {
  227. $this->render_field_block( $field );
  228. }
  229. }
  230. /*
  231. * render_field_block
  232. *
  233. * description
  234. *
  235. * @type function
  236. * @date 12/07/2016
  237. * @since 5.4.0
  238. *
  239. * @param $post_id (int)
  240. * @return $post_id (int)
  241. */
  242. function render_field_block( $field ) {
  243. // vars
  244. $label_placement = ( $field['layout'] == 'block' ) ? 'top' : 'left';
  245. // html
  246. echo '<div class="acf-fields -' . $label_placement . ' -border">';
  247. foreach ( $field['sub_fields'] as $sub_field ) {
  248. acf_render_field_wrap( $sub_field );
  249. }
  250. echo '</div>';
  251. }
  252. /*
  253. * render_field_table
  254. *
  255. * description
  256. *
  257. * @type function
  258. * @date 12/07/2016
  259. * @since 5.4.0
  260. *
  261. * @param $post_id (int)
  262. * @return $post_id (int)
  263. */
  264. function render_field_table( $field ) {
  265. ?>
  266. <table class="acf-table">
  267. <thead>
  268. <tr>
  269. <?php
  270. foreach ( $field['sub_fields'] as $sub_field ) :
  271. // prepare field (allow sub fields to be removed)
  272. $sub_field = acf_prepare_field( $sub_field );
  273. // bail ealry if no field
  274. if ( ! $sub_field ) {
  275. continue;
  276. }
  277. // vars
  278. $atts = array();
  279. $atts['class'] = 'acf-th';
  280. $atts['data-name'] = $sub_field['_name'];
  281. $atts['data-type'] = $sub_field['type'];
  282. $atts['data-key'] = $sub_field['key'];
  283. // Add custom width
  284. if ( $sub_field['wrapper']['width'] ) {
  285. $atts['data-width'] = $sub_field['wrapper']['width'];
  286. $atts['style'] = 'width: ' . $sub_field['wrapper']['width'] . '%;';
  287. }
  288. ?>
  289. <th <?php acf_esc_attr_e( $atts ); ?>>
  290. <?php acf_render_field_label( $sub_field ); ?>
  291. <?php acf_render_field_instructions( $sub_field ); ?>
  292. </th>
  293. <?php endforeach; ?>
  294. </tr>
  295. </thead>
  296. <tbody>
  297. <tr class="acf-row">
  298. <?php
  299. foreach ( $field['sub_fields'] as $sub_field ) {
  300. acf_render_field_wrap( $sub_field, 'td' );
  301. }
  302. ?>
  303. </tr>
  304. </tbody>
  305. </table>
  306. <?php
  307. }
  308. /*
  309. * render_field_settings()
  310. *
  311. * Create extra options for your field. This is rendered when editing a field.
  312. * The value of $field['name'] can be used (like bellow) to save extra data to the $field
  313. *
  314. * @type action
  315. * @since 3.6
  316. * @date 23/01/13
  317. *
  318. * @param $field - an array holding all the field's data
  319. */
  320. function render_field_settings( $field ) {
  321. // vars
  322. $args = array(
  323. 'fields' => $field['sub_fields'],
  324. 'parent' => $field['ID'],
  325. );
  326. ?>
  327. <tr class="acf-field acf-field-setting-sub_fields" data-setting="group" data-name="sub_fields">
  328. <td class="acf-label">
  329. <label><?php _e( 'Sub Fields', 'acf' ); ?></label>
  330. </td>
  331. <td class="acf-input">
  332. <?php
  333. acf_get_view( 'field-group-fields', $args );
  334. ?>
  335. </td>
  336. </tr>
  337. <?php
  338. // layout
  339. acf_render_field_setting(
  340. $field,
  341. array(
  342. 'label' => __( 'Layout', 'acf' ),
  343. 'instructions' => __( 'Specify the style used to render the selected fields', 'acf' ),
  344. 'type' => 'radio',
  345. 'name' => 'layout',
  346. 'layout' => 'horizontal',
  347. 'choices' => array(
  348. 'block' => __( 'Block', 'acf' ),
  349. 'table' => __( 'Table', 'acf' ),
  350. 'row' => __( 'Row', 'acf' ),
  351. ),
  352. )
  353. );
  354. }
  355. /*
  356. * validate_value
  357. *
  358. * description
  359. *
  360. * @type function
  361. * @date 11/02/2014
  362. * @since 5.0.0
  363. *
  364. * @param $post_id (int)
  365. * @return $post_id (int)
  366. */
  367. function validate_value( $valid, $value, $field, $input ) {
  368. // bail early if no $value
  369. if ( empty( $value ) ) {
  370. return $valid;
  371. }
  372. // bail early if no sub fields
  373. if ( empty( $field['sub_fields'] ) ) {
  374. return $valid;
  375. }
  376. // loop
  377. foreach ( $field['sub_fields'] as $sub_field ) {
  378. // get sub field
  379. $k = $sub_field['key'];
  380. // bail early if value not set (conditional logic?)
  381. if ( ! isset( $value[ $k ] ) ) {
  382. continue;
  383. }
  384. // required
  385. if ( $field['required'] ) {
  386. $sub_field['required'] = 1;
  387. }
  388. // validate
  389. acf_validate_value( $value[ $k ], $sub_field, "{$input}[{$k}]" );
  390. }
  391. // return
  392. return $valid;
  393. }
  394. /*
  395. * duplicate_field()
  396. *
  397. * This filter is appied to the $field before it is duplicated and saved to the database
  398. *
  399. * @type filter
  400. * @since 3.6
  401. * @date 23/01/13
  402. *
  403. * @param $field - the field array holding all the field options
  404. *
  405. * @return $field - the modified field
  406. */
  407. function duplicate_field( $field ) {
  408. // get sub fields
  409. $sub_fields = acf_extract_var( $field, 'sub_fields' );
  410. // save field to get ID
  411. $field = acf_update_field( $field );
  412. // duplicate sub fields
  413. acf_duplicate_fields( $sub_fields, $field['ID'] );
  414. // return
  415. return $field;
  416. }
  417. /**
  418. * prepare_field_for_export
  419. *
  420. * Prepares the field for export.
  421. *
  422. * @date 11/03/2014
  423. * @since 5.0.0
  424. *
  425. * @param array $field The field settings.
  426. * @return array
  427. */
  428. function prepare_field_for_export( $field ) {
  429. // Check for sub fields.
  430. if ( ! empty( $field['sub_fields'] ) ) {
  431. $field['sub_fields'] = acf_prepare_fields_for_export( $field['sub_fields'] );
  432. }
  433. return $field;
  434. }
  435. /**
  436. * prepare_field_for_import
  437. *
  438. * Returns a flat array of fields containing all sub fields ready for import.
  439. *
  440. * @date 11/03/2014
  441. * @since 5.0.0
  442. *
  443. * @param array $field The field settings.
  444. * @return array
  445. */
  446. function prepare_field_for_import( $field ) {
  447. // Check for sub fields.
  448. if ( ! empty( $field['sub_fields'] ) ) {
  449. $sub_fields = acf_extract_var( $field, 'sub_fields' );
  450. // Modify sub fields.
  451. foreach ( $sub_fields as $i => $sub_field ) {
  452. $sub_fields[ $i ]['parent'] = $field['key'];
  453. $sub_fields[ $i ]['menu_order'] = $i;
  454. }
  455. // Return array of [field, sub_1, sub_2, ...].
  456. return array_merge( array( $field ), $sub_fields );
  457. }
  458. return $field;
  459. }
  460. /*
  461. * delete_value
  462. *
  463. * Called when deleting this field's value.
  464. *
  465. * @date 1/07/2015
  466. * @since 5.2.3
  467. *
  468. * @param mixed $post_id The post ID being saved
  469. * @param string $meta_key The field name as seen by the DB
  470. * @param array $field The field settings
  471. * @return void
  472. */
  473. function delete_value( $post_id, $meta_key, $field ) {
  474. // bail ealry if no sub fields
  475. if ( empty( $field['sub_fields'] ) ) {
  476. return null;
  477. }
  478. // modify names
  479. $field = $this->prepare_field_for_db( $field );
  480. // loop
  481. foreach ( $field['sub_fields'] as $sub_field ) {
  482. acf_delete_value( $post_id, $sub_field );
  483. }
  484. }
  485. /**
  486. * delete_field
  487. *
  488. * Called when deleting a field of this type.
  489. *
  490. * @date 8/11/18
  491. * @since 5.8.0
  492. *
  493. * @param arra $field The field settings.
  494. * @return void
  495. */
  496. function delete_field( $field ) {
  497. // loop over sub fields and delete them
  498. if ( $field['sub_fields'] ) {
  499. foreach ( $field['sub_fields'] as $sub_field ) {
  500. acf_delete_field( $sub_field['ID'] );
  501. }
  502. }
  503. }
  504. /**
  505. * Return the schema array for the REST API.
  506. *
  507. * @param array $field
  508. * @return array
  509. */
  510. public function get_rest_schema( array $field ) {
  511. $schema = array(
  512. 'type' => array( 'object', 'null' ),
  513. 'properties' => array(),
  514. 'required' => ! empty( $field['required'] ),
  515. );
  516. foreach ( $field['sub_fields'] as $sub_field ) {
  517. if ( $sub_field_schema = acf_get_field_rest_schema( $sub_field ) ) {
  518. $schema['properties'][ $sub_field['name'] ] = $sub_field_schema;
  519. }
  520. }
  521. return $schema;
  522. }
  523. /**
  524. * Apply basic formatting to prepare the value for default REST output.
  525. *
  526. * @param mixed $value
  527. * @param int|string $post_id
  528. * @param array $field
  529. * @return array|mixed
  530. */
  531. public function format_value_for_rest( $value, $post_id, array $field ) {
  532. if ( empty( $value ) || ! is_array( $value ) || empty( $field['sub_fields'] ) ) {
  533. return $value;
  534. }
  535. // Loop through each row and within that, each sub field to process sub fields individually.
  536. foreach ( $field['sub_fields'] as $sub_field ) {
  537. // Extract the sub field 'field_key'=>'value' pair from the $value and format it.
  538. $sub_value = acf_extract_var( $value, $sub_field['key'] );
  539. $sub_value = acf_format_value_for_rest( $sub_value, $post_id, $sub_field );
  540. // Add the sub field value back to the $value but mapped to the field name instead
  541. // of the key reference.
  542. $value[ $sub_field['name'] ] = $sub_value;
  543. }
  544. return $value;
  545. }
  546. }
  547. // initialize
  548. acf_register_field_type( 'acf_field__group' );
  549. endif; // class_exists check
  550. ?>