Skip to content

Instantly share code, notes, and snippets.

@jasonbahl
Last active July 10, 2024 20:41
Show Gist options
  • Save jasonbahl/da87dbccb58f1323a324a9b3e8952d6c to your computer and use it in GitHub Desktop.
Save jasonbahl/da87dbccb58f1323a324a9b3e8952d6c to your computer and use it in GitHub Desktop.
Shows how to add a custom order value to a connection to order by a custom field.
add_filter( 'graphql_PostObjectsConnectionOrderbyEnum_values', function( $values ) {
$values['LIKE_COUNT'] = [
'value' => 'like_count',
'description' => __( 'The number of likes on the post', 'wp-graphql' ),
];
return $values;
} );
add_filter( 'graphql_post_object_connection_query_args', function( $query_args, $source, $input ) {
if ( isset( $input['where']['orderby'] ) && is_array( $input['where']['orderby'] ) ) {
foreach( $input['where']['orderby'] as $orderby ) {
if ( ! isset( $orderby['field'] ) || 'like_count' !== $orderby['field'] ) {
continue;
}
$query_args['meta_type'] = 'NUMERIC';
$query_args['meta_key'] = 'like_count';
$query_args['orderby']['meta_value_num'] = $orderby['order'];
}
}
return $query_args;
}, 10, 3);
@pmventura
Copy link

Hi @jasonbahl,

Thanks for sharing the gist. Beginner here... I wonder how I can add this to my WordPress site. I have tried to add the snippet above to my functions.php (wp-content/themes/twentytwenty/functions.php) but the orderBy seems not working...

Here's how I do the graphql request

query MyQuery {
  productCategories(where: {name: ["iPhone", "iPod", "Apple Watch"], orderby: {field: CATEGORY_ORDER  order: DESC}}) {
    edges {
      node {
        name
        order {
          categoryOrder
        }
      }
    }
  }
}

Btw, the category_order field was created from ACF and I'm trying to sort the product categories based on that custom field

Thanks in advance for your help. 🙏

@hereisfahad
Copy link

Hi @jasonbahl,

I have used the above code to sort posts based on view count but it's not working, please let me know if I am missing someting.

order by view count

add_filter( 'graphql_PostObjectsConnectionOrderbyEnum_values', function( $values ) {

	$values['VIEW_COUNT'] = [
		'value' => 'view_count',
		'description' => __( 'The number of views on the post', 'wp-graphql' ),
	];

	return $values;

} );

add_filter( 'graphql_post_object_connection_query_args', function( $query_args, $source, $input ) {
	if ( isset( $input['where']['orderby'] ) && is_array( $input['where']['orderby'] ) ) {

		foreach( $input['where']['orderby'] as $orderby ) {

			if ( ! isset( $orderby['field'] ) || 'view_count' !== $orderby['field'] ) {
				continue;
			}

			$query_args['meta_key'] = 'wp_post_view_count';
			$query_args['orderby'] = 'meta_value_num';
			$query_args['order'] = $orderby['order'];

		}

	}

	return $query_args;

}, 10, 3);

mutate view count

add_action( 'graphql_register_types', function() {
	register_graphql_field( 'Post', 'viewCount', [
		'type' => 'Int',
		'description' => __( 'The number of views for the post', 'your-textdomain' ),
		'resolve' => function( $post ) {
			$views = get_post_meta( $post->ID, 'views', true );
			return isset( $views ) ? (int) $views : 0;
		}
	] );

	register_graphql_mutation( 'viewPost', [
		'inputFields' => [
			'id' => [
				'type' => [
					'non_null' => 'ID'
				],
				'description' => __( 'ID of the post to increase view count', 'your-textdomain' ),
			],
		],
		'outputFields' => [
			'post' => [
				'type' => 'Post',
				'description' => __( 'The post that was viewed', 'your-textdomain' ),
			],
		],
		'mutateAndGetPayload' => function( $input ) {

			$id = null;

			if ( absint( $input['id'] ) ) {
				$id = absint( $input['id'] );
			} else {
				$id_parts = \GraphQLRelay\Relay::fromGlobalId( $input['id'] );
				if ( ! empty( $id_parts['id'] ) && absint( $id_parts['id'] ) ) {
					$id = absint( $id_parts['id'] );
				}
			}

			/**
			 * If the ID is invalid or the post object doesn't exist, throw an error
			 */
			if ( empty( $id ) || false == $post = get_post( absint( $id ) ) ) {
				throw new \GraphQL\Error\UserError( __( 'The ID entered is invalid' ) );
			}

			/**
			 * If you wanted to only allow certain users to "like" a post, you could do
			 * some capability checks here too, or whatever validation you want to apply. . .
			 */

			$current_views = (int) get_post_meta( $post->ID, 'views', true );
			update_post_meta( $post->ID, 'views', absint( $current_views + 1 ) );

			return [
				'post' => $post
			];

		}
	] );
} );

mutation works fine, but order by returning an empty array.

@pmventura
Copy link

Hi @hereisfahad, looks like your first comment has been removed.

Anyway didn't work for me either. For a workaround, I have added another field to specify ordering and handled the sorting myself in the front end.

@jeremyProwseYS
Copy link

jeremyProwseYS commented Aug 1, 2022

If you're after the code in a "plugin" format that also allows you to specify in the request the "meta field" you'd like to order by, try the following:

<?php
/*
Plugin Name: WP GraphQL Meta Field Ordering
Description: Adds functionality to order by a meta field as specified by the user.
Version:     1.0.0
*/

defined('ABSPATH') or die('Nope, not accessing this');

class WPGraphQLOrderByMetaField
{
    public function __construct()
    {
        add_filter('graphql_PostObjectsConnectionOrderbyEnum_values', [$this, 'register_meta_key_option']);
        add_filter('graphql_TermObjectsConnectionOrderbyEnum_values', [$this, 'register_meta_key_option']);

        add_filter('graphql_PostObjectsConnectionOrderbyInput_fields', [$this, 'register_meta_key_field']);
        add_filter('graphql_TermObjectsConnectionOrderbyInput_fields', [$this, 'register_meta_key_field']);

        add_filter('graphql_post_object_connection_query_args', [$this, 'graphql_orderby_meta'], 10, 3);
        add_filter('graphql_term_object_connection_query_args', [$this, 'graphql_orderby_meta'], 10, 3);
    }

    public function register_meta_key_option($values)
    {
        $values['META_KEY'] = ['value' => 'META_KEY', 'description' => __('Order posts by the meta value "order"', 'wp-graphql') , ];
        return $values;
    }

    public function register_meta_key_field($fields)
    {
        $fields['metaKeyField'] = ['type' => 'String', 'description' => __('Array of names to return term(s) for. Default empty.', 'wp-graphql') , ];
        return $fields;
    }

    public function graphql_orderby_meta($query_args, $source, $input)
    {
        if (isset($input['where']['orderby']) && is_array($input['where']['orderby']))
        {
            foreach ($input['where']['orderby'] as $orderby)
            {
                if (!isset($orderby['field']) || 'META_KEY' !== $orderby['field'] || !isset($orderby['metaKeyField']))
                {
                    continue;
                }

                $query_args['meta_key'] = $orderby['metaKeyField'];

                // ////////////////////////////////////////////////////////
                // Explanation for the below order values:
                // ////////////////////////////////////////////////////////
                //
                // Order is attached to the "META_KEY" value as defined on line 25 of this file.
                // This needs to be corrected to the value that has been already been processed by WP-Graphql
                // otherwise pagination will not work correctly as the value for the direction of pagination
                // is calculated here: wp-graphql/src/Data/Connection/PostObjectConnectionResolver.php | Lines 323-333
                //
                // See docs on order and orderby here for more info on how this is handled in wp queries:
                // https://developer.wordpress.org/reference/classes/wp_query/#order-orderby-parameters
                $query_args['order'] = isset($query_args['orderby']['META_KEY']) ? $query_args['orderby']['META_KEY'] : 'DESC';

                // We run this last which overwrites the processed value for 'orderby' with
                // the required value for meta ordering as per the above Wordpress docs.
                $query_args['orderby'] = 'meta_value';
            }
        }
        return $query_args;
    }
}

new WPGraphQLOrderByMetaField();

~ Updated (02 Aug 2022)

And the query would look something like this:

query GetAllEvents {
  events(
    where: {
      orderby: {
        order: DESC,
        field: META_KEY,
        metaKeyField: "event_date" // Or your custom field to order by
      }
    }
  ) {
    nodes {
      title
      slug
      event_date
    }
  }
}

Hope this helps.

@therealgilles
Copy link

@jeremyProwseYS I am trying to make this work but I am getting the following errors:

{
  "errors": [
    {
      "message": "Field \"posts\" argument \"where\" requires type PostObjectsConnectionOrderbyEnum, found META_KEY.",
      "extensions": {
        "category": "graphql"
      },
      "locations": [
        {
          "line": 3,
          "column": 30
        }
      ]
    },
    {
      "message": "Field \"metaKeyField\" is not defined by type PostObjectsConnectionOrderbyInput.",
      "extensions": {
        "category": "graphql"
      },
      "locations": [
        {
          "line": 3,
          "column": 52
        }
      ]
    },

@therealgilles
Copy link

Oh never mind, I was doing an unauthenticated request when my changes were only for an authenticated user.

@jeremyProwseYS
Copy link

All good, glad you got it sorted. I was running a couple of tests trying to replicate the issue with no success, so was scratching my head for a bit there!

@therealgilles
Copy link

Sorry for the false alarm :/ And thank you for posting your solution :) I'm trying to get the Events Calendar events properly sorted by event start dates, which is stored into a _EventStartDate meta field and after much research, I believe this is exactly what I needed!

@juanu96
Copy link

juanu96 commented Dec 12, 2022

If you're after the code in a "plugin" format that also allows you to specify in the request the "meta field" you'd like to order by, try the following:

<?php
/*
Plugin Name: WP GraphQL Meta Field Ordering
Description: Adds functionality to order by a meta field as specified by the user.
Version:     1.0.0
*/

defined('ABSPATH') or die('Nope, not accessing this');

class WPGraphQLOrderByMetaField
{
    public function __construct()
    {
        add_filter('graphql_PostObjectsConnectionOrderbyEnum_values', [$this, 'register_meta_key_option']);
        add_filter('graphql_TermObjectsConnectionOrderbyEnum_values', [$this, 'register_meta_key_option']);

        add_filter('graphql_PostObjectsConnectionOrderbyInput_fields', [$this, 'register_meta_key_field']);
        add_filter('graphql_TermObjectsConnectionOrderbyInput_fields', [$this, 'register_meta_key_field']);

        add_filter('graphql_post_object_connection_query_args', [$this, 'graphql_orderby_meta'], 10, 3);
        add_filter('graphql_term_object_connection_query_args', [$this, 'graphql_orderby_meta'], 10, 3);
    }

    public function register_meta_key_option($values)
    {
        $values['META_KEY'] = ['value' => 'META_KEY', 'description' => __('Order posts by the meta value "order"', 'wp-graphql') , ];
        return $values;
    }

    public function register_meta_key_field($fields)
    {
        $fields['metaKeyField'] = ['type' => 'String', 'description' => __('Array of names to return term(s) for. Default empty.', 'wp-graphql') , ];
        return $fields;
    }

    public function graphql_orderby_meta($query_args, $source, $input)
    {
        if (isset($input['where']['orderby']) && is_array($input['where']['orderby']))
        {
            foreach ($input['where']['orderby'] as $orderby)
            {
                if (!isset($orderby['field']) || 'META_KEY' !== $orderby['field'] || !isset($orderby['metaKeyField']))
                {
                    continue;
                }

                $query_args['meta_key'] = $orderby['metaKeyField'];

                // ////////////////////////////////////////////////////////
                // Explanation for the below order values:
                // ////////////////////////////////////////////////////////
                //
                // Order is attached to the "META_KEY" value as defined on line 25 of this file.
                // This needs to be corrected to the value that has been already been processed by WP-Graphql
                // otherwise pagination will not work correctly as the value for the direction of pagination
                // is calculated here: wp-graphql/src/Data/Connection/PostObjectConnectionResolver.php | Lines 323-333
                //
                // See docs on order and orderby here for more info on how this is handled in wp queries:
                // https://developer.wordpress.org/reference/classes/wp_query/#order-orderby-parameters
                $query_args['order'] = isset($query_args['orderby']['META_KEY']) ? $query_args['orderby']['META_KEY'] : 'DESC';

                // We run this last which overwrites the processed value for 'orderby' with
                // the required value for meta ordering as per the above Wordpress docs.
                $query_args['orderby'] = 'meta_value';
            }
        }
        return $query_args;
    }
}

new WPGraphQLOrderByMetaField();

~ Updated (02 Aug 2022)

And the query would look something like this:

query GetAllEvents {
  events(
    where: {
      orderby: {
        order: DESC,
        field: META_KEY,
        metaKeyField: "event_date" // Or your custom field to order by
      }
    }
  ) {
    nodes {
      title
      slug
      event_date
    }
  }
}

Hope this helps.

Hi, this answer is amazing, but it only works for the first element when it comes to numbers and sorts only the first numeric value, the rest of the digits are ignored.

I used it and I ordered the prices in this way

    "listings": {
      "nodes": [
        {
          "title": "Propiedad 9",
          "listing": {
            "price": 1
          }
        },
        {
          "title": "Propiedad 4",
          "listing": {
            "price": 10
          }
        },
        {
          "title": "Propiedad 6",
          "listing": {
            "price": 100
          }
        },
        {
          "title": "Propiedad 5",
          "listing": {
            "price": 10000
          }
        },
        {
          "title": "Propiedad 8",
          "listing": {
            "price": 3223
          }
        },
        {
          "title": "propiedad 2",
          "listing": {
            "price": 38888
          }
        },
        {
          "title": "Propiedad 10",
          "listing": {
            "price": 4123
          }
        },
        {
          "title": "Propiedad 3",
          "listing": {
            "price": 87
          }
        },
        {
          "title": "escazu",
          "listing": {
            "price": 9000
          }
        },
        {
          "title": "Propiedad 7",
          "listing": {
            "price": 999999
          }
        }
      ]
    }
  },```

@jasonbahl
Copy link
Author

@juanu96 I believe if the field is storing numbers, you need:

  $query_args['orderby'] = 'meta_value_num';

instead of:

$query_args['orderby'] = 'meta_value';

@juanu96
Copy link

juanu96 commented Dec 12, 2022

@juanu96 I believe if the field is storing numbers, you need:

  $query_args['orderby'] = 'meta_value_num';

instead of:

$query_args['orderby'] = 'meta_value';

yes, this worked for me, thank you very much for your help

@ndigenpcc
Copy link

Hi all,

I've used this solution in my own code.

add_filter( 'graphql_PostObjectsConnectionOrderbyEnum_values', function( $values ) {

    $values['PARTNER_TIER'] = [
	    'value' => 'partner_tier',
	    'description' => __( 'Tier Level of the partner'),
    ];

    return $values;} 
);


add_filter( 'graphql_post_object_connection_query_args', function( $query_args, $source, $input ) {

    if ( isset( $input['where']['orderby'] ) && is_array( $input['where']['orderby'] ) ) {

	    foreach( $input['where']['orderby'] as $orderby ) {

		    if ( ! isset( $orderby['field'] ) || 'partner_tier' !== $orderby['field'] ) {
		    	continue;
		    }

		    $query_args['meta_type'] = 'NUMERIC';
		    $query_args['meta_key'] = 'partner_tier';
		    $query_args['orderby']['meta_value_num'] = $orderby['order'];

	    }

    }

    return $query_args;

}, 10, 3);

And while the initial call works, the cursor pagination is not working, and returning and empty set of partners when calling the next page.

Here's the working initial query.
correctQuery

And here's the query calling the next page
errorQuery

Was wondering if anyone else here had a similar issue or if there's something I am doing incorrectly

@mickras
Copy link

mickras commented Jun 13, 2023

Was wondering if anyone else here had a similar issue or if there's something I am doing incorrectly

@ndigenpcc Did you ever find a solution to your problem? I'm facing something similar, and can't seem to find the bug.

@ndigenpcc
Copy link

@mickras I never ended up finding a solution, so I had moved back to the Wordpress REST API and created my own search filters

@8ctopotamus
Copy link

Thanks for posting this example, @jasonbahl ! It worked flawlessly for my use case (ordering posts by a Number ACF field VIEW_COUNT).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment