ZF2, Passing arrays as query parameters, esp in Zend\Http\Request - php

Question:
Anyone know of a way to cajole the Zend\Http\Request (or perhaps it would be in an implementer of Zend\Stdlib\ParametersInterface?) into creating urls where array query arg keys don't contain the indexes.
Background:
I'm attempting to pass an array of values as a query parameter on a GET request using the Zend\Http\Request object.
...
$httpClient = new Zend\Http\Client(); // cURL adapter setup omitted
$request = new Zend\Http\Request(); // set url, set GET method omitted
$myQueryArgArray = [
'key0' => 'val0',
'key1' => ['val1', 'val2'],
];
$request->getQuery()->fromArray($myQueryArgArray);
$response = $httpClient->send($request);
...
The cURL adapter is sending the request out the door with a url that looks like this:
hostname/path?key0=val0&key1%5B0%5D=val1&key1%5B1%5D=val2
Without the encoding:
hostname/path/?key0=val0&key1[0]=val1&key1[1]=val2
However, the server I'm calling out to fails unless I do NOT pass indexes in the query string. That is, before URL-encoding, I can call my API endpoint with a url like:
hostname/path?key0=val0&key1[]=val1&key1[]=val2
The question (again :):
Anyone know of a way to cajole the Zend\Http\Request (or perhaps it would be in an implementer of Zend\Stdlib\ParametersInterface?) into creating urls where array query arg keys don't contain the indexes.
What I've tried:
I've tried wrapping my array in a Zend\Stdlib\ArrayObject:
...
$myQueryArgArray = [
'key0' => 'val0',
'key1' => new \Zend\StdLib\ArrayObject(['val1', 'val2']),
];
...
Alas, to no avail.
I know that I accomplish the goal by manually constructing my query string and handing it directly to the Zend\Http\Request object, but I'm looking for a better way than creating my own query strings.
This page seems to indicate that there isn't a standard, so I suppose that neither ZF2 nor my api endpoint are doing it wrong:
How to pass an array within a query string?

I've looked at the sources and the problem is not the Zend\Http\Request class but the Zend\Http\Client.
Look at line 843 you see a call to the http_build_query function. And on the php.net site you have a solution in the comments:
$query = http_build_query($query);
$query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query);
So the cleanest solution probably would be to extend the Zend\Http\Client class and override the send method.

Related

Best practice to adapt Laravel HTTP Request for use with multiple API's?

We consume a significant, and growing, number of API's in our project. Over the course of years, the mechanisms I've had to come up with to deal with various different API schemes have frankly become quite cumbersome. In an effort to manage this with the most reusable code, I've started a new project using Laravel's HTTP packages, and want to eventually use it to replace all of the old code.
My approach has been to have a master "RequestManager" class the handles "Endpoint" classes with a common interface. The Endpoint classes contain everything needed for the request: the verb (POST, GET, etc.), the endpoint path, the query string, etc., and they all extend an abstract class that provides any string manipulation needed to build parameters from the arguments passed to their public methods and so forth. All this works very well, and makes it extremely easy to add new endpoints as needed.
The challenge I have at the moment is that one of the two API's I'm dealing with initially has no problem if pass the URI and an array of parameters to the Request post method, while the other wants the whole URI plus the query string in the first argument. Thus, this works for the first:
use Illuminate\Support\Facades\Http;
$request = Http::withHeaders(['Content-Type' => 'application/json']);
$uri = 'https://firstapi.com/get_user';
$params = [
'name' => 'Mark',
'email' => 'mark#example.com',
];
$response = $request->post($uri, $params);
While the other needs this:
$uri = 'https://secondapi.com/get_user?name=Mark&email=mark%40example.com';
$params = [];
$response = $request->post($uri, $params);
Don't ask me why, but it doesn't work otherwise.
I can't see in the Laravel docs any built-in way to handle this difference, so I've had to code a work-around:
$uri .= '?' . http_build_query($params);
Is there something built into the Laravel code that I am perhaps missing?

How can I create a request params method?

I am looking into building my own custom small framework and trying to make it as flexible as possible, I have now got to the input process of the design and I am wondering how I could implement the "request" class and how to return POST params from a POST request, etc.
I noticed that a lot of frameworks have a request class which hook directly into the "php://input" stream which I have tried to do and would like to utilize, however the only problem is this returns a string instead of an object or array of value=params.
What may be the best way for me to implement a method which would give me access to the param values of a POST request? I could probably explode the "php://input" string however this would be messy, I also tried the parse_str() on the stream which worked however sadly this also has issues if one of those values is something along the lines of foo">bar< as it seems to break out of the object and leave me with a partial incomplete value of foo.
My ultimate solution would be something along the lines of:
$_POST contains foo=bar;arg=value;
class request {
function get($arg=null)(){
if(!arg){/* return all, both foo and arg accessible*/}
else
/*return single value of the param with the $arg text*/
}
$foo = request->get('foo'); // "bar"
$all = request->get() // foo=bar;arg=value;
print $all->arg; // "value"

Yii2 Url Rewrite with Array parameter

I am trying to rewrite the following url,
http://mywebsite.com/web/myContlr?myContlrSearch[id]=900
Using Yii2 Url manager i am trying to change the url to be as below,
http://mywebsite.com/web/myContlr/900
How can this above format possible using urlmanger rules in Yii2. And also I am having multiple parameters like myContlrSearch[name], myContlrSearch[location],.. etc
Please guide me to build the URL restful.
If You want to make your API RESTful then You should start from resource naming. Resources should be a nouns in plural form.
For example if you have some class like a "car" then corresponding resource url should looks like <your-domain>/cars. In this case GET /cars will be a method to get a collection of cars, and GET /cars/900 -- a method to get specific car with ID=900.
Next, if your parameters are needed for filtering resource collection then you should add them as query parameters to collection resource url, like this: /cars?vendor=ferrari&count=10. If your parameters are needed to specify instance properties (when you create or update some object), then you should include them into a request body.
For instance creation you should use url of resource collection: POST /cars. And for updating an instance -- PUT /cars/<id>.
Lets look on your particular case. You have some resource named myContlr (i hope this is just example name), so myContlr should be an noun which express a object name. Your myContlrSearch in query parameters looks like some search criteria object, so it can be passed as array or as json in query parameters (it's better do not use request body for GET requests). Using array/json/any-other-structure here will not affect your routing -- this should be within the scope of responsibility of the controller.
But if you want to create new object or update existing one, then you should use POST /myContrlr or PUT /myContrlr/900 and then pass your object in request body (of type application/json or application/xml).
Then your routing config can looks as follows:
$config['components']['urlManager'] = [
...
'rules' => [
'GET myContlr/<id:\d+>' => 'myContlr/view',
'PUT myContlr/<id:\d+>' => 'myContlr/update',
'DELETE myContlr/<id:\d+>' => 'myContlr/delete',
'GET myContlr' => 'myContlr/index',
To learn more about building RESTful API in Yii2: http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html
For further reading: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

Zend Framework: get POST parameters

We're using Zend Framework 1.12 and in several actions we have:
$postParams = $this->getAllParams();
...
$domainModel->update($postParams)
I was wondering if it's a good approach of handling params. Or is it better to define what parameters we want to get like:
$postParams = array(
'email' => $this->_getParam('email'),
'company' => $this->_getParam('company')
)
Or maybe use array intersection function to filter out unexpected parameters?
Best practice should be using
$postParams = array(
'email' => $this->_getParam('email'),
'company' => $this->_getParam('company')
);
Using array intersection may work (for checking the keys not values of course!)
Why is passing all params to $domainModel->update not so good?
Depends on logic of update but assuming that parameters are getting into database query, by manipulating http request i can inject some additional code or params into db query - maybe update the field you do not want to update by that particular action.
Downside of this approach is that when you change your model, you have to check your code for these params.
If all columns in table (including IDs) can be changed, you can use getAllParams. Except one problem: POST can contain variables that are not table columns, so you will get an error on update (column '...' not found).
Its not a good idea to pass all post parameters to model directly. Sometimes you may not want pass specific values to model.
Second, you may want to set some default values to the data in case its not provided in that case you can do $this->_getParam('company', DEFAULT_VALUE)
But i would say that usage of any method depends on your requirements and then you need to pick one which is most suitable for you.

createAbsoluteUrl generates a path-like URL, how to avoid it?

I create a URL like this:
$app->createAbsoluteUrl('/', array(
'param1' => 'val1',
'param2' => 'var2',
);
The generated URL is:
http://mysite.com/param1/var1/param2/var2
But I expect a url like this:
http://mysite.com/?param1=var1&param2=var2
In function manual it says:
$params array additional GET parameters (name=>value). Both the name and value will be URL-encoded.
But it doesn't seem to work like that. How I can generate the expected URL? Thanks.
You need to specify that the urlManager application component should use the "get" format for the URLs it generates; the default is to use the "path" format. The Yii guide explains how to do it inside your application configuration:
array(
......
'components'=>array(
......
'urlManager'=>array(
'urlFormat'=>'get',
),
),
);
Update: So your urlFormat is "path" and that's by design... what about alternatives?
If you don't mind extending CWebApplication and using your own derived class in its place then you have several options such as:
Define your own createUrlEx method based on the original createUrl. It could look like this:
public function createUrlEx($format,$route,$params=array(),$ampersand='&')
{
$oldFormat = $this->getUrlManager()->getUrlFormat();
$this->getUrlManager()->setUrlFormat($format);
$url = $this->getUrlManager()->createUrl($route,$params,$ampersand);
$this->getUrlManager()->setUrlFormat($oldFormat);
return $url;
}
Override registerCoreComponents so that you can have a second url manager:
protected function registerCoreComponents()
{
parent::registerCoreComponents();
$components=array(
'specialUrlManager'=>array(
'class'=>'CUrlManager',
'urlFormat'=>'get',
),
);
$this->setComponents($components);
}
You can now call Yii::app()->specialUrlManager->createUrl(...) anytime.
You can also approach the problem in other ways:
Extend CUrlManager and expose a method that allows you to select the flavor of url to create on the spot.
If you only need "get" urls in one or two places, you can always create a new CUrlManager object, configure it on the spot, call createUrl and then discard it. You could also hide this ugliness behind a free function. Essentially this (admittedly not recommended) approach is a low-tech version of the first workaround given that has the advantage that you don't need to extend CWebApplication.
You should be able to use something like Yii::app()->urlManager->createPathInfo This will generate the query string as ...&var=val&... using a custom & and = if you like. You could use this to create a query string version of a URL on demand with:
$url = $this->createAbsoluteUrl('/').'index.php?'.Yii::app()->urlManager->createPathInfo($arrayOfStuff);
Or you might even be able to do:
Yii::app()->urlManager->urlFormat = 'get';
$this->createAbsoluteUrl('My/Path');
Yii::app()->urlManager->urlFormat = 'path';
Although I haven't and don't want to test the second method.
URL route should be in the format of 'ControllerID/ActionID'. manual
Your manual link is for CController and isn't for CApplication.

Categories