Your IP : 3.136.25.185
<?php
/**
* Bitrix Framework
* @package bitrix
* @subpackage highloadblock
* @copyright 2001-2014 1C-Bitrix
*/
namespace Bitrix\Highloadblock;
use Bitrix\Main,
Bitrix\Main\Application,
Bitrix\Main\DB\MssqlConnection,
Bitrix\Main\Entity;
Main\Localization\Loc::loadLanguageFile(__FILE__);
/**
* Class description
* @package bitrix
* @subpackage highloadblock
*/
class HighloadBlockTable extends Entity\DataManager
{
/**
* @return string
*/
public static function getTableName()
{
return 'b_hlblock_entity';
}
public static function getMap()
{
IncludeModuleLangFile(__FILE__);
$sqlHelper = Application::getConnection()->getSqlHelper();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$fieldsMap = array(
'ID' => array(
'data_type' => 'integer',
'primary' => true,
'autocomplete' => true
),
'NAME' => array(
'data_type' => 'string',
'required' => true,
'validation' => array(__CLASS__, 'validateName')
),
'TABLE_NAME' => array(
'data_type' => 'string',
'required' => true,
'validation' => array(__CLASS__, 'validateTableName')
),
'FIELDS_COUNT' => array(
'data_type' => 'integer',
'expression' => array(
'(SELECT COUNT(ID) FROM b_user_field WHERE b_user_field.ENTITY_ID = '.$sqlHelper->getConcatFunction("'HLBLOCK_'", $sqlHelper->castToChar('%s')).')', 'ID'
)
),
'LANG' => new Entity\ReferenceField(
'LANG',
'Bitrix\Highloadblock\HighloadBlockLangTable',
array('=this.ID' => 'ref.ID', 'ref.LID' => new \Bitrix\Main\DB\SqlExpression('?', LANG))
),
);
return $fieldsMap;
}
/**
* @param array $data
*
* @return Entity\AddResult
* @throws \Bitrix\Main\SystemException
*/
public static function add(array $data)
{
$result = parent::add($data);
if (!$result->isSuccess(true))
{
return $result;
}
// create table in db
$connection = Application::getConnection();
$dbtype = $connection->getType();
$sqlHelper = $connection->getSqlHelper();
if ($dbtype == 'mysql')
{
$connection->query('
CREATE TABLE '.$sqlHelper->quote($data['TABLE_NAME']).' (ID int(11) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (ID))
');
}
elseif ($dbtype == 'mssql')
{
$connection->query('
CREATE TABLE '.$sqlHelper->quote($data['TABLE_NAME']).' (ID int NOT NULL IDENTITY (1, 1),
CONSTRAINT '.$data['TABLE_NAME'].'_ibpk_1 PRIMARY KEY (ID))
');
}
elseif ($dbtype == 'oracle')
{
$connection->query('
CREATE TABLE '.$sqlHelper->quote($data['TABLE_NAME']).' (ID number(11) NOT NULL, PRIMARY KEY (ID))
');
$connection->query('
CREATE SEQUENCE sq_'.$data['TABLE_NAME'].'
');
$connection->query('
CREATE OR REPLACE TRIGGER '.$data['TABLE_NAME'].'_insert
BEFORE INSERT
ON '.$sqlHelper->quote($data['TABLE_NAME']).'
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL THEN
SELECT sq_'.$data['TABLE_NAME'].'.NEXTVAL INTO :NEW.ID FROM dual;
END IF;
END;
');
}
else
{
throw new Main\SystemException('Unknown DB type');
}
return $result;
}
/**
* @param mixed $primary
* @param array $data
*
* @return Entity\UpdateResult
*/
public static function update($primary, array $data)
{
global $USER_FIELD_MANAGER;
// get old data
$oldData = static::getByPrimary($primary)->fetch();
// update row
$result = parent::update($primary, $data);
if (!$result->isSuccess(true))
{
return $result;
}
// rename table in db
if (isset($data['TABLE_NAME']) && $data['TABLE_NAME'] !== $oldData['TABLE_NAME'])
{
$connection = Application::getConnection();
$sqlHelper = $connection->getSqlHelper();
$connection->renameTable($oldData['TABLE_NAME'], $data['TABLE_NAME']);
if ($connection instanceof MssqlConnection)
{
// rename constraint
$connection->query(sprintf(
"EXEC sp_rename %s, %s, 'OBJECT'",
$sqlHelper->quote($oldData['TABLE_NAME'].'_ibpk_1'),
$sqlHelper->quote($data['TABLE_NAME'].'_ibpk_1')
));
}
// rename also uf multiple tables and its constraints, sequences, and triggers
/** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
foreach ($USER_FIELD_MANAGER->getUserFields('HLBLOCK_'.$oldData['ID']) as $field)
{
if ($field['MULTIPLE'] == 'Y')
{
$oldUtmTableName = static::getMultipleValueTableName($oldData, $field);
$newUtmTableName = static::getMultipleValueTableName($data, $field);
$connection->renameTable($oldUtmTableName, $newUtmTableName);
}
}
}
return $result;
}
/**
* @param mixed $primary
*
* @return Main\DB\Result|Entity\DeleteResult
*/
public static function delete($primary)
{
global $USER_FIELD_MANAGER;
// get old data
$hlblock = static::getByPrimary($primary)->fetch();
// get file fields
$file_fields = array();
/** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
$fields = $USER_FIELD_MANAGER->getUserFields('HLBLOCK_'.$hlblock['ID']);
foreach ($fields as $name => $field)
{
if ($field['USER_TYPE']['BASE_TYPE'] === 'file')
{
$file_fields[] = $name;
}
}
// delete files
if (!empty($file_fields))
{
$oldEntity = static::compileEntity($hlblock);
$query = new Entity\Query($oldEntity);
// select file ids
$query->setSelect($file_fields);
// if they are not empty
$filter = array('LOGIC' => 'OR');
foreach ($file_fields as $file_field)
{
$filter['!'.$file_field] = false;
}
$query->setFilter($filter);
// go
$result = $query->exec();
while ($row = $result->fetch())
{
foreach ($file_fields as $file_field)
{
if (!empty($row[$file_field]))
{
if (is_array($row[$file_field]))
{
foreach ($row[$file_field] as $value)
{
\CFile::delete($value);
}
}
else
{
\CFile::delete($row[$file_field]);
}
}
}
}
}
$connection = Application::getConnection();
foreach ($fields as $field)
{
// delete from uf registry
if ($field['USER_TYPE']['BASE_TYPE'] === 'enum')
{
$enumField = new \CUserFieldEnum;
$enumField->DeleteFieldEnum($field['ID']);
}
$connection->query("DELETE FROM b_user_field_lang WHERE USER_FIELD_ID = ".$field['ID']);
$connection->query("DELETE FROM b_user_field WHERE ID = ".$field['ID']);
// if multiple - drop utm table
if ($field['MULTIPLE'] == 'Y')
{
$utmTableName = static::getMultipleValueTableName($hlblock, $field);
$connection->dropTable($utmTableName);
}
}
// clear uf cache
$managedCache = Application::getInstance()->getManagedCache();
if(CACHED_b_user_field !== false)
{
$managedCache->cleanDir("b_user_field");
}
// clear langs
$res = HighloadBlockLangTable::getList(array(
'filter' => array('ID' => $primary)
));
while ($row = $res->fetch())
{
HighloadBlockLangTable::delete($row['ID']);
}
// clear rights
$res = HighloadBlockRightsTable::getList(array(
'filter' => array('HL_ID' => $primary)
));
while ($row = $res->fetch())
{
HighloadBlockRightsTable::delete($row['ID']);
}
// remove row
$result = parent::delete($primary);
// drop hl table
$connection->dropTable($hlblock['TABLE_NAME']);
return $result;
}
/**
* @param array|int|string $hlblock Could be a block, ID or NAME of block.
*
* @return Entity\Base
* @throws \Bitrix\Main\SystemException
*/
public static function compileEntity($hlblock)
{
global $USER_FIELD_MANAGER;
// generate entity & data manager
$fieldsMap = array();
// add ID
$fieldsMap['ID'] = array(
'data_type' => 'integer',
'primary' => true,
'autocomplete' => true
);
// get hlblock
if (!is_array($hlblock))
{
if (is_numeric(substr($hlblock, 0, 1)))
{
// we have an id
$hlblock = HighloadBlockTable::getById($hlblock)->fetch();
}
else
{
// we have a name
$hlblock = HighloadBlockTable::query()->addSelect('*')->where('NAME', $hlblock)->exec()->fetch();
}
}
// build datamanager class
$entity_name = $hlblock['NAME'];
$entity_data_class = $hlblock['NAME'];
if (!preg_match('/^[a-z0-9_]+$/i', $entity_data_class))
{
throw new Main\SystemException(sprintf(
'Invalid entity name `%s`.', $entity_data_class
));
}
$entity_data_class .= 'Table';
if (class_exists($entity_data_class))
{
// rebuild if it's already exists
Entity\Base::destroy($entity_data_class);
}
else
{
$entity_table_name = $hlblock['TABLE_NAME'];
// make with an empty map
$eval = '
class '.$entity_data_class.' extends '.__NAMESPACE__.'\DataManager
{
public static function getTableName()
{
return '.var_export($entity_table_name, true).';
}
public static function getMap()
{
return '.var_export($fieldsMap, true).';
}
public static function getHighloadBlock()
{
return '.var_export($hlblock, true).';
}
}
';
eval($eval);
}
// then configure and attach fields
/** @var \Bitrix\Main\Entity\DataManager $entity_data_class */
$entity = $entity_data_class::getEntity();
/** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
$uFields = $USER_FIELD_MANAGER->getUserFields('HLBLOCK_'.$hlblock['ID']);
foreach ($uFields as $uField)
{
if ($uField['MULTIPLE'] == 'N')
{
// just add single field
$params = array(
'required' => $uField['MANDATORY'] == 'Y'
);
$field = $USER_FIELD_MANAGER->getEntityField($uField, $uField['FIELD_NAME'], $params);
$entity->addField($field);
foreach ($USER_FIELD_MANAGER->getEntityReferences($uField, $field) as $reference)
{
$entity->addField($reference);
}
}
else
{
// build utm entity
static::compileUtmEntity($entity, $uField);
}
}
return Entity\Base::getInstance($entity_name);
}
public static function OnBeforeUserTypeAdd($field)
{
if (preg_match('/^HLBLOCK_(\d+)$/', $field['ENTITY_ID'], $matches))
{
if (substr($field['FIELD_NAME'], -4) == '_REF')
{
/**
* postfix _REF reserved for references to other highloadblocks
* @see CUserTypeHlblock::getEntityReferences
*/
global $APPLICATION;
$APPLICATION->ThrowException(
Main\Localization\Loc::getMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_FIELD_NAME_REF_RESERVED')
);
return false;
}
else
{
return array('PROVIDE_STORAGE' => false);
}
}
return true;
}
public static function onAfterUserTypeAdd($field)
{
global $APPLICATION, $USER_FIELD_MANAGER;
if (preg_match('/^HLBLOCK_(\d+)$/', $field['ENTITY_ID'], $matches))
{
$field['USER_TYPE'] = $USER_FIELD_MANAGER->getUserType($field['USER_TYPE_ID']);
// get entity info
$hlblock_id = $matches[1];
$hlblock = HighloadBlockTable::getById($hlblock_id)->fetch();
if (empty($hlblock))
{
$APPLICATION->throwException(sprintf(
'Entity "HLBLOCK_%s" wasn\'t found.', $hlblock_id
));
return false;
}
// get usertype info
$sql_column_type = $USER_FIELD_MANAGER->getUtsDBColumnType($field);
// create field in db
$connection = Application::getConnection();
$sqlHelper = $connection->getSqlHelper();
$connection->query(sprintf(
'ALTER TABLE %s ADD %s %s',
$sqlHelper->quote($hlblock['TABLE_NAME']), $sqlHelper->quote($field['FIELD_NAME']), $sql_column_type
));
if ($field['MULTIPLE'] == 'Y')
{
// create table for this relation
$hlentity = static::compileEntity($hlblock);
$utmEntity = Entity\Base::getInstance(HighloadBlockTable::getUtmEntityClassName($hlentity, $field));
$utmEntity->createDbTable();
// add indexes
$connection->query(sprintf(
'CREATE INDEX %s ON %s (%s)',
$sqlHelper->quote('IX_UTM_HL'.$hlblock['ID'].'_'.$field['ID'].'_ID'),
$sqlHelper->quote($utmEntity->getDBTableName()),
$sqlHelper->quote('ID')
));
$connection->query(sprintf(
'CREATE INDEX %s ON %s (%s)',
$sqlHelper->quote('IX_UTM_HL'.$hlblock['ID'].'_'.$field['ID'].'_VALUE'),
$sqlHelper->quote($utmEntity->getDBTableName()),
$sqlHelper->quote('VALUE')
));
}
return array('PROVIDE_STORAGE' => false);
}
return true;
}
public static function OnBeforeUserTypeDelete($field)
{
global $USER_FIELD_MANAGER;
if (preg_match('/^HLBLOCK_(\d+)$/', $field['ENTITY_ID'], $matches))
{
// get entity info
$hlblock_id = $matches[1];
$hlblock = HighloadBlockTable::getById($hlblock_id)->fetch();
if (empty($hlblock))
{
// non-existent or zombie. let it go
return array('PROVIDE_STORAGE' => false);
}
/** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
$fieldType = $USER_FIELD_MANAGER->getUserType($field["USER_TYPE_ID"]);
if ($fieldType['BASE_TYPE'] == 'file')
{
// if it was file field, then delete all files
$entity = static::compileEntity($hlblock);
/** @var DataManager $dataClass */
$dataClass = $entity->getDataClass();
$rows = $dataClass::getList(array('select' => array($field['FIELD_NAME'])));
while ($oldData = $rows->fetch())
{
if (empty($oldData[$field['FIELD_NAME']]))
{
continue;
}
if(is_array($oldData[$field['FIELD_NAME']]))
{
foreach($oldData[$field['FIELD_NAME']] as $value)
{
\CFile::delete($value);
}
}
else
{
\CFile::delete($oldData[$field['FIELD_NAME']]);
}
}
}
// drop db column
$connection = Application::getConnection();
$connection->dropColumn($hlblock['TABLE_NAME'], $field['FIELD_NAME']);
// if multiple - drop utm table
if ($field['MULTIPLE'] == 'Y')
{
$utmTableName = static::getMultipleValueTableName($hlblock, $field);
$connection->dropTable($utmTableName);
}
return array('PROVIDE_STORAGE' => false);
}
return true;
}
protected static function compileUtmEntity(Entity\Base $hlentity, $userfield)
{
global $USER_FIELD_MANAGER;
// build utm entity
/** @var DataManager $hlDataClass */
$hlDataClass = $hlentity->getDataClass();
$hlblock = $hlDataClass::getHighloadBlock();
$utmClassName = static::getUtmEntityClassName($hlentity, $userfield);
$utmTableName = static::getMultipleValueTableName($hlblock, $userfield);
if (class_exists($utmClassName.'Table'))
{
// rebuild if it already exists
Entity\Base::destroy($utmClassName.'Table');
$utmEntity = Entity\Base::getInstance($utmClassName);
}
else
{
// create entity from scratch
$utmEntity = Entity\Base::compileEntity($utmClassName, array(), array(
'table_name' => $utmTableName,
'namespace' => $hlentity->getNamespace()
));
}
// main fields
$utmValueField = $USER_FIELD_MANAGER->getEntityField($userfield, 'VALUE');
$utmEntityFields = array(
new Entity\IntegerField('ID'),
$utmValueField
);
// references
$references = $USER_FIELD_MANAGER->getEntityReferences($userfield, $utmValueField);
foreach ($references as $reference)
{
$utmEntityFields[] = $reference;
}
foreach ($utmEntityFields as $field)
{
$utmEntity->addField($field);
}
// add original entity reference
$referenceField = new Entity\ReferenceField(
'OBJECT',
$hlentity,
array('=this.ID' => 'ref.ID')
);
$utmEntity->addField($referenceField);
// add short alias for back-reference
$aliasField = new Entity\ExpressionField(
$userfield['FIELD_NAME'].'_SINGLE',
'%s',
$utmEntity->getFullName().':'.'OBJECT.VALUE',
array(
'data_type' => get_class($utmEntity->getField('VALUE')),
'required' => $userfield['MANDATORY'] == 'Y'
)
);
$hlentity->addField($aliasField);
// add aliases to references
/*foreach ($references as $reference)
{
// todo after #44924 is resolved
// actually no. to make it work expression should support linking to references
}*/
// add seriazed cache-field
$cacheField = new Entity\TextField($userfield['FIELD_NAME'], array(
'required' => $userfield['MANDATORY'] == 'Y'
));
Main\UserFieldTable::setMultipleFieldSerialization($cacheField, $userfield);
$hlentity->addField($cacheField);
return $utmEntity;
}
public static function getUtmEntityClassName(Entity\Base $hlentity, $userfield)
{
return $hlentity->getName().'Utm'.Entity\Base::snake2camel($userfield['FIELD_NAME']);
}
public static function getMultipleValueTableName($hlblock, $userfield)
{
return $hlblock['TABLE_NAME'].'_'.strtolower($userfield['FIELD_NAME']);
}
public static function validateName()
{
return array(
new Entity\Validator\Unique,
new Entity\Validator\Length(
null,
100,
array('MAX' => GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_NAME_FIELD_LENGTH_INVALID'))
),
new Entity\Validator\RegExp(
'/^[A-Z][A-Za-z0-9]*$/',
GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_NAME_FIELD_REGEXP_INVALID')
),
new Entity\Validator\RegExp(
'/(?<!Table)$/i',
GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_NAME_FIELD_TABLE_POSTFIX_INVALID')
)
);
}
public static function validateTableName()
{
return array(
new Entity\Validator\Unique,
new Entity\Validator\Length(
null,
64,
array('MAX' => GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_TABLE_NAME_FIELD_LENGTH_INVALID'))
),
new Entity\Validator\RegExp(
'/^[a-z0-9_]+$/',
GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_TABLE_NAME_FIELD_REGEXP_INVALID')
),
array(__CLASS__, 'validateTableExisting')
);
}
public static function validateTableExisting($value, $primary, array $row, Entity\Field $field)
{
$checkName = null;
if (empty($primary))
{
// new row
$checkName = $value;
}
else
{
// update row
$oldData = static::getByPrimary($primary)->fetch();
if ($value != $oldData['TABLE_NAME'])
{
// table name has been changed for existing row
$checkName = $value;
}
}
if (!empty($checkName))
{
if (Application::getConnection()->isTableExists($checkName))
{
return GetMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_TABLE_NAME_ALREADY_EXISTS',
array('#TABLE_NAME#' => $value)
);
}
}
return true;
}
}