6

I want to construct this url using http_build_query:

https://www.googleapis.com/freebase/v1/topic/m/0d6lp?filter=/common/topic/notable_for&filter=/common/topic/alias

Please note that the "filter=" parameter comes twice in the url, to specify two filters.

I tried to do it this way but having a problem:

$service_url = 'https://www.googleapis.com/freebase/v1/topic';
$mid = '/m/0d6lp';
$params = array('filter' => '/common/topic/notable_for', 'filter' =>   '/common/topic/alias');
$url = $service_url . $mid . '?' . http_build_query($params);

The problem is as 'filter' array key repeats twice, only the last parameter appears in the http_build_query. How do I construct the original url with two filters?

Ninja
  • 5,082
  • 6
  • 37
  • 59
  • Possible duplicate of [http\_build\_query with same name parameters](http://stackoverflow.com/questions/8170306/http-build-query-with-same-name-parameters) – Cave Johnson Apr 26 '17 at 17:01

4 Answers4

8

The problem here of course is that every key in a PHP array (hash) can only have one value. Intrinsically, a PHP hash is not a good representation of a querystring, because the query string has an order and has no constraints about the uniqueness of the keys.

To combat this, you'll need a special querystring builder that can handle duplicate keys:

class QueryString {
    private $parts = array();

    public function add($key, $value) {
        $this->parts[] = array(
            'key'   => $key,
            'value' => $value
        );
    }

    public function build($separator = '&', $equals = '=') {
        $queryString = array();

        foreach($this->parts as $part) {
            $queryString[] = urlencode($part['key']) . $equals . urlencode($part['value']);
        }

        return implode($separator, $queryString);
    }

    public function __toString() {
        return $this->build();
    }
}

Example usage (Codepad Demo):

$qs = new QueryString();
$qs->add('filter', '1');
$qs->add('filter', '2');
var_dump($qs->build()); // filter=1&filter=2
Bailey Parker
  • 15,599
  • 5
  • 53
  • 91
  • But when it will be accessed via `$_GET['filter']` it will return `2` not `1 & 2`. You have to use 2 different key here. – Yogesh Suthar Jun 18 '13 at 05:40
  • 2
    @YogeshSuthar That is because $_GET is a hash (and cannot accurately represent a querystring). When PHP parses the QS, it overwrites duplicates. If you examine a querystring with duplicate keys (with `php://input` for POST or `$_SERVER['QUERY_STRING']` for GET) you can extract the two values. Also consider keys like `filter[]`, which PHP aggregates into an array. Querystrings allow for keys to be set multiple times. – Bailey Parker Jun 18 '13 at 05:45
  • 1
    @YogeshSuthar Also examine how [Java handles it](http://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html#getParameterValues(java.lang.String)) it returns an array of values for each key. [This SO question](http://stackoverflow.com/questions/1746507/authoritative-position-of-duplicate-http-get-query-keys) also confirms that it is an established (although not documented) standard to allow keys to be assigned multiple values. – Bailey Parker Jun 18 '13 at 05:50
1

I have extended @Bailey Parker's answer to handle the issue raised in the first comment to his answer. This code will create query string where one key has multiple values and both values will be preserved using []

class QueryString
{
        private $parts = array();

        public function add($key, $value) {
                if(empty($value))return;
                if(is_array($value))
                {
                        foreach ($value as $v)
                                $this->add($key,$v);
                }
                else
                {
                        $this->parts[$key][] = $value;
                }

        }

        public function build($separator = '&', $equals = '=') {
                $queryString = array();

                $parts = array();
                foreach($this->parts as $key=>$value)
                {
                        if(count($value) > 1)
                                $parts[$key] = $value;
                        else
                                $parts[$key] = $value[0];
                }
                $query = http_build_query($parts);
                return preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '[]=', $query);;

        }

        public function __toString() {
                return $this->build();
        }

}

this code can also accept an array of values for a particular key

$qs = new QueryString();
$qs->add('trialId', array('1','2'));
$qs->add('packageId', '12');
$qs->add('frequencyId', '4');
var_dump($qs->build());

This class will produce normal query string, with the key having multiple values added with [] sign, the output will be like this

?frequencyId=4&packageId=12&trialId[]=1&trialId[]=2
Usman Shaukat
  • 1,315
  • 16
  • 22
0
  1. You cannot store in accoc array two or more values with one index.
  2. Api does not take values like filter[]

So, I suggest you to create your own function to build that query.

sectus
  • 15,605
  • 5
  • 55
  • 97
  • The API does support multiple filters. You can run the url I have given in the browser and see that the two filters apply. It is mentioned in the API documentation too. – Ninja Jun 18 '13 at 06:27
  • @Ninja, removed from answer. – sectus Jun 18 '13 at 06:38
0

Not the best solution, but I have a workaround:

$item = [
   'key1' => array('value1','value2'),
   'key2' => array('value3','value4')
];

$array=[
    'aaa' => 'aaa',
    'null' => 'null&'.preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', http_build_query($item,null,'&')),
    'zzz' => 'zzz'
];

echo urldecode(http_build_query($array));

Output:

aaa=aaa&null=null&key1=value1&key1=value2&key2=value3&key2=value4&zzz=zzz

WARNING: probably this won't work cause url will be:

aaa=aaa&null=null%26key1%3Dvalue1%26key1%3Dvalue2%26key2%3Dvalue3%26key2%3Dvalue4&zzz=zzz

EDIT: Other option will be preg the result before assign it to the url:

preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', http_build_query($item))

output:

key1=value1&key1=value2&key2=value3&key2=value4

Daniel Prol
  • 94
  • 1
  • 10