Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
helper.php
1<?
10
11use Bitrix\Main;
15
16abstract class Helper
17{
18 const DEBUG_MODE_OPT = 'location2_debug_mode';
19
20 const IMPORT_PAGE_URL = 'sale_location_import.php';
21 const REINDEX_PAGE_URL = 'sale_location_reindex.php';
22 const MIGRATION_PAGE_URL = 'sale_location_migration.php';
23
24 const LOCATION_LINK_DATA_CACHE_TAG = 'sale-location-data';
25
26 #####################################
27 #### Entity settings
28 #####################################
29
30 abstract public static function getEntityRoadMap();
31
32 public static function getEntityRoadCode()
33 {
34 return 'main';
35 }
36
37 // this should be overlapped for each ancestor
38 public static function getColumns($page)
39 {
40 // in the middle of extension, should be like this:
41 //return array_merge(parent::getColumns(), self::getMap());
42
43 return self::getMap($page);
44 }
45
46 // get part of the whole field map for responsibility zone of the current entity
47 // call this only with self::
48 public static function getMap($page)
49 {
50 static $flds;
51
52 if($flds == null)
53 $flds = static::readMap(self::getEntityRoadCode(), $page);
54
55 return $flds;
56 }
57
58 #####################################
59 #### CRUD wrappers
60 #####################################
61
62 // columns shown in all grids
63 public static function getListGridColumns()
64 {
65 $columns = static::getColumns('list');
66 foreach($columns as &$col)
67 $col['DEFAULT'] = true;
68
69 return $columns;
70 }
71
72 // columns shown in all filters
73 public static function getFilterColumns()
74 {
75 $columns = static::getColumns('list');
76 foreach($columns as &$col)
77 $col['DEFAULT'] = true;
78
79 return $columns;
80 }
81
82 // columns shown in all forms
83 public static function getDetailPageRows()
84 {
85 return static::getColumns('list');
86 }
87
88 // generalized filter to orm filter proxy
89 public static function getParametersForList($proxed)
90 {
91 $columns = self::getMap('list'); // columns only for 'main' class
92
93 $parameters = array();
94
95 // filter
96 $filter = array();
97 if(is_array($proxed['FILTER']) && !empty($proxed['FILTER']))
98 {
99 foreach($columns as $code => $fld)
100 {
101 if($fld['data_type'] == 'integer' || $fld['data_type'] == 'float')
102 {
103 // range or list expected
104
105 if(is_array($proxed['FILTER'][$code]))
106 {
107 if(mb_strlen($proxed['FILTER'][$code]['FROM']) && mb_strlen($proxed['FILTER'][$code]['TO'])) // range
108 {
109 $filter['><'.$code] = array($proxed['FILTER'][$code]['FROM'], $proxed['FILTER'][$code]['TO']);
110 }
111 elseif(mb_strlen($proxed['FILTER'][$code]['FROM'])) // greather than
112 {
113 $filter['>='.$code] = $proxed['FILTER'][$code]['FROM'];
114 }
115 elseif(mb_strlen($proxed['FILTER'][$code]['TO'])) // less than
116 {
117 $filter['<='.$code] = $proxed['FILTER'][$code]['TO'];
118 }
119 }
120 elseif(mb_strlen($proxed['FILTER'][$code]))
121 {
122 $filter['='.$code] = (string)$proxed['FILTER'][$code];
123 }
124 }
125 else
126 {
127 if($proxed['FILTER'][$code] <> '')
128 {
129 $filter[static::getFilterModifier($fld['data_type']).$code] = $proxed['FILTER'][$code];
130 }
131 }
132 }
133 }
134
135 if(!empty($filter))
136 $parameters['filter'] = $filter;
137
138 // select
139 foreach($columns as $code => $col)
140 $parameters['select'][] = $code;
141
142 // order
143 if(is_array($proxed['ORDER']) && !empty($proxed['ORDER']))
144 $parameters['order'] = $proxed['ORDER'];
145
146 // nav (unused)
147 if(($page = intval($proxed['NAV']['PAGE_NUM'])) && ($lop = intval($proxed['NAV']['LOP'])))
148 {
149 $roadMap = static::getEntityRoadMap();
150 $road = $roadMap[self::getEntityRoadCode()]['name'];
151 $class = $road.'Table';
152
153 $count = $class::getList(array(
154 'filter' => is_array($parameters['filter']) ? $parameters['filter'] : array(),
155 'select' => array('CNT'),
156 'runtime' => array(
157 'CNT' => array(
158 'data_type' => 'integer',
159 'expression' => array(
160 'count(%u)',
161 'ID'
162 )
163 )
164 )
165 ))->fetch();
166
167 $bounds = Main\DB\Paginator::calculateQueryLimits($count['CNT'], $page, $lop);
168 $parameters['offset'] = $bounds[0];
169 $parameters['limit'] = $bounds[1];
170 }
171
172 return $parameters;
173 }
174
175 /*
176 * $parameters: array of keys: FILTER (generalized), ID, OPERATION
177 */
178 public static function performGridOperations($parameters)
179 {
180 $result = array(
181 'sucess' => true,
182 'errors' => array()
183 );
184
185 @set_time_limit(0);
186
187 if(is_array($parameters['ID']) && !empty($parameters['ID']))
188 {
189 $parameters['ID'] = array_unique($parameters['ID']);
190 foreach($parameters['ID'] as $id)
191 {
192 $res = static::delete($id);
193 if(!$res['success'])
194 {
195 $result['success'] = false;
196 $result['errors'] = array_merge($result['errors'], $res['errors']);
197 }
198 }
199 }
200 else if(is_array($parameters['FILTER'])) // filter can be empty
201 {
202 $entityClass = static::getEntityClass();
203 $parameters = Helper::getParametersForList($parameters); // from generalized to orm
204
205 $glParams = array('select' => array('ID'));
206
207 if(is_array($parameters['filter']) && !empty($parameters['filter']))
208 $glParams['filter'] = $parameters['filter'];
209
210 $resItems = $entityClass::getList($glParams);
211
212 while ($item = $resItems->fetch())
213 {
214
215 /* Locations have tree-style structure so
216 * we could have deleted some of them
217 * during previous iterations. Let's check this.
218 */
219 if(!$entityClass::getById($item['ID'])->fetch())
220 continue;
221
222
223 $res = static::delete($item['ID']);
224 if(!$res['success'])
225 {
226 $result['success'] = false;
227 $result['errors'] = array_merge($result['errors'], $res['errors']);
228 }
229 }
230 }
231
232 return $result;
233 }
234
235 // get data to display in a form
236 public static function getFormData($id)
237 {
238 $parameters = static::proxyListRequest('detail');
239 $parameters['filter']['='.static::getPrimaryFieldName()] = $id;
240
241 $formData = static::getList($parameters)->fetch();
242
243 if(!is_array($formData) || empty($formData))
244 throw new Main\SystemException(Loc::getMessage('SALE_LOCATION_E_ITEM_NOT_FOUND'));
245
246 return $formData;
247 }
248
249 public static function makeSafeDisplay(&$value, $code)
250 {
251 $columns = static::getColumns('');
252
253 $value = (string)$value;
254 if(!empty($columns[$code]))
255 {
256 if ($value === '' && isset($columns[$code]['default']))
257 {
258 $value = (string)$columns[$code]['default'];
259 }
260
261 switch($columns[$code]['data_type'])
262 {
263 case 'integer':
264 $value = (int)$value;
265 break;
266 case 'float':
267 $value = (float)$value;
268 break;
269 default:
270 $value = htmlspecialcharsbx($value);
271 }
272 }
273 else
274 $value = htmlspecialcharsbx($value);
275
276 return $value;
277 }
278
279 ##############################################
280 ##############################################
281 ##############################################
282
283 public static function validateUpdateRequest($data)
284 {
285 return array();
286 }
287
288 // this function could be much more complicated in the derivative classes
289 public static function proxyUpdateRequest($data)
290 {
291 unset($data['ID']); // drop id if presents
292
293 $proxed = array();
294 $columns = static::getColumns('list');
295
296 foreach($columns as $code => $void)
297 {
298 if(isset($data[$code]))
299 $proxed[$code] = $data[$code];
300 }
301
302 return $proxed;
303 }
304
305 // an adapter from CAdminList + CAdminFilter to ORM getList() logic
306 // deprecated: too strong relation with admin grid, replaced with getParametersForList
307 public static function proxyListRequest($page)
308 {
309 global $by;
310 global $order;
311
312 $columns = self::getMap($page); // columns only for 'main' class
313
314 $parameters = array('filter' => array());
315
316 foreach($columns as $code => $col)
317 $parameters['select'][] = $code;
318
319 $filter = array();
320 if(self::checkUseFilter())
321 {
322 foreach($columns as $code => $fld)
323 {
324 $from = 'find_'.$code.'_1';
325 $to = 'find_'.$code.'_2';
326
327 if ($fld['data_type'] === 'integer' && (isset($GLOBALS[$from]) || isset($GLOBALS[$to])))
328 {
329 // range expected
330
331 $valueFrom = trim((string)($GLOBALS[$from] ?? ''));
332 $valueTo = trim((string)($GLOBALS[$to] ?? ''));
333
334 if ($valueFrom !== '' && $valueTo !== '') // range
335 {
336 $filter['><'.$code] = [(int)$valueFrom, (int)$valueTo];
337 }
338 elseif ($valueFrom !== '') // greather than
339 {
340 $filter['>='.$code] = (int)$valueFrom;
341 }
342 elseif ($valueTo !== '') // less than
343 {
344 $filter['<='.$code] = (int)$valueTo;
345 }
346 }
347 else
348 {
349 $value = trim((string)($GLOBALS['find_'.$code] ?? ''));
350 if ($value !== '')
351 {
352 $filter[static::getFilterModifier($fld['data_type']).$code] = $value;
353 }
354 }
355 }
356 }
357
358 if(!empty($filter))
359 $parameters['filter'] = $filter;
360 if($by <> '')
361 {
362 $columns = static::getColumns($page); // check if that column really exists, for the whole extension hierarchy
363
364 if(isset($columns[$by]))
365 {
366 $parameters['order'] = array($by => isset($order)? $order : 'asc');
367 }
368 }
369
370 return $parameters;
371 }
372
373 // crud over entity: add
374 public static function add($data)
375 {
376 $success = true;
377 $id = false;
378 $entityClass = static::getEntityClass();
379
380 $data = static::convertToArray($data);
381
382 $data = static::proxyUpdateRequest($data);
383 $errors = static::validateUpdateRequest($data);
384
385 if(empty($errors))
386 {
387 $res = $entityClass::add($data);
388 if(!$res->isSuccess())
389 {
390 $success = false;
391 $errors = $res->getErrorMessages();
392 }
393 else
394 $id = $res->getId();
395 }
396 else
397 $success = false;
398
399 return array(
400 'success' => $success,
401 'errors' => $errors,
402 'id' => $id
403 );
404 }
405
406 // crud over entity: update
407 public static function update($primary, $data)
408 {
409 $success = true;
410 $entityClass = static::getEntityClass();
411
412 $data = static::convertToArray($data);
413 $data = static::proxyUpdateRequest($data);
414 $errors = static::validateUpdateRequest($data);
415
416 if(empty($errors))
417 {
418 $res = $entityClass::update($primary, $data);
419 if(!$res->isSuccess())
420 {
421 $success = false;
422 $errors = $res->getErrorMessages();
423 }
424 }
425 else
426 $success = false;
427
428 return array(
429 'success' => $success,
430 'errors' => $errors
431 );
432 }
433
434 // crud over entity: delete
435 public static function delete($primary)
436 {
437 $success = true;
438 $errors = array();
439 $entityClass = static::getEntityClass();
440
441 $res = $entityClass::delete($primary);
442 if(!$res->isSuccess())
443 {
444 $success = false;
445 $errors = $res->getErrorMessages();
446 }
447
448 return array(
449 'success' => $success,
450 'errors' => $errors
451 );
452 }
453
454 // function calculates limit and offset for sql select query, based on current request and session
455 // variables, then forms fake old-style database result
456 public static function getList($parameters = array(), $tableId = false, $navigation = 20, $params = array())
457 {
458 $entityClass = static::getEntityClass();
459 $navNum = ($GLOBALS['NavNum'] ?? 0) + 1;
460 $unique = md5($GLOBALS['APPLICATION']->GetCurPage());
461 $showAll = ($_SESSION[$unique.'SESS_ALL_'.$navNum] ?? null) || ($GET['SHOWALL_'.$navNum] ?? null);
462 $isAdminSection = defined('ADMIN_SECTION') && ADMIN_SECTION === true;
463 $tableId = trim((string)$tableId);
464
465 if (($params["uiMode"] ?? null))
466 {
467 $result = new \CSaleProxyAdminUiResult($parameters, $entityClass, $tableId);
468 }
469 elseif($isAdminSection && $tableId !== '')
470 {
471 $result = new \CSaleProxyAdminResult($parameters, $entityClass, $tableId); // being in admin and knowing table, do admin result api call
472 }
473 else
474 {
475 $result = new \CSaleProxyResult($parameters, $entityClass); // otherwise - public api call
476 }
477
478 if(!$showAll && $navigation !== false)
479 {
480 if($navigation === true)
481 {
482 $result->NavStart();
483 }
484 else
485 {
486 $result->NavStart($navigation);
487 }
488 }
489 else
490 {
491 $result->NavStart();
492 }
493
494 // temporal fix
495 $result->bShowAll = false;
496
497 return $result;
498 }
499
500 public static function convertToArray($data)
501 {
502 if(!is_array($data))
503 {
504 $converted = array();
505 foreach($data as $key => $value)
506 $converted[$key] = $value;
507
508 $data = $converted;
509 }
510
511 foreach($data as &$value)
512 {
513 if(is_string($value))
514 $value = trim($value);
515 }
516
517 return $data;
518 }
519
520 // deprecated: not optimal
521 public static function getIdsByFilter($listFilter)
522 {
523 $ids = array();
524 $entityClass = static::getEntityClass();
525
526 $res = $entityClass::getList(array(
527 'select' => array('ID'),
528 'filter' => is_array($listFilter) ? $listFilter : array()
529 ));
530 while($item = $res->fetch())
531 {
532 $ids[] = intval($item['ID']);
533 }
534
535 return $ids;
536 }
537
538 public static function getPrimaryFieldName()
539 {
540 $map = static::getEntityRoadMap();
541
542 return (string)($map['main']['primaryFieldName'] ?? '') !== '' ? $map['main']['primaryFieldName'] : 'ID';
543 }
544
545 // returns element name by it`s primary
546 public static function getNameToDisplay($id)
547 {
548 if(!($id = intval($id)))
549 return '';
550
551 $entityClass = static::getEntityClass('main');
552
553 $item = $entityClass::getById($id)->fetch();
554 return $item['CODE'];
555 }
556
557 public static function getListUrl($parameters = array())
558 {
559 return self::getUrl(static::LIST_PAGE_URL, $parameters);
560 }
561
562 public static function getEditUrl($parameters = array())
563 {
564 return self::getUrl(static::EDIT_PAGE_URL, $parameters);
565 }
566
567 public static function getImportUrl()
568 {
569 return self::getUrl(static::IMPORT_PAGE_URL, array());
570 }
571
572 public static function getReindexUrl()
573 {
574 return self::getUrl(static::REINDEX_PAGE_URL, array());
575 }
576
577 public static function getMigrationUrl()
578 {
579 return self::getUrl(static::MIGRATION_PAGE_URL, array());
580 }
581
582 public static function getUrl($page, $parameters = array())
583 {
584 if(!is_array($parameters))
585 $parameters = array();
586
587 $parameters['lang'] = LANGUAGE_ID;
588
589 $selfFolderUrl = (defined("SELF_FOLDER_URL") ? SELF_FOLDER_URL : "/bitrix/admin/");
590
591 $packed = self::packUrlParameters($parameters);
592 return $selfFolderUrl.$page.($packed <> ''? '?'.$packed : '');
593 }
594
595 #####################################
596 #### Utilily methods for CRUD
597 #####################################
598
599 // deprecated: too strong relation with admin grid
600 public static function checkUseFilter()
601 {
602 return ($GLOBALS['filter'] ?? null) === 'Y' && !$GLOBALS['del_filter'];
603 }
604
605 public static function readMap($entityRoadCode, $page = 'list')
606 {
607 $roads = static::getEntityRoadMap();
608 $road = $roads[$entityRoadCode];
609
610 if(!$road['name'])
611 throw new Main\SystemException('Undefined entity name in entity map');
612
613 if($page == '')
614 $page = 'list';
615
616 $flds = array();
617 $class = $road['name'].'Table';
618 $excluded = $road['pages'][$page]['excludedColumns'] ?? null;
619 $included = $road['pages'][$page]['includedColumns'] ?? null;
620
621 $map = $class::getMap();
622 if (!empty($road['additional']) && is_array($road['additional']))
623 {
624 $map = array_merge($map, $road['additional']);
625 }
626
627 foreach ($map as $fldCode => $fldDesc)
628 {
629 if (is_array($excluded) && in_array($fldCode, $excluded))
630 {
631 continue;
632 }
633
634 if (is_array($included) && !in_array($fldCode, $included))
635 {
636 continue;
637 }
638
639 if (
640 is_array($fldDesc)
641 && (
642 (isset($fldDesc['title']) && mb_strlen($fldDesc['title']))
643 || (isset($fldDesc['required']) && $fldDesc['required'])
644 || (isset($fldDesc['primary']) && $fldDesc['primary'])
645 || $fldCode === 'ID'
646 )
647 )
648 {
649 $title = trim((string)($fldDesc['title'] ?? ''));
650 $fldDesc['title'] = $title !== ''? htmlspecialcharsbx($title) : $fldCode;
651 $fldDesc['ownerEntity'] = $road['name']; // map can be cumulative, from several entites, so we need to know who is an owner
652 $flds[$fldCode] = $fldDesc;
653 }
654 }
655
656 return $flds;
657 }
658
659 protected static function getFilterModifier($type)
660 {
661 return $type == 'string' ? '?' : '=';
662 }
663
664 protected static function packUrlParameters($parameters = array())
665 {
666 $params = array();
667 foreach($parameters as $param => $value)
668 {
669 if($value <> '')
670 {
671 if(mb_strpos($param, '=') === 0)
672 {
673 // value goes as-is, unsafe
674 $param = mb_substr($param, 1);
675 }
676 else
677 {
678 $value = urlencode($value);
679 }
680
681 $params[] = urlencode($param).'='.$value;
682 }
683 }
684
685 return implode('&', $params);
686 }
687
688 protected static function getEntityClass($code = '')
689 {
690 $entityRoad = static::getEntityRoadMap();
691 $entityName = $entityRoad[$code <> ''? $code : self::getEntityRoadCode()]['name'];
692
693 if(!$entityName)
694 throw new Main\SystemException('Undefined entity name in helper');
695
696 return $entityName.'Table';
697 }
698
699 public static function getWidgetAppearance()
700 {
701 $appearance = Config\Option::get("sale", "sale_location_selector_appearance");
702
703 if(!mb_strlen($appearance) || !in_array($appearance, array('search', 'steps')))
704 return 'steps';
705
706 return $appearance;
707 }
708
709 protected static function normalizeList($list, $expectNumeric = true)
710 {
711 $list = array_unique(array_values($list));
712 foreach($list as $i => $id)
713 {
714 if($expectNumeric)
715 {
716 if(intval($id) != $id)
717 unset($list[$i]);
718
719 $list[$i] = intval($id);
720 if(!$list[$i])
721 unset($list[$i]);
722 }
723 else
724 {
725 if($list[$i] == '')
726 unset($list[$i]);
727 }
728 }
729
730 return $list;
731 }
732
733 // proxy between $_REQUEST and resulting array to save links between entites and locations
734 public static function prepareLinksForSaving($connectorClass, $links)
735 {
736 $useIds = !$connectorClass::getUseCodes();
737 $useGroups = $connectorClass::getUseGroups();
738 $l = $connectorClass::DB_LOCATION_FLAG;
739 $g = $connectorClass::DB_GROUP_FLAG;
740
741 if(isset($links[$l]))
742 {
743 if(is_string($links[$l]))
744 $links[$l] = explode(':', $links[$l]);
745 }
746 else
747 $links[$l] = array();
748
749 $links[$l] = self::normalizeList($links[$l], $useIds);
750
751 if(!$useGroups)
752 unset($links[$g]);
753 else
754 {
755 if(isset($links[$g]))
756 {
757 if(is_string($links[$g]))
758 $links[$g] = explode(':', $links[$g]);
759 }
760 else
761 $links[$g] = array();
762
763 $links[$g] = self::normalizeList($links[$g], $useIds);
764 }
765
766 return $links;
767 }
768
769 public static function resetLocationsForEntity($entityId, $locations, $entityName, $expectCodes = false)
770 {
771 $locList = array();
772
773 if(is_array($locations) && !empty($locations))
774 {
775 foreach($locations as $loc)
776 {
777 if($loc['LOCATION_TYPE'] == 'L')
778 $locList[Location\Connector::DB_LOCATION_FLAG][] = $loc['LOCATION_ID'];
779 elseif($loc['LOCATION_TYPE'] == 'G')
780 $locList[Location\Connector::DB_GROUP_FLAG][] = $loc['LOCATION_ID'];
781 }
782 }
783
784 $entityClass = $entityName.'Table';
785
786 try
787 {
788 if(!empty($locList) && !$expectCodes)
789 {
790 $locList[Location\Connector::DB_LOCATION_FLAG] = $entityClass::normalizeLocationList($locList[Location\Connector::DB_LOCATION_FLAG]);
791
792 $gf = Location\Connector::DB_GROUP_FLAG;
793 if(!empty($locList[$gf]))
794 {
795 $groupCodes = array();
796 $locList[$gf] = array_flip($locList[$gf]);
797 // here we must get codes by ids for groups. There will be no thousands of groups, so we can do the following:
798 $res = Location\GroupTable::getList(array('select' => array('ID', 'CODE')));
799 while($item = $res->fetch())
800 {
801 if(isset($locList[$gf][$item['ID']]))
802 $groupCodes[$item['CODE']] = 1;
803 }
804
805 $locList[$gf] = array_keys($groupCodes);
806 }
807 }
808
809 $entityClass::resetMultipleForOwner($entityId, $locList);
810 }
811 catch(Exception $e)
812 {
813 }
814 }
815}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static normalizeList($list, $expectNumeric=true)
Definition helper.php:709
static getEditUrl($parameters=array())
Definition helper.php:562
static prepareLinksForSaving($connectorClass, $links)
Definition helper.php:734
static getEntityClass($code='')
Definition helper.php:688
static getListUrl($parameters=array())
Definition helper.php:557
static getList($parameters=array(), $tableId=false, $navigation=20, $params=array())
Definition helper.php:456
static readMap($entityRoadCode, $page='list')
Definition helper.php:605
static makeSafeDisplay(&$value, $code)
Definition helper.php:249
static getUrl($page, $parameters=array())
Definition helper.php:582
static performGridOperations($parameters)
Definition helper.php:178
static update($primary, $data)
Definition helper.php:407
static resetLocationsForEntity($entityId, $locations, $entityName, $expectCodes=false)
Definition helper.php:769
static getIdsByFilter($listFilter)
Definition helper.php:521
static getParametersForList($proxed)
Definition helper.php:89
static validateUpdateRequest($data)
Definition helper.php:283
static packUrlParameters($parameters=array())
Definition helper.php:664
$GLOBALS['____1444769544']
Definition license.php:1