Как использовать CLOB в Oracle 11g с Yii Framework
У меня есть объект в Yii, сопоставленный с таблицей ведьма содержит один столбец CLOB.
Как я могу вставить с помощью метода save() вызов строки длиной ~54k символов в строку?
2 ответа
У Yii есть методы событий beforeSave() и afterSave() в его моделях ActiveRecord. Я бы использовал их вместо переопределения метода save(). Поместите туда все необходимые методы Oracle. В основном: обнулите поля в beforeSave(), затем в afterSave() запишите значения в БД с помощью OCIParse() и т. Д.
Вот хорошая статья в блоге об этом с использованием фреймворка Cake PHP, который очень похож (MVC с методами до и после Save). Вам, очевидно, нужно будет изменить этот код для работы с Yii, но он должен привести вас на правильный путь:
http://nik.chankov.net/2008/01/03/cakephp-and-oracle-handling-clob-fields/
В последней версии Yii теперь также есть построитель запросов, который может помочь в коде afterSave(), который вам нужно будет написать:
http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder
Удачи!
У меня это сработало, но я мало хвалю, я начинаю адаптацию MySQL к ORACLE. Сумасшедший!!!
Это бета-версия, если можно улучшить, это поможет.
перезапишите функцию __get в Model.
класс Entidad расширяет \ common \myclass\MyActiveRecord{.......
public function __get($name) {
$columna = $this->getTableSchema()->getColumn($name);
$current = parent::__get($name);
if (isset($columna->dbType) && $columna->dbType=='CLOB') {
if (is_string($current)) {
return $current;
}
else if (is_null($current)) {
return $current;
}
else if ($this->isAttributeChanged($name)) {
return $this->getAttribute($name);
}
else {
$valor = stream_get_contents($current);
$this->setAttribute($name,$valor);
return $this->getAttribute($name);
}
} else {
return $current;
}
}
.....
}
Я решаю это только сейчас. Мне также нужно использовать BLOB в Oracle 11g, я хочу вставить изображение в таблицу Oracle.
Я обыскиваю его по всей сети, и, наконец, я нашел, вероятно, решение при вводе описания ссылки здесь.
пожалуйста, щелкните по нему.
Я следую примеру № 2
<?php
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // some function to allocate a new ID
// assume that we are running as part of a file upload form
// You can find more information in the PHP documentation
$fp = fopen($_FILES['file']['tmp_name'], 'rb');
$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES['file']['type']);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$db->beginTransaction();
$stmt->execute();
$db->commit();
?>
Я получаю
$db = Yii::app()->db->getPdoInstance()
вместо
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
и все остальное одинаково.
Удачи!
В нашем проекте также были проблемы с чтением и записью в LOB-поля при использовании yii2 и Oracle DB. В результате на данный момент я пришел к решению написать класс, переопределяющий некоторые методы класса ActiveRecord в yii2, и наследовать некоторые модели не от ActiveRecord, а от него. Я сделал его относительно универсальным, за основу можно взять https://github.com/pivasikkost/yii2-improvements/blob/master/models/ActiveRecordOciLob.php.
<?php
namespace app\models\system;
use Yii;
use \yii\db\ActiveRecord;
use \yii\db\Query;
/**
* ActiveRecordOciLob is the base class for classes representing relational data in terms of objects,
* that should work correctly with LOB data in Oracle DB. Used Oci8Connection (in my case - neconix\src\Oci8Connection).
*
* Just inherit your gii generated model class not from ActiveRecord, but from this class,
* set $clob_attributes, $blob_attributes and $dbOciLobName to work.
*
* @property array $clob_attributes an array of attribute names of the form "['name_1', 'name_2', ...]"
* @property array $blob_attributes an array of attribute names of the form "['name_1', 'name_2', ...]"
* @property array $primaryKeyOciLob an array of attribute names of the form "['name_1', 'name_2', ...]"
* @property string $dbOciLobName Oci8Connection name
*
* @author Konstantin Zosimenko <pivasikkost@gmail.com>
* @since 2.0
*/
class ActiveRecordOciLob extends ActiveRecord
{
// METHOD 4. Almost completed, it works!
public static $clob_attributes = [];
public static $blob_attributes = [];
public static $dbOciLobName;
public $primaryKeyOciLob; // Not static! for some reason, being static, when saving one descendant of this class from another descendant of this class, this variable is overwritten and takes the wrong value
/**
* @inheritdoc
*/
public function init()
{
parent::init();
$this->primaryKeyOciLob = static::primaryKey();
}
/**
* @return \yii\db\Connection the database connection used by this AR class.
*/
public static function getDbOciLob()
{
return static::$dbOciLobName
? Yii::$app->get(static::$dbOciLobName)
: static::getDb()
;
}
/**
* Override ActiveRecord afterFind() method to fix LOB data receiveing
* Maybe it's better to do this in populateRecord() method
*
* @inheritdoc
*/
public function afterFind ()
{
// Get lob fields as string, because standardly ActiveRecord gets them as resource
$lob_attributes = array_merge(static::$clob_attributes, static::$blob_attributes);
$where = [];
foreach ($this->primaryKeyOciLob as $attribute) {
$where[$attribute] = $this->$attribute;
}
foreach ($lob_attributes as $lob_attribute) {
//$this->$lob_attribute = stream_get_contents($this->$lob_attribute); // Does not work, for some reason always returns 1 value if you try to get multiple records
$this->$lob_attribute = (new Query())
->select($lob_attribute)
->from(static::tableName())
->where($where)
->createCommand(static::getDbOciLob())
->queryScalar();
$this->setOldAttribute($lob_attribute, $this->$lob_attribute);
}
parent::afterFind ();
}
/**
* Override ActiveRecord update() method to fix LOB data update
*
* @inheritdoc
*/
public function update($runValidation = true, $attributeNames = null)
{
if ($runValidation && !$this->validate($attributeNames)) {
Yii::info('Model not updated due to validation error.', __METHOD__);
return false;
}
if (!$this->beforeSave(false)) {
return false;
}
$db = static::getDbOciLob()->getDbh();
$fields_blob = static::$blob_attributes;
$fields_clob = static::$clob_attributes; // array with the fields to be updated
$fields_dirty = $this->getDirtyAttributes($attributeNames); // changed fields with values
$exist_fields_dirty_clob = array_values(
array_intersect(array_keys($fields_dirty), $fields_clob)
);
if (empty($fields_dirty)) {
$this->afterSave(false, $fields_dirty);
return 0;
}
$set = [];
$into = [];
foreach ($fields_dirty as $name => $value) {
if (in_array($name, $fields_clob)) {
$set[] = $name . " = EMPTY_CLOB()";
$into[] = ":" . $name;
/*} elseif (in_array($name, $fields_blob)) {
$set[] = $name . " = EMPTY_BLOB()";
$into[] = ":" . $name;*/
} else {
$set[] = $name . " = :" . $name;
}
}
$set_stmt = implode(", ", $set); // array to string to fill 'set' clause in the sql
$where = [];
foreach ($this->primaryKeyOciLob as $attribute) {
$where[] = $attribute . "=" .$this->$attribute;
}
$where_stmt = implode(" AND ", $where);
//$returning = implode(", ", array_merge($fields_clob, $fields_blob));
$returning = implode(", ", $exist_fields_dirty_clob); // array to string to fill 'returning' clause in the sql
$into_stmt = implode(", ", $into); // array to string to fill 'into' clause in the sql
$sql = "UPDATE " . static::tableName() . "
SET " . $set_stmt . "
WHERE " . $where_stmt
;
if ($returning && $into_stmt) {
$sql .= " RETURNING " . $returning . "
INTO " . $into_stmt
;
}
$stmt = oci_parse($db, $sql);
$my_lob = [];
// just see http://www.oracle.com/technology/pub/articles/oracle_php_cookbook/fuecks_lobs.html
// you'll get it i'm sure
foreach ($into as $key => $value) {
$my_lob[$key] = oci_new_descriptor($db, OCI_D_LOB);
oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_CLOB);
//oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_BLOB);
}
foreach ($fields_dirty as $name => $value) { // don't use $value in oci_bind_by_name! the link inside this variable is changing
if (!in_array($name, $fields_clob)) {
oci_bind_by_name($stmt, ":".$name, $fields_dirty[$name]);
}
}
$result = oci_execute($stmt, OCI_DEFAULT); // or die ("Unable to execute query\n"); //echo oci_error()
if ($result === false) {
oci_rollback($db);
return false;
}
if ($exist_fields_dirty_clob) {
foreach ($exist_fields_dirty_clob as $key => $name) {
//if (!$my_lob[$key]->savefile( Yii::getAlias('@webroot/uploads/') . $model->files[0]->name )) {
if (!$my_lob[$key]->save($this->$name)) {
oci_rollback($db);
return false; //die("Unable to update clob\n");
}
}
}
oci_commit($db);
//$my_lob[$key]->free();
oci_free_statement($stmt);
oci_close($db); // not sure
$changedAttributes = [];
$oldArrtibutes = $this->getOldAttributes();
foreach ($fields_dirty as $name => $value) {
$changedAttributes[$name] = isset($oldArrtibutes[$name]) ? $oldArrtibutes[$name] : null;
$this->setOldAttribute($name, $value);
}
$this->afterSave(false, $changedAttributes);
return $result;
}
/**
* Override ActiveRecord insert() method to fix LOB data insertion
*
* @inheritdoc
*/
public function insert($runValidation = true, $attributes = null)
{
//return parent::insert($runValidation, $attributes);
if ($runValidation && !$this->validate($attributes)) {
Yii::info('Model not inserted due to validation error.', __METHOD__);
return false;
}
if (!$this->beforeSave(true)) {
return false;
}
$db = static::getDbOciLob()->getDbh();
//$fields_blob = static::$blob_attributes;
$fields_clob = static::$clob_attributes; // array with the fields to be updated
$fields_dirty = $this->getDirtyAttributes($attributes); // changed fields with values
$fields_dirty_names = array_keys($fields_dirty);
$exist_fields_dirty_clob = array_intersect(array_keys($fields_dirty), $fields_clob);
$values = [];
$into = [];
foreach ($fields_dirty as $name => $value) {
if (in_array($name, $fields_clob)) {
$values[] = "EMPTY_CLOB()";
$into[] = ":" . $name;
/*} elseif (in_array($name, $fields_blob)) {
$values[] = "EMPTY_BLOB()";
$into[] = ":" . $name;*/
} else {
$values[] = ":" . $name;
}
}
$fields_stmt = implode(", ", $fields_dirty_names);
$values_stmt = implode(", ", $values);
//$returning = implode(", ", $fields_clob + $fields_blob);
$returning = implode(", ", $fields_clob);
$into_stmt = implode(", ", $into);
$sql = "INSERT INTO " . static::tableName() . "
(" . $fields_stmt . ")
VALUES(" . $values_stmt . ")
";
if ($returning && $into_stmt) {
$sql .= " RETURNING " . $returning . "
INTO " . $into_stmt
;
}
$stmt = oci_parse($db, $sql);
$my_lob = [];
// just see http://www.oracle.com/technology/pub/articles/oracle_php_cookbook/fuecks_lobs.html
// you'll get it i'm sure
foreach ($into as $key => $value) {
$my_lob[$key] = oci_new_descriptor($db, OCI_D_LOB);
oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_CLOB);
//oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_BLOB);
}
foreach ($fields_dirty as $name => $value) { // don't use $value in oci_bind_by_name!
if (!in_array($name, $fields_clob)) {
oci_bind_by_name($stmt, ":".$name, $fields_dirty[$name]);
}
}
$result = oci_execute($stmt, OCI_DEFAULT); //or die ("Unable to execute query\n"); //echo oci_error()
if ($result === false) {
oci_rollback($db);
return false;
}
if ($exist_fields_dirty_clob) {
foreach ($fields_clob as $key => $name) {
//if (!$my_lob[$key]->savefile( Yii::getAlias('@webroot/uploads/') . $model->files[0]->name )) {
if (!$my_lob[$key]->save($this->$name)) {
oci_rollback($db);
return false; //die("Unable to update clob\n");
}
}
}
oci_commit($db);
//$my_lob[$key]->free();
oci_free_statement($stmt);
oci_close($db); // not sure
// Set primary key for new record
$orderBy = [];
foreach ($this->primaryKeyOciLob as $attribute) {
$orderBy[$attribute] = SORT_DESC;
}
foreach ($this->primaryKeyOciLob as $attribute) {
$fields_dirty[$attribute] = $this->find()
->orderBy($orderBy)
->limit(1)
->one()
->$attribute;
}
foreach ($fields_dirty as $name => $value) {
$id = static::getTableSchema()->columns[$name]->phpTypecast($value);
$this->setAttribute($name, $id);
$fields_dirty[$name] = $id;
}
$changedAttributes = array_fill_keys(array_keys($fields_dirty), null);
$this->setOldAttributes($fields_dirty);
$this->afterSave(true, $changedAttributes);
return $result;
}