Codexshaper / laravel-woocommerce

WooCommerce Rest API for Laravel
MIT License
198 stars 57 forks source link

Using query parameters with requests #112

Open julien-MD opened 9 months ago

julien-MD commented 9 months ago

Hi,

I had an issue where orders created were too big for the Woocommerce server to be able to build a response, the query ending in a server timeout. I asked Woocommerce if it was possible to request the REST API to not send back the whole order data, as I send it in the first place and do not need it. They replied that I could use the query parameters in order to achieve that (see here).

It appears that this library does not allow this possibility (or I didn't find out how it is possible to do it), so I had to edit files for myself.

Is it possible to add this feature in a future release?

Thanks

Codexshaper commented 8 months ago

Hello @julien-MD,

Thanks for creating the issue and explaining your problem. We will be happy if we can include them. Can you share some examples that you are trying to do?

julien-MD commented 8 months ago

Hi,

Here is some more data about the request.

I have a Controller which build the data array needed:

 $data = [
            'customer_note'         => $order_comment,
            'billing' => [
                'first_name' => $customer['billing']->first_name,
                'last_name'  => $customer['billing']->last_name,
                'address_1'  => $customer['billing']->address_1,
                'address_2'  => $customer['billing']->address_2,
                'city'       => $customer['billing']->city,
                'state'      => $customer['billing']->state,
                'postcode'   => $customer['billing']->postcode,
                'country'    => $customer['billing']->country,
                'email'      => $customer['billing']->email,
                'phone'      => $customer['billing']->phone,
            ],
            'shipping' => [
                'first_name' => $customer['shipping']->first_name,
                'last_name'  => $customer['shipping']->last_name,
                'address_1'  => $customer['shipping']->address_1,
                'address_2'  => $customer['shipping']->address_2,
                'city'       => $customer['shipping']->city,
                'state'      => $customer['shipping']->state,
                'postcode'   => $customer['shipping']->postcode,
                'country'    => $customer['shipping']->country,
            ],
            'line_items'            => [],
            'shipping_lines' => [
                [
                    'method_id'     => 'gls_chezvous',
                    'method_title'  => 'GLS',
                    'total'         => '0'
                ]
            ]
        ];

        foreach ($order_products as $order_product) {
            $product = Products::where('id', $order_product->product_id)->first();
            $data['line_items'][] =
                [
                    'product_id'        => $product->wc_product_id,
                    'variation_id'      => $product->wc_variation_id,
                    'quantity'          => $order_product->quantity,
                    'meta_data'         => [
                        ['key' => 'Commentaire', 'value' => $order_product->product_comment ?? '']
                    ]
                ];
        }

Here, the thing is my $order_products array will eventually be quite big because the app is intended to be used by customers who buy a lot of products.

Then I have a Job which process the sending of the order to Woocommerce, it is called in the Controller like so: ProcessExport::dispatch(['order_data' => $data, 'order_id' => $order->id, 'customer_id' => $customer_id]);

Here is the job:

    public function handle(): void
    {
        $wc_order = Order::create($this->data['order_data']);

        if(isset($wc_order['id'])) {
            do_things();
        }
        else{
            do_other_things();
        }
    }

You can see that I need to wait for the Woocommerce response in order to go on with the code. The issue is at this point, because the Woocommerce API default behavior is to send me back the whole order data. If the server is almost idling and the order not to big, it has time to build its response. Otherway, it timeouts (but the order is created on the Woocommerce side).

In order to get around the issue I had to edit the Woocommerce API, forcing it to send me back only the data I need: File vendor/automattic/woocommerce/src/WooCommerce/HttpClient/HttpClient.php

    protected function createRequest($endpoint, $method, $data = [], $parameters = [])
    {
        $body    = '';
        $url     = $this->url . $endpoint;
        $hasData = !empty($data);
        $headers = $this->getRequestHeaders($hasData);

        // HTTP method override feature which masks PUT and DELETE HTTP methods as POST method with added
        // ?_method=PUT query parameter and/or X-HTTP-Method-Override HTTP header.
        if (!in_array($method, ['GET', 'POST'])) {
            $usePostMethod = false;
            if ($this->options->isMethodOverrideQuery()) {
                $parameters = array_merge(['_method' => $method], $parameters);
                $usePostMethod = true;
            }
            if ($this->options->isMethodOverrideHeader()) {
                $headers['X-HTTP-Method-Override'] = $method;
                $usePostMethod = true;
            }
            if ($usePostMethod) {
                $method = 'POST';
            }
        }

        // Setup authentication.
        $parameters = $this->authenticate($url, $method, $parameters);

        // Setup method.
        $this->setupMethod($method);

        // Include post fields.
        if ($hasData) {
            $body = \json_encode($data);
            \curl_setopt($this->ch, CURLOPT_POSTFIELDS, $body);
        }

/* Start of what I added */

        if($method == 'POST' && $endpoint == 'orders')
            $parameters = array_merge(['_fields' => 'id'], $parameters);

/* End of what I added */

        $this->request = new Request(
            $this->buildUrlQuery($url, $parameters),
            $method,
            $parameters,
            $headers,
            $body
        );

        return $this->getRequest();
    }

This method allows a $parameters input which can be used to do a lot of things but in my case it let me tell it that I only needed the id of the created order using the _fields key.

I did not find a proper way to send this $parameters parameter using the Order::create() method within laravel-woocommerce.

Let me know if you need more info.