数据库操作 Zend\Db\Adapter

Adapter 对象是 Zend\Db 最重要的子组件。它负责让 Zend\Db 的代码适应目标 PHP 扩展和数据库。以这种方式,它为 PHP 扩展创建了一个抽象层,被称为 Zend\Db 适配器的“Driver(驱动)”部分。它也为厂商指定的 SQL/RDBMS 实现平台创建了一个轻量级的抽象层,被称为适配器的“Platform(平台)”部分。

创建适配器——快速入门(Creating an Adapter – Quickstart)

实例化 Zend\Db\Adapter\Adapter 类就可以简单地创建一个适配器。最常见但不是最明显的使用情况,是像适配器传入一个配置项数组。

1
$adapter = new Zend\Db\Adapter\Adapter($configArray);

此驱动数组是扩展层所需参数的抽象。下面表格包含了配置数组中可用的“键-值”对。

要求?
driver要求Mysqli, Sqlsrv, Pdo_Sqlite, Pdo_Mysql, Pdo=OtherPdoDriver
database通常要求数据库(架构)名称
username通常要求链接的用户名
password通常要求链接的密码
hostname一般不要求链接的IP地址或主机名
port一般不要求the port to connect to (if applicable)
charset一般不要求the character set to use

注意

其他名称也可以正常工作。实际上,如果 PHP 手册使用特定的命名,我们的 Diver 都会支持此命名。例如,dbname 大多数情况下可以取替“database”。另一个示例,在 Sqlsrv 的情况下,UID 会替换点 username。选择哪种格式取决于你,上面的表格代表了官方的抽象名称。

 

例如,使用 ext/mysqli 的 MYSQL 链接:

1
2
3
4
5
6
 $adapter = new Zend\Db\Adapter\Adapter(array(
    'driver' => 'Mysqli',
    'database' => 'zend_db_example',
    'username' => 'developer',
    'password' => 'developer-password'
 ));

另一个示例,使用 PDO 的 Sqlite 链接:

1
2
3
4
 $adapter = new Zend\Db\Adapter\Adapter(array(
    'driver' => 'Pdo_Sqlite',
    'database' => 'path/to/sqlite.db'
 ));

重要的是要知道,通过使用这种创建适配器的风格,Adapter 将试图创建任何没有明确提供的依赖。Driver 对象将由构造方法中的配置数组创建。Platform 对象会根据已实例化 Driver 类的类型而创建。最后,一个默认的 ResultSet 对象将创建并应用。这些对象都将被注入,要做到这一点,请看下一节内容。

官方支持的驱动的列表:

  • Mysqli :ext/mysql 驱动
  • Pgsql:ext/pgsql 驱动
  • Sqlsrv:ext/sqlsrv 驱动(微软的)
  • Pdo_Mysql: MySQL PDO 扩展
  • Pdo_Sqlite:SQLite PDO 扩展
  • Pdo_Pgsql:PostgreSQL PDO 扩展

使用依赖注入创建适配器(Creating an Adapter Using Dependency Injection)

创建适配器更具表象力、更明确方式是预先注入所有的依赖。Zend\Db\Adapter\Adapter 使用构造方法注入,所有需要的依赖都是通过构造方法注入的,具有以下签名(伪代码):

1
2
3
4
5
6
use Zend\Db\Adapter\Platform\PlatformInterface;
use Zend\Db\ResultSet\ResultSet;

class Zend\Db\Adapter\Adapter {
    public function __construct($driver, PlatformInterface $platform = null, ResultSet $queryResultSetPrototype = null)
}

可以注入的内容:

  • $driver – 一个由连接参数组成的数组(见上文),或者 Zend\Db\Adapter\Driver\DriverInterface 的实例。
  • $platform – (可选)Zend\Db\PlatformInterface 的实例,默认会根据 driver 实现创建。
  • $queryResultSetPrototype – (可选)Zend\Db\ResultSet\Resultset 的实例,要了解这个对象的角色,阅读下面通过适配器查询一节。

通过 Zend\Db\Adapter\Adapt::query()查询预处理(Query Preparation Through Zend\Db\Adapter\Adapter::query())

默认情况下,query() 代表你使用“预处理”作为执行 SQL 语句的方式。这通常意味这你将提供一个以占位符代替值的 SQL 语句,然后分别提供这些占位符的值。使用Zend\Db\Adapter\Adapter 示例:

1
$adapter->query('SELECT * FROM `artist` WHERE `id` = ?', array(5));

上面实例将通过以下步骤:

  • 创建一个新的 Statement 对象
  • 如果有必要,准备一个进入 ParameterContainer 的数组
  • 将 ParameterContainer 注入到 Statement 对象中
  • 执行 Statement 对象,生成一个 Result 对象
  • 检查 Result 对象,检查已提供的 sql 是一条“query”还是一个生成语句的结果集
  • 如果为生成查询的结果集,克隆 ResultSet 原型,将 Result 作为数据源注入,返回
  • 否则,返回Result

创建一个新的 Statement 对象 如果有必要,预备一个传入 ParameterContainer 中的数组 将 ParameterContainer 注入进 Statement 对象中 执行 Statement 对象,产生一个 Result 对象

通过 Zend\Db\Adapter\Adapter::query()查询执行(Query Execution Through Zend\Db\Adapter\Adapter::query())

某些情况下,你需要直接执行一条语句。执行 sql 语句而不是执行预处理的目的可能是引文你尝试执行一条 DDL 语句(数据定义语言)。

1
$adapter->query('ALTER TABLE ADD INDEX(`foo_index`) ON (`foo_column`)', Adapter::QUERY_MODE_EXECUTE);

主要的区别是,你必须提供 Adapter::Query_MODE_EXECUTE作为第二个参数。

创建语句(Creating Statements)

虽然 query() 对通过 Adapter 的一次性快速的数据库查询很有用,通常创建语句并直接与其交互是明智的,那样你就对“预处理-然后-执行”工作流程有了更大的控制权。要做到这点,Adapter 给了你一段叫做 createStatement()的程序,允许你创建一个 Driver 特定的 Statement,因此你可以管理你自己的“预处理-然后-执行”的工作流程。

1
2
3
// with optional parameters to bind up-front
$statement = $adapter->createStatement($sql, $optionalParameters);
$result = $statement->execute();

使用驱动对象(Using the Driver Object)

Zend\Adapter\Adapter 主要在 Driver 对象中通过各种 ext/mysqli、ext/sqlsrv、PDO、PHP级驱动完成实现连接级抽象的工作,从而让使用 ZendDb 的所有接口变成了可能。为了做到这点,每个驱动都得由三个对象组成:

  • 连接:Zend\Db\Adapter\Driver\ConnectionInterface
  • 语句:Zend\Db\Adapter\Driver\StatementInterface
  • 结果:Zend\Db\Adapter\Driver\ResultInterface

当有新的实例被请求时,每个内置的驱动都会将“原型设计”作为创建对象的方式。工作流程如下:

  • 使用连接参数集合创建适配器
  • 适配器选择适当的驱动进行实例化,例如 Zend\Db\Adapter\Driver\Mysqli
  • 驱动类被实例化
  • 如果没有连接,statement 或 result 对象被注入,默认的被实例化

当请求特定的工作流程时,此驱动就可以被调用了。下面是 Driver API 大致的样子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
namespace Zend\Db\Adapter\Driver;

 interface DriverInterface
 {
     const PARAMETERIZATION_POSITIONAL = 'positional';
     const PARAMETERIZATION_NAMED = 'named';
     const NAME_FORMAT_CAMELCASE = 'camelCase';
     const NAME_FORMAT_NATURAL = 'natural';
     public function getDatabasePlatformName($nameFormat = self::NAME_FORMAT_CAMELCASE);
     public function checkEnvironment();
     public function getConnection();
     public function createStatement($sqlOrResource = null);
     public function createResult($resource);
     public function getPrepareType();
     public function formatParameterName($name, $type = null);
     public function getLastGeneratedValue();
 }

从此 DriverInterface 接口出发,你可以

  • 确定此驱动支持的平台的名称(用于选择正确的 platform 对象)
  • 检验环境是否支持此驱动
  • 返回 Connection 对象
  • 创建 Statement 对象,可选,由 SQL 语句植入(这通常是一个原型的 statement 对象的克隆)
  • 创建 Result 对象 ,可选,由语句资源植入(这通常是一个原型的 result 对象的克隆)
  • 格式化参数名,注意区分扩展间参数命名方式的不同。
  • 检索最后产生的值(例如自动递增值)

Statement 对象大致是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
namespace Zend\Db\Adapter\Driver;

interface StatementInterface extends StatementContainerInterface
{
    public function getResource();
    public function prepare($sql = null);
    public function isPrepared();
    public function execute($parameters = null);

    /** Inherited from StatementContainerInterface */
    public function setSql($sql);
    public function getSql();
    public function setParameterContainer(ParameterContainer $parameterContainer);
    public function getParameterContainer();
}

Result 对象大致是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
namespace Zend\Db\Adapter\Driver;

interface ResultInterface extends \Countable, \Iterator
{
    public function buffer();
    public function isQueryResult();
    public function getAffectedRows();
    public function getGeneratedValue();
    public function getResource();
    public function getFieldCount();
}

使用 Platform 对象(Using The Platform Object)

Platform 对象提供了一个 API ,以针对特定平台特定的 SQL 实现的方式协助执行查询。细微之处如怎样引用标识符或值,或者标识符分隔符字符是什么都是由这个对象处理的。要做到这些,platform 对象的接口大致是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace Zend\Db\Adapter\Platform;

interface PlatformInterface
{
    public function getName();
    public function getQuoteIdentifierSymbol();
    public function quoteIdentifier($identifier);
    public function quoteIdentifierChain($identiferChain)
    public function getQuoteValueSymbol();
    public function quoteValue($value);
    public function quoteValueList($valueList);
    public function getIdentifierSeparator();
    public function quoteIdentifierInFragment($identifier, array $additionalSafeWords = array());
}

虽然可以实例化自己的 Platform 对象,但一般来讲,从已配置的适配器中获取适当的 Platform 实例更容易些(默认情况下,Plateform 对象将匹配底层的驱动实现):

1
2
3
$platform = $adapter->getPlatform();
// 或者
$platform = $adapter->platform; // 魔术属性访问

下面是使用 Platform 的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/** @var $adapter Zend\Db\Adapter\Adapter */
/** @var $platform Zend\Db\Adapter\Platform\Sql92 */
$platform = $adapter->getPlatform();

// "first_name"
echo $platform->quoteIdentifier('first_name');

// "
echo $platform->getQuoteIdentifierSymbol();

// "schema"."mytable"
echo $platform->quoteIdentifierChain(array('schema','mytable')));

// '
echo $platform->getQuoteValueSymbol();

// 'myvalue'
echo $platform->quoteValue('myvalue');

// 'value', 'Foo O\\'Bar'
echo $platform->quoteValueList(array('value',"Foo O'Bar")));

// .
echo $platform->getIdentifierSeparator();

// "foo" as "bar"
echo $platform->quoteIdentifierInFragment('foo as bar');

// additionally, with some safe words:
// ("foo"."bar" = "boo"."baz")
echo $platform->quoteIdentifierInFragment('(foo.bar = boo.baz)', array('(', ')', '='));

使用 Parameter 容器(Using The Parameter Container)

ParameterContainer 对象是一个存放各种参数的容器,这些对象需要传到 Statement 对象中以填充 SQL 语句的所有参数化的部分。此对象实现了 ArrayAccess 接口。下面是 ParameterContainer API:

namespace Zend\Db\Adapter;

 class ParameterContainer implements \Iterator, \ArrayAccess, \Countable {
     public function __construct(array $data = array())

     /** methods to interact with values */
     public function offsetExists($name)
     public function offsetGet($name)
     public function offsetSetReference($name, $from)
     public function offsetSet($name, $value, $errata = null)
     public function offsetUnset($name)

     /** set values from array (will reset first) */
     public function setFromArray(Array $data)

     /** methods to interact with value errata */
     public function offsetSetErrata($name, $errata)
     public function offsetGetErrata($name)
     public function offsetHasErrata($name)
     public function offsetUnsetErrata($name)

     /** errata only iterator */
     public function getErrataIterator()

     /** get array with named keys */
     public function getNamedArray()

     /** get array with int keys, ordered by position */
     public function getPositionalArray()

     /** iterator: */
     public function count()
     public function current()
     public function next()
     public function key()
     public function valid()
     public function rewind()

     /** merge existing array of parameters with existing parameters */
     public function merge($parameters)
 }

除了处理参数的名称和值,容器也会协助追踪参数类型,将PHP类型转为 SQL 可处理的类型。例如,这可能很重要:

$container->offsetSet('limit', 5);

作为整型绑定。要做到这一点,传入 ParameterContainer::TYPE_INTEGER 常量作为第三个参数:

$container->offsetSet('limit', 5, $container::TYPE_INTEGER);

这将确保如果底层驱动支持参数类型约束,此转译过的信息也将一并传入到实际的 PHP 数据库驱动中。

示例(Examples)

创建一个 Driver 和 轻巧的查询,预处理并迭代结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$adapter = new Zend\Db\Adapter\Adapter($driverConfig);

$qi = function($name) use ($adapter) { return $adapter->platform->quoteIdentifier($name); };
$fp = function($name) use ($adapter) { return $adapter->driver->formatParameterName($name); };

$sql = 'UPDATE ' . $qi('artist')
    . ' SET ' . $qi('name') . ' = ' . $fp('name')
    . ' WHERE ' . $qi('id') . ' = ' . $fp('id');

/** @var $statement Zend\Db\Adapter\Driver\StatementInterface */
$statement = $adapter->query($sql);

$parameters = array(
    'name' => 'Updated Artist',
    'id' => 1
);

$statement->execute($parameters);

// DATA INSERTED, NOW CHECK

/* @var $statement Zend\Db\Adapter\DriverStatementInterface */
$statement = $adapter->query('SELECT * FROM '
    . $qi('artist')
    . ' WHERE id = ' . $fp('id'));

/* @var $results Zend\Db\ResultSet\ResultSet */
$results = $statement->execute(array('id' => 1));

$row = $results->current();
$name = $row['name'];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值