Skip to content

Commit 491df25

Browse files
yassinebourakbaYassine Bourakba
andauthored
Add mass update feature (#35)
* Add mass update method to client * Change `update` and `patch` methods to support mass update * Use delay between subsequent requests * Update readme * Fix cs * Fix issue when fetching records in all pages (#32) * Support offset in query params * Fix getAllPages() to send handle response with offset correctly Co-authored-by: Yassine Bourakba <bourakbayassine@gmail.com> * Add mass update method to client * Change `update` and `patch` methods to support mass update * Use delay between subsequent requests * Update readme * Fix cs Co-authored-by: Yassine Bourakba <bourakbayassine@gmail.com>
1 parent 781eddb commit 491df25

File tree

5 files changed

+99
-6
lines changed

5 files changed

+99
-6
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,23 @@ Airtable::table('companies')->update('rec5N7fr8GhDtdNxx', [ 'name' => 'Google',
124124
Airtable::table('companies')->patch('rec5N7fr8GhDtdNxx', ['country' => 'US']);
125125
```
126126

127+
#### Mass Update or Patch
128+
- Array of data to be updated or patched
129+
130+
``` php
131+
Airtable::table('companies')->patch([
132+
[
133+
'id' => 'rec5N7fr8GhDtdNxx',
134+
'fields' => ['country' => 'US']
135+
],
136+
[
137+
'id' => 'rec8BhDt4fs2',
138+
'fields' => ['country' => 'UK']
139+
],
140+
...
141+
]);
142+
```
143+
127144
### Testing
128145

129146
``` bash

config/config.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,26 @@
4747

4848
],
4949

50+
/*
51+
|--------------------------------------------------------------------------
52+
| Default Airtable Client Settings
53+
|--------------------------------------------------------------------------
54+
|
55+
| This value can be found on the API docs page:
56+
| https://airtable.com/[BASE_ID]/api/docs#curl/table:tasks
57+
| The value will be hilighted at the beginning of each table section.
58+
| Example:
59+
| Each record in the `Tasks` contains the following fields
60+
|
61+
*/
62+
5063
'log_http' => env('AIRTABLE_LOG_HTTP', false),
5164
'log_http_format' => env('AIRTABLE_LOG_HTTP_FORMAT', '{request} >>> {res_body}'),
5265

5366
'typecast' => env('AIRTABLE_TYPECAST', false),
67+
68+
// The API is limited to 5 requests per second per base.
69+
// If you exceed this rate, you will receive a 429 status code and will need to wait 30 seconds before a successful request.
70+
// This value is the delay in microseconds between subsequent requests.
71+
'delay_between_requests' => env('AIRTABLE_DELAY_BETWEEN_REQUESTS', 200000),
5472
];

src/Airtable.php

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Tapp\Airtable;
44

5+
use InvalidArgumentException;
6+
57
class Airtable
68
{
79
private $api;
@@ -21,14 +23,46 @@ public function create($data)
2123
return $this->api->post($data);
2224
}
2325

24-
public function update(string $id, $data)
26+
/**
27+
* @param dynamic $args (string) $id, $data | (array) $data
28+
* @return mixed
29+
*
30+
* @throws \InvalidArgumentException
31+
*/
32+
public function update(...$args)
2533
{
26-
return $this->api->put($id, $data);
34+
if (is_string($args[0])) {
35+
if (! isset($args[1])) {
36+
throw new InvalidArgumentException('$data argument is required.');
37+
}
38+
39+
return $this->api->put($args[0], $args[1]);
40+
} elseif (is_array($args[0])) {
41+
return $this->api->massUpdate('put', $args[0]);
42+
}
43+
44+
throw new InvalidArgumentException('Update accepts either an array or an id and array of data.');
2745
}
2846

29-
public function patch(string $id, $data)
47+
/**
48+
* @param dynamic $args (string) $id, $data | (array) $data
49+
* @return mixed
50+
*
51+
* @throws \InvalidArgumentException
52+
*/
53+
public function patch(...$args)
3054
{
31-
return $this->api->patch($id, $data);
55+
if (is_string($args[0])) {
56+
if (! isset($args[1])) {
57+
throw new InvalidArgumentException('$data argument is required.');
58+
}
59+
60+
return $this->api->patch($args[0], $args[1]);
61+
} elseif (is_array($args[0])) {
62+
return $this->api->massUpdate('patch', $args[0]);
63+
}
64+
65+
throw new InvalidArgumentException('Patch accepts either an array or an id and array of data.');
3266
}
3367

3468
public function destroy(string $id)

src/AirtableManager.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ protected function createAirtable($table)
120120
}
121121

122122
$airtableTypeCast = $this->app['config']['airtable.typecast'];
123+
$delay = $this->app['config']['airtable.delay_between_requests'];
123124

124-
$client = new AirtableApiClient($base, $table, $access_token, $httpLogFormat, null, $airtableTypeCast);
125+
$client = new AirtableApiClient($base, $table, $access_token, $httpLogFormat, null, $airtableTypeCast, $delay);
125126

126127
return new Airtable($client, $table);
127128
}

src/Api/AirtableApiClient.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ class AirtableApiClient implements ApiClient
1212
private $typecast;
1313
private $base;
1414
private $table;
15+
private $delay;
1516

1617
private $filters = [];
1718
private $fields = [];
1819
private $offset = false;
1920
private $pageSize = 100;
2021
private $maxRecords = 100;
2122

22-
public function __construct($base, $table, $access_token, $httpLogFormat = null, Client $client = null, $typecast = false)
23+
public function __construct($base, $table, $access_token, $httpLogFormat = null, Client $client = null, $typecast = false, $delayBetweenRequests = 200000)
2324
{
2425
$this->base = $base;
2526
$this->table = $table;
2627
$this->typecast = $typecast;
28+
$this->delay = $delayBetweenRequests;
2729

2830
$stack = \GuzzleHttp\HandlerStack::create();
2931

@@ -121,6 +123,27 @@ public function patch(string $id, $contents = null)
121123
return $this->decodeResponse($this->client->patch($url, $params));
122124
}
123125

126+
public function massUpdate(string $method, array $data)
127+
{
128+
$url = $this->getEndpointUrl();
129+
$records = [];
130+
131+
// Update & Patch request body can include an array of up to 10 record objects
132+
$chunks = array_chunk($data, 10);
133+
foreach ($chunks as $key => $data_chunk) {
134+
$params = ['json' => ['records' => $data_chunk, 'typecast' => $this->typecast]];
135+
136+
$response = $this->decodeResponse($this->client->$method($url, $params));
137+
$records += $response['records'];
138+
139+
if (isset($chunks[$key + 1])) {
140+
usleep($this->delay);
141+
}
142+
}
143+
144+
return ['records' => $records];
145+
}
146+
124147
public function delete(string $id)
125148
{
126149
$url = $this->getEndpointUrl($id);

0 commit comments

Comments
 (0)