1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
dialog.php
См. документацию.
1<?
2
3namespace Bitrix\UI\EntitySelector;
4
5use Bitrix\Main\Localization\Loc;
6use Bitrix\Main\ORM\Fields\ExpressionField;
7use Bitrix\Main\UI\EntitySelector\EntityUsageTable;
8
9class Dialog implements \JsonSerializable
10{
11 protected $id;
12
14 protected $itemCollection;
15
17 protected $tabs = [];
18
20 protected $entities = [];
21
23 protected $recentItems;
24
27
30
32 protected $errors;
33
35 protected $context;
36
38 protected $header;
39
41 protected $headerOptions;
42
44 protected $footer;
45
47 protected $footerOptions;
48
50 protected $clearUnavailableItems = false;
51
53 protected int $recentItemsLimit = 50;
54
55 protected const MAX_RECENT_ITEMS_LIMIT = 50;
56
57 public function __construct(array $options)
58 {
59 if (isset($options['entities']) && is_array($options['entities']))
60 {
61 foreach ($options['entities'] as $entityOptions)
62 {
63 if (is_array($entityOptions))
64 {
65 $entity = Entity::create($entityOptions);
66 if ($entity)
67 {
68 $this->addEntity($entity);
69 }
70 }
71 }
72 }
73
74 if (isset($options['id']) && is_string($options['id']))
75 {
76 $this->id = $options['id'];
77 }
78
79 if (isset($options['context']) && is_string($options['context']) && strlen($options['context']) > 0)
80 {
81 $this->context = $options['context'];
82 }
83
84 if (isset($options['clearUnavailableItems']) && is_bool($options['clearUnavailableItems']))
85 {
86 $this->clearUnavailableItems = $options['clearUnavailableItems'];
87 }
88
89 $this->itemCollection = new ItemCollection();
90 $this->recentItems = new RecentCollection();
91 $this->globalRecentItems = new RecentCollection();
92 $this->preselectedItems = new PreselectedCollection();
93 $this->errors = new EntityErrorCollection();
94
95 if (isset($options['preselectedItems']) && is_array($options['preselectedItems']))
96 {
97 $this->setPreselectedItems($options['preselectedItems']);
98 }
99
100 if (isset($options['recentItemsLimit']) && is_int($options['recentItemsLimit']))
101 {
102 $this->recentItemsLimit = max(1, min($options['recentItemsLimit'], static::MAX_RECENT_ITEMS_LIMIT));
103 }
104 }
105
106 public function getId(): ?string
107 {
108 return $this->id;
109 }
110
111 public function getContext(): ?string
112 {
113 return $this->context;
114 }
115
117 {
119 }
120
121 public function getCurrentUserId(): int
122 {
123 return is_object($GLOBALS['USER']) ? $GLOBALS['USER']->getId() : 0;
124 }
125
126 public function addItem(Item $item)
127 {
128 $success = $this->getItemCollection()->add($item);
129 if ($success)
130 {
131 $this->handleItemAdd($item);
132 }
133 }
134
135 public function addItems(array $items)
136 {
137 foreach ($items as $item)
138 {
139 $this->addItem($item);
140 }
141 }
142
143 public function addRecentItem(Item $item)
144 {
145 $this->addItem($item);
146
147 $recentItem = $this->getRecentItems()->getByItem($item);
148 if (!$recentItem && $item->isAvailableInRecentTab())
149 {
150 $this->getRecentItems()->add(
151 new RecentItem(
152 [
153 'id' => $item->getId(),
154 'entityId' => $item->getEntityId(),
155 'loaded' => true,
156 ]
157 )
158 );
159 }
160 }
161
162 public function addRecentItems(array $items)
163 {
164 foreach ($items as $item)
165 {
166 $this->addRecentItem($item);
167 }
168 }
169
171 {
172 $this->recentItems = new RecentCollection();
173
174 return $this->recentItems;
175 }
176
177 public function setHeader(string $header, array $options = [])
178 {
179 if (strlen($header) > 0)
180 {
181 $this->header = $header;
182 $this->headerOptions = $options;
183 }
184 }
185
186 public function getHeader(): ?string
187 {
188 return $this->header;
189 }
190
191 public function getHeaderOptions(): ?array
192 {
194 }
195
196 public function setFooter(string $footer, array $options = [])
197 {
198 if (strlen($footer) > 0)
199 {
200 $this->footer = $footer;
201 $this->footerOptions = $options;
202 }
203 }
204
205 public function getFooter(): ?string
206 {
207 return $this->footer;
208 }
209
210 public function getFooterOptions(): ?array
211 {
213 }
214
215 public function handleItemAdd(Item $item): void
216 {
217 $item->setDialog($this);
218
219 $recentItem = $this->getRecentItems()->getByItem($item);
220 if ($recentItem)
221 {
222 $recentItem->setLoaded(true);
223 $recentItem->setAvailable($item->isAvailableInRecentTab());
224 $item->setContextSort($recentItem->getLastUseDate());
225 }
226
227 $globalRecentItem = $this->getGlobalRecentItems()->getByItem($item);
228 if ($globalRecentItem)
229 {
230 $globalRecentItem->setLoaded(true);
231 $item->setGlobalSort($globalRecentItem->getLastUseDate());
232 }
233
234 $preselectedItem = $this->getPreselectedCollection()->getByItem($item);
235 if ($preselectedItem && !$preselectedItem->getItem())
236 {
237 $preselectedItem->setItem($item);
238 }
239
240 foreach ($item->getChildren() as $childItem)
241 {
242 $this->handleItemAdd($childItem);
243 }
244 }
245
247 {
248 return $this->recentItems;
249 }
250
252 {
254 }
255
256 public function addTab(Tab $tab): void
257 {
258 if (!empty($tab->getId()))
259 {
260 $this->tabs[$tab->getId()] = $tab;
261 }
262 }
263
267 public function getTabs(): array
268 {
269 return $this->tabs;
270 }
271
272 public function getTab(string $tabId): ?Tab
273 {
274 return $this->tabs[$tabId] ?? null;
275 }
276
277 public function addEntity(Entity $entity)
278 {
279 if (!empty($entity->getId()))
280 {
281 $this->entities[$entity->getId()] = $entity;
282 }
283 }
284
288 public function getEntities(): array
289 {
290 return $this->entities;
291 }
292
298 public function getEntity(string $entityId): ?Entity
299 {
300 return $this->entities[$entityId] ?? null;
301 }
302
306 public function load(): void
307 {
308 $entities = [];
309 foreach ($this->getEntities() as $entity)
310 {
311 if ($entity->hasDynamicLoad())
312 {
313 $entities[] = $entity->getId();
314 }
315 }
316
317 if (empty($entities))
318 {
319 return;
320 }
321
322 $this->fillRecentItems($entities);
323 if ($this->getContext() !== null)
324 {
325 $this->fillGlobalRecentItems($entities);
326 }
327
328 foreach ($entities as $entityId)
329 {
330 $this->getEntity($entityId)->getProvider()->fillDialog($this);
331 }
332
333 $this->loadRecentItems();
334 $this->loadPreselectedItems();
335 }
336
341 public function doSearch(SearchQuery $searchQuery)
342 {
343 if (empty($searchQuery->getQueryWords()))
344 {
345 return;
346 }
347
348 $entities = [];
349 foreach ($this->getEntities() as $entity)
350 {
351 $hasDynamicSearch =
352 $entity->isSearchable() &&
353 ($entity->hasDynamicSearch() || $searchQuery->hasDynamicSearchEntity($entity->getId()))
354 ;
355
356 if ($hasDynamicSearch)
357 {
358 $entities[] = $entity->getId();
359 }
360 }
361
362 if ($this->getContext() !== null)
363 {
364 $this->fillRecentItems($entities);
365 }
366
367 $this->fillGlobalRecentItems($entities);
368 foreach ($entities as $entityId)
369 {
370 $this->getEntity($entityId)->getProvider()->doSearch($searchQuery, $this);
371 }
372 }
373
378 public function getChildren(Item $parentItem)
379 {
380 $entities = [];
381 foreach ($this->getEntities() as $entity)
382 {
383 if ($entity->hasDynamicLoad())
384 {
385 $entities[] = $entity->getId();
386 }
387 }
388
389 $entity = $this->getEntity($parentItem->getEntityId());
390 if ($entity && $entity->hasDynamicLoad())
391 {
392 $this->fillGlobalRecentItems($entities);
393 $entity->getProvider()->getChildren($parentItem, $this);
394 }
395 }
396
398 {
399 $this->preselectedItems->load($preselectedItems);
400 }
401
406
410 public function loadPreselectedItems($preselectedMode = true): void
411 {
412 if ($this->getPreselectedCollection()->count() < 1)
413 {
414 return;
415 }
416
418 {
419 $unloadedIds = [];
420 $entity = $this->getEntity($entityId) ?? Entity::create(['id' => $entityId]);
421 foreach ($preselectedItems as $preselectedItem)
422 {
423 // Entity doesn't exist
424 if (!$entity && $preselectedMode)
425 {
426 $this->addItem(self::createHiddenItem($preselectedItem->getId(), $entityId));
427 }
428 else if (!$preselectedItem->isLoaded())
429 {
430 $unloadedIds[] = $preselectedItem->getId();
431 }
432 }
433
434 if ($entity && !empty($unloadedIds))
435 {
436 $availableItems = [];
437 $items =
438 $preselectedMode
439 ? $entity->getProvider()->getPreselectedItems($unloadedIds)
440 : $entity->getProvider()->getItems($unloadedIds)
441 ;
442
443 foreach ($items as $item)
444 {
445 $availableItems[$item->getId()] = $item;
446 }
447
448 foreach ($unloadedIds as $unloadedId)
449 {
450 $item = $availableItems[$unloadedId] ?? null;
451 if ($item)
452 {
453 $this->addItem($item);
454 }
455 else if ($preselectedMode)
456 {
457 $this->addItem(self::createHiddenItem($unloadedId, $entityId));
458 }
459 }
460 }
461 }
462 }
463
464 public function shouldClearUnavailableItems(): bool
465 {
467 }
468
469 public static function createHiddenItem($id, $entityId): Item
470 {
471 return new Item([
472 'id' => $id,
473 'entityId' => $entityId,
474 'title' => Loc::getMessage("UI_SELECTOR_HIDDEN_ITEM_TITLE"),
475 'hidden' => true,
476 'deselectable' => false,
477 'searchable' => false,
478 'saveable' => false,
479 'link' => '',
480 'avatar' => '',
481 'availableInRecentTab' => false,
482 ]);
483 }
484
489 public static function getSelectedItems(array $ids, array $options = []): ItemCollection
490 {
491 return self::getItemsInternal($ids, $options, true);
492 }
493
494 public static function getPreselectedItems(array $ids, array $options = []): ItemCollection
495 {
496 return self::getItemsInternal($ids, $options, true);
497 }
498
499 public static function getItems(array $ids, array $options = []): ItemCollection
500 {
501 return self::getItemsInternal($ids, $options, false);
502 }
503
504 private static function getItemsInternal(array $ids, array $options = [], $preselectedMode = true): ItemCollection
505 {
506 $isAssocArray = array_keys($options) !== range(0, count($options) - 1);
507 $dialogOptions = $isAssocArray ? $options : ['entities' => $options];
508
509 $dialog = new self($dialogOptions);
510 $dialog->setPreselectedItems($ids);
511 $dialog->loadPreselectedItems($preselectedMode);
512 $dialog->applyFilters();
513
514 return $dialog->getItemCollection();
515 }
516
518 {
519 return $this->errors;
520 }
521
522 public function addError(EntityError $error): void
523 {
524 $this->errors->add($error);
525 }
526
528 {
529 if ($this->getContext() === null)
530 {
531 return;
532 }
533
534 foreach ($recentItems as $recentItemOptions)
535 {
536 if (!is_array($recentItemOptions))
537 {
538 continue;
539 }
540
541 $recentItem = new Item($recentItemOptions);
542 $entity = $this->getEntity($recentItem->getEntityId());
543
544 if ($entity)
545 {
546 $entity->getProvider()->handleBeforeItemSave($recentItem);
547 if ($recentItem->isSaveable())
548 {
549 EntityUsageTable::merge([
550 'USER_ID' => $GLOBALS['USER']->getId(),
551 'CONTEXT' => $this->getContext(),
552 'ENTITY_ID' => $recentItem->getEntityId(),
553 'ITEM_ID' => $recentItem->getId(),
554 ]);
555 }
556 }
557 }
558 }
559
560 private function fillRecentItems(array $entities)
561 {
562 if (empty($entities))
563 {
564 return;
565 }
566
567 if ($this->getContext() === null)
568 {
569 $usages = $this->getGlobalUsages($entities, $this->recentItemsLimit);
570 while ($usage = $usages->fetch())
571 {
572 $this->getRecentItems()->add(
573 new RecentItem(
574 [
575 'id' => $usage['ITEM_ID'],
576 'entityId' => $usage['ENTITY_ID'],
577 'lastUseDate' => $usage['MAX_LAST_USE_DATE']->getTimestamp(),
578 ]
579 )
580 );
581 }
582 }
583 else
584 {
585 $usages = $this->getContextUsages($entities, $this->recentItemsLimit);
586 foreach ($usages as $usage)
587 {
588 $this->getRecentItems()->add(
589 new RecentItem(
590 [
591 'id' => $usage->getItemId(),
592 'entityId' => $usage->getEntityId(),
593 'lastUseDate' => $usage->getLastUseDate()->getTimestamp(),
594 ]
595 )
596 );
597 }
598 }
599 }
600
601 private function fillGlobalRecentItems(array $entities)
602 {
603 if (empty($entities))
604 {
605 return;
606 }
607
608 $usages = $this->getGlobalUsages($entities);
609 while ($usage = $usages->fetch())
610 {
611 $this->getGlobalRecentItems()->add(
612 new RecentItem(
613 [
614 'id' => $usage['ITEM_ID'],
615 'entityId' => $usage['ENTITY_ID'],
616 'lastUseDate' => $usage['MAX_LAST_USE_DATE']->getTimestamp(),
617 ]
618 )
619 );
620 }
621 }
622
623 private function getContextUsages(array $entities, int $limit = 50)
624 {
625 return EntityUsageTable::getList(
626 [
627 'select' => ['*'],
628 'filter' => [
629 '=USER_ID' => $this->getCurrentUserId(),
630 '=CONTEXT' => $this->getContext(),
631 '@ENTITY_ID' => $entities,
632 ],
633 'limit' => $limit,
634 'order' => [
635 'LAST_USE_DATE' => 'DESC',
636 ],
637 ]
638 )->fetchCollection();
639 }
640
641 private function getGlobalUsages(array $entities, int $limit = 200)
642 {
643 $query = EntityUsageTable::query();
644 $query->setSelect(['ENTITY_ID', 'ITEM_ID', 'MAX_LAST_USE_DATE']);
645 $query->setGroup(['ENTITY_ID', 'ITEM_ID']);
646 $query->where('USER_ID', $this->getCurrentUserId());
647 $query->whereIn('ENTITY_ID', $entities);
648
649 if ($this->getContext() !== null)
650 {
651 $query->whereNot('CONTEXT', $this->getContext());
652 }
653
654 $query->registerRuntimeField(new ExpressionField('MAX_LAST_USE_DATE', 'MAX(%s)', 'LAST_USE_DATE'));
655 $query->setOrder(['MAX_LAST_USE_DATE' => 'desc']);
656 $query->setLimit($limit);
657
658 return $query->exec();
659 }
660
661 private function loadRecentItems()
662 {
663 foreach ($this->getEntities() as $entity)
664 {
665 $unloadedIds = [];
666 $unavailableIds = [];
667 $recentItems = $this->getRecentItems()->getEntityItems($entity->getId());
668 foreach ($recentItems as $recentItem)
669 {
670 if (!$recentItem->isAvailable())
671 {
672 $unavailableIds[] = $recentItem->getId();
673 }
674 else if (!$recentItem->isLoaded())
675 {
676 $unloadedIds[] = $recentItem->getId();
677 }
678 }
679
680 if (!empty($unloadedIds))
681 {
682 $availableItems = [];
683 $items = $entity->getProvider()->getItems($unloadedIds);
684 foreach ($items as $item)
685 {
686 if ($item instanceof Item)
687 {
688 $availableItems[$item->getId()] = $item;
689 }
690 }
691
692 foreach ($unloadedIds as $unloadedId)
693 {
694 $item = $availableItems[$unloadedId] ?? null;
695 if ($item && $item->isAvailableInRecentTab())
696 {
697 $this->addRecentItem($item);
698 }
699 else
700 {
701 $unavailableIds[] = $unloadedId;
702 }
703 }
704 }
705
706 if ($this->getContext() !== null && $this->shouldClearUnavailableItems() && !empty($unavailableIds))
707 {
708 EntityUsageTable::deleteByFilter([
709 '=USER_ID' => $this->getCurrentUserId(),
710 '=CONTEXT' => $this->getContext(),
711 '=ENTITY_ID' => $entity->getId(),
712 '@ITEM_ID' => $unavailableIds,
713 ]);
714 }
715 }
716 }
717
718 public function applyFilters(): void
719 {
720 foreach ($this->getEntities() as $entity)
721 {
722 $items = $this->getItemCollection()->getEntityItems($entity->getId());
723 if (empty($items))
724 {
725 continue;
726 }
727
728 $filters = $entity->getFilters();
729 foreach ($filters as $filter)
730 {
731 $filter->apply($items, $this);
732 }
733 }
734 }
735
739 public function getAjaxData(): array
740 {
741 $this->applyFilters();
742
743 return $this->jsonSerialize();
744 }
745
746 public function jsonSerialize(): array
747 {
748 $json = [
749 'id' => $this->getId(),
750 'items' => $this->getItemCollection(),
751 'tabs' => array_values($this->getTabs()),
752 'entities' => array_values($this->getEntities()),
753 ];
754
755 if ($this->getHeader())
756 {
757 $json['header'] = $this->getHeader();
758 $json['headerOptions'] = $this->getHeaderOptions();
759 }
760
761 if ($this->getFooter())
762 {
763 $json['footer'] = $this->getFooter();
764 $json['footerOptions'] = $this->getFooterOptions();
765 }
766
767 if ($this->getRecentItems()->count() > 0)
768 {
769 $json['recentItems'] = $this->getRecentItems();
770 }
771
772 if ($this->getPreselectedCollection()->count() > 0)
773 {
774 $json['preselectedItems'] = $this->getPreselectedCollection();
775 }
776
777 if ($this->getErrors()->count() > 0)
778 {
779 $json['errors'] = $this->getErrors();
780 }
781
782 return $json;
783 }
784
785 public function removeTab(string $id): void
786 {
787 unset($this->tabs[$id]);
788 }
789}
__construct(array $options)
Определения dialog.php:57
getChildren(Item $parentItem)
Определения dialog.php:378
loadPreselectedItems($preselectedMode=true)
Определения dialog.php:410
const MAX_RECENT_ITEMS_LIMIT
Определения dialog.php:55
setPreselectedItems(array $preselectedItems)
Определения dialog.php:397
shouldClearUnavailableItems()
Определения dialog.php:464
int $recentItemsLimit
Определения dialog.php:53
addError(EntityError $error)
Определения dialog.php:522
setFooter(string $footer, array $options=[])
Определения dialog.php:196
$clearUnavailableItems
Определения dialog.php:50
static getSelectedItems(array $ids, array $options=[])
Определения dialog.php:489
getEntity(string $entityId)
Определения dialog.php:298
getGlobalRecentItems()
Определения dialog.php:251
static getItems(array $ids, array $options=[])
Определения dialog.php:499
removeTab(string $id)
Определения dialog.php:785
doSearch(SearchQuery $searchQuery)
Определения dialog.php:341
setHeader(string $header, array $options=[])
Определения dialog.php:177
static createHiddenItem($id, $entityId)
Определения dialog.php:469
addItems(array $items)
Определения dialog.php:135
static getPreselectedItems(array $ids, array $options=[])
Определения dialog.php:494
addItem(Item $item)
Определения dialog.php:126
addEntity(Entity $entity)
Определения dialog.php:277
addTab(Tab $tab)
Определения dialog.php:256
saveRecentItems(array $recentItems)
Определения dialog.php:527
getTab(string $tabId)
Определения dialog.php:272
addRecentItem(Item $item)
Определения dialog.php:143
getItemCollection()
Определения dialog.php:116
getPreselectedCollection()
Определения dialog.php:402
addRecentItems(array $items)
Определения dialog.php:162
handleItemAdd(Item $item)
Определения dialog.php:215
static create(array $entityOptions)
Определения entity.php:49
getEntityId()
Определения item.php:185
setDialog(Dialog $dialog)
Определения item.php:651
isAvailableInRecentTab()
Определения item.php:583
getChildren()
Определения item.php:455
setContextSort(?int $sort)
Определения item.php:627
setGlobalSort(?int $sort)
Определения item.php:639
hasDynamicSearchEntity(string $entityId)
Определения searchquery.php:67
getId()
Определения tab.php:175
$options
Определения commerceml2.php:49
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$query
Определения get_search.php:11
$entity
$filter
Определения iblock_catalog_list.php:54
$success
Определения mail_entry.php:69
$entityId
Определения payment.php:4
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$items
Определения template.php:224
$error
Определения subscription_card_product.php:20
$GLOBALS['_____370096793']
Определения update_client.php:1