acf-meta-functions.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <?php
  2. /**
  3. * Returns an array of "ACF only" meta for the given post_id.
  4. *
  5. * @date 9/10/18
  6. * @since 5.8.0
  7. *
  8. * @param mixed $post_id The post_id for this data.
  9. *
  10. * @return array
  11. */
  12. function acf_get_meta( $post_id = 0 ) {
  13. // Allow filter to short-circuit load_value logic.
  14. $null = apply_filters( 'acf/pre_load_meta', null, $post_id );
  15. if ( $null !== null ) {
  16. return ( $null === '__return_null' ) ? null : $null;
  17. }
  18. // Decode $post_id for $type and $id.
  19. $decoded = acf_decode_post_id( $post_id );
  20. /**
  21. * Determine CRUD function.
  22. *
  23. * - Relies on decoded post_id result to identify option or meta types.
  24. * - Uses xxx_metadata(type) instead of xxx_type_meta() to bypass additional logic that could alter the ID.
  25. */
  26. if ( $decoded['type'] === 'option' ) {
  27. $allmeta = acf_get_option_meta( $decoded['id'] );
  28. } else {
  29. $allmeta = get_metadata( $decoded['type'], $decoded['id'], '' );
  30. }
  31. // Loop over meta and check that a reference exists for each value.
  32. $meta = array();
  33. if ( $allmeta ) {
  34. foreach ( $allmeta as $key => $value ) {
  35. // If a reference exists for this value, add it to the meta array.
  36. if ( isset( $allmeta[ "_$key" ] ) ) {
  37. $meta[ $key ] = $allmeta[ $key ][0];
  38. $meta[ "_$key" ] = $allmeta[ "_$key" ][0];
  39. }
  40. }
  41. }
  42. // Unserialized results (get_metadata does not unserialize if $key is empty).
  43. $meta = array_map( 'maybe_unserialize', $meta );
  44. /**
  45. * Filters the $meta array after it has been loaded.
  46. *
  47. * @date 25/1/19
  48. * @since 5.7.11
  49. *
  50. * @param array $meta The array of loaded meta.
  51. * @param string $post_id The $post_id for this meta.
  52. */
  53. return apply_filters( 'acf/load_meta', $meta, $post_id );
  54. }
  55. /**
  56. * acf_get_option_meta
  57. *
  58. * Returns an array of meta for the given wp_option name prefix in the same format as get_post_meta().
  59. *
  60. * @date 9/10/18
  61. * @since 5.8.0
  62. *
  63. * @param string $prefix The wp_option name prefix.
  64. * @return array
  65. */
  66. function acf_get_option_meta( $prefix = '' ) {
  67. // Globals.
  68. global $wpdb;
  69. // Vars.
  70. $meta = array();
  71. $search = "{$prefix}_%";
  72. $_search = "_{$prefix}_%";
  73. // Escape underscores for LIKE.
  74. $search = str_replace( '_', '\_', $search );
  75. $_search = str_replace( '_', '\_', $_search );
  76. // Query database for results.
  77. $rows = $wpdb->get_results(
  78. $wpdb->prepare(
  79. "SELECT *
  80. FROM $wpdb->options
  81. WHERE option_name LIKE %s
  82. OR option_name LIKE %s",
  83. $search,
  84. $_search
  85. ),
  86. ARRAY_A
  87. );
  88. // Loop over results and append meta (removing the $prefix from the option name).
  89. $len = strlen( "{$prefix}_" );
  90. foreach ( $rows as $row ) {
  91. $meta[ substr( $row['option_name'], $len ) ][] = $row['option_value'];
  92. }
  93. // Return results.
  94. return $meta;
  95. }
  96. /**
  97. * Retrieves specific metadata from the database.
  98. *
  99. * @date 16/10/2015
  100. * @since 5.2.3
  101. *
  102. * @param int|string $post_id The post id.
  103. * @param string $name The meta name.
  104. * @param bool $hidden If the meta is hidden (starts with an underscore).
  105. *
  106. * @return mixed
  107. */
  108. function acf_get_metadata( $post_id = 0, $name = '', $hidden = false ) {
  109. // Allow filter to short-circuit logic.
  110. $null = apply_filters( 'acf/pre_load_metadata', null, $post_id, $name, $hidden );
  111. if ( $null !== null ) {
  112. return ( $null === '__return_null' ) ? null : $null;
  113. }
  114. // Decode $post_id for $type and $id.
  115. $decoded = acf_decode_post_id( $post_id );
  116. $id = $decoded['id'];
  117. $type = $decoded['type'];
  118. // Hidden meta uses an underscore prefix.
  119. $prefix = $hidden ? '_' : '';
  120. // Bail early if no $id (possible during new acf_form).
  121. if ( ! $id ) {
  122. return null;
  123. }
  124. // Determine CRUD function.
  125. // - Relies on decoded post_id result to identify option or meta types.
  126. // - Uses xxx_metadata(type) instead of xxx_type_meta() to bypass additional logic that could alter the ID.
  127. if ( $type === 'option' ) {
  128. return get_option( "{$prefix}{$id}_{$name}", null );
  129. } else {
  130. $meta = get_metadata( $type, $id, "{$prefix}{$name}", false );
  131. return isset( $meta[0] ) ? $meta[0] : null;
  132. }
  133. }
  134. /**
  135. * Updates metadata in the database.
  136. *
  137. * @date 16/10/2015
  138. * @since 5.2.3
  139. *
  140. * @param int|string $post_id The post id.
  141. * @param string $name The meta name.
  142. * @param mixed $value The meta value.
  143. * @param bool $hidden If the meta is hidden (starts with an underscore).
  144. *
  145. * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
  146. */
  147. function acf_update_metadata( $post_id = 0, $name = '', $value = '', $hidden = false ) {
  148. // Allow filter to short-circuit logic.
  149. $pre = apply_filters( 'acf/pre_update_metadata', null, $post_id, $name, $value, $hidden );
  150. if ( $pre !== null ) {
  151. return $pre;
  152. }
  153. // Decode $post_id for $type and $id.
  154. $decoded = acf_decode_post_id( $post_id );
  155. $id = $decoded['id'];
  156. $type = $decoded['type'];
  157. // Hidden meta uses an underscore prefix.
  158. $prefix = $hidden ? '_' : '';
  159. // Bail early if no $id (possible during new acf_form).
  160. if ( ! $id ) {
  161. return false;
  162. }
  163. // Determine CRUD function.
  164. // - Relies on decoded post_id result to identify option or meta types.
  165. // - Uses xxx_metadata(type) instead of xxx_type_meta() to bypass additional logic that could alter the ID.
  166. if ( $type === 'option' ) {
  167. $value = wp_unslash( $value );
  168. $autoload = (bool) acf_get_setting( 'autoload' );
  169. return update_option( "{$prefix}{$id}_{$name}", $value, $autoload );
  170. } else {
  171. return update_metadata( $type, $id, "{$prefix}{$name}", $value );
  172. }
  173. }
  174. /**
  175. * Deletes metadata from the database.
  176. *
  177. * @date 16/10/2015
  178. * @since 5.2.3
  179. *
  180. * @param int|string $post_id The post id.
  181. * @param string $name The meta name.
  182. * @param bool $hidden If the meta is hidden (starts with an underscore).
  183. *
  184. * @return bool
  185. */
  186. function acf_delete_metadata( $post_id = 0, $name = '', $hidden = false ) {
  187. // Allow filter to short-circuit logic.
  188. $pre = apply_filters( 'acf/pre_delete_metadata', null, $post_id, $name, $hidden );
  189. if ( $pre !== null ) {
  190. return $pre;
  191. }
  192. // Decode $post_id for $type and $id.
  193. $decoded = acf_decode_post_id( $post_id );
  194. $id = $decoded['id'];
  195. $type = $decoded['type'];
  196. // Hidden meta uses an underscore prefix.
  197. $prefix = $hidden ? '_' : '';
  198. // Bail early if no $id (possible during new acf_form).
  199. if ( ! $id ) {
  200. return false;
  201. }
  202. // Determine CRUD function.
  203. // - Relies on decoded post_id result to identify option or meta types.
  204. // - Uses xxx_metadata(type) instead of xxx_type_meta() to bypass additional logic that could alter the ID.
  205. if ( $type === 'option' ) {
  206. return delete_option( "{$prefix}{$id}_{$name}" );
  207. } else {
  208. return delete_metadata( $type, $id, "{$prefix}{$name}" );
  209. }
  210. }
  211. /**
  212. * acf_copy_postmeta
  213. *
  214. * Copies meta from one post to another. Useful for saving and restoring revisions.
  215. *
  216. * @date 25/06/2016
  217. * @since 5.3.8
  218. *
  219. * @param (int|string) $from_post_id The post id to copy from.
  220. * @param (int|string) $to_post_id The post id to paste to.
  221. * @return void
  222. */
  223. function acf_copy_metadata( $from_post_id = 0, $to_post_id = 0 ) {
  224. // Get all postmeta.
  225. $meta = acf_get_meta( $from_post_id );
  226. // Check meta.
  227. if ( $meta ) {
  228. // Slash data. WP expects all data to be slashed and will unslash it (fixes '\' character issues).
  229. $meta = wp_slash( $meta );
  230. // Loop over meta.
  231. foreach ( $meta as $name => $value ) {
  232. acf_update_metadata( $to_post_id, $name, $value );
  233. }
  234. }
  235. }
  236. /**
  237. * acf_copy_postmeta
  238. *
  239. * Copies meta from one post to another. Useful for saving and restoring revisions.
  240. *
  241. * @date 25/06/2016
  242. * @since 5.3.8
  243. * @deprecated 5.7.11
  244. *
  245. * @param int $from_post_id The post id to copy from.
  246. * @param int $to_post_id The post id to paste to.
  247. * @return void
  248. */
  249. function acf_copy_postmeta( $from_post_id = 0, $to_post_id = 0 ) {
  250. return acf_copy_metadata( $from_post_id, $to_post_id );
  251. }
  252. /**
  253. * acf_get_meta_field
  254. *
  255. * Returns a field using the provided $id and $post_id parameters.
  256. * Looks for a reference to help loading the correct field via name.
  257. *
  258. * @date 21/1/19
  259. * @since 5.7.10
  260. *
  261. * @param string $key The meta name (field name).
  262. * @param (int|string) $post_id The post_id where this field's value is saved.
  263. * @return (array|false) The field array.
  264. */
  265. function acf_get_meta_field( $key = 0, $post_id = 0 ) {
  266. // Try reference.
  267. $field_key = acf_get_reference( $key, $post_id );
  268. if ( $field_key ) {
  269. $field = acf_get_field( $field_key );
  270. if ( $field ) {
  271. $field['name'] = $key;
  272. return $field;
  273. }
  274. }
  275. // Return false.
  276. return false;
  277. }
  278. /**
  279. * acf_get_metaref
  280. *
  281. * Retrieves reference metadata from the database.
  282. *
  283. * @date 16/10/2015
  284. * @since 5.2.3
  285. *
  286. * @param (int|string) $post_id The post id.
  287. * @param string type The reference type (fields|groups).
  288. * @param string $name An optional specific name
  289. * @return mixed
  290. */
  291. function acf_get_metaref( $post_id = 0, $type = 'fields', $name = '' ) {
  292. // Load existing meta.
  293. $meta = acf_get_metadata( $post_id, "_acf_$type" );
  294. // Handle no meta.
  295. if ( ! $meta ) {
  296. return $name ? '' : array();
  297. }
  298. // Return specific reference.
  299. if ( $name ) {
  300. return isset( $meta[ $name ] ) ? $meta[ $name ] : '';
  301. // Or return all references.
  302. } else {
  303. return $meta;
  304. }
  305. }
  306. /**
  307. * acf_update_metaref
  308. *
  309. * Updates reference metadata in the database.
  310. *
  311. * @date 16/10/2015
  312. * @since 5.2.3
  313. *
  314. * @param (int|string) $post_id The post id.
  315. * @param string type The reference type (fields|groups).
  316. * @param array $references An array of references.
  317. * @return (int|bool) Meta ID if the key didn't exist, true on successful update, false on failure.
  318. */
  319. function acf_update_metaref( $post_id = 0, $type = 'fields', $references = array() ) {
  320. // Get current references.
  321. $current = acf_get_metaref( $post_id, $type );
  322. // Merge in new references.
  323. $references = array_merge( $current, $references );
  324. // Simplify groups
  325. if ( $type === 'groups' ) {
  326. $references = array_values( $references );
  327. }
  328. // Remove duplicate references.
  329. $references = array_unique( $references );
  330. // Update metadata.
  331. return acf_update_metadata( $post_id, "_acf_$type", $references );
  332. }