Advertisement
Guest User

Untitled

a guest
Jul 16th, 2019
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.08 KB | None | 0 0
  1. <?php
  2. /**
  3. * Config for WPGraphQL ACF
  4. *
  5. * @package wp-graphql-acf
  6. */
  7.  
  8. namespace WPGraphQL\ACF;
  9.  
  10. use WPGraphQL\Data\DataSource;
  11. use WPGraphQL\Model\Comment;
  12. use WPGraphQL\Model\Menu;
  13. use WPGraphQL\Model\MenuItem;
  14. use WPGraphQL\Model\Post;
  15. use WPGraphQL\Model\Term;
  16. use WPGraphQL\TypeRegistry;
  17. use WPGraphQL\Types;
  18.  
  19. /**
  20. * Config class.
  21. */
  22. class Config {
  23.  
  24. /**
  25. * Initialize WPGraphQL to ACF
  26. */
  27. public function init() {
  28. /**
  29. * Add ACF Fields to GraphQL Types
  30. */
  31. $this->add_acf_fields_to_post_object_types();
  32. $this->add_acf_fields_to_term_objects();
  33. $this->add_acf_fields_to_comments();
  34. $this->add_acf_fields_to_menus();
  35. $this->add_acf_fields_to_menu_items();
  36. $this->add_acf_fields_to_media_items();
  37. $this->add_acf_fields_to_individual_posts();
  38. }
  39.  
  40. /**
  41. * Determines whether a field group should be exposed to the GraphQL Schema. By default, field
  42. * groups will not be exposed to GraphQL.
  43. *
  44. * @param array $field_group Undocumented.
  45. *
  46. * @return bool
  47. */
  48. protected function should_field_group_show_in_graphql( $field_group ) {
  49.  
  50. /**
  51. * By default, field groups will not be exposed to GraphQL.
  52. */
  53. $show = false;
  54.  
  55. /**
  56. * If
  57. */
  58. if ( isset( $field_group['show_in_graphql'] ) && true === (bool) $field_group['show_in_graphql'] ) {
  59. $show = true;
  60. }
  61.  
  62. /**
  63. * Determine conditions where the GraphQL Schema should NOT be shown in GraphQL for
  64. * root groups, not nested groups with parent.
  65. */
  66. if ( ! isset( $field_group['parent'] ) ) {
  67. if (
  68. ( isset( $field_group['active'] ) && true != $field_group['active'] ) ||
  69. ( empty( $field_group['location'] ) || ! is_array( $field_group['location'] ) )
  70. ) {
  71. $show = false;
  72. }
  73. }
  74.  
  75. /**
  76. * Whether a field group should show in GraphQL.
  77. *
  78. * @var boolean $show Whether the field group should show in the GraphQL Schema
  79. * @var array $field_group The ACF Field Group
  80. * @var Config $this The Config for the ACF Plugin
  81. */
  82. return apply_filters( 'wpgraphql_acf_should_field_group_show_in_graphql', $show, $field_group, $this );
  83.  
  84. }
  85.  
  86. /**
  87. * Undocumented function
  88. *
  89. * @todo: This may be a good utility to add to WPGraphQL Core? May even have something already?
  90. *
  91. * @param string $str Unknown.
  92. * @param array $no_strip Unknown.
  93. *
  94. * @return mixed|null|string|string[]
  95. */
  96. public static function camel_case( $str, array $no_strip = [] ) {
  97. // non-alpha and non-numeric characters become spaces.
  98. $str = preg_replace( '/[^a-z0-9' . implode( '', $no_strip ) . ']+/i', ' ', $str );
  99. $str = trim( $str );
  100. // Lowercase the string
  101. $str = strtolower( $str );
  102. // uppercase the first character of each word.
  103. $str = ucwords( $str );
  104. // Replace spaces
  105. $str = str_replace( ' ', '', $str );
  106. // Lowecase first letter
  107. $str = lcfirst( $str );
  108.  
  109. return $str;
  110. }
  111.  
  112. /**
  113. * Add ACF Fields to Post Object Types.
  114. *
  115. * This gets the Post Types that are configured to show_in_graphql and iterates
  116. * over them to expose ACF Fields to their Type in the GraphQL Schema.
  117. */
  118. protected function add_acf_fields_to_post_object_types() {
  119.  
  120. /**
  121. * Get a list of post types that have been registered to show in graphql
  122. */
  123. $graphql_post_types = get_post_types(['show_in_graphql' => true]);
  124.  
  125. /**
  126. * If there are no post types exposed to GraphQL, bail
  127. */
  128. if ( empty( $graphql_post_types ) || ! is_array( $graphql_post_types ) ) {
  129. return;
  130. }
  131.  
  132. /**
  133. * Loop over the post types exposed to GraphQL
  134. */
  135. foreach ( $graphql_post_types as $post_type ) {
  136.  
  137. /**
  138. * Get the field groups associated with the post type
  139. */
  140. $field_groups = acf_get_field_groups(
  141. [
  142. 'post_type' => $post_type,
  143. ]
  144. );
  145.  
  146. /**
  147. * If there are no field groups for this post type, move on to the next one.
  148. */
  149. if ( empty( $field_groups ) || ! is_array( $field_groups ) ) {
  150. continue;
  151. }
  152.  
  153. /**
  154. * Get the post_type_object
  155. */
  156. $post_type_object = get_post_type_object( $post_type );
  157.  
  158. /**
  159. * Loop over the field groups for this post type
  160. */
  161. foreach ( $field_groups as $field_group ) {
  162.  
  163. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  164.  
  165. $field_group['type'] = 'group';
  166. $field_group['name'] = $field_name;
  167. $config = [
  168. 'name' => $field_name,
  169. 'description' => $field_group['description'],
  170. 'acf_field' => $field_group,
  171. 'acf_field_group' => null,
  172. 'resolve' => function( $root ) use ( $field_group ) {
  173. return isset( $root ) ? $root : null;
  174. }
  175. ];
  176.  
  177. $this->register_graphql_field( $post_type_object->graphql_single_name, $field_name, $config );
  178. }
  179. }
  180.  
  181. }
  182.  
  183. /**
  184. * Undocumented function
  185. *
  186. * @param [type] $root Undocumented.
  187. * @param [type] $acf_field Undocumented.
  188. *
  189. * @return mixed
  190. */
  191. protected function get_acf_field_value( $root, $acf_field ) {
  192.  
  193. $value = null;
  194. if ( is_array( $root ) ) {
  195. if ( isset( $root[ $acf_field['key'] ] ) ) {
  196. $value = $root[ $acf_field['key'] ];
  197.  
  198. if ( 'wysiwyg' === $acf_field['type'] ) {
  199. $value = apply_filters( 'the_content', $value );
  200. }
  201.  
  202. }
  203. } else {
  204.  
  205. switch (true) {
  206. case $root instanceof Term:
  207. $id = acf_get_term_post_id( $root->taxonomyName, $root->term_id );
  208. break;
  209. case $root instanceof Post:
  210. $id = absint( $root->ID );
  211. break;
  212. case $root instanceof MenuItem:
  213. $id = absint( $root->menuItemId );
  214. break;
  215. case $root instanceof Menu:
  216. $id = acf_get_term_post_id( 'nav_menu', $root->menuId );
  217. break;
  218. case $root instanceof Comment:
  219. $id = 'comment_' . absint( $root->comment_ID );
  220. break;
  221. default:
  222. $id = null;
  223. break;
  224. }
  225.  
  226. if ( empty( $id ) ) {
  227. return null;
  228. }
  229.  
  230. $format = false;
  231.  
  232. if ( 'wysiwyg' === $acf_field['type'] ) {
  233. $format = true;
  234. }
  235.  
  236. $field_value = get_field( $acf_field['key'], $id, $format );
  237.  
  238. $value = ! empty( $field_value ) ? $field_value : null;
  239. }
  240.  
  241. return $value;
  242.  
  243. }
  244.  
  245. /**
  246. * Get a list of supported fields that WPGraphQL for ACF supports.
  247. *
  248. * This is helpful for determining whether UI should be output for the field, and whether
  249. * the field should be added to the Schema.
  250. *
  251. * Some fields, such as "Accordion" are not supported currently.
  252. *
  253. * @return array
  254. */
  255. public static function get_supported_fields() {
  256. $supported_fields = [
  257. 'text',
  258. 'textarea',
  259. 'number',
  260. 'range',
  261. 'email',
  262. 'url',
  263. 'password',
  264. 'image',
  265. 'file',
  266. 'wysiwyg',
  267. 'oembed',
  268. 'gallery',
  269. 'select',
  270. 'checkbox',
  271. 'radio',
  272. 'button_group',
  273. 'true_false',
  274. 'link',
  275. 'post_object',
  276. 'page_link',
  277. 'relationship',
  278. 'taxonomy',
  279. 'user',
  280. 'google_map',
  281. 'date_picker',
  282. 'date_time_picker',
  283. 'time_picker',
  284. 'color_picker',
  285. 'group',
  286. 'repeater',
  287. 'flexible_content'
  288. ];
  289.  
  290. /**
  291. * filter the supported fields
  292. *
  293. * @param array $supported_fields
  294. */
  295. return apply_filters( 'wpgraphql_acf_supported_fields', $supported_fields );
  296. }
  297.  
  298. /**
  299. * Undocumented function
  300. *
  301. * @param [type] $type_name Undocumented.
  302. * @param [type] $field_name Undocumented.
  303. * @param [type] $config Undocumented.
  304. *
  305. * @return mixed
  306. */
  307. protected function register_graphql_field( $type_name, $field_name, $config ) {
  308.  
  309. $acf_field = isset( $config['acf_field'] ) ? $config['acf_field'] : null;
  310. $acf_type = isset( $acf_field['type'] ) ? $acf_field['type'] : null;
  311.  
  312. if ( empty( $acf_type ) ) {
  313. return false;
  314. }
  315.  
  316. $field_config = [
  317. 'type' => null,
  318. 'resolve' => isset( $config['resolve'] ) && is_callable( $config['resolve'] ) ? $config['resolve'] : function( $root, $args, $context, $info ) use ( $acf_field ) {
  319. $value = $this->get_acf_field_value( $root, $acf_field );
  320.  
  321. return ! empty( $value ) ? $value : null;
  322. },
  323. ];
  324.  
  325.  
  326. switch ( $acf_type ) {
  327. case 'button_group':
  328. case 'color_picker':
  329. case 'email':
  330. case 'textarea':
  331. case 'text':
  332. case 'message':
  333. case 'oembed':
  334. case 'password':
  335. case 'wysiwyg':
  336. case 'url':
  337. // Even though Selects and Radios in ACF can _technically_ be an integer
  338. // we're chosing to always cast as a string because with
  339. // GraphQL we can't return different types
  340. case 'select':
  341. case 'radio':
  342. $field_config['type'] = 'String';
  343. break;
  344. case 'range':
  345. $field_config['type'] = 'Integer';
  346. break;
  347. case 'number':
  348. $field_config['type'] = 'Float';
  349. break;
  350. case 'true_false':
  351. $field_config['type'] = 'Boolean';
  352. break;
  353. case 'date_picker':
  354. case 'time_picker':
  355. case 'date_time_picker':
  356. $field_config = [
  357. 'type' => 'String',
  358. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  359. return isset( $root->ID ) ? get_field( $acf_field['key'], $root->ID, true ) : null;
  360. },
  361. ];
  362. break;
  363. case 'relationship':
  364.  
  365. if ( isset( $acf_field['post_type'] ) && is_array( $acf_field['post_type'] ) ) {
  366. $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) );
  367. if ( TypeRegistry::get_type( $field_type_name ) == $field_type_name ) {
  368. $type = $field_type_name;
  369. } else {
  370. $type_names = [];
  371. foreach ( $acf_field['post_type'] as $post_type ) {
  372. if ( in_array( $post_type, \WPGraphQL::$allowed_post_types, true ) ) {
  373. $type_names[ $post_type ] = get_post_type_object( $post_type )->graphql_single_name;
  374. }
  375. }
  376.  
  377. if ( empty( $type_names ) ) {
  378. $field_config['type'] = null;
  379. break;
  380. }
  381.  
  382. register_graphql_union_type( $field_type_name, [
  383. 'typeNames' => $type_names,
  384. 'resolveType' => function( $value ) use ( $type_names ) {
  385. return ! empty( $value->post_type ) ? Types::post_object( $value->post_type ) : null;
  386. }
  387. ] );
  388.  
  389. $type = $field_type_name;
  390. }
  391. } else {
  392. $type = 'PostObjectUnion';
  393. }
  394.  
  395. $field_config = [
  396. 'type' => [ 'list_of' => $type ],
  397. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  398. $relationship = [];
  399. $value = $this->get_acf_field_value( $root, $acf_field );
  400. if ( ! empty( $value ) && is_array( $value ) ) {
  401. foreach ( $value as $post_id ) {
  402. $post_object = get_post( $post_id );
  403. if ( $post_object instanceof \WP_Post ) {
  404. $post_model = new Post( $post_object );
  405. $relationship[] = $post_model;
  406. }
  407. }
  408. }
  409. return isset( $value ) ? $relationship : null;
  410.  
  411. },
  412. ];
  413. break;
  414. case 'page_link':
  415. case 'post_object':
  416.  
  417. if ( isset( $acf_field['post_type'] ) && is_array( $acf_field['post_type'] ) ) {
  418. $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) );
  419. if ( TypeRegistry::get_type( $field_type_name ) == $field_type_name ) {
  420. $type = $field_type_name;
  421. } else {
  422. $type_names = [];
  423. foreach ( $acf_field['post_type'] as $post_type ) {
  424. if ( in_array( $post_type, \WPGraphQL::$allowed_post_types, true ) ) {
  425. $type_names[ $post_type ] = get_post_type_object( $post_type )->graphql_single_name;
  426. }
  427. }
  428.  
  429. if ( empty( $type_names ) ) {
  430. $field_config['type'] = null;
  431. break;
  432. }
  433.  
  434. register_graphql_union_type( $field_type_name, [
  435. 'typeNames' => $type_names,
  436. 'resolveType' => function( $value ) use ( $type_names ) {
  437. return ! empty( $value->post_type ) ? Types::post_object( $value->post_type ) : null;
  438. }
  439. ] );
  440.  
  441. $type = $field_type_name;
  442. }
  443. } else {
  444. $type = 'PostObjectUnion';
  445. }
  446.  
  447. $field_config = [
  448. 'type' => $type,
  449. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  450. $value = $this->get_acf_field_value( $root, $acf_field );
  451. if ( $value instanceof \WP_Post ) {
  452. return new Post( $value );
  453. }
  454.  
  455. return absint( $value ) ? DataSource::resolve_post_object( (int) $value, $context ) : null;
  456.  
  457. },
  458. ];
  459. break;
  460. case 'link':
  461.  
  462. $field_type_name = 'ACF_Link';
  463. if ( TypeRegistry::get_type( $field_type_name ) == $field_type_name ) {
  464. $field_config['type'] = $field_type_name;
  465. break;
  466. }
  467.  
  468. register_graphql_object_type(
  469. $field_type_name,
  470. [
  471. 'description' => __( 'ACF Link field', 'wp-graphql-acf' ),
  472. 'fields' => [
  473. 'url' => [
  474. 'type' => 'String',
  475. 'description' => __( 'The url of the link', 'wp-graphql-acf' ),
  476. ],
  477. 'title' => [
  478. 'type' => 'String',
  479. 'description' => __( 'The title of the link', 'wp-graphql-acf' ),
  480. ],
  481. 'target' => [
  482. 'type' => 'String',
  483. 'description' => __( 'The target of the link (_blank, etc)', 'wp-graphql-acf' ),
  484. ],
  485. ],
  486. ]
  487. );
  488. $field_config['type'] = $field_type_name;
  489. break;
  490. case 'image':
  491. case 'file':
  492. $field_config = [
  493. 'type' => 'MediaItem',
  494. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  495. $value = $this->get_acf_field_value( $root, $acf_field );
  496.  
  497. return DataSource::resolve_post_object( (int) $value, $context );
  498. },
  499. ];
  500. break;
  501. case 'checkbox':
  502. $field_config = [
  503. 'type' => [ 'list_of' => 'String' ],
  504. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  505. $value = $this->get_acf_field_value( $root, $acf_field );
  506.  
  507. return is_array( $value ) ? $value : null;
  508. },
  509. ];
  510. break;
  511. case 'gallery':
  512. $field_config = [
  513. 'type' => [ 'list_of' => 'MediaItem' ],
  514. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  515. $value = $this->get_acf_field_value( $root, $acf_field );
  516. $gallery = [];
  517. if ( ! empty( $value ) && is_array( $value ) ) {
  518. foreach ( $value as $image ) {
  519. $post_object = get_post( (int) $image );
  520. if ( $post_object instanceof \WP_Post ) {
  521. $post_model = new Post( $post_object );
  522. $gallery[] = $post_model;
  523. }
  524. }
  525. }
  526.  
  527. return isset( $value ) ? $gallery : null;
  528. },
  529. ];
  530. break;
  531. case 'user':
  532. $field_config = [
  533. 'type' => 'User',
  534. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  535. $value = $this->get_acf_field_value( $root, $acf_field );
  536.  
  537. return DataSource::resolve_user( (int) $value, $context );
  538. },
  539. ];
  540. break;
  541. case 'taxonomy':
  542. $field_config = [
  543. 'type' => [ 'list_of' => 'TermObjectUnion' ],
  544. 'resolve' => function( $root, $args, $context, $info ) use ( $acf_field ) {
  545. $value = $this->get_acf_field_value( $root, $acf_field );
  546. $terms = [];
  547. if ( ! empty( $value ) && is_array( $value ) ) {
  548. foreach ( $value as $term ) {
  549. $terms[] = DataSource::resolve_term_object( (int) $term, $context );
  550. }
  551. }
  552.  
  553. return $terms;
  554. },
  555. ];
  556. break;
  557.  
  558. // Accordions are not represented in the GraphQL Schema.
  559. case 'accordion':
  560. $field_config = null;
  561. break;
  562. case 'group':
  563. $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) );
  564. if ( TypeRegistry::get_type( $field_type_name ) ) {
  565. $field_config['type'] = $field_type_name;
  566. break;
  567. }
  568.  
  569. register_graphql_object_type(
  570. $field_type_name,
  571. [
  572. 'description' => __( 'Field Group', 'wp-graphql-acf' ),
  573. 'fields' => [
  574. 'fieldGroupName' => [
  575. 'type' => 'String',
  576. 'resolve' => function( $source ) use ( $acf_field ) {
  577. return ! empty( $acf_field['name'] ) ? $acf_field['name'] : null;
  578. },
  579. ],
  580. ],
  581. ]
  582. );
  583.  
  584.  
  585. $this->add_field_group_fields( $acf_field, $field_type_name );
  586.  
  587. $field_config['type'] = $field_type_name;
  588. break;
  589.  
  590. case 'google_map':
  591. $field_type_name = 'ACF_GoogleMap';
  592. if ( TypeRegistry::get_type( $field_type_name ) == $field_type_name ) {
  593. $field_config['type'] = $field_type_name;
  594. break;
  595. }
  596.  
  597. register_graphql_object_type(
  598. $field_type_name,
  599. [
  600. 'description' => __( 'Google Map field', 'wp-graphql-acf' ),
  601. 'fields' => [
  602. 'streetAddress' => [
  603. 'type' => 'String',
  604. 'description' => __( 'The street address associated with the map', 'wp-graphql-acf' ),
  605. 'resolve' => function( $root ) {
  606. return isset( $root['address'] ) ? $root['address'] : null;
  607. },
  608. ],
  609. 'latitude' => [
  610. 'type' => 'Float',
  611. 'description' => __( 'The latitude associated with the map', 'wp-graphql-acf' ),
  612. 'resolve' => function( $root ) {
  613. return isset( $root['lat'] ) ? $root['lat'] : null;
  614. },
  615. ],
  616. 'longitude' => [
  617. 'type' => 'Float',
  618. 'description' => __( 'The longitude associated with the map', 'wp-graphql-acf' ),
  619. 'resolve' => function( $root ) {
  620. return isset( $root['lng'] ) ? $root['lng'] : null;
  621. },
  622. ],
  623. ],
  624. ]
  625. );
  626. $field_config['type'] = $field_type_name;
  627. break;
  628. case 'repeater':
  629. $field_type_name = $type_name . '_' . self::camel_case( $acf_field['name'] );
  630.  
  631. if ( TypeRegistry::get_type( $field_type_name ) ) {
  632. $field_config['type'] = $field_type_name;
  633. break;
  634. }
  635.  
  636. register_graphql_object_type(
  637. $field_type_name,
  638. [
  639. 'description' => __( 'Field Group', 'wp-graphql-acf' ),
  640. 'fields' => [
  641. 'fieldGroupName' => [
  642. 'type' => 'String',
  643. 'resolve' => function( $source ) use ( $acf_field ) {
  644. return ! empty( $acf_field['name'] ) ? $acf_field['name'] : null;
  645. },
  646. ],
  647. ],
  648. 'resolve' => function( $source ) use ( $acf_field ) {
  649. $repeater = $this->get_acf_field_value( $source, $acf_field );
  650. return ! empty( $repeater ) ? $repeater : [];
  651. },
  652. ]
  653. );
  654.  
  655. $this->add_field_group_fields( $acf_field, $field_type_name );
  656.  
  657. $field_config['type'] = [ 'list_of' => $field_type_name ];
  658. break;
  659.  
  660. /**
  661. * Flexible content fields should return a Union of the Layouts that can be configured.
  662. *
  663. *
  664. * Example Query of a flex field with the name "flex_field" and 2 groups
  665. *
  666. * {
  667. * post {
  668. * flexField {
  669. * ...on GroupOne {
  670. * textField
  671. * textAreaField
  672. * }
  673. * ...on GroupTwo {
  674. * imageField {
  675. * id
  676. * title
  677. * }
  678. * }
  679. * }
  680. * }
  681. * }
  682. *
  683. */
  684. case 'flexible_content':
  685.  
  686. $field_config = null;
  687. $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) );
  688. if ( TypeRegistry::get_type( $field_type_name ) ) {
  689. $field_config['type'] = $field_type_name;
  690. break;
  691. }
  692.  
  693. if ( ! empty( $acf_field['layouts'] ) && is_array( $acf_field['layouts'] ) ) {
  694.  
  695. $union_types = [];
  696. foreach ( $acf_field['layouts'] as $layout ) {
  697.  
  698. $flex_field_layout_name = ! empty( $layout['name'] ) ? ucfirst( self::camel_case( $layout['name'] ) ) : null;
  699. $flex_field_layout_name = ! empty( $flex_field_layout_name ) ? $field_type_name . '_' . $flex_field_layout_name : null;
  700. $layout_type = TypeRegistry::get_type( $flex_field_layout_name );
  701.  
  702. if ( $layout_type ) {
  703. $union_types[ $layout['name'] ] = $layout_type;
  704. } else {
  705. register_graphql_object_type( $flex_field_layout_name, [
  706. 'description' => __( 'Group within the flex field', 'wp-graphql-acf' ),
  707. 'fields' => [
  708. 'fieldGroupName' => [
  709. 'type' => 'String',
  710. 'resolve' => function( $source ) use ( $flex_field_layout_name ) {
  711. return ! empty( $flex_field_layout_name ) ? $flex_field_layout_name : null;
  712. },
  713. ],
  714. ],
  715. ] );
  716. $layout_type = TypeRegistry::get_type( $flex_field_layout_name );
  717. $union_types[ $layout['name'] ] = $layout_type;
  718.  
  719. $layout['parent'] = $acf_field;
  720. $layout['show_in_graphql'] = isset( $acf_field['show_in_graphql'] ) ? (bool) $acf_field['show_in_graphql'] : true;
  721. $this->add_field_group_fields( $layout, $flex_field_layout_name );
  722. }
  723. }
  724.  
  725. register_graphql_union_type( $field_type_name, [
  726. 'types' => $union_types,
  727. 'resolveType' => function( $value ) use ( $union_types ) {
  728. return isset( $union_types[ $value['acf_fc_layout'] ] ) ? $union_types[ $value['acf_fc_layout'] ] : null;
  729. }
  730. ] );
  731.  
  732. $field_config['type'] = [ 'list_of' => $field_type_name ];
  733. $field_config['resolve'] = function( $root, $args, $context, $info ) use ( $acf_field ) {
  734. $value = $this->get_acf_field_value( $root, $acf_field );
  735. return ! empty( $value ) ? $value : [];
  736. };
  737. }
  738. break;
  739. default:
  740. break;
  741. }
  742.  
  743. if ( empty( $field_config ) || empty( $field_config['type'] ) ) {
  744. return null;
  745. }
  746.  
  747. $config = array_merge( $config, $field_config );
  748.  
  749. return register_graphql_field( $type_name, $field_name, $config );
  750. }
  751.  
  752. /**
  753. * Given a field group array, this adds the fields to the specified Type in the Schema
  754. *
  755. * @param array $field_group The group to add to the Schema.
  756. * @param string $type_name The Type name in the GraphQL Schema to add fields to.
  757. */
  758. protected function add_field_group_fields( $field_group, $type_name ) {
  759.  
  760. /**
  761. * If the field group has the show_in_graphql setting configured, respect it's setting
  762. * otherwise default to true (for nested fields)
  763. */
  764. $field_group['show_in_graphql'] = isset( $field_group['show_in_graphql'] ) ? (boolean) $field_group['show_in_graphql'] : true;
  765.  
  766. /**
  767. * Determine if the field group should be exposed
  768. * to graphql
  769. */
  770. if ( ! $this->should_field_group_show_in_graphql( $field_group ) ) {
  771. return;
  772. }
  773.  
  774. /**
  775. * Get the fields in the group.
  776. */
  777. $acf_fields = ! empty( $field_group['sub_fields'] ) ? $field_group['sub_fields'] : acf_get_fields( $field_group );
  778.  
  779. /**
  780. * If there are no fields, bail
  781. */
  782. if ( empty( $acf_fields ) || ! is_array( $acf_fields ) ) {
  783. return;
  784. }
  785.  
  786. /**
  787. * Loop over the fields and register them to the Schema
  788. */
  789. foreach ( $acf_fields as $acf_field ) {
  790.  
  791. /**
  792. * Setup data for register_graphql_field
  793. */
  794. $name = ! empty( $acf_field['name'] ) ? self::camel_case( $acf_field['name'] ) : null;
  795. $show_in_graphql = isset( $acf_field['show_in_graphql'] ) ? (bool) $acf_field['show_in_graphql'] : true;
  796. $description = isset( $acf_field['instructions'] ) ? $acf_field['instructions'] : __( 'ACF Field added to the Schema by WPGraphQL ACF' );
  797.  
  798. /**
  799. * If the field is missing a name or a type,
  800. * we can't add it to the Schema.
  801. */
  802. if (
  803. empty( $name ) ||
  804. true != $show_in_graphql
  805. ) {
  806.  
  807. /**
  808. * Uncomment line below to determine what fields are not going to be output
  809. * in the Schema.
  810. */
  811. continue;
  812. }
  813.  
  814. $config = [
  815. 'name' => $name,
  816. 'description' => $description,
  817. 'acf_field' => $acf_field,
  818. 'acf_field_group' => $field_group,
  819. ];
  820.  
  821. $this->register_graphql_field( $type_name, $name, $config );
  822.  
  823. }
  824.  
  825. }
  826.  
  827. /**
  828. * Add field groups to Taxonomies
  829. *
  830. * @return void
  831. */
  832. protected function add_acf_fields_to_term_objects() {
  833.  
  834. /**
  835. * Get a list of taxonomies that have been registered to show in graphql
  836. */
  837. $graphql_taxonomies = \WPGraphQL::get_allowed_taxonomies();
  838.  
  839. /**
  840. * If there are no taxonomies exposed to GraphQL, bail
  841. */
  842. if ( empty( $graphql_taxonomies ) || ! is_array( $graphql_taxonomies ) ) {
  843. return;
  844. }
  845.  
  846. /**
  847. * Loop over the taxonomies exposed to GraphQL
  848. */
  849. foreach ( $graphql_taxonomies as $taxonomy ) {
  850.  
  851. /**
  852. * Get the field groups associated with the taxonomy
  853. */
  854. $field_groups = acf_get_field_groups(
  855. [
  856. 'taxonomy' => $taxonomy,
  857. ]
  858. );
  859.  
  860. /**
  861. * If there are no field groups for this taxonomy, move on to the next one.
  862. */
  863. if ( empty( $field_groups ) || ! is_array( $field_groups ) ) {
  864. continue;
  865. }
  866.  
  867. /**
  868. * Get the Taxonomy object
  869. */
  870. $tax_object = get_taxonomy( $taxonomy );
  871.  
  872. /**
  873. * Loop over the field groups for this post type
  874. */
  875. foreach ( $field_groups as $field_group ) {
  876.  
  877. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  878.  
  879. $field_group['type'] = 'group';
  880. $field_group['name'] = $field_name;
  881. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  882. $config = [
  883. 'name' => $field_name,
  884. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was assigned to the "%2$s" taxonomy', 'wp-graphql-acf' ), $field_group['title'], $tax_object->name ),
  885. 'acf_field' => $field_group,
  886. 'acf_field_group' => null,
  887. 'resolve' => function( $root ) use ( $field_group ) {
  888. return isset( $root ) ? $root : null;
  889. }
  890. ];
  891.  
  892. $this->register_graphql_field( $tax_object->graphql_single_name, $field_name, $config );
  893. }
  894. }
  895. }
  896.  
  897. /**
  898. * Add ACF Fields to comments
  899. *
  900. * @return void
  901. */
  902. protected function add_acf_fields_to_comments() {
  903.  
  904. $comment_field_groups = [];
  905.  
  906. /**
  907. * Get the field groups associated with the taxonomy
  908. */
  909. $field_groups = acf_get_field_groups();
  910.  
  911. foreach( $field_groups as $field_group ) {
  912. if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) {
  913. foreach ( $field_group['location'] as $locations ) {
  914. if ( ! empty( $locations ) && is_array( $locations ) ) {
  915. foreach ( $locations as $location ) {
  916. if ( 'comment' === $location['param'] && '!=' === $location['operator'] ) {
  917. continue;
  918. }
  919. if ( 'comment' === $location['param'] && '==' === $location['operator'] ) {
  920. $comment_field_groups[] = $field_group;
  921. }
  922. }
  923. }
  924. }
  925. }
  926. }
  927.  
  928. if ( empty( $comment_field_groups ) ) {
  929. return;
  930. }
  931.  
  932. /**
  933. * Loop over the field groups for this post type
  934. */
  935. foreach ( $comment_field_groups as $field_group ) {
  936.  
  937. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  938.  
  939. $field_group['type'] = 'group';
  940. $field_group['name'] = $field_name;
  941. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  942. $config = [
  943. 'name' => $field_name,
  944. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Comments', 'wp-graphql-acf' ), $field_group['title'] ),
  945. 'acf_field' => $field_group,
  946. 'acf_field_group' => null,
  947. 'resolve' => function( $root ) use ( $field_group ) {
  948. return isset( $root ) ? $root : null;
  949. }
  950. ];
  951.  
  952. $this->register_graphql_field( 'Comment', $field_name, $config );
  953.  
  954. }
  955.  
  956. }
  957.  
  958. /**
  959. * Add Fields to Menus in the GraphQL Schema
  960. *
  961. * @return void
  962. */
  963. protected function add_acf_fields_to_menus() {
  964.  
  965. $menu_field_groups = [];
  966.  
  967. /**
  968. * Get the field groups associated with the taxonomy
  969. */
  970. $field_groups = acf_get_field_groups();
  971.  
  972. foreach( $field_groups as $field_group ) {
  973. if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) {
  974. foreach ( $field_group['location'] as $locations ) {
  975. if ( ! empty( $locations ) && is_array( $locations ) ) {
  976. foreach ( $locations as $location ) {
  977. if ( 'nav_menu' === $location['param'] && '!=' === $location['operator'] ) {
  978. continue;
  979. }
  980. if ( 'nav_menu' === $location['param'] && '==' === $location['operator'] ) {
  981. $menu_field_groups[] = $field_group;
  982. break;
  983. }
  984. }
  985. }
  986. }
  987. }
  988. }
  989.  
  990. if ( empty( $menu_field_groups ) ) {
  991. return;
  992. }
  993.  
  994. /**
  995. * Loop over the field groups for this post type
  996. */
  997. foreach ( $menu_field_groups as $field_group ) {
  998.  
  999. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  1000.  
  1001. $field_group['type'] = 'group';
  1002. $field_group['name'] = $field_name;
  1003. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  1004. $config = [
  1005. 'name' => $field_name,
  1006. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Menus', 'wp-graphql-acf' ), $field_group['title'] ),
  1007. 'acf_field' => $field_group,
  1008. 'acf_field_group' => null,
  1009. 'resolve' => function( $root ) use ( $field_group ) {
  1010. return isset( $root ) ? $root : null;
  1011. }
  1012. ];
  1013.  
  1014. $this->register_graphql_field( 'Menu', $field_name, $config );
  1015.  
  1016. }
  1017.  
  1018. }
  1019.  
  1020. /**
  1021. * Add ACF Field Groups to Menu Items
  1022. *
  1023. * @return void
  1024. */
  1025. protected function add_acf_fields_to_menu_items() {
  1026.  
  1027. $menu_item_field_groups = [];
  1028.  
  1029. /**
  1030. * Get the field groups associated with the taxonomy
  1031. */
  1032. $field_groups = acf_get_field_groups();
  1033. foreach( $field_groups as $field_group ) {
  1034. if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) {
  1035. foreach ( $field_group['location'] as $locations ) {
  1036. if ( ! empty( $locations ) && is_array( $locations ) ) {
  1037. foreach ( $locations as $location ) {
  1038. if ( 'nav_menu_item' === $location['param'] && '!=' === $location['operator'] ) {
  1039. continue;
  1040. }
  1041. if ( 'nav_menu_item' === $location['param'] && '==' === $location['operator'] ) {
  1042. $menu_item_field_groups[] = $field_group;
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. }
  1049.  
  1050. if ( empty( $menu_item_field_groups ) ) {
  1051. return;
  1052. }
  1053.  
  1054. /**
  1055. * Loop over the field groups for this post type
  1056. */
  1057. foreach ( $menu_item_field_groups as $field_group ) {
  1058.  
  1059. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  1060.  
  1061. $field_group['type'] = 'group';
  1062. $field_group['name'] = $field_name;
  1063. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  1064. $config = [
  1065. 'name' => $field_name,
  1066. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Menu Items', 'wp-graphql-acf' ), $field_group['title'] ),
  1067. 'acf_field' => $field_group,
  1068. 'acf_field_group' => null,
  1069. 'resolve' => function( $root ) use ( $field_group ) {
  1070. return isset( $root ) ? $root : null;
  1071. }
  1072. ];
  1073.  
  1074. $this->register_graphql_field( 'MenuItem', $field_name, $config );
  1075.  
  1076. }
  1077. }
  1078.  
  1079. /**
  1080. * Add ACF Field Groups to Media Items (attachments)
  1081. *
  1082. * @return void
  1083. */
  1084. protected function add_acf_fields_to_media_items() {
  1085.  
  1086. $media_item_field_groups = [];
  1087.  
  1088. /**
  1089. * Get the field groups associated with the taxonomy
  1090. */
  1091. $field_groups = acf_get_field_groups();
  1092.  
  1093. foreach( $field_groups as $field_group ) {
  1094. if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) {
  1095. foreach ( $field_group['location'] as $locations ) {
  1096. if ( ! empty( $locations ) && is_array( $locations ) ) {
  1097. foreach ( $locations as $location ) {
  1098. if ( 'attachment' === $location['param'] && '!=' === $location['operator'] ) {
  1099. continue;
  1100. }
  1101. if ( 'attachment' === $location['param'] && '==' === $location['operator'] ) {
  1102. $media_item_field_groups[] = $field_group;
  1103. }
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109.  
  1110. if ( empty( $media_item_field_groups ) ) {
  1111. return;
  1112. }
  1113.  
  1114. /**
  1115. * Loop over the field groups for this post type
  1116. */
  1117. foreach ( $media_item_field_groups as $field_group ) {
  1118.  
  1119. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  1120.  
  1121. $field_group['type'] = 'group';
  1122. $field_group['name'] = $field_name;
  1123. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  1124. $config = [
  1125. 'name' => $field_name,
  1126. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to attachments', 'wp-graphql-acf' ), $field_group['title'] ),
  1127. 'acf_field' => $field_group,
  1128. 'acf_field_group' => null,
  1129. 'resolve' => function( $root ) use ( $field_group ) {
  1130. return isset( $root ) ? $root : null;
  1131. }
  1132. ];
  1133.  
  1134. $this->register_graphql_field( 'MediaItem', $field_name, $config );
  1135.  
  1136. }
  1137. }
  1138.  
  1139. protected function add_acf_fields_to_individual_posts() {
  1140.  
  1141. $post_field_groups = [];
  1142.  
  1143. /**
  1144. * Get the field groups associated with the taxonomy
  1145. */
  1146. $field_groups = acf_get_field_groups();
  1147.  
  1148. foreach( $field_groups as $field_group ) {
  1149. if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) {
  1150. foreach ( $field_group['location'] as $locations ) {
  1151. if ( ! empty( $locations ) && is_array( $locations ) ) {
  1152. foreach ( $locations as $location ) {
  1153.  
  1154. /**
  1155. * If the operator is not equal to, we don't need to do anything,
  1156. * so we can just continue
  1157. */
  1158. if ( '!=' === $location['operator'] ) {
  1159. continue;
  1160. }
  1161.  
  1162. /**
  1163. * If the param (the post_type) is in the array of allowed_post_types
  1164. */
  1165. if ( 'post' === $location['param'] && '==' === $location['operator'] ) {
  1166. $post_field_groups[] = [
  1167. 'field_group' => $field_group,
  1168. 'post_id' => $location['value']
  1169. ];
  1170. }
  1171. }
  1172. }
  1173. }
  1174. }
  1175. }
  1176.  
  1177. /**
  1178. * If no field groups are assigned to a specific post, we don't need to modify the Schema
  1179. */
  1180. if ( empty( $post_field_groups ) ) {
  1181. return;
  1182. }
  1183.  
  1184. $allowed_post_types = get_post_types([
  1185. 'show_ui' => true,
  1186. 'show_in_graphql' => true
  1187. ]);
  1188.  
  1189. /**
  1190. * Remove the `attachment` post_type, as it's treated special and we don't
  1191. * want to add field groups in the same way we do for other post types
  1192. */
  1193. unset( $allowed_post_types['attachment'] );
  1194.  
  1195. /**
  1196. * Loop over the field groups assigned to a specific post
  1197. * and register them to the Schema
  1198. */
  1199. foreach ( $post_field_groups as $key => $group ) {
  1200.  
  1201. if ( empty( $group['field_group'] ) || ! is_array( $group['field_group'] ) ) {
  1202. continue;
  1203. }
  1204.  
  1205. $post_object = get_post( (int) $group['post_id'] );
  1206.  
  1207. if ( ! $post_object instanceof \WP_Post ) {
  1208. continue;
  1209. }
  1210.  
  1211. $field_group = $group['field_group'];
  1212. $post_type = get_post_type_object( $post_object->post_type );
  1213. $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] );
  1214.  
  1215. $field_group['type'] = 'group';
  1216. $field_group['name'] = $field_name;
  1217. $description = $field_group['description'] ? $field_group['description'] . ' | ' : '';
  1218. $config = [
  1219. 'name' => $field_name,
  1220. 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was assigned to an individual post of the post_type: "%2$s". The group will be present in the Schema for the "%3$s" Type, but will only resolve if the entity has content saved.', 'wp-graphql-acf' ), $field_group['title'], $post_type->name, $post_type->graphql_plural_name ),
  1221. 'acf_field' => $field_group,
  1222. 'acf_field_group' => null,
  1223. 'resolve' => function( $root ) use ( $field_group ) {
  1224. return isset( $root ) ? $root : null;
  1225. }
  1226. ];
  1227.  
  1228. $this->register_graphql_field( $post_type->graphql_single_name, $field_name, $config );
  1229.  
  1230. }
  1231.  
  1232. }
  1233.  
  1234. protected function add_acf_fields_to_options_pages() {
  1235. // @todo: Coming soon
  1236. }
  1237.  
  1238. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement