Skip to content

Commit e65fbc8

Browse files
committed
dev: add graphql_pre_resolve_menu_item_connected_node filter
1 parent afe6a52 commit e65fbc8

File tree

2 files changed

+158
-8
lines changed

2 files changed

+158
-8
lines changed

src/Type/ObjectType/MenuItem.php

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,27 @@ public static function register_type() {
3636
$object_id = (int) get_post_meta( $menu_item->databaseId, '_menu_item_object_id', true );
3737
$object_type = get_post_meta( $menu_item->databaseId, '_menu_item_type', true );
3838

39-
$resolver = null;
39+
/**
40+
* When this filter returns anything other than null it will be used as the resolved connection for the menu item's connected node, short-circuiting the default resolution.
41+
*
42+
* This is useful since we often add taxonomy terms to menus but would prefer to represent the menu item in other ways.
43+
* E.g., a linked post object (or vice-versa).
44+
*
45+
* @param ?\GraphQL\Deferred $deferred_connection The AbstractConnectionResolver's connection, or null to continue with the default resolution.
46+
* @param \WPGraphQL\Model\MenuItem $menu_item The MenuItem model.
47+
* @param array<string,mixed> $args The GraphQL args for the connection.
48+
* @param \WPGraphQL\AppContext $context The AppContext object.
49+
* @param \GraphQL\Type\Definition\ResolveInfo $info The ResolveInfo object.
50+
* @param int $object_id The ID of the connected object.
51+
* @param string $object_type The type of the connected object.
52+
*/
53+
$deferred_connection = apply_filters( 'graphql_pre_resolve_menu_item_connected_node', null, $menu_item, $args, $context, $info, $object_id, $object_type );
54+
55+
if ( null !== $deferred_connection ) {
56+
return $deferred_connection;
57+
}
58+
59+
// Handle the default resolution.
4060
switch ( $object_type ) {
4161
// Post object
4262
case 'post_type':
@@ -53,6 +73,7 @@ public static function register_type() {
5373
$resolver->set_query_arg( 'include', $object_id );
5474
break;
5575
default:
76+
$resolver = null;
5677
break;
5778
}
5879

@@ -180,14 +201,19 @@ public static function register_type() {
180201
*
181202
* @since 0.0.30
182203
*/
183-
return apply_filters(
204+
return apply_filters_deprecated(
184205
'graphql_resolve_menu_item',
185-
$resolved_object,
186-
$args,
187-
$context,
188-
$info,
189-
$object_id,
190-
$object_type
206+
[
207+
$resolved_object,
208+
$args,
209+
$context,
210+
$info,
211+
$object_id,
212+
$object_type,
213+
],
214+
'@todo',
215+
'graphql_pre_resolve_menu_item_connected_node',
216+
__( 'Use the `graphql_pre_resolve_menu_item_connected_node` filter on `connectedNode` instead.', 'wp-graphql' )
191217
);
192218
},
193219
],

tests/wpunit/MenuItemQueriesTest.php

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,125 @@ public function testMenuItemQueryWithPostObject() {
100100
$this->assertEquals( $menu_item_relay_id, $actual['data']['menuItem']['id'] );
101101
}
102102

103+
public function testMenuItemQueryWithTermObject() {
104+
$term_id = $this->factory()->term->create( [ 'taxonomy' => 'category' ] );
105+
$permalink = get_term_link( $term_id );
106+
107+
$menu_args = [
108+
'menu-item-attr-title' => 'Menu item',
109+
'menu-item-classes' => 'my-class my-other-class',
110+
'menu-item-description' => 'Some description',
111+
'menu-item-object-id' => $term_id,
112+
'menu-item-object' => 'category',
113+
'menu-item-position' => 1,
114+
'menu-item-status' => 'publish',
115+
'menu-item-title' => 'Menu item',
116+
'menu-item-type' => 'taxonomy',
117+
'menu-item-target' => '_blank',
118+
];
119+
120+
$menu_item_id = wp_update_nav_menu_item( $this->menu_id, 0, $menu_args );
121+
122+
$menu_item_relay_id = Relay::toGlobalId( 'post', $menu_item_id );
123+
124+
codecept_debug( get_theme_mod( 'nav_menu_locations' ) );
125+
126+
// test with database ID.
127+
$query = $this->get_query();
128+
129+
$variables = [
130+
'id' => $menu_item_id,
131+
'idType' => 'DATABASE_ID',
132+
];
133+
134+
$actual = $this->graphql( compact( 'query', 'variables' ) );
135+
136+
$this->assertEquals( explode( ' ', $menu_args['menu-item-classes'] ), $actual['data']['menuItem']['cssClasses'] );
137+
$this->assertEquals( $term_id, $actual['data']['menuItem']['connectedNode']['node']['databaseId'] );
138+
$this->assertEquals( $menu_item_id, $actual['data']['menuItem']['databaseId'] );
139+
$this->assertEquals( $menu_args['menu-item-description'], $actual['data']['menuItem']['description'] );
140+
$this->assertEquals( $menu_item_relay_id, $actual['data']['menuItem']['id'] );
141+
$this->assertEquals( $menu_args['menu-item-title'], $actual['data']['menuItem']['label'] );
142+
$this->assertEquals( [ \WPGraphQL\Type\WPEnumType::get_safe_name( $this->location_name ) ], $actual['data']['menuItem']['locations'] );
143+
$this->assertEquals( [ \WPGraphQL\Type\WPEnumType::get_safe_name( $this->location_name ) ], $actual['data']['menuItem']['menu']['node']['locations'] );
144+
$this->assertEquals( $this->menu_slug, $actual['data']['menuItem']['menu']['node']['slug'] );
145+
$this->assertEquals( $menu_args['menu-item-position'], $actual['data']['menuItem']['order'] );
146+
$this->assertEquals( $menu_args['menu-item-target'], $actual['data']['menuItem']['target'] );
147+
$this->assertEquals( $menu_args['menu-item-attr-title'], $actual['data']['menuItem']['title'] );
148+
$this->assertEquals( str_ireplace( home_url(), '', $permalink ), $actual['data']['menuItem']['uri'] );
149+
$this->assertEquals( $permalink, $actual['data']['menuItem']['url'] );
150+
151+
// Test with relay Id.
152+
$variables = [
153+
'id' => $menu_item_relay_id,
154+
'idType' => 'ID',
155+
];
156+
157+
$actual = $this->graphql( compact( 'query', 'variables' ) );
158+
159+
$this->assertEquals( $menu_item_id, $actual['data']['menuItem']['databaseId'] );
160+
$this->assertEquals( $menu_item_relay_id, $actual['data']['menuItem']['id'] );
161+
}
162+
163+
public function testMenuItemWithCustomFilters() {
164+
$term_id = $this->factory()->term->create( [ 'taxonomy' => 'category' ] );
165+
$post_id = $this->factory()->post->create(); // We'll resolve to this.
166+
$permalink = get_term_link( $term_id ); // The menu permalink stays the same.
167+
168+
$menu_args = [
169+
'menu-item-attr-title' => 'Menu item',
170+
'menu-item-classes' => 'my-class my-other-class',
171+
'menu-item-description' => 'Some description',
172+
'menu-item-object-id' => $term_id,
173+
'menu-item-object' => 'category',
174+
'menu-item-position' => 1,
175+
'menu-item-status' => 'publish',
176+
'menu-item-title' => 'Menu item',
177+
'menu-item-type' => 'taxonomy',
178+
'menu-item-target' => '_blank',
179+
];
180+
181+
$menu_item_id = wp_update_nav_menu_item( $this->menu_id, 0, $menu_args );
182+
183+
$menu_item_relay_id = Relay::toGlobalId( 'post', $menu_item_id );
184+
185+
// Filter the resolver.
186+
add_filter( 'graphql_pre_resolve_menu_item_connected_node', static function ( $deferred_connection, $source, $args, $context, $info ) use ( $post_id ) {
187+
$resolver = new \WPGraphQL\Data\Connection\PostObjectConnectionResolver( $source, [], $context, $info, 'any' );
188+
$resolver->set_query_arg( 'p', $post_id );
189+
190+
return $resolver->one_to_one()->get_connection();
191+
}, 10, 7 );
192+
193+
// test with database ID.
194+
$query = $this->get_query();
195+
196+
$variables = [
197+
'id' => $menu_item_id,
198+
'idType' => 'DATABASE_ID',
199+
];
200+
201+
$actual = $this->graphql( compact( 'query', 'variables' ) );
202+
203+
$this->assertEquals( explode( ' ', $menu_args['menu-item-classes'] ), $actual['data']['menuItem']['cssClasses'] );
204+
$this->assertEquals( $post_id, $actual['data']['menuItem']['connectedNode']['node']['databaseId'] );
205+
$this->assertEquals( $menu_item_id, $actual['data']['menuItem']['databaseId'] );
206+
$this->assertEquals( $menu_args['menu-item-description'], $actual['data']['menuItem']['description'] );
207+
$this->assertEquals( $menu_item_relay_id, $actual['data']['menuItem']['id'] );
208+
$this->assertEquals( $menu_args['menu-item-title'], $actual['data']['menuItem']['label'] );
209+
$this->assertEquals( [ \WPGraphQL\Type\WPEnumType::get_safe_name( $this->location_name ) ], $actual['data']['menuItem']['locations'] );
210+
$this->assertEquals( [ \WPGraphQL\Type\WPEnumType::get_safe_name( $this->location_name ) ], $actual['data']['menuItem']['menu']['node']['locations'] );
211+
$this->assertEquals( $this->menu_slug, $actual['data']['menuItem']['menu']['node']['slug'] );
212+
$this->assertEquals( $menu_args['menu-item-position'], $actual['data']['menuItem']['order'] );
213+
$this->assertEquals( $menu_args['menu-item-target'], $actual['data']['menuItem']['target'] );
214+
$this->assertEquals( $menu_args['menu-item-attr-title'], $actual['data']['menuItem']['title'] );
215+
$this->assertEquals( str_ireplace( home_url(), '', $permalink ), $actual['data']['menuItem']['uri'] );
216+
$this->assertEquals( $permalink, $actual['data']['menuItem']['url'] );
217+
218+
// Cleanup
219+
remove_all_filters( 'graphql_pre_resolve_menu_item_connected_node' );
220+
}
221+
103222
public function testCustomMenuItemWithChildren() {
104223
$parent_args = [
105224
'menu-item-title' => 'Parent Item',
@@ -152,10 +271,15 @@ public function get_query() {
152271
}
153272
connectedNode {
154273
node {
274+
__typename
155275
... on Post {
156276
id
157277
databaseId
158278
}
279+
... on TermNode {
280+
id
281+
databaseId
282+
}
159283
}
160284
}
161285
cssClasses

0 commit comments

Comments
 (0)