Bitrix-D7  20.5.0
main/lib/userfield.php
См. документацию.
1 <?php
2 /**
3  * Bitrix Framework
4  * @package bitrix
5  * @subpackage main
6  * @copyright 2001-2014 Bitrix
7  */
8 
9 namespace Bitrix\Main;
10 
13 use Bitrix\Main\ORM;
15 
16 /**
17  * Entity representation of UserFields.
18  * @package bitrix
19  * @subpackage main
20  */
22 {
23  // to use in uts serialized fields
24  const MULTIPLE_DATE_FORMAT = 'Y-m-d';
25  const MULTIPLE_DATETIME_FORMAT = 'Y-m-d H:i:s';
26 
27  public static function getMap()
28  {
29  return array(
30  'ID' => array(
31  'data_type' => 'integer',
32  'primary' => true,
33  'autocomplete' => true,
34  ),
35  'ENTITY_ID' => array(
36  'data_type' => 'string',
37  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_ENTITY_ID_TITLE'),
38  ),
39  'FIELD_NAME' => array(
40  'data_type' => 'string',
41  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_FIELD_NAME_TITLE'),
42  ),
43  'USER_TYPE_ID' => array(
44  'data_type' => 'string',
45  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_USER_TYPE_ID_TITLE'),
46  ),
47  'XML_ID' => array(
48  'data_type' => 'string',
49  ),
50  'SORT' => array(
51  'data_type' => 'integer',
52  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_SORT_TITLE'),
53  ),
54  'MULTIPLE' => array(
55  'data_type' => 'boolean',
56  'values' => array('N', 'Y'),
57  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_MULTIPLE_TITLE'),
58  ),
59  'MANDATORY' => array(
60  'data_type' => 'boolean',
61  'values' => array('N', 'Y'),
62  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_MANDATORY_TITLE'),
63  ),
64  'SHOW_FILTER' => array(
65  'data_type' => 'boolean',
66  'values' => array('N', 'Y'),
67  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_SHOW_FILTER_TITLE'),
68  ),
69  'SHOW_IN_LIST' => array(
70  'data_type' => 'boolean',
71  'values' => array('N', 'Y'),
72  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_SHOW_IN_LIST_TITLE'),
73  ),
74  'EDIT_IN_LIST' => array(
75  'data_type' => 'boolean',
76  'values' => array('N', 'Y'),
77  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_EDIT_IN_LIST_TITLE'),
78  ),
79  'IS_SEARCHABLE' => array(
80  'data_type' => 'boolean',
81  'values' => array('N', 'Y'),
82  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_IS_SEARCHABLE_TITLE'),
83  ),
84  'SETTINGS' => array(
85  'data_type' => 'text',
86  'serialized' => true,
87  'title' => Loc::getMessage('MAIN_USER_FIELD_TABLE_SETTINGS_TITLE'),
88  ),
89  );
90  }
91 
92  public static function getLabelsReference(string $referenceName = null, string $languageId = null): ORM\Fields\Relations\Reference
93  {
94  if(!$referenceName)
95  {
96  $referenceName = 'LABELS';
97  }
98 
99  $filter = [
100  '=this.ID' => 'ref.USER_FIELD_ID',
101  ];
102 
103  if($languageId)
104  {
105  $filter['=ref.LANGUAGE_ID'] = new SqlExpression('?s', $languageId);
106  }
107 
109  $referenceName,
110  UserFieldLangTable::class,
111  $filter
112  );
113  }
114 
115  public static function getLabelFields(): array
116  {
117  return [
118  'LANGUAGE_ID',
119  'EDIT_FORM_LABEL',
120  'LIST_COLUMN_LABEL',
121  'LIST_FILTER_LABEL',
122  'ERROR_MESSAGE',
123  'HELP_MESSAGE',
124  ];
125  }
126 
127  public static function getLabelsSelect(string $referenceName = null): array
128  {
129  if(!$referenceName)
130  {
131  $referenceName = 'LABELS';
132  }
133 
134  $result = [];
135  foreach(static::getLabelFields() as $labelField)
136  {
137  $result[$labelField] = $referenceName . '.' . $labelField;
138  }
139 
140  return $result;
141  }
142 
143  public static function getFieldData(int $id): ?array
144  {
145  $labelFields = static::getLabelFields();
146  $field = [];
147  $list = static::getList([
148  'select' => array_merge(['*'], UserFieldTable::getLabelsSelect()),
149  'filter' => [
150  '=ID' => $id,
151  ],
152  'runtime' => [
153  static::getLabelsReference(),
154  ]
155  ]);
156  foreach($list as $data)
157  {
158  if(empty($field))
159  {
160  $field = $data;
161  unset(
162  $field['LANGUAGE_ID'],
163  $field['EDIT_FORM_LABEL'],
164  $field['LIST_COLUMN_LABEL'],
165  $field['LIST_FILTER_LABEL'],
166  $field['ERROR_MESSAGE'],
167  $field['HELP_MESSAGE'],
168  $field['UALIAS_0']
169  );
170  }
171 
172  foreach($labelFields as $labelField)
173  {
174  $field[$labelField][$data['LANGUAGE_ID']] = $data[$labelField];
175  }
176  }
177 
178  if(empty($field))
179  {
180  return null;
181  }
182 
183  if($field['USER_TYPE_ID'] === 'enumeration')
184  {
185  $field['ENUM'] = [];
186  $enumEntity = new \CUserFieldEnum();
187  $enumList = $enumEntity->GetList(
188  [
189  'SORT' => 'ASC'
190  ], [
191  'USER_FIELD_ID' => $field['ID'],
192  ]
193  );
194  while($enum = $enumList->Fetch())
195  {
196  $field['ENUM'][] = $enum;
197  }
198  }
199 
200  return $field;
201  }
202 
203  /**
204  * @param array $data
205  *
206  * @return \Bitrix\Main\ORM\Data\AddResult|void
207  * @throws NotImplementedException
208  */
209  public static function add(array $data)
210  {
211  throw new NotImplementedException('Use \CUserTypeEntity API instead.');
212  }
213 
214  /**
215  * @param mixed $primary
216  * @param array $data
217  *
218  * @return \Bitrix\Main\ORM\Data\UpdateResult|void
219  * @throws NotImplementedException
220  */
221  public static function update($primary, array $data)
222  {
223  throw new NotImplementedException('Use \CUserTypeEntity API instead.');
224  }
225 
226  /**
227  * @param mixed $primary
228  *
229  * @return ORM\Data\DeleteResult|void
230  * @throws NotImplementedException
231  */
232  public static function delete($primary)
233  {
234  throw new NotImplementedException('Use \CUserTypeEntity API instead.');
235  }
236 
237  /**
238  * @param ORM\Entity $entity
239  * @param $ufId
240  *
241  * @throws ArgumentException
242  * @throws SystemException
243  */
244  public static function attachFields(ORM\Entity $entity, $ufId)
245  {
246  global $USER_FIELD_MANAGER;
247 
248  $utsFields = array();
249  $utsFieldNames = array();
250 
251  $utmFields = array();
252  $utmFieldNames = array();
253 
254  $fields = $USER_FIELD_MANAGER->getUserFields($ufId);
255 
256  foreach ($fields as $field)
257  {
258  if ($field['MULTIPLE'] === 'Y')
259  {
260  $utmFields[] = $field;
261  $utmFieldNames[$field['FIELD_NAME']] = true;
262  }
263  else
264  {
265  $utsFields[] = $field;
266  $utsFieldNames[$field['FIELD_NAME']] = true;
267  }
268  }
269 
270  if (!empty($utsFields) || !empty($utmFields))
271  {
272  // create uts entity & put fields into it
273  $utsEntity = static::createUtsEntity($entity, $utsFields, $utmFields, $ufId);
274 
275  // create reference to uts entity
276  $utsReference = new ORM\Fields\Relations\Reference('UTS_OBJECT', $utsEntity->getDataClass(), array(
277  '=this.ID' => 'ref.VALUE_ID'
278  ));
279 
280  $entity->addField($utsReference);
281 
282  // add UF_* aliases
283  foreach ($fields as $userfield)
284  {
285  $utsFieldName = $userfield['FIELD_NAME'];
286 
287  /** @var \Bitrix\Main\ORM\Fields\ScalarField $utsField */
288  $utsField = $utsEntity->getField($utsFieldName);
289 
290  $aliasField = new ORM\Fields\UserTypeField(
291  $utsFieldName,
292  '%s',
293  'UTS_OBJECT.'.$utsFieldName,
294  array('data_type' => get_class($utsField))
295  );
296 
297  if ($userfield['MULTIPLE'] == 'Y')
298  {
299  $aliasField->configureMultiple();
300  static::setMultipleFieldSerialization($aliasField, $userfield);
301  }
302 
303  $entity->addField($aliasField);
304  }
305 
306 
307  if (!empty($utsFields))
308  {
309  foreach ($utsFields as $utsField)
310  {
311  /** @var \Bitrix\Main\ORM\Fields\ScalarField $utsEntityField */
312  $utsEntityField = $utsEntity->getField($utsField['FIELD_NAME']);
313 
314  foreach ($USER_FIELD_MANAGER->getEntityReferences($utsField, $utsEntityField) as $reference)
315  {
316  // rewrite reference from this.field to this.uts_object.field
317  $referenceDesc = static::rewriteUtsReference($reference->getReference());
318 
319  $aliasReference = new ORM\Fields\Relations\Reference(
320  $reference->getName(),
321  $reference->getRefEntityName(),
322  $referenceDesc
323  );
324 
325  $entity->addField($aliasReference);
326  }
327  }
328  }
329 
330  if (!empty($utmFields))
331  {
332  // create utm entity & put base fields into it
333  $utmEntity = static::createUtmEntity($entity, $utmFields, $ufId);
334 
335  // add UF_* aliases
336  foreach ($utmFieldNames as $utmFieldName => $true)
337  {
338  /** @var \Bitrix\Main\ORM\Fields\ScalarField $utmField */
339  $utmField = $utmEntity->getField($utmFieldName);
340 
341  $aliasField = new ORM\Fields\ExpressionField(
342  $utmFieldName.'_SINGLE',
343  '%s',
344  $utmEntity->getFullName().':PARENT_'.$utmFieldName.'.'.$utmField->getColumnName(),
345  array('data_type' => get_class($utmField))
346  );
347 
348  $entity->addField($aliasField);
349  }
350  }
351  }
352  }
353 
354  /**
355  * @param ORM\Entity $srcEntity
356  * @param array $utsFields
357  * @param array $utmFields
358  * @param null $ufId
359  *
360  * @return ORM\Entity
361  * @throws ArgumentException
362  * @throws SystemException
363  */
364  protected static function createUtsEntity(ORM\Entity $srcEntity, array $utsFields, array $utmFields, $ufId = null)
365  {
366  global $USER_FIELD_MANAGER;
367 
368  // get namespace & class
369  /** @var \Bitrix\Main\ORM\Data\DataManager $utsClassFull */
370  $utsClassFull = static::getUtsEntityClassNameBySrcEntity($srcEntity);
371  $utsClassPath = explode('\\', ltrim($utsClassFull, '\\'));
372 
373  $utsNamespace = join('\\', array_slice($utsClassPath, 0, -1));
374  $utsClass = end($utsClassPath);
375 
376  // get table name
377  $utsTable = static::getUtsEntityTableNameBySrcEntity($srcEntity, $ufId);
378 
379  // base fields
380  $fieldsMap = array(
381  'VALUE_ID' => array(
382  'data_type' => 'integer',
383  'primary' => true
384  ),
385  'PARENT' => array(
386  'data_type' => $srcEntity->getDataClass(),
387  'reference' => array(
388  '=this.VALUE_ID' => 'ref.ID'
389  )
390  )
391  );
392 
393  // initialize entity
394  if (class_exists($utsNamespace."\\".$utsClass))
395  {
396  ORM\Entity::destroy($utsNamespace."\\".$utsClass);
397  $entity = ORM\Entity::getInstance($utsNamespace."\\".$utsClass);
398 
399  foreach ($fieldsMap as $fieldName => $field)
400  {
401  $entity->addField($field, $fieldName);
402  }
403  }
404  else
405  {
406  $entity = ORM\Entity::compileEntity($utsClass, $fieldsMap, array(
407  'namespace' => $utsNamespace, 'table_name' => $utsTable
408  ));
409  }
410 
411  foreach ($utsFields as $utsField)
412  {
413  $field = $USER_FIELD_MANAGER->getEntityField($utsField);
414  $entity->addField($field);
415 
416  foreach ($USER_FIELD_MANAGER->getEntityReferences($utsField, $field) as $reference)
417  {
418  $entity->addField($reference);
419  }
420  }
421 
422  foreach ($utmFields as $utmField)
423  {
424  // add serialized utm cache-fields
425  $cacheField = new ORM\Fields\TextField($utmField['FIELD_NAME']);
426  static::setMultipleFieldSerialization($cacheField, $utmField);
427  $entity->addField($cacheField);
428  }
429 
430  return $entity;
431  }
432 
433  /**
434  * @param ORM\Fields\Field $entityField
435  * @param ORM\Fields\Field|array $fieldAsType
436  *
437  * @throws ArgumentException
438  */
439  public static function setMultipleFieldSerialization(ORM\Fields\Field $entityField, $fieldAsType)
440  {
441  global $USER_FIELD_MANAGER;
442 
443  if (!($fieldAsType instanceof ORM\Fields\Field))
444  {
445  $fieldAsType = $USER_FIELD_MANAGER->getEntityField($fieldAsType);
446  }
447 
448  if ($fieldAsType instanceof ORM\Fields\DatetimeField)
449  {
450  if ($entityField instanceof ORM\Fields\ArrayField)
451  {
452  $entityField->configureSerializeCallback([__CLASS__, 'serializeMultipleDatetime']);
453  $entityField->configureUnserializeCallback([__CLASS__, 'unserializeMultipleDatetime']);
454  }
455  else
456  {
457  $entityField->addSaveDataModifier([__CLASS__, 'serializeMultipleDatetime']);
458  $entityField->addFetchDataModifier([__CLASS__, 'unserializeMultipleDatetime']);
459  }
460  }
461  elseif ($fieldAsType instanceof ORM\Fields\DateField)
462  {
463  if ($entityField instanceof ORM\Fields\ArrayField)
464  {
465  $entityField->configureSerializeCallback([__CLASS__, 'serializeMultipleDate']);
466  $entityField->configureUnserializeCallback([__CLASS__, 'unserializeMultipleDate']);
467  }
468  else
469  {
470  $entityField->addSaveDataModifier([__CLASS__, 'serializeMultipleDate']);
471  $entityField->addFetchDataModifier([__CLASS__, 'unserializeMultipleDate']);
472  }
473  }
474  else
475  {
476  if ($entityField instanceof ORM\Fields\ArrayField)
477  {
478  $entityField->configureSerializationPhp();
479  }
480  else
481  {
482  $entityField->setSerialized();
483  }
484  }
485  }
486 
487  public static function rewriteUtsReference($referenceDesc)
488  {
489  $new = array();
490 
491  foreach ($referenceDesc as $k => $v)
492  {
493  if (is_array($v))
494  {
495  $new[$k] = static::rewriteUtsReference($v);
496  }
497  else
498  {
499  $k = str_replace('this.', 'this.UTS_OBJECT.', $k);
500  $new[$k] = $v;
501  }
502  }
503 
504  return $new;
505  }
506 
507  protected static function getUtsEntityClassNameBySrcEntity(ORM\Entity $srcEntity)
508  {
509  return $srcEntity->getFullName().'UtsTable';
510  }
511 
512  protected static function getUtsEntityTableNameBySrcEntity(ORM\Entity $srcEntity, $ufId = null)
513  {
514  return 'b_uts_'.mb_strtolower($ufId ?: $srcEntity->getUfId());
515  }
516 
517  /**
518  * @param ORM\Entity $srcEntity
519  * @param array $utmFields
520  * @param null $ufId
521  *
522  * @return ORM\Entity
523  * @throws ArgumentException
524  * @throws SystemException
525  */
526  protected static function createUtmEntity(ORM\Entity $srcEntity, array $utmFields, $ufId = null)
527  {
528  global $USER_FIELD_MANAGER;
529 
530  /** @var \Bitrix\Main\ORM\Data\DataManager $utmClassFull */
531  $utmClassFull = static::getUtmEntityClassNameBySrcEntity($srcEntity);
532  $utmClassPath = explode('\\', ltrim($utmClassFull, '\\'));
533 
534  $utmNamespace = join('\\', array_slice($utmClassPath, 0, -1));
535  $utmClass = end($utmClassPath);
536 
537  // get table name
538  $utmTable = static::getUtmEntityTableNameBySrcEntity($srcEntity, $ufId);
539 
540  // collect fields
541  $fieldsMap = array(
542  'ID' => array(
543  'data_type' => 'integer',
544  'primary' => true,
545  'autocomplete' => true
546  ),
547  'VALUE_ID' => array(
548  'data_type' => 'integer',
549  'primary' => true
550  ),
551  'PARENT' => array(
552  'data_type' => $srcEntity->getDataClass(),
553  'reference' => array(
554  '=this.VALUE_ID' => 'ref.ID'
555  )
556  ),
557  'FIELD_ID' => array(
558  'data_type' => 'integer'
559  ),
560 
561  // base values fields
562  'VALUE' => array(
563  'data_type' => 'text'
564  ),
565  'VALUE_INT' => array(
566  'data_type' => 'integer'
567  ),
568  'VALUE_DOUBLE' => array(
569  'data_type' => 'float'
570  ),
571  'VALUE_DATE' => array(
572  'data_type' => 'datetime'
573  )
574  );
575 
576  // initialize entity
577  if (class_exists($utmNamespace."\\".$utmClass))
578  {
579  ORM\Entity::destroy($utmNamespace."\\".$utmClass);
580  $entity = ORM\Entity::getInstance($utmNamespace."\\".$utmClass);
581 
582  foreach ($fieldsMap as $fieldName => $field)
583  {
584  $entity->addField($field, $fieldName);
585  }
586  }
587  else
588  {
589  $entity = ORM\Entity::compileEntity($utmClass, $fieldsMap, array(
590  'namespace' => $utmNamespace, 'table_name' => $utmTable
591  ));
592  }
593 
594  // add utm fields being mapped on real column name
595  foreach ($utmFields as $utmField)
596  {
597  $field = $USER_FIELD_MANAGER->getEntityField($utmField);
598 
599  if ($field instanceof ORM\Fields\IntegerField)
600  {
601  $columnName = 'VALUE_INT';
602  }
603  elseif ($field instanceof ORM\Fields\FloatField)
604  {
605  $columnName = 'VALUE_DOUBLE';
606  }
607  elseif ($field instanceof ORM\Fields\DateField || $field instanceof ORM\Fields\DatetimeField)
608  {
609  $columnName = 'VALUE_DATE';
610  }
611  else
612  {
613  $columnName = 'VALUE';
614  }
615 
616  $field->setColumnName($columnName);
617 
618  $entity->addField($field);
619 
620  foreach ($USER_FIELD_MANAGER->getEntityReferences($utmField, $field) as $reference)
621  {
622  $entity->addField($reference);
623  }
624 
625  // add back-reference
626  $refField = new ORM\Fields\Relations\Reference(
627  'PARENT_'.$utmField['FIELD_NAME'],
628  $srcEntity->getDataClass(),
629  array('=this.VALUE_ID' => 'ref.ID', '=this.FIELD_ID' => array('?i', $utmField['ID']))
630  );
631 
632  $entity->addField($refField);
633  }
634 
635  return $entity;
636  }
637 
638  protected static function getUtmEntityClassNameBySrcEntity(ORM\Entity $srcEntity)
639  {
640  return $srcEntity->getFullName().'UtmTable';
641  }
642 
643  protected static function getUtmEntityTableNameBySrcEntity(ORM\Entity $srcEntity, $ufId = null)
644  {
645  return 'b_utm_'.mb_strtolower($ufId ?: $srcEntity->getUfId());
646  }
647 
648  /**
649  * @param Type\DateTime[] $value
650  *
651  * @return string
652  */
653  public static function serializeMultipleDatetime($value)
654  {
655  if (is_array($value) || $value instanceof \Traversable)
656  {
657  $tmpValue = array();
658 
659  foreach ($value as $k => $singleValue)
660  {
661  /** @var Type\DateTime $singleValue */
662  $tmpValue[$k] = $singleValue->format(static::MULTIPLE_DATETIME_FORMAT);
663  }
664 
665  return serialize($tmpValue);
666  }
667 
668  return $value;
669  }
670 
671  /**
672  * @param string $value
673  *
674  * @return array
675  * @throws ObjectException
676  */
677  public static function unserializeMultipleDatetime($value)
678  {
679  if($value <> '')
680  {
681  $value = unserialize($value);
682 
683  foreach($value as &$singleValue)
684  {
685  try
686  {
687  //try new independent datetime format
688  $singleValue = new Type\DateTime($singleValue, static::MULTIPLE_DATETIME_FORMAT);
689  }
690  catch(ObjectException $e)
691  {
692  //try site format
693  $singleValue = new Type\DateTime($singleValue);
694  }
695  }
696  }
697 
698  return $value;
699  }
700 
701  /**
702  * @param Type\Date[] $value
703  *
704  * @return string
705  */
706  public static function serializeMultipleDate($value)
707  {
708  if (is_array($value) || $value instanceof \Traversable)
709  {
710  $tmpValue = array();
711 
712  foreach ($value as $k => $singleValue)
713  {
714  /** @var Type\Date $singleValue */
715  $tmpValue[$k] = $singleValue->format(static::MULTIPLE_DATE_FORMAT);
716  }
717 
718  return serialize($tmpValue);
719  }
720 
721  return $value;
722  }
723 
724  /**
725  * @param string $value
726  *
727  * @return array
728  * @throws ObjectException
729  */
730  public static function unserializeMultipleDate($value)
731  {
732  if($value <> '')
733  {
734  $value = unserialize($value);
735 
736  foreach($value as &$singleValue)
737  {
738  try
739  {
740  //try new independent datetime format
741  $singleValue = new Type\Date($singleValue, static::MULTIPLE_DATE_FORMAT);
742  }
743  catch(ObjectException $e)
744  {
745  //try site format
746  $singleValue = new Type\Date($singleValue);
747  }
748  }
749  }
750 
751  return $value;
752  }
753 }
static getMessage($code, $replace=null, $language=null)
Returns translation by message code.
Definition: loc.php:29
Exception is thrown when operation is not implemented but should be.
static getInstance($entityName)
static compileEntity($entityName, $fields=null, $parameters=array())
Exception is thrown when the object can't be constructed.
static createUtsEntity(ORM\Entity $srcEntity, array $utsFields, array $utmFields, $ufId=null)
static getLabelsSelect(string $referenceName=null)
static getUtsEntityClassNameBySrcEntity(ORM\Entity $srcEntity)
static getLabelsReference(string $referenceName=null, string $languageId=null)
static createUtmEntity(ORM\Entity $srcEntity, array $utmFields, $ufId=null)
static getUtmEntityTableNameBySrcEntity(ORM\Entity $srcEntity, $ufId=null)
static serializeMultipleDatetime($value)
static setMultipleFieldSerialization(ORM\Fields\Field $entityField, $fieldAsType)
static rewriteUtsReference($referenceDesc)
static attachFields(ORM\Entity $entity, $ufId)
static unserializeMultipleDatetime($value)
static getUtsEntityTableNameBySrcEntity(ORM\Entity $srcEntity, $ufId=null)
static getUtmEntityClassNameBySrcEntity(ORM\Entity $srcEntity)
static update($primary, array $data)