PHPackages                             thomas-institut/datatable - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. thomas-institut/datatable

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

thomas-institut/datatable
=========================

A simple data table interface

2.0.2(1y ago)099[1 issues](https://github.com/thomas-institut/datatable/issues)[1 PRs](https://github.com/thomas-institut/datatable/pulls)MITPHPPHP &gt;=8.1CI failing

Since Mar 14Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/thomas-institut/datatable)[ Packagist](https://packagist.org/packages/thomas-institut/datatable)[ RSS](/packages/thomas-institut-datatable/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (10)Dependencies (4)Versions (21)Used By (0)

DataTable
=========

[](#datatable)

[![Latest Stable Version](https://camo.githubusercontent.com/4f79e9ce1cec1b10094834086d93e49c1bfaa65817d4f5270849763110c0fd5c/68747470733a2f2f706f7365722e707567782e6f72672f74686f6d61732d696e7374697475742f646174617461626c652f762f737461626c65)](https://packagist.org/packages/thomas-institut/datatable)[![License](https://camo.githubusercontent.com/a469d56f1cf80ca3fbf397078797ddecf84cadb7e650b7fe6b06ededcd5780db/68747470733a2f2f706f7365722e707567782e6f72672f74686f6d61732d696e7374697475742f646174617461626c652f6c6963656e7365)](https://packagist.org/packages/thomas-institut/datatable)

This package defines abstract data access and manipulation of SQL-like tables (tables made out of rows with a unique integer id as its key), and provides in-memory and MySQL implementations of that abstraction. The purpose is to decouple basic data functions from actual database details and to allow for faster testing by using in-memory tables.

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

[](#installation)

Install the latest version with

```
$ composer require thomas-institut/datatable
```

Usage
-----

[](#usage)

### DataTable

[](#datatable-1)

The main component of this package is the `DataTable` interface, which captures the basic functionality of an SQL table with unique integer ids. Implementations deal with the underlying storage, which can be shared among different DataTable instances. Basic id generation mechanisms are provided as alternatives to sequential ids.

```
$dt = new DataTableImplementationClass(...)

```

`DataTable` objects also implement the `ArrayAccess`, `IteratorAggregate`, `LoggerAwareInterface`and `ErrorReporter` interfaces.

The default id generation mechanism behaves exactly like auto-increment in databases, but it is handled without database intervention. Alternative id generators can be devised and set with `setIdGenerator`.

The `MySqlDataTable` implementation provided in this package can also defer id generation to MySql's AUTO INCREMENT functionality, and this is preferred over DataTable's default implementation.

A random id generator is provided. This generator will try to generate random Ids between two given values to up to a maximum number of attempts, after which it will default to the maximum current id plus 1:

```
$dt->setIdGenerator(new RandomIdGenerator($min, $max, $maxAttempts));

```

If proper values of `$min`, `$max` and `$maxAttempts` are chosen, it can be practically impossible for the random id generator to go to the default.

#### Error Handling and Reporting

[](#error-handling-and-reporting)

Invalid argument errors are handled by custom exceptions (see each method's documentation for details) and all other problems normally result in a RunTimeException being thrown.

The latest error can be inspected by calling the `getErrorCode` and `getErrorMessage` methods. These two methods are defined in the `ErrorReporter` interface.

Per the `LoggerAwareInteface`, a PSR Logger can be set as well and implementations should normally report all errors here too.

#### Row Creation

[](#row-creation)

```
$newRow = [ 'field1' => 'exampleStringValue', // or
            'field2' => null,
            'field3' => true, // or false
            'field4' => $someIntegerVariable
            // ...
             'fieldN' => $nthValue ];

$newId  = $dt->createRow($newRow);

// $newId is unique within the table

```

If an id is present in the row used for creation it will be used as the new id and a `RowAlreadyExists` exception will be thrown if that id is in use.

The default name for the id field/column is `id`, if the underlying table in actual storage uses a different name, this can be set setIdColumnName, normally right after construction:

```
$dt = new DataTableImplementation();
$dt->setIdColumnName('row_id');

$dt->getIdColumnName();  // 'row_id'

```

Setting the column name in a DataTable does not change anything in the underlying database. It only tells the DataTable what the actual database id name is.

Array access can also be used to create new rows:

```
$dt[] = $newRow;

// new row with a given id
$dt[$desiredId] = $newRow;
// but this updates the row if $desiredId already exists

```

Any number of fields/column and types can be used in the creation row as long as this makes sense with the implementation and actual storage. It's up to the implementation to ignore extra data or throw an error.

#### Read / Search Rows

[](#read--search-rows)

To check whether a row exists:

```
$result = $dt->rowExists($rowId);

```

To get a particular row:

```
$row = $dt->getRow($rowId);
//  $row === [ 'id' => $rowId, 'col1' => value, .... 'colN' => value];

// OR

$row = $dt[$rowId];

// both throw a RowDoesNotExist exception if the row does not exist

```

To get all rows:

```
$rows = $dt->getAllRows();

```

All methods that may return multiple rows return an `DataTableIterator` object. This is a normal PHP iterator that can be used in a `foreach` statement extended with a `count()` method that returns the number of results.

The iterator also provides a `getFirst()` convenience method that returns the first result in the set. However, there is no guarantee that a `foreach` statement on the iterator will work after `getFirst()` or that `getFirst()` will work after a `foreach` because rewinding might not be possible in particular implementations.

```
$rows = $dt->getAllRows();

$numRows = $rows->count();

// EITHER

foreach($rows as $row) {
   // do something ...
}

// OR

$firstRow = $rows->getFirst(); // null if there are no rows in the result set

```

The `search` method performs a general search on the DataTable based on an array of search conditions and a search type according to the following rules:

```
public function search(array $searchSpecArray, int $searchType = self::SEARCH_AND, int $maxResults = 0) : array;

/**
  * Searches the datatable according to the given $searchSpec
  *
  * $searchSpecArray is an array of conditions.
  *
  * If $searchType is SEARCH_AND, the row must satisfy:
  *      $searchSpecArray[0] && $searchSpecArray[1] && ...  && $searchSpecArray[n]
  *
  * if  $searchType is SEARCH_OR, the row must satisfy the negation of the spec:
  *
  *      $searchSpecArray[0] || $searchSpecArray[1] || ...  || $searchSpecArray[n]
  *
  *
  * A condition is an array of the form:
  *
  *  $condition = [
  *      'column' => 'columnName',
  *      'condition' => one of (EQUAL_TO, NOT_EQUAL_TO, LESS_THAN, LESS_OR_EQUAL_TO, GREATER_THAN, GREATER_OR_EQUAL_TO)
  *      'value' => someValue
  * ]
  *
  * Notice that each condition type has a negation:
  *      EQUAL_TO   NOT_EQUAL_TO
  *      LESS_THAN    GREATER_OR_EQUAL_TO
  *      LESS_OR_EQUAL_TO  GREATER_THAN
  *
  * if $maxResults > 0, an array of max $maxResults will be returned
  * if $maxResults updateRow($row);

// OR

$dt[$rowId] = $row;

```

In `updateRow` the given row must have an id field that corresponds to a row in the table or else a `RowDoesNotExist` exception will be thrown. In the array access version, the given `$rowId` will be used regardless of whether there is an id field in the given row.

Only the fields in `$row` are updated. An incomplete row may produce errors if the underlying database schema expects values for those columns.

#### Delete Rows

[](#delete-rows)

```
$result = $dt->deleteRow($rowId);

// OR

unset($dt[$rowId]);

```

The result is the number of columns affected, which is 0 if the row did not exist in the first place.

#### Transactions

[](#transactions)

DataTables provide a basic interface to underlying database transaction capabilities, if they exist.

To check if transactions are supported:

```
$supported = $dt->supportsTransactions(); // true if supported

```

If transactions are supported, they can be started with `startTransaction()` and ended with `commit()` or `rollBack()`

```
if($dt->supportsTransactions()){
  if ($dt->startTransaction()) {

    // create, update,delete rows ...
    // decide whether to commit or rollback

    if ($goAheadWithCommit)  {
       if ($dt->commit()){
          // all went well, changes are committed
       } else {
          // error during commit
          $errorMessage = $dt->getErrorMessage();
          $errorCode = $dt->getErrorCode();
       }
    } else {  // roll back
       if ($dt->rollBack() {
          // rollBack done, changes to the database not saved
       } else {
          // error during rollBack
          $errorMessage = $dt->getErrorMessage();
          $errorCode = $dt->getErrorCode();
       }
    }
  } else {
     // error starting transaction
     $errorMessage = $dt->getErrorMessage();
     $errorCode = $dt->getErrorCode();
  }
}

```

There is also a convenience method to have the DataTable check if the underlying database is in a transaction:

```
$result = $dt->isUnderlyingDatabaseInTransaction();

```

Extra care should be taken when working with transactions, especially if a database connection is shared among different DataTables. DataTables will not start a transaction if the underlying database reports that a transaction is currently going on (which may or may not be reliable depending on the database). Also, commits can only be executed on the DataTable that initiated the transaction.

If a DataTable does not support transactions, `startTransaction()`, `commit()` and `rollBack()` will always return `false`.

### InMemoryDataTable

[](#inmemorydatatable)

A `DataTable` implementation using simple PHP arrays, no storage. This makes it possible to perform tests on data tables without having to set up a database.

### MySqlDataTable

[](#mysqldatatable)

A `DataTable` implementation using a MySQL table.

```
$dt = new MySqlDataTable($pdoDatabaseConnection, $mySqlTableName, $useAutoInc, $idColumnName);

```

`MySqlDataTable` assumes that there is a table setup with at least an int or bigint `id` column with the given name (`$idColumnName`, which defaults to 'id').

If `$useAutoInc` is true, `MySqlDataTable` assumes that the `id` column has the `AUTO_INCREMENT` attribute and will create rows so that MySQL will take care of generating IDs. Otherwise, `MySqlDataTable` itself takes care of generating incremental IDs.

For compatibility with previous versions of this library, `$useAutoInc` defaults to false. However, it is recommended that you use MySQL auto-increment functionality. In a scenario where there are multiple calls to `createRow` concurrently, `DataTable`'s internal ID generator may fail to generate a unique id.

The table in MySQL can have any number of extra columns of any type. As long as calls to `createRow` and `updateRow` agree with columns names and types, everything should work fine. You can also have default values defined in MySQL and leave those out when calling `createRow`.

### MySqlDataTableWithRandomIds

[](#mysqldatatablewithrandomids)

The same as `MySqlDatable` but using the randomId generator.

### MySqlUnitemporalDataTable

[](#mysqlunitemporaldatatable)

A MySQL table with time-tagged rows. Every row not only has a unique id, but also a valid\_from and a valid\_until time. When using the normal `DataTable` methods `MySQLUnitemporalDataTable` behaves exactly the same as MySqlDataTable, but it does not delete any rows, it just makes them invalid.

There is a set of time methods to create, read, update and delete previous versions of the data. For example:

```
$dt = new MySqlUnitemporalDataTable($pdoDatabaseConnection, $mySqlTableName);

$oldRow = $dt->getRowWithTime($rowId, $timeString);

```

`$timeString ` is a string formatted as a valid MySQL datetime with microseconds, e.g. `'2018-01-01  12:00:00.123123'` Use the static methods in the `TimeString`class to generate such strings from MySQL date and datetime strings, and from UNIX timestamps with or without microseconds.

The underlying MySQL table must have two datetime fields: `valid_from` and `valid_until`

The user is responsible for setting the PDO connection with the timezone that is going to be used in all queries using time parameters.

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance62

Regular maintenance activity

Popularity11

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity81

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 100% 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 ~166 days

Recently: every ~159 days

Total

18

Last Release

525d ago

Major Versions

0.9.0 → 1.0.02024-01-05

1.0.0 → 2.0.02024-01-30

PHP version history (3 changes)0.1PHP &gt;=7.0

0.6.0PHP &gt;=7.1

0.9.0PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/bdaa55fa14e6465a3a36c0814583c3cde96e14b6f556d01d866065d0bcb42b3e?d=identicon)[rafaelnajera](/maintainers/rafaelnajera)

---

Top Contributors

[![rafaelnajera](https://avatars.githubusercontent.com/u/17843226?v=4)](https://github.com/rafaelnajera "rafaelnajera (68 commits)")

---

Tags

data

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/thomas-institut-datatable/health.svg)

```
[![Health](https://phpackages.com/badges/thomas-institut-datatable/health.svg)](https://phpackages.com/packages/thomas-institut-datatable)
```

###  Alternatives

[fakerphp/faker

Faker is a PHP library that generates fake data for you.

3.9k358.5M3.5k](/packages/fakerphp-faker)[dflydev/dot-access-data

Given a deep data structure, access data by dot notation.

718359.1M86](/packages/dflydev-dot-access-data)[mbezhanov/faker-provider-collection

A collection of custom providers for the Faker library

2138.6M24](/packages/mbezhanov-faker-provider-collection)[php-units-of-measure/php-units-of-measure

A PHP library for converting between standard units of measure.

3123.4M20](/packages/php-units-of-measure-php-units-of-measure)[amenadiel/jpgraph

Composer Friendly, full refactor of JpGraph, library to make graphs and charts

1492.2M7](/packages/amenadiel-jpgraph)[jbzoo/data

An extended version of the ArrayObject object for working with system settings or just for working with data arrays

891.6M23](/packages/jbzoo-data)

PHPackages © 2026

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