thomas-institut/datatable

简单的数据表界面

2.0.1 2024-01-30 10:52 UTC

This package is auto-updated.

Last update: 2024-08-30 01:57:09 UTC


README

Latest Stable Version License

此包定义了一个接口,用于抽象数据访问和由具有唯一整数ID作为键的行组成的类似SQL表的操纵,并提供内存和MySQL实现。目的是将基本数据功能与实际数据库细节解耦,并通过使用内存表来实现更快的测试。

安装

使用以下命令安装最新版本:

$ composer require thomas-institut/datatable

用法

DataTable

主要组件是 DataTable 接口,它捕获了具有唯一整数ID的SQL表的基本功能。实现处理底层存储,这些存储可以在不同的DataTable实例之间共享。提供了基本的ID生成机制,作为顺序ID的替代。

$dt = new DataTableImplementationClass(...) 

DataTable 对象还实现了 ArrayAccessIteratorAggregateLoggerAwareInterfaceErrorReporter 接口。

默认的ID生成机制的行为与数据库中的自动递增完全相同,但它不需要数据库干预。可以使用 setIdGenerator 设置其他ID生成器。

此包中提供的 MySqlDataTable 实现还可以将ID生成推迟到MySQL的 AUTO INCREMENT 功能,并且这比DataTable的默认实现更受欢迎。

提供了一个随机ID生成器。此生成器将尝试在两个给定值之间生成随机ID,最多尝试指定次数,之后将默认为当前最大ID加1。

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

如果选择了适当的 $min$max$maxAttempts 值,随机ID生成器实际上无法回到默认值。

错误处理和报告

无效参数错误由自定义异常处理(请参阅每个方法的文档以获取详细信息),所有其他问题通常会导致抛出 RunTimeException。

可以通过调用 getErrorCodegetErrorMessage 方法来检查最新错误。这两个方法定义在 ErrorReporter 接口中。

根据 LoggerAwareInteface,还可以设置PSR Logger,并且实现应通常在此处报告所有错误。

行创建

$newRow = [ 'field1' => 'exampleStringValue', // or  
            'field2' => null, 
            'field3' => true, // or false
            'field4' => $someIntegerVariable
            // ... 
             'fieldN' => $nthValue ];  
             
$newId  = $dt->createRow($newRow);  

// $newId is unique within the table

如果创建时使用的行中存在ID,则将使用该ID作为新ID,如果该ID正在使用中,则将抛出 RowAlreadyExists 异常。

ID字段/列的默认名称为 id,如果实际存储中的底层表使用不同的名称,则可以通过 setIdColumnName 设置,通常在构造之后立即设置

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

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

在DataTable中设置列名不会更改底层数据库中的任何内容。它仅告诉DataTable实际数据库ID的名称。

也可以使用数组访问来创建新行

$dt[] = $newRow; 

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

可以在创建行时使用任意数量的字段/列和类型,只要这与实现和实际存储相符合。忽略额外数据或抛出错误取决于实现。

读取/搜索行

检查行是否存在

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

获取特定行

$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

获取所有行

$rows = $dt->getAllRows();

可能返回多行的所有方法都返回一个 DataTableIterator 对象。这是一个正常的PHP迭代器,可以在 foreach 语句中使用,并扩展了 count() 方法,该方法返回结果数。

迭代器还提供了一个方便的getFirst()方法,返回集合中的第一个结果。但是,无法保证在getFirst()之后对迭代器的foreach语句将正常工作,或者getFirst()foreach之后将正常工作,因为在特定实现中,回滚可能不可行。

$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
    

search方法根据以下规则在DataTable上执行基于搜索条件数组和搜索类型的通用搜索:

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 <= 0, all results will be returned

通常,可以使用简单的实用方法findRows来执行简单的列和值匹配。

public function findRows(array $rowToMatch, int $maxResults = 0) : array;

如果一行与$rowToMatch中每个键的值匹配,则它将作为结果集的一部分返回。这相当于为$rowToMatch中的每个键执行AND搜索,并具有EQUAL_TO条件。

更新行

$dt->updateRow($row);

// OR 

$dt[$rowId] = $row;

updateRow中,给定的行必须有一个id字段,该字段对应于表中的一行,否则将抛出RowDoesNotExist异常。在数组访问版本中,无论给定的行中是否有id字段,都会使用给定的$rowId

只有$row中的字段会被更新。如果底层数据库模式期望这些列的值,则不完整的行可能会产生错误。

删除行

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

// OR

unset($dt[$rowId]);

结果是受影响的列数,如果行最初就不存在,则该值为0。

事务

DataTables提供了一种基本接口,用于访问底层数据库事务功能(如果存在)。

要检查是否支持事务:

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

如果支持事务,可以使用startTransaction()开始事务,并使用commit()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();
  }
}

还有一个方便的方法来让DataTable检查底层数据库是否处于事务中。

$result = $dt->isUnderlyingDatabaseInTransaction();

在处理事务时,应格外小心,特别是当数据库连接在多个DataTable之间共享时。如果底层数据库报告正在执行事务(这可能或可能不可靠,取决于数据库),DataTable将不会开始事务。此外,提交只能在启动事务的DataTable上执行。

如果DataTable不支持事务,则startTransaction()commit()rollBack()始终返回false

InMemoryDataTable

这是一个使用简单PHP数组实现的DataTable实现,没有存储。这使得可以在不设置数据库的情况下对数据表进行测试。

MySqlDataTable

这是一个使用MySQL表实现的DataTable实现。

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

MySqlDataTable假定存在一个至少有一个具有给定名称的整数id列的表($idColumnName,默认为'id')。

如果$useAutoInc为true,则MySqlDataTable假定id列具有AUTO_INCREMENT属性,并将创建行,以便MySQL负责生成ID。否则,MySqlDataTable本身将负责生成增量ID。

为了与该库的早期版本兼容,$useAutoInc默认为false。但是,建议使用MySQL自动增量功能。在并发调用createRow的情况下,DataTable的内部ID生成器可能无法生成唯一的ID。

MySQL中的表可以有任何数量和类型的额外列。只要createRowupdateRow的调用与列名和类型一致,一切都应该正常工作。您还可以在MySQL中定义默认值,并在调用createRow时省略这些值。

MySqlDataTableWithRandomIds

MySqlDatable相同,但使用随机ID生成器。

MySqlUnitemporalDataTable

一个带有时间标记行的 MySQL 表。每一行不仅有一个唯一的 id,还有一个有效的 from 和 until 时间。当使用正常的 DataTable 方法时,MySQLUnitemporalDataTable 的行为与 MySqlDataTable 完全相同,但它不会删除任何行,只是使它们无效。

有一套时间方法用于创建、读取、更新和删除数据的旧版本。例如

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


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

$timeString 是一个格式化为有效 MySQL 日期时间的字符串,例如 '2018-01-01 12:00:00.123123' 使用 TimeString 类的静态方法从 MySQL 日期和日期时间字符串,以及带有或没有微秒的 UNIX 时间戳生成这样的字符串。

底层的 MySQL 表必须有两个日期时间字段:valid_fromvalid_until

用户负责设置 PDO 连接,以使用所有使用时间参数的查询中将要使用的时区。