PHPackages                             zadorin/airtable-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. zadorin/airtable-php

ActiveLibrary[API Development](/categories/api)

zadorin/airtable-php
====================

Simple PHP wrapper for Airtable API

v1.0.1(1y ago)2770.6k—2.2%7[4 issues](https://github.com/eugenezadorin/airtable-php/issues)MITPHPPHP ^8.2

Since Apr 3Pushed 1y ago2 watchersCompare

[ Source](https://github.com/eugenezadorin/airtable-php)[ Packagist](https://packagist.org/packages/zadorin/airtable-php)[ RSS](/packages/zadorin-airtable-php/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (5)Versions (10)Used By (0)

Airtable PHP client
===================

[](#airtable-php-client)

Installation
------------

[](#installation)

```
composer require zadorin/airtable-php

```

Please note that library requires PHP 8.2 since `v1.0.0` release.

If you have lower PHP version, use `v0.*.*` releases:

```
composer require zadorin/airtable-php:^0

```

Usage
-----

[](#usage)

### Basic setup

[](#basic-setup)

```
$apiKey = 'key***********';
$database = 'app***********';
$tableName = 'my-table';

$client = new \Zadorin\Airtable\Client($apiKey, $database);
```

You can find API key in your [account settings](https://airtable.com/account) and database name in [API Docs](https://airtable.com/api).

### Insert some rows

[](#insert-some-rows)

```
$client->table($tableName)
    ->insert([
        ['name' => 'Ivan', 'email' => 'ivan@test.tld'],
        ['name' => 'Peter', 'email' => 'peter@test.tld']
    ])
    ->execute();
```

### Fetch multiple rows

[](#fetch-multiple-rows)

```
$recordset = $client->table($tableName)
    ->select('id', 'name', 'email') // you can use shortcut select('*') to fetch all columns
    ->where(['name' => 'Ivan', 'email' => 'ivan@test.tld'])
    ->orderBy(['id' => 'desc'])
    ->limit(10)
    ->execute();

var_dump($recordset->fetchAll()); // returns set of Record objects
var_dump($recordset->asArray()); // returns array of arrays
```

### Fetch specific rows by record id

[](#fetch-specific-rows-by-record-id)

```
$recordset = $client->table($tableName)
    ->find('rec1*******', 'rec2*******')
    ->execute();
```

### Iterate and update records

[](#iterate-and-update-records)

```
while ($record = $recordset->fetch()) {
    var_dump($record->getId()); // rec**********
    var_dump($record->getFields()); // [id => 1, name => Ivan, email => ivan@test.tld]

    $record->setFields(['name' => 'Ivan the 1st']);
    $client->table($tableName)->update($record);
}
```

### Pagination

[](#pagination)

```
$query = $client->table($tableName)
    ->select('*')
    ->orderBy(['id' => 'desc'])
    ->paginate(50); // limit(50) works the same. Default (and maximal) page size is 100

while ($recordset = $query->nextPage()) {
    var_dump($recordset->fetchAll());
}
```

### Remove rows

[](#remove-rows)

```
$records = $client->table($tableName)
    ->select('id', 'email')
    ->where(['email' => 'peter@test.tld'])
    ->execute()
    ->fetchAll();

$client->delete(...$records)->execute();
```

Complex filters
---------------

[](#complex-filters)

You can build complex formulas to filter records, but be careful, because formula applies to each record and can slow down your query.

Assume we prepared following query object:

```
$query = $client->table('my-table')->select('*');
```

### Query builder

[](#query-builder)

The following lines give the same results:

```
$query->where(['email' => 'ivan@test.tld']);
$query->where('email', 'ivan@test.tld');
$query->where('email', '=', 'ivan@test.tld');
```

You can use different logical operators:

```
$query->where('email', '!=', 'ivan@test.tld');
$query->where('code', '>', 100);
```

It's possible to concat multiple where statements:

```
$query->where([
    ['code', '>', 100],
    ['code', '', 100)->andWhere('code', '', 100)
    ->andWhere('code', 'where('email', 'match', '(?i)^(.+)@gmail.com$');
```

Keyword `like` also uses `REGEXP_MATCH()` under the hood, but provides more SQL-like syntax:

```
// look for emails, which ends with @gmail.com
$query->where('email', 'like', '%@gmail.com');

// look for names, which starts with Ivan
$query->where('name', 'like', 'Ivan%');

// look for urls, which contains substring (both variants below works the same):
$query->where('url', 'like', '%github%');
$query->where('url', 'like', 'github');
```

Please note, that `like` is case-sensitive, so if you want to ignore case, you'd better use `match` with `i`-flag.

### Date filtering

[](#date-filtering)

Library provides few methods to filter records by date and time:

```
$query->whereDate('birthdate', new \DateTimeImmutable('2022-03-08'));
$query->whereDateTime('meeting_start', '2022-04-01 11:00:00');
```

First parameter is your column name.

You can pass `DateTimeImmutable` object or datetime string, which will be cast into `DateTimeImmutable` automatically.

You can filter records by date range instead of strict equality:

```
$query
    ->whereDate('birthdate', '>=', new \DateTimeImmutable('2022-03-01'))
    ->andWhereDate('birthdate', '', 100],
    ['Code', 'table('tasks')
    ->select('*')
    ->whereView('active tasks')
    ->execute();
```

You can combine view and additional filters, specify subset of selected fields and override order just like normal select query:

```
$records = $client->table('tasks')
    ->select('Name', 'Priority')
    ->whereView('active tasks')
    ->andWhere('Status', 'todo')
    ->orderBy(['Id' => 'desc'])
    ->execute();
```

You can use alias `andWhereView()` but method `orWhereView()` will throw `LogicError`. This is because view is not actually part of the filter formula, it always works like "view AND formula", so you can't use `OR` operator here.

Also note that if view not exists `RequestError` exception will be thrown.

### Macros

[](#macros)

You can extend query builder methods with your own using macros:

```
\Zadorin\Airtable\Client::macro('whereCanDriveCar', function() {
    $this->where('age', '>=', 21);
});

$query->where('state', 'Florida')->andWhereCanDriveCar();
```

Macro name must not start with `or`/`and`. These logic prefixes are reserved and handles automatically.

Context `$this` inside macro callback references to query builder instance. It allows you to use other query builder methods or even other macros:

```
Client::macro('whereStateIsFlorida', function () {
    $this->where('state', 'Florida');
});

Client::macro('canDriveCar', function() {
    $this->where('age', '>=', 21);
});

Client::macro('whereFloridaDriver', function() {
    $this->whereStateIsFlorida()->andCanDriveCar();
});
```

You can pass variables into macro callback:

```
Client::macro('whereName', function ($name) {
    $this->where('Name', '=', $name);
});

$query->whereName('Ivan')->orWhereName('John');
```

And of course you can use raw formula to build something more complex:

```
Client::macro('whereBornInMay', function($year) {
    $this->whereRaw("AND(IS_AFTER(birthdate, '$year-04-30 23:59:59'), IS_BEFORE(birthdate, '$year-06-01 00:00:00'))");
});
```

But remember that raw formula overrides other query builder setup.

Typecast
--------

[](#typecast)

Airtable supports linked fields, which references other rows from current or another table. Assume you have `users` table where `contacts` field is a link to row in another table.

By default, you have to specify concrete row ID while inserting or updating such fields:

```
$client
    ->table('users')
    ->insert(['name' => 'Ivan', 'contacts' => 'recSPVbdx5vXwyLoH'])
    ->execute();
```

It's not very handy, so Airtable API supports `typecast` parameter, which enables automatic data conversion from string values.

Automatic conversion is disabled by default to ensure data integrity, but sometimes it may be helpful.

This is how you can enable that feature:

```
$client
    ->table('users')
    ->insert(['name' => 'Ivan', 'contacts' => 'ivan@test.tld'])
    ->typecast(true) // true is default value and can be skipped
    ->execute();
```

Update queries works the same.

Throttling
----------

[](#throttling)

Airtable API is limited to 5 requests per second per base. Client uses simple throttling library to keep this limit.

You can disable this behavior:

```
$client = new \Zadorin\Airtable\Client($apiKey, $database);
$client->throttling(false);
```

Debug
-----

[](#debug)

Client keeps last request object so you can use this for debugging purposes.

**Be careful with debug information because it contains all HTTP headers including authorization token**

```
$recordset = $client->table($tableName)->select('*')->execute();
$request = $client->getLastRequest();

$request->getResponseCode(); // http code (int)
$request->getPlainResponse(); // response body (string)
$request->getResponseInfo(); // array provided by curl_getinfo()
```

Exceptions
----------

[](#exceptions)

All package exceptions inherits from common `Zadorin\Airtable\Errors\AirtableError` class.

Also you may be interested in `Zadorin\Airtable\Errors\RequestError` which contains last request instance:

```
try {
    $inserted = $client->table($tableName)->insert()->execute();
} catch (RequestError $e) {

    // catch Airtable responses here
    var_dump($e->getMessage());
    var_dump($e->getLastRequest()->getResponseInfo());

} catch (AirtableError $e) {

    // catch package errors. In that case it will be "No records specified for insert"

}
```

Known problems
--------------

[](#known-problems)

Client uses `ext-curl` to make requests and `ext-json` to encode/decode results. Make sure this php extensions installed and properly configured.

If you see `SSL certificate problem: unable to get local issuer certificate` you probably have to configure option `curl.cainfo` in your `php.ini`. [Source](https://stackoverflow.com/questions/28858351/php-ssl-certificate-error-unable-to-get-local-issuer-certificate)

License and contributing
------------------------

[](#license-and-contributing)

MIT License. Any feedback is highly appreciated — welcome to [issues](https://github.com/eugenezadorin/airtable-php/issues).

If you want to send pull request make sure all tests are pass.

Tests
-----

[](#tests)

Copy this [readonly test database](https://airtable.com/shrs2bB37sScbDuLX) into your Airtable account, then fill env variables specified in `phpunit.xml.dist`.

And finally run test suite:

```
./vendor/bin/pest

```

It's also recommended to use static analysis tool to avoid errors:

```
./vendor/bin/psalm

```

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance28

Infrequent updates — may be unmaintained

Popularity42

Moderate usage in the ecosystem

Community13

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 92.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~143 days

Recently: every ~190 days

Total

9

Last Release

724d ago

Major Versions

v0.6.0 → v1.0.02023-02-25

PHP version history (2 changes)v0.0.1PHP ^7.4|^8.0

v1.0.0PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7566566?v=4)[Eugene Zadorin](/maintainers/eugenezadorin)[@eugenezadorin](https://github.com/eugenezadorin)

---

Top Contributors

[![eugenezadorin](https://avatars.githubusercontent.com/u/7566566?v=4)](https://github.com/eugenezadorin "eugenezadorin (25 commits)")[![andrewmenich](https://avatars.githubusercontent.com/u/29585821?v=4)](https://github.com/andrewmenich "andrewmenich (2 commits)")

---

Tags

airtableapi-clientphp

###  Code Quality

TestsPest

Static AnalysisPsalm, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/zadorin-airtable-php/health.svg)

```
[![Health](https://phpackages.com/badges/zadorin-airtable-php/health.svg)](https://phpackages.com/packages/zadorin-airtable-php)
```

###  Alternatives

[twilio/sdk

A PHP wrapper for Twilio's API

1.6k92.9M272](/packages/twilio-sdk)[facebook/php-business-sdk

PHP SDK for Facebook Business

90821.9M34](/packages/facebook-php-business-sdk)[meilisearch/meilisearch-php

PHP wrapper for the Meilisearch API

74513.7M114](/packages/meilisearch-meilisearch-php)[google/common-protos

Google API Common Protos for PHP

173103.7M50](/packages/google-common-protos)[hubspot/api-client

Hubspot API client

23914.2M16](/packages/hubspot-api-client)[sleiman/airtable-php

A PHP wrapper for the Airtable API

165741.5k](/packages/sleiman-airtable-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
