advanced-query-loop icon indicating copy to clipboard operation
advanced-query-loop copied to clipboard

array_merge overwrites $query_args['tax_query'] after it's added in aql_query_vars filter

Open robistek opened this issue 1 year ago • 4 comments

When modifying the $query_args using the aql_query_vars filter, the array_merge in the query_loop_block_query_vars callback function overwrites the tax_query in $query_args. This causes issues when custom tax queries are added via filters, as demonstrated in the code below

function remove_pasta_recipes_from_aql_block( array $query_args, array $block_query, bool $inherited ): array {

        $query_args['tax_query'] = array(
            array(
                'taxonomy'         => 'recipe_category',
                'field'            => 'slug',
                'terms'            => 'pasta',
                'operator'         => 'NOT IN',
                'include_children' => false,
            ),
        );

    return $query_args;
}
add_filter( 'aql_query_vars', 'remove_pasta_recipes_from_aql_block', 10, 3 );

The line for reference: https://github.com/ryanwelcher/advanced-query-loop/blob/17bf11c08bbc3756382022fc64c3c593a18b2ce8/includes/query-loop.php#L77

$default_query contains taxonomy filters set via 'Filters' in the 'Block' settings/tab.

In this example the array key tax_query replaces the previous one when array_merge is used, losing any previous taxonomies defined in $default_query.

A possible fix could be to use array_merge_recursive instead of array_merge

robistek avatar Sep 06 '24 12:09 robistek

Thanks for opening this issues. I'll look at it and get some unit tests wrapped around this.

ryanwelcher avatar Oct 07 '24 17:10 ryanwelcher

@robistek looking at your code again, it looks like you are actually replacing the key in the hook by assigning tax_query to an array rather than adding to it.

You should check to see that it's defined first but the code below is going to add to the existing tax_query array.

function remove_pasta_recipes_from_aql_block( array $query_args, array $block_query, bool $inherited ): array {

        $query_args['tax_query'][] = 
            array(
                'taxonomy'         => 'recipe_category',
                'field'            => 'slug',
                'terms'            => 'pasta',
                'operator'         => 'NOT IN',
                'include_children' => false,
            );

    return $query_args;
}
add_filter( 'aql_query_vars', 'remove_pasta_recipes_from_aql_block', 10, 3 );

ryanwelcher avatar Oct 07 '24 18:10 ryanwelcher

Thanks for the reply and the patience, @ryanwelcher.

I have modified the code, tested it and written a more complete example.

I have updated my filter function to make it more robust - thanks for your input:

function remove_pasta_recipes_from_aql_block( array $query_args, array $block_query, bool $inherited ): array {

    if ( ! $inherited ) {
		$additional_query_args =  array(
                      'taxonomy'         => 'recipe_category',
                      'field'            => 'slug',
                      'terms'            => 'pasta',
                      'operator'         => 'NOT IN',
                      'include_children' => false,
                  );

		// Exclude posts where the taxonomy 'recipe_category' has the slug 'pasta'.
		if ( isset( $query_args['tax_query'] ) && is_array( $query_args['tax_query'] ) ) {
			$query_args['tax_query'][] = $additional_query_args;
		} else {
			$query_args['tax_query'] = array(
				$additional_query_args,
			);
		}
    }

    return $query_args;
}
add_filter( 'aql_query_vars', 'remove_pasta_recipes_from_aql_block', 10, 3 );

When this filter returns $query_args in query-loop.php, $default_query and $filtered_query_args are merged.

During debugging I found out that even if $default_query has a 'tax_query' key is doesn't mean that $query_args (which is passed to the aql_query_vars filter) has a 'tax_query' key.

$default_query = array (
  'post_type' => 'recipe',
  'order' => 'DESC',
  'orderby' => 'date',
  'tax_query' => 
  array (
    0 => 
    array (
      'taxonomy' => 'recipe_ingredient',
      'terms' => 
      array (
        0 => 123,
      ),
      'include_children' => false,
    ),
  ),
  'offset' => 0,
  'posts_per_page' => 4,
  'author__in' => 
  array (
  ),
);

$filtered_query_args = array (
  'post__not_in' => 
  array (
    0 => 1000,
  ),
  'tax_query' => 
  array (
    0 => 
    array (
      'taxonomy' => 'recipe_category',
      'field' => 'slug',
      'terms' => 'pasta',
      'operator' => 'NOT IN',
      'include_children' => false,
    ),
  ),
);

The result of print_r( array_merge( $default_query, $filtered_query_args ) ) will be an array with the key 'recipe_ingredient' missing:

Array
(
    [post_type] => sd_recipe
    [order] => DESC
    [orderby] => date
    [tax_query] => Array
        (
            [0] => Array
                (
                    [taxonomy] => recipe_category
                    [field] => slug
                    [terms] => pasta
                    [operator] => NOT IN
                    [include_children] => 
                )

        )

    [offset] => 0
    [posts_per_page] => 4
    [author__in] => Array
        (
        )

    [post__not_in] => Array
        (
            [0] => 1000
        )

)

Am I using the aql_query_vars filter as intended? Should I be using the $block_query parameter as a base to build a the query array before it is returned?

robistek avatar Nov 19 '24 17:11 robistek

Thank you for the detailed follow up on this. I think I am now seeing the issue. I am in the process of adding some unit tests for this that should provide better insights. Thanks for your patience!

ryanwelcher avatar Mar 12 '25 15:03 ryanwelcher