saloonphp / saloon

🤠 Build beautiful API integrations and SDKs with Saloon

Home Page:https://docs.saloon.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Query parameters customisation

asamofal opened this issue · comments

Hi! I have to work with 3d party API that expects such request:

POST /consignments/label_direct?printer_id=123&consignment_ids[]=1&consignment_ids[]=2

I tried these ways:

protected function defaultQuery(): array
{
    return [
        'printer_id'      => $this->printerId,
        'consignment_ids' => [$this->consignmentId],
    ];
}

and

public function resolveEndpoint(): string
{
    return "/consignments/label_direct?printer_id=$this->printerId&consignment_ids[]=$this->consignmentId";
}

But they both give me the same result:

/consignments/label_direct?printer_id=1393&consignment_ids%5B0%5D=51872349

The issue lies in that index: %5B0%5D (with 0 placed between encoded brackets). That amazing API throws 500 in my face because of it. Any chance for me to customize a query in such way as that API require me?

Alright, I've worked out the details and found a solution.
Guzzle constructs the query string using the http_build_query function, which includes indexes for arrays. Fortunately, Saloon supports PSR request modification at both the Connector and Request levels. In my request, I simply modify the PSR request and use regex to remove the numbers within the square brackets.

public function handlePsrRequest(RequestInterface $request, PendingRequest $pendingRequest): RequestInterface
{
    return $request->withUri(
        $request->getUri()->withQuery(
            preg_replace(
                '/[[\d]+]/',
                '[]',
                rawurldecode($request->getUri()->getQuery())
            )
        )
    );
}

@Sammyjo20 Thanks for this level of flexibility :)

commented

@asamofal Thank you, that helped me a lot!

Further information in the documentation: Modifying the PSR-7 Request

Hey @asamofal @hofmannsven

Thank you for posting the issue and letting me know - I've had a few people mention this actually and I was wondering if we needed to have a conversation around potentially changing Saloon's implementation. I'm not quite sure if there's a way to do it without a breaking change.

I guess the issue is that different APIs have different standards for "multiple" query parameters. I'll check RFC and see if it's defined somewhere!

Update: I found this Stack Overflow answer: https://stackoverflow.com/questions/24059773/correct-way-to-pass-multiple-values-for-same-parameter-name-in-get-request

@Sammyjo20 Yeah, the http_build_query PHP function has an option encoding_type that refers to two RFCs:
RFC 1738 and RFC 3986. However, I didn't find anything about multiple values in query strings there. So, I think it depends just on the specific implementation.

I'm not sure how common this problem is. I mean, is there any sense in adding a method to modify the process of building a query string? It sounds too specific, considering you can do whatever you need in just handlePsrRequest.

I have met all kinds of required calls for multiple values like:
consignment_ids[]=1&consignment_ids[]=2
consignment_ids[0]=1&consignment_ids[1]=2
consignment_ids=1&consignment_ids=2

and of course, when you send it in a different way, everything breaks 🤦‍♂️ So even if there is any standard, it's not kept by API creators.

Not sure if the library accepts it, but maybe instead of an array with parameters and values we could just pass custom built query as a string?

Yep I believe Saloon does accept providing the query string in the resolveBaseUrl and resolveEndpoint methods. I don't think this gets parsed, I think it just gets joined with the other query parameters. Might be worth trying that as it may be a nicer workaround.

I just spent quite a while trying to figure this out myself – the API I'm working with requires query param arrays in key=foo,bar,baz format. Implementing the fix in handlePsrRequest was pretty straightforward:

public function handlePsrRequest(RequestInterface $request, PendingRequest $pendingRequest): RequestInterface
{
    $query = $pendingRequest->query()->all();
    $uri = $request->getUri();
    $uri = $uri->withQuery(Query::build($query));
    $request = $request->withUri($uri);

    return $request;
}

I think it might be helpful to have some mention of this issue in the Query Parameters section of the Requests documentation. It wasn't immediately obvious to me how to make a docs PR, but I'd be happy to do so if that would be helpful.

Edit: nevermind, figured out how to edit the docs. PR here: Sammyjo20/saloon-docs#60

Thank you again, @jlevers

My pleasure! Thanks for making such a great library :)

Thanks man, I'm glad you find it useful :D