How to get the query parameters in a Guzzle/ Psr7 request - guzzle

I am using Guzzle 6.
I am trying to mock a client and use it like so:
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
$mock_handler = new MockHandler([
new Response(200, ['Content-Type' => 'application/json'], 'foo'),
]);
$history = [];
$history_middleware = Middleware::history($history);
$handler_stack = HandlerStack::create($mock_handler);
$handler_stack->push($history_middleware);
$mock_client = new Client(['handler' => $handler_stack]);
// Use mock client in some way
$mock_client->get("http://example.com", [
'query' => [
'bar' => '10',
'hello' => '20'
],
]);
// ------
// get original request using history
$transaction = $history[0];
/** #var Request $request */
$request = $transaction['request'];
// How can I get the query parameters that was used in the request (i.e. bar)
My question is how can I get the query parameters used in the GuzzleHttp\Psr7\Request class?
The closest I managed to get is the following: $request->getUri()->getQuery(), but this just returns a string like so: bar=10&hello=20.

I seem to have solved my problem.
I can simply do this:
parse_str($request->getUri()->getQuery(), $query);
and I now have an array of the query parameters.
Other solutions are welcome!

Related

WordPress API passing email argument issue

Using a WordPress REST API custom endpoint, I am attempting to get user data (or at least the user id) with the following code in the functions.php file:
function getUser(WP_REST_Request $request) {
global $wpdb;
$email = $request->get_param( 'email' );
$query = "SELECT * FROM wp_users WHERE user_email = $email";
$result = $wpdb->get_results($query);
return $result;
}
add_action( 'rest_api_init', function () {
register_rest_route( 'myapi/v1', '/getcustomer/(?P<email>[^/]+)', array(
'methods' => 'GET',
'callback' => 'getUser'
) );
} );
Testing the function with the endpoint /wp-json/myapi/v1/getcustomer/joe#anymail.com it returns with empty brackets [ ]. Am I missing something here? Any help would be greatly appreciated.
There are multiple issues with your code:
You should encode your user emails or send it via POST method.
Your current query is open to SQL Injection
Your value must be enclosed in quotes. Now it translates to .. WHERE user_email = joe#anymail.com and that is SQL syntax error.
So your code should look like this:
$query = "SELECT * FROM wp_users WHERE user_email = %s";
$result = $wpdb->get_results($wpdb->prepare($query, $email));

Guzzle 6: Get URL that was "resolved" from base_uri

In Guzzle 3 you can get the resolved URL (without actually opening it) like this:
$client = new Client([
'base_uri' => 'http://foo.com',
]);
$request = $client->get('bar.html');
echo $request->getUrl();
In Guzzle 6 this is not working anymore. Is there another way to get "http://foo.com/bar.html"?
You can use the history middleware, works as advertised:
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
$container = [];
$stack = HandlerStack::create();
$stack->push(Middleware::history($container));
$client = new Client([
'base_uri' => 'http://foo.com',
'handler' => $stack,
]);
$response = $client->request('GET', 'bar.html');
/* #var RequestInterface $request */
$request = $container[0]['request'];
echo $request->getUri();
For reference, see http://docs.guzzlephp.org/en/latest/testing.html#history-middleware.
It is a bit late, but for the reference.
You can do it with \GuzzleHttp\Psr7\UriResolver::resolve($baseUri, $relUri);
It converts the relative URI into a new URI that is resolved against the base URI.
$baseUri and $relUri are instances of \Psr\Http\Message\UriInterfaceUriInterface.

Multiple duplicate uri parameters in GuzzleHttp

I am accessing the Echo Nest API, which requires me to repeat the same uri parameter name bucket. However I can't make this work in Guzzle 6. I read a similar issue from 2012, however the approach does not work.
I have tried adding it manually into the query string without any success.
A sample API call could be:
http://developer.echonest.com/api/v4/song/search?format=json&results=10&api_key=someKey&artist=Silbermond&title=Ja&bucket=id:spotify&bucket=tracks&bucket=audio_summary
Here's my example Client:
/**
* #param array $urlParameters
* #return Client
*/
protected function getClient()
{
return new Client([
'base_uri' => 'http://developer.echonest.com/api/v4/',
'timeout' => 5.0,
'headers' => [
'Accept' => 'application/json',
],
'query' => [
'api_key' => 'someKey',
'format' => 'json',
'results' => '10',
'bucket' => 'id:spotify' // I need multiple bucket parameter values with the 'bucket'-name
]);
}
/**
* #param $artist
* #param $title
* #return stdClass|null
*/
public function searchForArtistAndTitle($artist, $title)
{
$response = $this->getClient()->get(
'song/search?' . $this->generateBucketUriString(),
[
'query' => array_merge($client->getConfig('query'), [
'artist' => $artist,
'title' => $title
])
]
);
// ...
}
Can you help me?
In the Guzzle 6 you are not allowed to pass any aggregate function anymore. Whenever you will pass an array to the query config it will be serialized with the http_build_query function:
if (isset($options['query'])) {
$value = $options['query'];
if (is_array($value)) {
$value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
}
To avoid it you should serialize a query string by your own and pass it as string.
new Client([
'query' => $this->serializeWithDuplicates([
'bucket' => ['id:spotify', 'id:spotify2']
]) // serialize the way to get bucket=id:spotify&bucket=id:spotify2
...
$response = $this->getClient()->get(
...
'query' => $client->getConfig('query').$this->serializeWithDuplicates([
'artist' => $artist,
'title' => $title
])
...
);
Otherwise you could pass into the handler option an adjusted HandlerStack that will have in its stack your Middleware Handler. The one will read some new config param, say, query_with_duplicates, build acceptable Query String and modify Request's Uri with it accordingly.
I had the same need today, but now we are on Guzzle 7, the easiest way of getting duplicates for params (bucket=value1&bucket=value2&bucket=value3...) is to use the Query Build method. For this to work do the following:
// Import the class
use GuzzleHttp\Psr7\Query;
Example params
$params = [
'bucket' => 'value1',
'bucket' => 'value2',
'bucket' => 'value3',
];
Then when passing the params array to the query key, first pass it through the Query::build method
$response = $client->get('/api', [
'query' => Query::build($params),
]);

Laravel 4 list and detail route

I want to achieve the following:
devices > Controller#devices
devices/{id} > Controller#devices
Is this possible with Laravel ? I'm trying to map a domotic box with an android application ImperiHome, and they expect me to have the same route for devices list and for any device action.
So far I've tried this:
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#devices');
Route::get('devices', 'DomoticzController#devices');
But I cannot retrieve the argument when I call the devices/id url
Ok, so to solve the php strict standard error I just splitted the routes to two methods as follows:
routes.php
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#device');
Route::get('devices', 'DomoticzController#devices');
Route::get('rooms', 'DomoticzController#rooms');
//Route::get('action_ret', 'DomoticzController#action_ret');
Route::get('system', 'DomoticzController#system');
Route::get('/', 'DomoticzController#system');
DomoticzController.php
/**
* Call for an action on the device identified by $deviceId.
* #return string Json formated action status.
*/
public function device($deviceId, $actionName, $actionParam = null)
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url("json.htm?type=command&param={$actionName}&idx={$deviceId}}&switchcmd=$actionParam"));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = array('success' => ('OK' === $input['status'] ? true : false), 'errormsg' => ('ERR' === $input['status'] ? 'An error occured' : ''));
return Response::json($output);
}
/**
* Retrieve the list of the available devices.
* #return string Json formatted devices list.
*/
public function devices()
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url('json.htm?type=devices&used=true'));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = new stdClass();
$output->devices = array();
foreach ($input['result'] as $device) {
$output->devices[] = array (
'id' => $device['idx'],
'name' => $device['Name'],
'type' => 'DevSwitch',
'room' => null,
'params' => array(),
);
}
return Response::json($output);
}
maybe there is a better way to solve this, I would be glad to hear it.
If you let both routes use the same controller action, you need to make the parameters optional in the controller I think.
Try this public function device($deviceId = null, $actionName = null, $actionParam = null) and see if you still get the PHP strict error.
You can not have a route without parameters be redirected to a controller action that expects parameters. You can, on the other hand, make a route with parameters be redirected to a controller action with optional parameters (this does not mean that your route parameters need to be optional).

How to add parameters to controller redirect?

In Zend Framework 2 I want to pass an array of parameters from one action to another within the same controller which I did in ZF1 in the following manner:
$this->_helper->redirector->gotoSimple('foo', null, null, $params);
and in fooAction:
$params = $this->_request->getParams();
In ZF2, trying the various answers I have seen here on SO, I came up with the following:
$this->redirect()->toRoute('home/default', array(
'controller' => 'client',
'action' => 'foo',
'param' => 'bar'),
array('param' => 'bar'));
(trying both the $params and $options arguments of toRoute())
and in fooAction:
$param = $this->getEvent()->getRouteMatch()->getParams();
or
$param = $this->params()->fromRoute());
None works for me. Is there a simple way to achieve what I want (passing parameters with a redirect) or should I go the route of using a container, session or even global variables?
You could use the forward plugin:
http://framework.zend.com/manual/2.0/en/modules/zend.mvc.plugins.html#the-forward-plugin
public function someAction()
{
$returnValue = $this->forward()->dispatch('application/controller/index', array(
'action' => 'other'
));
return $returnValue;
}
public function otherAction()
{
return 99;
}
You will be able to pass parameters too
In the end, what I did was, instead of using route parameters, using query parameters, since the parameters I used where not route related. That solved the problem.