Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
typedatamanager.php
1<?php
2
4
8use Bitrix\Main\Entity\Validator\RegExp;
24
28abstract class TypeDataManager extends DataManager
29{
30 public const MAXIMUM_TABLE_NAME_LENGTH = 64;
31
32 protected static $temporaryStorage;
33
34 public static function getMap(): array
35 {
36 return [
37 (new IntegerField('ID'))
38 ->configurePrimary()
39 ->configureAutocomplete(),
40 (new StringField('NAME'))
41 ->configureRequired()
42 ->configureUnique()
43 ->configureSize(100)
44 ->configureFormat('/^[A-Z][A-Za-z0-9]*$/')
45 ->addValidator(new RegExp(
46 '/(?<!Table)$/i'
47 )),
48 (new StringField('TABLE_NAME'))
49 ->configureRequired()
50 ->configureUnique()
51 ->configureSize(64)
52 ->configureFormat('/^[a-z0-9_]+$/')
53 ->addValidator([get_called_class(), 'validateTableExisting']),
54 ];
55 }
56
57 public static function getFactory(): TypeFactory
58 {
59 return Registry::getInstance()->getFactoryByTypeDataClass(static::class);
60 }
61
62 protected static function getTemporaryStorage(): TemporaryStorage
63 {
64 if(!static::$temporaryStorage)
65 {
66 static::$temporaryStorage = new TemporaryStorage();
67 }
68
69 return static::$temporaryStorage;
70 }
71
77 public static function onAfterAdd(Event $event): EventResult
78 {
79 $id = $event->getParameter('id');
80
81 static::compileEntity($id)->createDbTable();
82
83 return new EventResult();
84 }
85
90 public static function onBeforeUpdate(Event $event): EventResult
91 {
92 $data = static::getByPrimary($event->getParameter('id'))->fetch();
93 static::getTemporaryStorage()->saveData($event->getParameter('id'), $data);
94
95 return new EventResult();
96 }
97
103 public static function onAfterUpdate(Event $event): EventResult
104 {
105 $id = $event->getParameter('id');
106 $data = $event->getParameter('fields');
107 $oldData = static::getTemporaryStorage()->getData($id);
108 if(!$oldData)
109 {
110 return new EventResult();
111 }
112
113 // rename table in db
114 if (isset($data['TABLE_NAME']) && $data['TABLE_NAME'] !== $oldData['TABLE_NAME'])
115 {
116 $userFieldManager = UserFieldHelper::getInstance()->getManager();
117 $connection = Application::getConnection();
118 $sqlHelper = $connection->getSqlHelper();
119 $connection->renameTable($oldData['TABLE_NAME'], $data['TABLE_NAME']);
120
121 if ($connection instanceof MssqlConnection)
122 {
123 // rename constraint
124 $connection->query(sprintf(
125 "EXEC sp_rename %s, %s, 'OBJECT'",
126 $sqlHelper->quote($oldData['TABLE_NAME'].'_ibpk_1'),
127 $sqlHelper->quote($data['TABLE_NAME'].'_ibpk_1')
128 ));
129 }
130
131 // rename also uf multiple tables and its constraints, sequences, and triggers
133 foreach ($userFieldManager->getUserFields(static::getFactory()->getUserFieldEntityId($oldData['ID'])) as $field)
134 {
135 if ($field['MULTIPLE'] == 'Y')
136 {
137 $oldUtmTableName = static::getMultipleValueTableName($oldData, $field);
138 $newUtmTableName = static::getMultipleValueTableName($data, $field);
139
140 $connection->renameTable($oldUtmTableName, $newUtmTableName);
141 }
142 }
143 }
144
145 return new EventResult();
146 }
147
155 public static function onBeforeDelete(Event $event): EventResult
156 {
157 $result = new EventResult();
158
159 $id = $event->getParameter('id');
160 $oldData = static::getByPrimary($id)->fetch();
161 static::getTemporaryStorage()->saveData($id, $oldData);
162 if(!$oldData)
163 {
164 return $result;
165 }
166
167 $userFieldManager = UserFieldHelper::getInstance()->getManager();
168
169 // get file fields
170 $file_fields = [];
172 $fields = $userFieldManager->getUserFields(static::getFactory()->getUserFieldEntityId($oldData['ID']));
173
174 foreach ($fields as $name => $field)
175 {
176 if ($field['USER_TYPE']['BASE_TYPE'] === 'file')
177 {
178 $file_fields[] = $name;
179 }
180 }
181
182 // delete files
183 if (!empty($file_fields))
184 {
185 $oldEntity = static::compileEntity($oldData);
186
187 $query = new Query($oldEntity);
188
189 // select file ids
190 $query->setSelect($file_fields);
191
192 // if they are not empty
193 $filter = array('LOGIC' => 'OR');
194
195 foreach ($file_fields as $file_field)
196 {
197 $filter['!'.$file_field] = false;
198 }
199
200 $query->setFilter($filter);
201
202 // go
203 $queryResult = $query->exec();
204
205 while ($row = $queryResult->fetch())
206 {
207 foreach ($file_fields as $file_field)
208 {
209 if (!empty($row[$file_field]))
210 {
211 if (is_array($row[$file_field]))
212 {
213 foreach ($row[$file_field] as $value)
214 {
215 \CFile::delete($value);
216 }
217 }
218 else
219 {
220 \CFile::delete($row[$file_field]);
221 }
222 }
223 }
224 }
225 }
226
227 $connection = Application::getConnection();
228
229 foreach ($fields as $field)
230 {
231 // delete from uf registry
232 if ($field['USER_TYPE']['BASE_TYPE'] === 'enum')
233 {
234 $enumField = new \CUserFieldEnum;
235 $enumField->DeleteFieldEnum($field['ID']);
236 }
237
238 $connection->query("DELETE FROM b_user_field_lang WHERE USER_FIELD_ID = ".$field['ID']);
239 $connection->query("DELETE FROM b_user_field WHERE ID = ".$field['ID']);
240
241 // if multiple - drop utm table
242 if ($field['MULTIPLE'] == 'Y')
243 {
244 try
245 {
246 $utmTableName = static::getMultipleValueTableName($oldData, $field);
247 $connection->dropTable($utmTableName);
248 }
249 catch(SqlQueryException $e)
250 {
251 $result->addError(new EntityError($e->getMessage()));
252 }
253 }
254 }
255
256 // clear uf cache
257 $managedCache = Application::getInstance()->getManagedCache();
258 if(CACHED_b_user_field !== false)
259 {
260 $managedCache->cleanDir("b_user_field");
261 }
262
263 return $result;
264 }
265
272 public static function onAfterDelete(Event $event): EventResult
273 {
274 $id = $event->getParameter('id');
275 $oldData = static::getTemporaryStorage()->getData($id);
276 if(!$oldData)
277 {
278 return new EventResult();
279 }
280
281 if(Application::getConnection()->isTableExists($oldData['TABLE_NAME']))
282 {
283 Application::getConnection()->dropTable($oldData['TABLE_NAME']);
284 }
285
286 return new EventResult();
287 }
288
293 public static function resolveType($type): ?array
294 {
295 if($type instanceof Type)
296 {
297 $type = $type->collectValues();
298 }
299 if (!is_array($type))
300 {
301 if (is_int($type) || is_numeric(mb_substr($type, 0, 1)))
302 {
303 // we have an id
304 $type = static::getById($type)->fetch();
305 }
306 elseif (is_string($type) && $type !== '')
307 {
308 // we have a name
309 $type = static::query()->addSelect('*')->where('NAME', $type)->exec()->fetch();
310 }
311 else
312 {
313 $type = null;
314 }
315 }
316 if (empty($type))
317 return null;
318
319 if (!isset($type['ID']))
320 return null;
321 if (!isset($type['NAME']) || !preg_match('/^[a-z0-9_]+$/i', $type['NAME']))
322 return null;
323 if (empty($type['TABLE_NAME']))
324 return null;
325
326 return $type;
327 }
328
335 public static function compileEntity($type): Entity
336 {
337 $rawType = $type;
338 $type = static::resolveType($type);
339 if (empty($type))
340 {
341 throw new SystemException(sprintf(
342 'Invalid type description `%s`.', mydump($rawType)
343 ));
344 }
345 $factory = static::getFactory();
346 $type['code'] = $factory->getCode();
348 $itemDataClass = $factory->getItemPrototypeDataClass();
349 $userFieldManager = UserFieldHelper::getInstance()->getManager();
350
351 $userFields = $userFieldManager->getUserFields($factory->getUserFieldEntityId($type['ID']));
352
353 $entityName = $type['code'] . '_items_' . $type['ID'];
354 $entityClassName = $entityName.'Table';
355 $entityTableName = $type['TABLE_NAME'];
356 if(class_exists($entityClassName))
357 {
358 // rebuild if it already exists
359 Entity::destroy($entityClassName);
360 $entity = Entity::getInstance($entityClassName);
361 }
362 else
363 {
364 $entity = Entity::compileEntity($entityName, [], [
365 'table_name' => $entityTableName,
366 'parent' => $itemDataClass,
367 'object_parent' => $factory->getItemParentClass(),
368 //'namespace' => __NAMESPACE__,
369 ]);
370 }
371 Registry::getInstance()->registerTypeByEntity($entity, $type);
372
373 foreach ($userFields as $userField)
374 {
375 if ($userField['MULTIPLE'] == 'N')
376 {
377 // just add single field
378 $params = [
379 'required' => ($userField['MANDATORY'] === 'Y')
380 ];
381 $field = $userFieldManager->getEntityField($userField, $userField['FIELD_NAME'], $params);
382 $entity->addField($field);
383 foreach ($userFieldManager->getEntityReferences($userField, $field) as $reference)
384 {
385 $entity->addField($reference);
386 }
387 }
388 else
389 {
390 // build utm entity
391 static::compileUtmEntity($entity, $userField);
392 }
393 }
394
395 return $entity;
396 }
397
403 protected static function compileUtmEntity(Entity $typeEntity, $userField): Entity
404 {
405 $userFieldManager = UserFieldHelper::getInstance()->getManager();
406
407 // build utm entity
409 $itemDataClass = $typeEntity->getDataClass();
410 $typeData = $itemDataClass::getType();
411
412 $utmClassName = static::getUtmEntityClassName($typeEntity, $userField);
413 $utmTableName = static::getMultipleValueTableName($typeData, $userField);
414
415 if (class_exists($utmClassName.'Table'))
416 {
417 // rebuild if it already exists
418 Entity::destroy($utmClassName.'Table');
419 $utmEntity = Entity::getInstance($utmClassName);
420 }
421 else
422 {
423 // create entity from scratch
424 $utmEntity = Entity::compileEntity($utmClassName, [], [
425 'table_name' => $utmTableName,
426 //'namespace' => __NAMESPACE__,
427 ]);
428 }
429
430 // main fields
431 $utmValueField = $userFieldManager->getEntityField($userField, 'VALUE');
432
433 $utmEntityFields = array(
434 new IntegerField('ID'),
435 $utmValueField
436 );
437
438 // references
439 $references = $userFieldManager->getEntityReferences($userField, $utmValueField);
440
441 foreach ($references as $reference)
442 {
443 $utmEntityFields[] = $reference;
444 }
445
446 foreach ($utmEntityFields as $field)
447 {
448 $utmEntity->addField($field);
449 }
450
451 // add original entity reference
452 $referenceField = new Reference(
453 'OBJECT',
454 $typeEntity,
455 ['=this.ID' => 'ref.ID']
456 );
457
458 $utmEntity->addField($referenceField);
459
460 // add short alias for back-reference
461 $aliasField = new ExpressionField(
462 $userField['FIELD_NAME'].'_SINGLE',
463 '%s',
464 $utmEntity->getFullName().':'.'OBJECT.VALUE',
465 array(
466 'data_type' => get_class($utmEntity->getField('VALUE')),
467 'required' => $userField['MANDATORY'] == 'Y'
468 )
469 );
470
471 $typeEntity->addField($aliasField);
472
473 $cacheField = new ArrayField($userField['FIELD_NAME']);
474 $cacheField->configureRequired($userField['MANDATORY'] === 'Y');
475 $cacheField->configureSerializationPhp();
476
477 $typeEntity->addField($cacheField);
478
479 return $utmEntity;
480 }
481
487 public static function getUtmEntityClassName(Entity $typeEntity, array $userField): string
488 {
489 return $typeEntity->getName() . 'Utm' . StringHelper::snake2camel($userField['FIELD_NAME']);
490 }
491
497 public static function getMultipleValueTableName(array $type, array $userField): string
498 {
499 $tableName = $type['TABLE_NAME'] . '_' . mb_strtolower($userField['FIELD_NAME']);
500
501 if (mb_strlen($tableName) > static::MAXIMUM_TABLE_NAME_LENGTH && !empty($userField['ID']))
502 {
503 $tableName = $type['TABLE_NAME'] . '_' . $userField['ID'];
504 }
505
506 return $tableName;
507 }
508
509 public static function validateTableExisting($value, $primary, array $row, Field $field)
510 {
511 $checkName = null;
512
513 if (empty($primary))
514 {
515 // new row
516 $checkName = $value;
517 }
518 else
519 {
520 $factory = static::getFactory();
521 $typeDataClass = $factory->getTypeDataClass();
522 // update row
523 $oldData = $typeDataClass::getByPrimary($primary)->fetch();
524
525 if ($value != $oldData['TABLE_NAME'])
526 {
527 // table name has been changed for existing row
528 $checkName = $value;
529 }
530 }
531
532 if (!empty($checkName))
533 {
534 if (Application::getConnection()->isTableExists($checkName))
535 {
536 Loc::loadLanguageFile(__DIR__.'/highloadblock.php');
537 return Loc::getMessage('HIGHLOADBLOCK_HIGHLOAD_BLOCK_ENTITY_TABLE_NAME_ALREADY_EXISTS',
538 ['#TABLE_NAME#' => $value]
539 );
540 }
541 }
542
543 return true;
544 }
545
546 public static function getObjectParentClass(): string
547 {
548 return Type::class;
549 }
550}
static getConnection($name="")
getParameter($key)
Definition event.php:80
static loadLanguageFile($file, $language=null, $normalize=true)
Definition loc.php:224
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static getUtmEntityClassName(Entity $typeEntity, array $userField)
static validateTableExisting($value, $primary, array $row, Field $field)
static getMultipleValueTableName(array $type, array $userField)