定制 activecollab/databaseconnection 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

activecollab/databaseconnection

Composer 安装命令:

composer require activecollab/databaseconnection

包简介

A couple of simple helper methods wrapped around MySQLi connection

README 文档

README

Build Status

Purpose of this library is not to abstract the database, but to make work with MySQLi connections a bit easier. Features:

  1. Results that can be easily iterated,
  2. Results can be arrays of rows, or objects, loaded by a known class name, or by a class name read from the row field,
  3. Automatic value casting based on field name.

What's the thinking behind yet another database abstraction layer? Focus and history. This library has been part of Active Collab for many years, so it works really well. On the other hand, it's simple - works only with MySQL, can be read and understood in an hour and still manages to save you a lot of time.

Getting the Data

This library makes query execution quick and easy. You can fetch all records, only first record, only first column or only first cell (first column of the first record). Here's a couple of examples:

<?php

use ActiveCollab\DatabaseConnection\Connection\MysqliConnection;
use MySQLi;
use RuntimeException;

$database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test');

if ($database_link->connect_error) {
  throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error);
}

$connection = new MysqliConnection($database_link);

// List all writers
foreach ($connection->execute('SELECT `id`, `name` FROM `writers` WHERE `name` = ? ORDER BY `id`', 'Leo Tolstoy') as $row) {
  print '#' . $row['id'] . ' ' . $row['name'] . "\n";
}

// Get the first cell of the first row (so we can print Tolstoy's birthday)
print $connection->executeFirstCell('SELECT `birthday` FROM `writers` WHERE `name` = ? LIMIT 0, 1', 'Leo Tolstoy');

// Get everything that we have on Leo Tolstoy
print_r($connection->executeFirstRow('SELECT * FROM `writers` WHERE `name` = ?', 'Leo Tolstoy'));

// Show names of all authors
print_r($connection->executeFirstColumn('SELECT `name` FROM `writers` ORDER BY `name`'));

Selecting Records

To run SELECT queries from arguments, instead of writing your own SELECT query, use the following methods:

  1. select() - maps to execute(),
  2. selectFirstRow() - maps to executeFirstRow(),
  3. selectFirstColumn() - maps to executeFirstColumn(),
  4. selectFirstCell() - maps to executeFirstCell().

All methods accept following arguments:

  1. $table_name - Name of the table. This is the only required argument,
  2. $fields - List of fields that need to be fetched. Provide a string (single field), array of fields, or NULL (all fields),
  3. $conditions - Query conditions. Provide a string, an array (pattern + arguments), or NULL,
  4. $order_by_fields - List of fields that we want to order records by. Provide a string (single field), array of fields, or NULL (all fields).
$writers = $connection->select('writers', ['id', 'name'], ['birthday >= ?', '1821-11-11'], 'name');

Counting Records

DatabaseConnection lets you easily count records from the table:

$num = $connection->count('writers');

By default, it returns number of all records from the table. To filter, you can provide $conditions argument:

$num = $connection->count('writers', "`name` = 'Leo Tolstoy'");
$num = $this->connection->count('writers', ['name = ?', 'Leo Tolstoy']);

count() method also assumes that there's id primary key in the table, so it prepares query as COUNT(id). Name of the column that we count agains can also be changed (even to *):

$num = $this->connection->count('writers', null, '*');
$num = $this->connection->count('writers', null, 'name')

Running Queries from File

In order to run all queries from a file, use executeFromFile() method:

$connection->executeFromFile('/path/to/file');

Note: This method is not implemented to handle large data dumps. Use mysqldump instead, or another specialized backup utility instead.

Connection Factory

Quickest way to connect is to use ConnectionFactory class:

<?php

use ActiveCollab\DatabaseConnection\Connection\MysqliConnection;
use ActiveCollab\DatabaseConnection\ConnectionFactory;

$connection = (new ConnectionFactory())->mysqli('localhost', 'root', '', 'activecollab_database_connection_test', 'utf8mb4');

if ($connection instanceof MysqliConnection) {
    print_r($connection->getTableNames());
}

First three arguments are required (MySQL hostname, username and password). Additionally, this function accepts:

  1. Name of the database that needs to be selected. When not specified, database will not be selected,
  2. Connection encoding that we would like to enforce. When not specified, default connection encoding will be used.

Batch Inserts

Batch insert utility helps you prepare and insert a lot of rows of the same type. Example:

// Insert 3 rows per INSERT query in `writers` table
$batch_insert = $connection->batchInsert('writers', ['name', 'birthday'], 3);

$batch_insert->insert('Leo Tolstoy', new DateTime('1828-09-09')); // No insert
$batch_insert->insert('Alexander Pushkin', new DateTime('1799-06-06')); // No insert
$batch_insert->insert('Fyodor Dostoyevsky', new DateTime('1821-11-11')); // Insert
$batch_insert->insert('Anton Chekhov', new DateTime('1860-01-29')); // No insert

$batch_insert->done(); // Insert remaining rows and close the batch insert

Note: Calling insert, insertEscaped, flush or done methods once batch is done will throw RuntimeException.

Batch insert can also be used to replace records (uses REPLACE INTO instead of INSERT INTO queries):

$batch_replace = $this->connection->batchInsert('writers', ['name', 'birthday'], 3, ConnectionInterface::REPLACE);

Casting

Unless specified differently, following conventions apply:

  1. id and row_count fields are always cast to integers,
  2. Fields with name ending with _id are cast to integers,
  3. Fields with name starting with is_, has_, had_, was_, were_ and have_ are cast to boolean,
  4. Fields with name ending with _at or _on are cast to DateValue.

Spatial data

Library works with spatial data types, as defined by OpenGIS. Supported types:

  1. Point
  2. Multi Point
  3. Line String
  4. Multi Line String
  5. Linear Ring
  6. Polygon
  7. Multi Polygon
  8. Geometry Collection

Each supported type is defined as a separate value object, under \\ActiveCollab\DatabaseConnection\Spatial namespace. To specify which fields should be treated as spatial, instruct value caster to use \\ActiveCollab\DatabaseConnection\Record\ValueCasterInterface::CAST_SPATIAL. Example:

$rows = $this->connection->execute('SELECT ST_AsText(`geometry`) AS "geometry" FROM `example`');
$rows->setValueCaster(
    new ValueCaster(
        [
            'geometry' => ValueCasterInterface::CAST_SPATIAL,
        ]
    )
);

Object Hydration

This library enables quick and easy object hydration. To hydrate objects, you'll need a class that implements \ActiveCollab\DatabaseConnection\Record\LoadFromRow interface, for example:

<?php

use ActiveCollab\DatabaseConnection\Record\LoadFromRow;
use DateTime;

class Writer implements LoadFromRow
{
  /**
   * @var array
   */
  private $row;

  /**
   * @param array $row
   */
  public function loadFromRow(array $row)
  {
    $this->row = $row;
  }

  /**
   * @return integer
   */
  public function getId()
  {
    return $this->row['id'];
  }

  /**
   * @return string
   */
  public function getName()
  {
    return $this->row['name'];
  }

  /**
   * @return DateTime
   */
  public function getBirthday()
  {
    return new DateTime($this->row['birthday']);
  }
}

You can get a list of hydrated objects by passing the name of the class to advancedExecute() method:

<?php

use ActiveCollab\DatabaseConnection\Connection\MysqliConnection;
use DateTime;
use MySQLi;
use RuntimeException;

$database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test');

if ($database_link->connect_error) {
  throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error);
}

$connection = new MysqliConnection($database_link);

foreach ($this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_CLASS, '\ActiveCollab\DatabaseConnection\Test\Fixture\Writer') as $writer) {
  print '#' . $writer->getId() . ' ' . $writer->getName() . ' (' . $writer->getBirthday()->format('Y-m-d') . ')';
}

If you store objects of multiple types in the same table, you can tell advancedExecute() method where to look for the class name. This example assumes that we store the full class name in type field:

<?php

use ActiveCollab\DatabaseConnection\Connection\MysqliConnection;
use DateTime;
use MySQLi;
use RuntimeException;

$database_link = new MySQLi('localhost', 'root', '', 'activecollab_database_connection_test');

if ($database_link->connect_error) {
  throw new RuntimeException('Failed to connect to database. MySQL said: ' . $database_link->connect_error);
}

$connection = new MysqliConnection($database_link);

foreach ($this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_FIELD, 'type') as $writer) {
  print '#' . $writer->getId() . ' ' . $writer->getName() . ' (' . $writer->getBirthday()->format('Y-m-d') . ')';
}

Object Hydration + DIC

If objects that you are hydrating implement ActiveCollab\ContainerAccess\ContainerAccessInterface interface, and you pass an Interop\Container\ContainerInterface instance to advancedExecute() method, container will be provided to all instances that result hydrates:

use ActiveCollab\DatabaseConnection\Test\Fixture\Container;
use ActiveCollab\DatabaseConnection\Test\Fixture\WriterWithContainer;
use Psr\Container\ContainerInterface;

$container = new Container([
    'dependency' => 'it works!'
]);

$result = $this->connection->advancedExecute('SELECT * FROM `writers` ORDER BY `id`', null, ConnectionInterface::LOAD_ALL_ROWS, ConnectionInterface::RETURN_OBJECT_BY_CLASS, WriterWithContainer::class, null, $container);

foreach ($result as $writer) {
    print $writer->dependency . "\n"; // Prints 'it works!'
}

Helper Methods

Database connection instance offers a couple of methods that are helpful in everyday development:

  1. databaseExists($database_name) - Return TRUE if database with a given name exists,
  2. userExists($user_name) - Return TRUE if user with a given name exists.
  3. getTableNames() - Return names of all tables that are in a database taht we are connected to,
  4. tableExists($table_name) - Return TRUE if table with a given name exists in a database that we are connected to,
  5. dropTable($table_name) - Drop a table.

Tests

To test a library you need to manually create a database:

CREATE DATABASE activecollab_database_connection_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;

Then from a project root execute following command:

phpunit

To do

  1. Use prepared statements for all queries that have extra arguments,
  2. Enable library to use two database connections, one for writes, and the other for reads,
  3. Properly handle MySQL has gone away errors and deadlocks (stubbed).
  4. Consider support MySQL Native Driver, and features that it enables

activecollab/databaseconnection 适用场景与选型建议

activecollab/databaseconnection 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 77.66k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2015 年 08 月 12 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「mysql」 「activecollab」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 activecollab/databaseconnection 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 activecollab/databaseconnection 我们能提供哪些服务?
定制开发 / 二次开发

基于 activecollab/databaseconnection 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 77.66k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 30
  • 依赖项目数: 6
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 2
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-08-12