Home » Php » Implementing filter of nested arrays in PHP/WordPress

Implementing filter of nested arrays in PHP/WordPress

Posted by: admin February 25, 2020 Leave a comment

Questions:

I have in the process of moving some code from the front-end (in JavaScript) to the server-side (which is PHP) where it will be filtered and sent out in an API call, and I can’t seem to get the filter working properly on the back-end. The code takes an array of objects and filters it for the objects where a certain nested field (which is also an array of objects) contains certain values. The basic shape of the API:

{
  "id": 1217,
  "name": "Best product ever",
  "tags": [
      {
        "id": 125,
        "name": "Important Value",
        "slug": "important-value"
      },
      {
        "id": 157,
        "name": "Value",
        "slug": "value"
      },
      {
    "id": 180,
    "name": "Value",
    "slug": "value"
  },
  {
    "id": 126,
    "name": "Value",
    "slug": "value"
  },
  {
    "id": 206,
    "name": "Other Important Value",
    "slug": "other-important-value"
  }
}

The working JS code:

let productAttributes = ['important-value', 'value', 'value', 'value', 'other-important-value'];

filterResults(results) {
let filteredResults = results.filter(product => {
  return product.tags.find(tag => {
    return tag.slug === this.productAttributes[0];
  });
});

if (this.productAttributes[0] !== 'certain important value') {
  filteredResults = filteredResults.filter(product => {
    return product.tags.find(tag => {
      return tag.slug === this.productAttributes[4];
    });
  });
}

return filteredResults;
}

And the (not yet working) PHP code:

function get_awesome_products() {
 $baseRequest = 'https://myawesomeapi/wp-json/wc/v3/products/? 
 consumer_key=xxxx&consumer_secret=xxxx&per_page=100&page=';
 for ($count = 1; $count <= 9; $count++ ) {
$request = wp_remote_get( $baseRequest . (string)$count);
$body = wp_remote_retrieve_body( $request );
$data = array_values( json_decode( $body, true ));

  if ($count < 2) {
    $completeProductList = $data;
  } else {
    $completeProductList = array_merge($completeProductList, $data);
  }
}
// The code above this comment is doing what I expect, the code below is not.


$filteredProducts = null;
foreach ($completeProductList as &$product) {
  $tagArray = $product['tags'];
  if (in_array($reg_test_array[0], $tagArray, true) && 
  in_array($reg_test_array[4], $tagArray, true)) 
  {
    array_push($filteredProducts, $product);
  }

  unset($product);
  return new WP_REST_Response($filteredProducts, 200);

The impression I get is that I need to write a custom function to take the place of Array.prototype.find(), but I’m not strong in PHP and am having trouble wrapping my head around it.

EDIT: Edited to add example of object being filtered and additional PHP code

How to&Answers:

You could also use the PHP equivalent function array_filter (among a few other array-specific functions) for this task.

Example:

// Your 0 and 4 index values from $reg_test_array
$importantTags = [ "important-value", "other-important-value" ];

$filteredProducts = array_filter($completeProductList, function($product) use ($importantTags) {
  return (bool)array_intersect($importantTags, array_column($product['tags'], 'slug'));
});

return new WP_REST_Response($filteredProducts , 200);

Sandbox

Answer:

This should be equivalent to the JavaScript code you posted, but done without looping through the filtered results twice.

Without knowing the context of important-value and other-important-value, and how they come to be ordered in the $attributes array, it’s a little difficult to improve upon the conditional checks used. What I’ve written thus far however feels like a code smell to me, because it’s reliant hard coded values.

function filterResults(array $results, array $attributes)
{
    return array_reduce($results, function ($filteredResults, $result) use ($attributes) {
        // Extract tag slugs from result
        $tagSlugs = array_column($result['tags'], 'slug');

        // Append result to filtered results where first attribute exists in tag slugs; 
        // Or first attribute is not *other-important-value* and fourth attribute exists in tag slugs
        if (in_array($attribute[0], $tagSlugs) && ($attribute[0] === 'other-important-value' || in_array($attribute[4], $tagSlugs))) {
            $filteredResults[] = $result;
        }

        return $filteredResults;
    }, []);
}