Yii2.0 API改造(返回数据库对应字段数据类型)
Posted 冰狼爱魔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Yii2.0 API改造(返回数据库对应字段数据类型)相关的知识,希望对你有一定的参考价值。
【改造背景】
使用Yii2.0开发api时,使用asArray()函数时,框架本身默认将所有数据类型都转换成字符串处理。在与前端强类型语言交互时极不方便。所以希望改造框架返回数据表对应字段类型,而无需每次手工进行转换。
【改造步骤】
1、数据库连接配置attributes中增加两个PDO参数:
return [ \'class\' => \'common\\ext\\ConnectionExt\', // 这里使用自定义数据库连接拓展类 \'dsn\' => \'mysql:host=127.0.0.1;dbname=test\', \'username\' => \'root\', \'password\' => \'123456\', \'charset\' => \'utf8mb4\', \'enableSchemaCache\' => true, \'schemaCacheDuration\' => 60, \'enableLogging\' => true, \'commandClass\' => \'common\\ext\\CommandExt\', \'attributes\' => [ PDO::ATTR_STRINGIFY_FETCHES => false, PDO::ATTR_EMULATE_PREPARES => false, ] ];
2、Yii2.0 (use version:2.0.31)嵌套事务改造:
重要说明:因为增加了上面两个PDO参数后,Yii2.0的原有嵌套事务执行会报错,主要原因是框架本身的嵌套事务依赖于模拟预处理。所以还需要下面的改造处理。
继承覆写原有的数据库连接类yii\\db\\Connection:
<?php namespace common\\ext; use yii\\db\\Connection; /** * 数据库连接拓展类 * * Class ConnectionExt * @package common\\ext */ class ConnectionExt extends Connection { /** * @var Transaction the currently active transaction */ private $_transaction; /** * Returns the currently active transaction. * @return Transaction|null the currently active transaction. Null if no active transaction. */ public function getTransaction() { return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null; } /** * Starts a transaction. * @param string|null $isolationLevel The isolation level to use for this transaction. * See [[Transaction::begin()]] for details. * @throws * @return Transaction the transaction initiated */ public function beginTransaction($isolationLevel = null) { $this->open(); if (($transaction = $this->getTransaction()) === null) { $transaction = $this->_transaction = new TransactionExt([\'db\' => $this]); // 这里实例新的事务类 } $transaction->begin($isolationLevel); return $transaction; } }
继承覆写事务处理类yii\\db\\Transaction:
<?php namespace common\\ext; use Yii; use yii\\base\\InvalidConfigException; use yii\\base\\NotSupportedException; use yii\\db\\Connection; use yii\\db\\Exception; use yii\\db\\Transaction; /** * 事务拓展类 * * Class TransactionExt * @package common\\ext */ class TransactionExt extends Transaction { /** * @var int the nesting level of the transaction. 0 means the outermost level. */ private $_level = 0; /** * Returns a value indicating whether this transaction is active. * @return bool whether this transaction is active. Only an active transaction * can [[commit()]] or [[rollBack()]]. */ public function getIsActive() { return $this->_level > 0 && $this->db && $this->db->isActive; } /** * 覆写开始事务method * * @param string|null $isolationLevel The [isolation level][] to use for this transaction. * This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but * also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`. * If not specified (`null`) the isolation level will not be set explicitly and the DBMS default will be used. * * > Note: This setting does not work for PostgreSQL, where setting the isolation level before the transaction * has no effect. You have to call [[setIsolationLevel()]] in this case after the transaction has started. * * > Note: Some DBMS allow setting of the isolation level only for the whole connection so subsequent transactions * may get the same isolation level even if you did not specify any. When using this feature * you may need to set the isolation level for all transactions explicitly to avoid conflicting settings. * At the time of this writing affected DBMS are MSSQL and SQLite. * * [isolation level]: http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels * * Starting from version 2.0.16, this method throws exception when beginning nested transaction and underlying DBMS * does not support savepoints. * @throws InvalidConfigException if [[db]] is `null` * @throws NotSupportedException if the DBMS does not support nested transactions * @throws Exception if DB connection fails */ public function begin($isolationLevel = null) { if ($this->db === null) { throw new InvalidConfigException(\'Transaction::db must be set.\'); } $this->db->open(); if ($this->_level === 0) { if ($isolationLevel !== null) { $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel); } Yii::debug(\'Begin transaction\' . ($isolationLevel ? \' with isolation level \' . $isolationLevel : \'\'), __METHOD__); $this->db->trigger(Connection::EVENT_BEGIN_TRANSACTION); $this->db->pdo->beginTransaction(); $this->_level = 1; return; } $schema = $this->db->getSchema(); if ($schema->supportsSavepoint()) { Yii::debug(\'Set savepoint \' . $this->_level, __METHOD__); // Notice: solute error: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, true); $schema->createSavepoint(\'LEVEL\' . $this->_level); $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, false); } else { Yii::info(\'Transaction not started: nested transaction not supported\', __METHOD__); throw new NotSupportedException(\'Transaction not started: nested transaction not supported.\'); } $this->_level++; } /** * 覆写提交事务method * * Commits a transaction. * @throws \\Exception */ public function commit() { if (!$this->getIsActive()) { throw new Exception(\'Failed to commit transaction: transaction was inactive.\'); } $this->_level--; if ($this->_level === 0) { Yii::debug(\'Commit transaction\', __METHOD__); $this->db->pdo->commit(); $this->db->trigger(Connection::EVENT_COMMIT_TRANSACTION); return; } $schema = $this->db->getSchema(); if ($schema->supportsSavepoint()) { Yii::debug(\'Release savepoint \' . $this->_level, __METHOD__); $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, true); $schema->releaseSavepoint(\'LEVEL\' . $this->_level); $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, false); } else { Yii::info(\'Transaction not committed: nested transaction not supported\', __METHOD__); } } /** * 覆写回滚事务method * * Rolls back a transaction. */ public function rollBack() { if (!$this->getIsActive()) { // do nothing if transaction is not active: this could be the transaction is committed // but the event handler to "commitTransaction" throw an exception return; } $this->_level--; if ($this->_level === 0) { Yii::debug(\'Roll back transaction\', __METHOD__); $this->db->pdo->rollBack(); $this->db->trigger(Connection::EVENT_ROLLBACK_TRANSACTION); return; } $schema = $this->db->getSchema(); if ($schema->supportsSavepoint()) { Yii::debug(\'Roll back to savepoint \' . $this->_level, __METHOD__); $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, true); $schema->rollBackSavepoint(\'LEVEL\' . $this->_level); $this->db->pdo->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, false); } else { Yii::info(\'Transaction not rolled back: nested transaction not supported\', __METHOD__); } } }
【补充说明】
1、PDO相关参数说明:
array( // 强制 PDO 获取的表字段字符的大小写转换,或原样使用列信息 PDO::ATTR_CASE => PDO::CASE_LOWER, // 执行出错时抛出异常 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 将返回的空字符串转换为 SQL 的 NULL PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, // 返回数据的时候不将数值转换为字符串 PDO::ATTR_STRINGIFY_FETCHES => false, // 设置为false禁止PDO模拟预处理语句,而使用真正的预处理语句,即有MySQL执行预处理语句 PDO::ATTR_EMULATE_PREPARES => false, ));
2、此api改造不受框架对数据类型的处理,参考文章:https://www.cnblogs.com/itsharehome/p/12275979.html
以上是关于Yii2.0 API改造(返回数据库对应字段数据类型)的主要内容,如果未能解决你的问题,请参考以下文章
阿里规范 - MySQL 数据库 - ORM映射 - 3 - 强制不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应