Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
connector.php
1<?php
10
11use Bitrix\Main;
16
19
20abstract class Connector extends Entity\DataManager
21{
22 const LINK_ID = 0x01;
23 const LINK_CODE = 0x02;
24
25 const DB_LOCATION_FLAG = 'L';
26 const DB_GROUP_FLAG = 'G';
27
29 const LSTAT_IS_CONNECTOR = 0x02;
32
33 #####################################
34 #### Entity settings
35 #####################################
36
43 abstract public static function getLinkField();
44
49 abstract public static function getTargetEntityName();
50
56 public static function getTypeField()
57 {
58 return 'LOCATION_TYPE';
59 }
60
66 public static function getLocationLinkField()
67 {
68 return 'LOCATION_ID';
69 }
70
76 public static function getTargetEntityPrimaryField()
77 {
78 return 'ID';
79 }
80
87 public static function getUseGroups()
88 {
89 return true;
90 }
91
98 public static function getUseLinkTracking()
99 {
100 return false;
101 }
102
108 public static function getConnectType()
109 {
110 $map = static::getMap();
111 $locLinkFld = static::getLocationLinkField();
112
113 if(isset($map[$locLinkFld]))
114 return $map[$locLinkFld]['data_type'] == 'integer' ? self::LINK_ID : self::LINK_CODE;
115
116 Assert::announceNotImplemented(Loc::getMessage('SALE_LOCATION_CONNECTOR_ENTITY_LINK_FLD_NOT_FOUND_EXCEPTION'));
117 }
118
124 public static function getUseCodes()
125 {
126 return static::getConnectType() == static::LINK_CODE;
127 }
128
129 #####################################
130 #### CRUD wrappers
131 #####################################
132
140 public static function add(array $data)
141 {
142 $res = parent::add($data);
143 if($res->isSuccess())
144 static::setLinkUsage($data[static::getLinkField()], $data[static::getTypeField()], true);
145
146 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
147 static::onAfterModifiy();
148
149 return $res;
150 }
151
160 public static function update($primary, array $data)
161 {
162 $linkFld = static::getLinkField();
163
164 if($primary && isset($data[$linkFld])) // it will break below, at parent::delete()
165 $link = static::getByPrimary($primary)->fetch();
166
167 $res = parent::update($primary, $data);
168 if($res->isSuccess() && isset($data[$linkFld]) && $data[$linkFld] != $link[$linkFld]) // if switched from one entity to another
169 {
170 static::resetLinkUsage($link[static::getLinkField()]); // for donor entity we need to recalc link existence
171 static::setLinkUsage($data[$linkFld], $data[static::getTypeField()], true); // we know there ARE links for retsepient entity
172 }
173
174 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
175 static::onAfterModifiy();
176
177 return $res;
178 }
179
187 public static function delete($primary)
188 {
189 if($primary) // it will break below, at parent::delete()
190 $link = static::getByPrimary($primary)->fetch();
191
192 $res = parent::delete($primary);
193 if($res->isSuccess())
194 static::resetLinkUsage($link[static::getLinkField()]);
195
196 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
197 static::onAfterModifiy();
198
199 return $res;
200 }
201
202 public static function onAfterModifiy()
203 {
204 }
205
222 public static function updateMultipleForOwner($entityPrimary, $links = array(), $behaviour = array('REMOVE_ABSENT' => true))
223 {
224 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
225 $links = static::checkUpdateLinks($links);
226
227 $updateLocations = is_array($links[static::DB_LOCATION_FLAG]);
228 $updateGroups = is_array($links[static::DB_GROUP_FLAG]) && static::getUseGroups();
229
230 // get existed relations
231 $existed = static::getLinkedLocations($entityPrimary);
232
233 if($updateLocations)
234 static::updateMultipleLinkType($entityPrimary, $links[static::DB_LOCATION_FLAG], $existed[static::DB_LOCATION_FLAG], static::DB_LOCATION_FLAG, $behaviour['REMOVE_ABSENT']);
235
236 if($updateGroups)
237 static::updateMultipleLinkType($entityPrimary, $links[static::DB_GROUP_FLAG], $existed[static::DB_GROUP_FLAG], static::DB_GROUP_FLAG, $behaviour['REMOVE_ABSENT']);
238
239 static::resetLinkUsage($entityPrimary);
240
241 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
242 static::onAfterModifiy();
243
244 return true;
245 }
246
247 // removes all links with a given entity
248 public static function deleteAllForOwner($entityPrimary, $behaviour = array('BATCH_MODE' => false))
249 {
250 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
251
252 if($behaviour['BATCH_MODE'])
253 {
254 // low-level drop
255 $dbConnection = Main\HttpApplication::getConnection();
256 $typeField = static::getTypeField();
257
258 $sql ='
259 delete
260 from
261 '.static::getTableName().'
262 where
263 '.static::getLinkField().' = \''.$dbConnection->getSqlHelper()->forSql($entityPrimary).'\'';
264
265 if($typeField <> '')
266 {
267 $sql .= ' AND (
268 '.$typeField.' = \''.static::DB_LOCATION_FLAG.'\'
269 OR '.$typeField.' = \''.static::DB_GROUP_FLAG.'\'
270 )';
271 }
272
273 $dbConnection->query($sql);
274 }
275 else
276 {
277 // get existed relations
278 $existed = static::getLinkedLocations($entityPrimary);
279
280 if (
281 !empty($existed[static::DB_LOCATION_FLAG])
282 && is_array($existed[static::DB_LOCATION_FLAG])
283 )
284 {
285 static::updateMultipleLinkType(
286 $entityPrimary,
287 [],
288 $existed[static::DB_LOCATION_FLAG],
289 static::DB_LOCATION_FLAG,
290 true
291 );
292 }
293
294 if (
295 static::getUseGroups()
296 && !empty($existed[static::DB_GROUP_FLAG])
297 && is_array($existed[static::DB_GROUP_FLAG])
298 )
299 {
300 static::updateMultipleLinkType(
301 $entityPrimary,
302 [],
303 $existed[static::DB_GROUP_FLAG],
304 static::DB_GROUP_FLAG,
305 true
306 );
307 }
308 }
309
310 static::setLinkUsage($entityPrimary, static::DB_LOCATION_FLAG, false);
311 static::setLinkUsage($entityPrimary, static::DB_GROUP_FLAG, false);
312
313 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
314 static::onAfterModifiy();
315 }
316
317 public static function deleteAll()
318 {
319 $dbConnection = Main\HttpApplication::getConnection();
320 $dbConnection->query('truncate table '.static::getTableName());
321
322 static::deleteLinkUsageOption();
323
324 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
325 static::onAfterModifiy();
326 }
327
332 public static function resetMultipleForOwner($entityPrimary, $links = array())
333 {
334 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
335 $links = static::checkUpdateLinks($links);
336
337 static::deleteAllForOwner($entityPrimary, array('BATCH_MODE' => true));
338
339 $map = static::getMap();
340 $linkFld = static::getLinkField();
341 $locationLinkFld = static::getLocationLinkField();
342 $typeFld = static::getTypeField();
343
344 $fields = array(
345 $linkFld => $map[$linkFld], // DELIVERY_ID, SITE_ID, smth else
346 $locationLinkFld => $map[$locationLinkFld] // LOCATION_ID or LOCATION_CODE or smth else
347 );
348 if($useGroups = static::getUseGroups())
349 $fields[$typeFld] = $map[$typeFld]; // LOCATION_TYPE: L or G
350
351 $inserter = new BlockInserter(array(
352 'tableName' => static::getTableName(),
353 'exactFields' => $fields
354 ));
355
356 $smthAdded = false;
357 if (
358 !empty($links[static::DB_LOCATION_FLAG])
359 && is_array($links[static::DB_LOCATION_FLAG])
360 )
361 {
362 foreach($links[static::DB_LOCATION_FLAG] as $id)
363 {
364 $data = array(
365 $linkFld => $entityPrimary,
366 $locationLinkFld => $id,
367 );
368 if($useGroups)
369 $data[$typeFld] = static::DB_LOCATION_FLAG;
370
371 $inserter->insert($data);
372 $smthAdded = true;
373 }
374 }
375 static::setLinkUsage($entityPrimary, static::DB_LOCATION_FLAG, $smthAdded);
376
377 $smthAdded = false;
378 if (
379 !empty($links[static::DB_GROUP_FLAG])
380 && is_array($links[static::DB_GROUP_FLAG])
381 && $useGroups
382 )
383 {
384 foreach($links[static::DB_GROUP_FLAG] as $id)
385 {
386 $data = array(
387 $linkFld => $entityPrimary,
388 $locationLinkFld => $id,
389 );
390 if($useGroups)
391 $data[$typeFld] = static::DB_GROUP_FLAG;
392
393 $inserter->insert($data);
394 $smthAdded = true;
395 }
396 }
397 static::setLinkUsage($entityPrimary, static::DB_GROUP_FLAG, $smthAdded);
398
399 $inserter->flush();
400
401 $GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
402 }
403
404 private static function updateMultipleLinkType($entityPrimary, $links, $existed, $linkType = self::DB_LOCATION_FLAG, $doRemove = true)
405 {
406 $useGroups = static::getUseGroups();
407
408 $smthAdded = false;
409 foreach($links as $k => $loc)
410 {
411 if(!isset($existed[$loc]))
412 {
413 $data = array(
414 static::getLinkField() => $entityPrimary,
415 static::getLocationLinkField() => $loc
416 );
417 if($useGroups)
418 $data[static::getTypeField()] = $linkType;
419
420 $res = static::add($data);
421 if(!$res->isSuccess())
422 throw new Main\SystemException(Loc::getMessage('SALE_LOCATION_CONNECTOR_ENTITY_CANNOT_ADD_EXCEPTION'));
423
424 $smthAdded = true;
425 }
426 else
427 unset($existed[$loc]);
428 }
429
430 if($doRemove && !empty($existed))
431 {
432 foreach($existed as $loc => $k)
433 {
434 $data = array(
435 static::getLinkField() => $entityPrimary,
436 static::getLocationLinkField() => $loc
437 );
438 if($useGroups)
439 $data[static::getTypeField()] = $linkType;
440
441 $res = static::delete($data);
442 if(!$res->isSuccess())
443 throw new Main\SystemException(Loc::getMessage('SALE_LOCATION_CONNECTOR_ENTITY_CANNOT_DELETE_EXCEPTION'));
444 }
445 }
446 }
447
448 #####################################
449 #### Getters
450 #####################################
451
467 public static function getConnectedLocationsQuery($entityPrimary, $parameters = array(), $behaviour = array('GET_LINKED_THROUGH_GROUPS' => false))
468 {
469 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
470
471 if(!is_array($parameters))
472 $parameters = array();
473 if(!is_array($behaviour))
474 $behaviour = array();
475 if(!isset($behaviour['GET_LINKED_THROUGH_GROUPS']))
476 $behaviour['GET_LINKED_THROUGH_GROUPS'] = false;
477
478 $useGroups = GroupTable::checkGroupUsage() && static::getUseGroups(); // check if we have groups in project and entity uses groups
479 $getLinkedThroughGroups = $behaviour['GET_LINKED_THROUGH_GROUPS'];
480 if(!$useGroups)
481 $getLinkedThroughGroups = false;
482
483 $connType = static::getConnectType();
484
485 // proxy select
486 $select = array();
487 if (empty($parameters['select']) || !is_array($parameters['select']))
488 {
489 $select = ['' => 'LOCATION'];
490 }
491 else
492 {
493 foreach($parameters['select'] as $k => $v)
494 {
495 if($v == '*')
496 {
497 $select[''] = 'LOCATION';
498 }
499 else
500 {
501 if(is_numeric($k) && mb_strpos((string)$v, '.') === false) // is NOT a reference
502 {
503 $k = $v;
504 }
505
506 $select[$k] = 'LOCATION.' . $v;
507 }
508 }
509 }
510
511 // proxy filter
512 $filter = array();
513 if (!empty($parameters['filter']) && is_array($parameters['filter']))
514 {
515 foreach($parameters['filter'] as $k => $v)
516 {
517 $filter['LOCATION.' . $k] = $v;
518 }
519 }
520
521 // proxy order
522 $order = array();
523 if (!empty($parameters['order']) && is_array($parameters['order']))
524 {
525 //if($getLinkedThroughGroups)
526 Assert::announceNotImplemented('Sorry, order-over-union clause is not implemented currently.');
527
528 foreach($parameters['order'] as $k => $v)
529 {
530 $order['LOCATION.' . $k] = $v;
531 }
532 }
533
534 if (!empty($parameters['runtime']) && is_array($parameters['runtime']))
535 {
536 Assert::announceNotImplemented('Sorry, runtime clause is not implemented currently.');
537 }
538
539 $sqls = array();
540
541 if(static::checkLinkUsage($entityPrimary, static::DB_LOCATION_FLAG))
542 {
543 $strictFilter = array_merge($filter, array(
544 '='.static::getLinkField() => $entityPrimary
545 ));
546
547 if($useGroups)
548 $strictFilter['='.static::getTypeField()] = static::DB_LOCATION_FLAG;
549
550 $query = new Entity\Query(self::getEntity());
551 $query
552 ->setSelect($select)
553 ->setFilter($strictFilter)
554 ->setOrder($order);
555
556 $sqls[] = $query->getQuery();
557 }
558
559 if(static::checkLinkUsage($entityPrimary, static::DB_GROUP_FLAG) && $getLinkedThroughGroups)
560 {
561 $query = new Entity\Query(static::getEntity());
562
563 if($connType == self::LINK_CODE) // entity connected by CODE
564 {
565 $query
566 ->registerRuntimeField(
567 'G',
568 array(
569 'data_type' => '\Bitrix\Sale\Location\GroupTable',
570 'reference' => array(
571 '=this.'.static::getLocationLinkField() => 'ref.CODE',
572 '=this.'.static::getTypeField() => array('?', static::DB_GROUP_FLAG)
573 ),
574 'join_type' => 'inner'
575 )
576 )
577 ->registerRuntimeField(
578 'LG',
579 array(
580 'data_type' => '\Bitrix\Sale\Location\GroupLocation',
581 'reference' => array(
582 '=this.G.ID' => 'ref.'.GroupLocationTable::getLinkField(),
583 ),
584 'join_type' => 'inner'
585 )
586 )
587 ->registerRuntimeField(
588 'LOCATION',
589 array(
590 'data_type' => '\Bitrix\Sale\Location\Location',
591 'reference' => array(
592 '=this.LG.LOCATION_ID' => 'ref.ID'
593 ),
594 'join_type' => 'inner'
595 )
596 )
597 ->setSelect($select)
598 ->setFilter(array_merge($filter, array(
599 '='.static::getLinkField() => $entityPrimary
600 )))
601 ->setOrder($order);
602
603 /*
604 select LOCATION.*
605 from b_sale_delivery2location DL
606 inner join b_sale_location_group G on (G.CODE = DL.LOCATION_CODE and DL.LOCATION_TYPE = static::DB_GROUP_FLAG)
607 inner join b_sale_location2location_group LG on (LG.LOCATION_GROUP_ID = G.ID)
608 inner join b_sale_location LOCATION on (LG.LOCATION_ID = LOCATION.ID)
609 where
610 DL.DELIVERY_ID = 2;
611 */
612 }
613 else // entity connected by ID
614 {
615 $query
616 ->registerRuntimeField(
617 'LG',
618 array(
619 'data_type' => '\Bitrix\Sale\Location\GroupLocation',
620 'reference' => array(
621 '=this.'.static::getLocationLinkField() => 'ref.'.GroupLocationTable::getLinkField(),
622 '=this.'.static::getTypeField() => array('?', static::DB_GROUP_FLAG)
623 ),
624 'join_type' => 'inner'
625 )
626 )
627 ->registerRuntimeField(
628 'LOCATION',
629 array(
630 'data_type' => '\Bitrix\Sale\Location\Location',
631 'reference' => array(
632 '=this.LG.LOCATION_ID' => 'ref.ID'
633 ),
634 'join_type' => 'inner'
635 )
636 )
637 ->setSelect($select)
638 ->setFilter(array_merge($filter, array(
639 '='.static::getLinkField() => $entityPrimary
640 )))
641 ->setOrder($order);
642
643 /*
644 select LOCATION.*
645 from b_sale_delivery2location DL
646 inner join b_sale_location2location_group LG on (LG.LOCATION_GROUP_ID = DL.LOCATION_ID and DL.LOCATION_TYPE = static::DB_GROUP_FLAG)
647 inner join b_sale_location LOCATION on (LG.LOCATION_ID = LOCATION.ID)
648 where
649 DL.DELIVERY_ID = 2;
650 */
651 }
652
653 $sqls[] = $query->getQuery();
654 }
655
656 if(empty($sqls)) // entity is not connected, so it means "all connected"
657 return false;
658
659 return static::unionize($sqls);
660 }
661
665 public static function getConnectedLocationsSql($entityPrimary, $parameters = array(), $behaviour = array('GET_LINKED_THROUGH_GROUPS' => false))
666 {
667 return static::getConnectedLocationsQuery($entityPrimary, $parameters, $behaviour);
668 }
669
670 public static function getConnectedLocations($entityPrimary, $parameters = array(), $behaviour = array('GET_LINKED_THROUGH_GROUPS' => false))
671 {
672 $query = static::getConnectedLocationsQuery($entityPrimary, $parameters, $behaviour);
673
674 if(!$query)
675 return new DB\ArrayResult(array());
676
677 return static::queryPage(
678 $query,
679 $parameters['limit'] ?? 0,
680 $parameters['offset'] ?? 0
681 );
682 }
683
695 public static function getConnectedGroups($entityPrimary, $parameters = array())
696 {
697 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
698
699 if(!static::getUseGroups())
700 Assert::announceNotSupported(Loc::getMessage('SALE_LOCATION_CONNECTOR_ENTITY_DOESNT_SUPPORT_GROUPS'));
701
702 if(!is_array($parameters))
703 $parameters = array();
704
705 $parameters['runtime']['C'] = array(
706 'data_type' => static::getEntity()->getFullName(),
707 'reference' => array(
708 '=ref.'.static::getLinkField() => array('?', $entityPrimary),
709 '=ref.'.static::getTypeField() => array('?', static::DB_GROUP_FLAG)
710 ),
711 'join_type' => 'inner'
712 );
713
714 $parameters['runtime']['C']['reference']['=ref.'.static::getLocationLinkField()] = (static::getConnectType() == self::LINK_CODE ? 'this.CODE' : 'this.ID');
715
716 return GroupTable::getList($parameters);
717 }
718
719 // returns list of connected entities for location with ID == $locationPrimary, with an optional filter applied
720 public static function getConnectedEntites($locationPrimary, $parameters = array())
721 {
722 return static::getConnectedEntitiesByCondition($locationPrimary, 'id', $parameters);
723 }
724
725 // returns list of connected entities for location with CODE == $locationPrimary, with an optional filter applied
726 public static function getConnectedEntitesByCode($locationPrimary, $parameters = array()) // getConnectedEntitiesByLocationCode
727 {
728 return static::getConnectedEntitiesByCondition($locationPrimary, 'code', $parameters);
729 }
730
731 // returns sql-select query that can be embeded to another sql query in 'ID in (select * from ...)' manner
732 public static function getConnectedEntitiesQuery($locationPrimary, $linkType = 'id', $parameters = array()) // // getConnectedEntitiesSql
733 {
734 if($linkType == 'id')
735 $locationPrimary = Assert::expectIntegerPositive($locationPrimary, '$locationPrimary');
736 else
737 $locationPrimary = Assert::expectStringNotNull($locationPrimary, '$locationPrimary');
738
739 $useGroups = GroupTable::checkGroupUsage() && static::getUseGroups(); // check if we have groups in project and entity uses groups
740 $useCodes = static::getUseCodes(); // this entity uses codes
741 $groupUseCodes = GroupLocationTable::getUseCodes(); // group entity uses codes
742
743 $typeFld = static::getTypeField();/*LOCATION_TYPE*/
744 $linkFld = static::getLinkField();/*DELIVERY_ID*/
745 $locationLinkFld = static::getLocationLinkField();/*LOCATION_ID*/
746 $targetPrimaryFld = static::getTargetEntityPrimaryField();/*ID*/
747 $groupLocationLinkFld = GroupLocationTable::getLocationLinkField();/*LOCATION_ID*/
748 $groupLinkFld = GroupLocationTable::getLinkField();/*LOCATION_GROUP_ID*/
749
750 $seachById = $linkType == 'id';
751
752 $dbConnection = Main\HttpApplication::getConnection();
753
754 if(!is_array($parameters))
755 $parameters = array();
756
757 if(isset($parameters['runtime']) && is_array($parameters['runtime']))
758 Assert::announceNotImplemented('Sorry, runtime clause is not implemented currently.');
759
760 $order = array();
761 if(isset($parameters['order']) && is_array($parameters['order']))
762 Assert::announceNotImplemented('Sorry, order-over-union clause is not implemented currently.');
763
764 $filter = array();
765 if(!empty($parameters['filter']) && is_array($parameters['filter']))
766 $filter = $parameters['filter'];
767
768 $select = array('*');
769 if(is_array($parameters['select']) && !empty($parameters['select']))
770 $select = $parameters['select'];
771
772 /*
773 query example when working with delivery:
774
775 select distinct D.* from b_sale_delivery D
776 inner join b_sale_delivery2location DL on D.ID = DL.DELIVERY_ID and DL.LOCATION_TYPE = 'L'
777 inner join b_sale_location L1 on L1.CODE = DL.LOCATION_ID
778 inner join b_sale_location L2 on L2.ID(there will be CODE, if we search by code) = 65683 and L2.LEFT_MARGIN >= L1.LEFT_MARGIN and L2.RIGHT_MARGIN <= L1.RIGHT_MARGIN;
779 */
780
781 $query = new Entity\Query(static::getTargetEntityName());
782
783 $DLCondition = array(
784 '=this.'.$targetPrimaryFld/*ID*/ => 'ref.'.$linkFld/*DELIVERY_ID*/
785 );
786 if($useGroups)
787 $DLCondition['=ref.'.$typeFld/*LOCATION_TYPE*/] = array('?', static::DB_LOCATION_FLAG);
788
789 $query
790 ->registerRuntimeField(
791 'DL',
792 array(
793 'data_type' => get_called_class(),
794 'reference' => $DLCondition,
795 'join_type' => 'inner'
796 )
797 )
798 ->registerRuntimeField(
799 'L1',
800 array(
801 'data_type' => '\Bitrix\Sale\Location\Location',
802 'reference' => array(
803 '=this.DL.'.$locationLinkFld/*LOCATION_ID*/ => 'ref.'.($useCodes ? 'CODE' : 'ID'),
804 ),
805 'join_type' => 'inner'
806 )
807 )
808 ->registerRuntimeField(
809 'L2',
810 array(
811 'data_type' => '\Bitrix\Sale\Location\Location',
812 'reference' => array(
813 '=ref.'.($seachById ? 'ID' : 'CODE') => array('?', $locationPrimary), // among parents we have element with ID or CODE is equal to $locationPrimary
814 '>=ref.LEFT_MARGIN' => 'this.L1.LEFT_MARGIN', // and its left_margin
815 '<=ref.RIGHT_MARGIN' => 'this.L1.RIGHT_MARGIN' // and right_margin fit our restrictions
816 ),
817 'join_type' => 'inner'
818 )
819 )
820 ->setSelect($select)
821 ->setFilter($filter)
822 ->setOrder($order);
823
824 if(!$useGroups)
825 {
826 // emulate "select distinct"
827 $query->setGroup($select);
828
829 return $query->getQuery();
830 }
831 else
832 {
833 $sqls = array($query->getQuery());
834
835 $query = new Entity\Query(static::getTargetEntityName());
836
837 /*
838 query example when working with delivery:
839
840 select D.* from b_sale_delivery D
841 inner join b_sale_delivery2location DL on D.ID = DL.DELIVERY_ID and DL.LOCATION_TYPE = 'G'
842 inner join b_sale_location_group G on G.CODE = DL.LOCATION_ID (if this entity uses ID, skip this join)
843 inner join b_sale_grouplocation GL on GL.LOCATION_GROUP_ID = G.ID (if this entity uses ID, there will be DL.LOCATION_ID)
844 inner join b_sale_location L1 on L1.ID (there will be CODE, if grouplocation entity uses CODE) = GL.LOCATION_ID
845 inner join b_sale_location L2 on L2.ID (there will be CODE, if we seach by code) = 65683 and L2.LEFT_MARGIN >= L1.LEFT_MARGIN and L2.RIGHT_MARGIN <= L1.RIGHT_MARGIN;
846 */
847
848 $query
849 ->registerRuntimeField(
850 'DL',
851 array(
852 'data_type' => get_called_class(),
853 'reference' => array(
854 '=this.'.$targetPrimaryFld/*ID*/ => 'ref.'.$linkFld/*DELIVERY_ID*/,
855 '=ref.'.$typeFld/*LOCATION_TYPE*/ => array('?', static::DB_GROUP_FLAG)
856 ),
857 'join_type' => 'inner'
858 )
859 );
860
861 if($useCodes)
862 {
863 $query
864 ->registerRuntimeField(
865 'G',
866 array(
867 'data_type' => '\Bitrix\Sale\Location\Group',
868 'reference' => array(
869 '=this.DL.'.$locationLinkFld/*LOCATION_ID*/ => 'ref.CODE', //$useCodes == true here, so always CODE
870 ),
871 'join_type' => 'inner'
872 )
873 );
874 }
875
876 $query
877 ->registerRuntimeField(
878 'GL',
879 array(
880 'data_type' => '\Bitrix\Sale\Location\GroupLocation',
881 'reference' => array(
882 ($useCodes ? '=this.G.ID' : '=this.DL.'.$locationLinkFld/*LOCATION_ID*/) => 'ref.'.$groupLinkFld/*LOCATION_GROUP_ID*/
883 ),
884 'join_type' => 'inner'
885 )
886 )
887 ->registerRuntimeField(
888 'L1',
889 array(
890 'data_type' => '\Bitrix\Sale\Location\Location',
891 'reference' => array(
892 '=this.GL.'.$groupLocationLinkFld/*LOCATION_ID*/ => 'ref.'.($groupUseCodes ? 'CODE' : 'ID'),
893 ),
894 'join_type' => 'inner'
895 )
896 )
897 ->registerRuntimeField(
898 'L2',
899 array(
900 'data_type' => '\Bitrix\Sale\Location\Location',
901 'reference' => array(
902 '=ref.'.($seachById ? 'ID' : 'CODE') => array('?', $locationPrimary),
903 '>=ref.LEFT_MARGIN' => 'this.L1.LEFT_MARGIN',
904 '<=ref.RIGHT_MARGIN' => 'this.L1.RIGHT_MARGIN'
905 ),
906 'join_type' => 'inner'
907 )
908 )
909 ->setSelect($select)
910 ->setFilter($filter)
911 ->setOrder($order);
912
913 $sqls[] = $query->getQuery();
914
915 return static::unionize($sqls);
916 }
917 }
918
919 protected static function getConnectedEntitiesByCondition($locationPrimary, $linkType = 'id', $parameters = array())
920 {
921 $useGroups = GroupTable::checkGroupUsage() && static::getUseGroups(); // check if we have groups in project and entity uses groups
922
923 $sql = static::getConnectedEntitiesQuery($locationPrimary, $linkType, $parameters);
924 $res = static::queryPage($sql, $parameters['limit'], $parameters['offset']);
925
926 return $res;
927 }
928
929 protected static function getLinkedLocations($entityPrimary)
930 {
931 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
932
933 $existed = [];
934 $linkFld = static::getLocationLinkField();
935 $typeFld = static::getTypeField();
936 if ($typeFld === '')
937 {
938 return $existed;
939 }
940 $res = static::getList([
941 'filter' => [
942 static::getLinkField() => $entityPrimary
943 ],
944 ]);
945 while($item = $res->fetch())
946 {
947 if ($item[$typeFld] !== static::DB_GROUP_FLAG && $item[$typeFld] !== static::DB_LOCATION_FLAG) // strange record found. skip it
948 {
949 continue;
950 }
951 $existed[$item[$typeFld]] ??= [];
952 $existed[$item[$typeFld]][$item[$linkFld]] = true;
953
954 /*if(!in_array($item[static::getTypeField()], array(static::DB_GROUP_FLAG, static::DB_LOCATION_FLAG))) // strange record found. skip it
955 continue;
956
957 $existed[$item[static::getTypeField()]][$item[$linkFld]] = true; */
958 }
959 unset($item, $res);
960
961 return $existed;
962 }
963
969 public static function getLinkStatusForMultipleNodes($nodeInfo, $entityPrimary, $connectors = false) // rename to: getConnectionStatusForMultipleNodes
970 {
971 $nodeInfo = Assert::expectArray($nodeInfo, '$nodeInfo');
972 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
973
974 $result = array();
975
976 if(!static::checkLinkUsageAny($entityPrimary)) // if there are no links at all, connection virtually exists
977 {
978 foreach($nodeInfo as $node)
979 {
980 $result[$node['ID']] = self::LSTAT_BELOW_CONNECTOR;
981 }
982
983 return $result;
984 }
985
986 if(!is_array($connectors))
987 $connectors = static::getConnectedLocationsInfo($entityPrimary);
988
989 foreach($nodeInfo as $node)
990 {
991 $node = Assert::expectNotEmptyArray($node, '$nodeInfo[]');
992 $node['ID'] = Assert::expectIntegerPositive($node['ID'], '$nodeInfo[][ID]');
993 $node['LEFT_MARGIN'] = Assert::expectIntegerNonNegative($node['LEFT_MARGIN'], '$nodeInfo[][LEFT_MARGIN]');
994 $node['RIGHT_MARGIN'] = Assert::expectIntegerPositive($node['RIGHT_MARGIN'], '$nodeInfo[][RIGHT_MARGIN]');
995
996 $result[$node['ID']] = false;
997 foreach($connectors as $connector)
998 {
999 if($connector['ID'] == $node['ID'])
1000 {
1001 $result[$node['ID']] = self::LSTAT_IS_CONNECTOR;
1002 break;
1003 }
1004 elseif($node['LEFT_MARGIN'] >= $connector['LEFT_MARGIN'] && $node['RIGHT_MARGIN'] <= $connector['RIGHT_MARGIN'])
1005 {
1006 $result[$node['ID']] = self::LSTAT_BELOW_CONNECTOR;
1007 break;
1008 }
1009 elseif($node['LEFT_MARGIN'] <= $connector['LEFT_MARGIN'] && $node['RIGHT_MARGIN'] >= $connector['RIGHT_MARGIN'])
1010 {
1011 $result[$node['ID']] = self::LSTAT_ABOVE_CONNECTOR;
1012 break;
1013 }
1014 }
1015
1016 if(!$result[$node['ID']])
1017 $result[$node['ID']] = self::LSTAT_IN_NOT_CONNECTED_BRANCH;
1018 }
1019
1020 return $result;
1021 }
1022
1035 public static function checkConnectionExists($entityPrimary, $locationPrimary, array $behaviour = array('LOCATION_LINK_TYPE' => 'ID'))
1036 {
1037 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
1038 $locationPrimary = Assert::expectStringNotNull($locationPrimary, '$locationPrimary');
1039
1040 if(!isset($behaviour['LOCATION_LINK_TYPE']))
1041 {
1042 $behaviour['LOCATION_LINK_TYPE'] = 'ID';
1043 }
1044 else
1045 {
1046 $behaviour['LOCATION_LINK_TYPE'] = Assert::expectEnumerationMember($behaviour['LOCATION_LINK_TYPE'], array('AUTO', 'ID', 'CODE'), '$behaviour[LOCATION_LINK_TYPE]');
1047 }
1048
1049 if(!static::checkLinkUsageAny($entityPrimary)) // if there are no links at all, connection virtually exists
1050 {
1051 return true;
1052 }
1053
1054 if($behaviour['LOCATION_LINK_TYPE'] == 'AUTO')
1055 {
1056 $field = static::getUseCodes() ? 'CODE' : 'ID';
1057 }
1058 else
1059 {
1060 $field = $behaviour['LOCATION_LINK_TYPE'];
1061 }
1062
1063 $node = LocationTable::getList(
1064 array(
1065 'filter' => array('='.$field => $locationPrimary),
1066 'select' => array('ID', 'LEFT_MARGIN', 'RIGHT_MARGIN'),
1067 'limit' => 1
1068 )
1069 )->fetch();
1070
1071 if(!intval($node['ID']))
1072 {
1073 throw new \Bitrix\Sale\Location\Tree\NodeNotFoundException(false, array('INFO' => array($field => $locationPrimary)));
1074 }
1075
1076 $result = static::getLinkStatusForMultipleNodes(array($node), $entityPrimary);
1077
1078 return $result[$node['ID']] == self::LSTAT_IS_CONNECTOR || $result[$node['ID']] == self::LSTAT_BELOW_CONNECTOR;
1079 }
1080
1081 // a wrapper to getConnectedLocations which returns simple info about connected locations
1082 protected static function getConnectedLocationsInfo($entityPrimary)
1083 {
1084 $result = array();
1085
1086 $res = static::getConnectedLocations($entityPrimary, array('select' => array(
1087 'ID' => 'ID',
1088 'LEFT_MARGIN' => 'LEFT_MARGIN',
1089 'RIGHT_MARGIN' => 'RIGHT_MARGIN',
1090 'LNAME' => 'NAME.NAME',
1091 ),
1092 'filter' => array('NAME.LANGUAGE_ID' => LANGUAGE_ID)
1093 ), array('GET_LINKED_THROUGH_GROUPS' => true));
1094
1095 while($item = $res->fetch())
1096 $result[] = $item;
1097
1098 return $result;
1099 }
1100
1101 #####################################
1102 #### Link tracking
1103 #####################################
1104
1105 public static function checkLinkUsageAny($entityPrimary)
1106 {
1107 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
1108
1109 return static::checkLinkUsage($entityPrimary, static::DB_LOCATION_FLAG) || static::checkLinkUsage($entityPrimary, static::DB_GROUP_FLAG);
1110 }
1111
1122 public static function checkLinkUsage($entityPrimary, $linkType = self::DB_LOCATION_FLAG)
1123 {
1124 $entityPrimary = Assert::expectStringNotNull($entityPrimary, '$entityPrimary');
1125 $linkType = Assert::expectEnumerationMember(
1126 $linkType,
1127 array(static::DB_LOCATION_FLAG, static::DB_GROUP_FLAG),
1128 '$linkType'
1129 );
1130
1131 if(!static::getUseLinkTracking())
1132 return true; // force to true if link tracking is off
1133
1134 $useGroups = static::getUseGroups();
1135
1136 if(!$useGroups && $linkType == static::DB_GROUP_FLAG)
1137 return false; // we know we dont use groups
1138
1139 $usageFlags = static::getLinkUsageOptionValue();
1140
1141 if(isset($usageFlags[$entityPrimary][$linkType]))
1142 return $usageFlags[$entityPrimary][$linkType];
1143
1144 $strictFilter = array(
1145 static::getLinkField() => $entityPrimary
1146 );
1147 if($useGroups)
1148 $strictFilter['LOCATION_TYPE'] = static::DB_LOCATION_FLAG;
1149
1150 $usageFlags[$entityPrimary][static::DB_LOCATION_FLAG] = !!static::getList(array(
1151 'limit' => 1,
1152 'filter' => $strictFilter
1153 ))->fetch();
1154
1155 if($useGroups)
1156 {
1157 $usageFlags[$entityPrimary][static::DB_GROUP_FLAG] = !!static::getList(array(
1158 'limit' => 1,
1159 'filter' => array(
1160 static::getLinkField() => $entityPrimary,
1161 'LOCATION_TYPE' => static::DB_GROUP_FLAG
1162 )
1163 ))->fetch();
1164 }
1165
1166 static::setLinkUsageOptionValue($usageFlags);
1167
1168 return $usageFlags[$entityPrimary];
1169 }
1170
1175 private static function setLinkUsage($entityPrimary, $linkType = self::DB_LOCATION_FLAG, $way = true)
1176 {
1177 if(!static::getUseLinkTracking())
1178 return;
1179
1180 if(!in_array($linkType, array(static::DB_LOCATION_FLAG, static::DB_GROUP_FLAG)))
1181 $linkType = static::DB_LOCATION_FLAG;
1182
1183 $usageFlags = static::getLinkUsageOptionValue();
1184 $usageFlags[$entityPrimary][$linkType] = $way;
1185
1186 static::setLinkUsageOptionValue($usageFlags);
1187 }
1188
1193 private static function resetLinkUsage($entityPrimary)
1194 {
1195 if(!static::getUseLinkTracking())
1196 return;
1197
1198 $useGroups = static::getUseGroups();
1199 $typeFld = static::getTypeField();
1200 $groupBy = array();
1201 if($useGroups)
1202 $groupBy[] = $typeFld;
1203
1204 $res = static::getList(array(
1205 'filter' => array(
1206 static::getLinkField() => $entityPrimary
1207 ),
1208 'group' => $groupBy
1209 ));
1210
1211 $hasLocationLink = false;
1212 $hasGroupLink = false;
1213 while($item = $res->fetch())
1214 {
1215 if(isset($item[$typeFld]) && $item[$typeFld] == static::DB_GROUP_FLAG)
1216 $hasGroupLink = true;
1217 else
1218 $hasLocationLink = true;
1219 }
1220
1221 static::setLinkUsage($entityPrimary, static::DB_LOCATION_FLAG, $hasLocationLink);
1222 static::setLinkUsage($entityPrimary, static::DB_GROUP_FLAG, $hasGroupLink);
1223 }
1224
1229 private static function getLinkUsageOptionValue()
1230 {
1231 $usageFlagsOpt = Config\Option::get("sale", static::getLinkOptionName());
1232 if(!mb_strlen($usageFlagsOpt) || !is_array($usageFlags = unserialize($usageFlagsOpt, ['allowed_classes' => false])))
1233 $usageFlags = array();
1234
1235 return $usageFlags;
1236 }
1237
1242 private static function setLinkUsageOptionValue($usageFlags)
1243 {
1244 Config\Option::set("sale", static::getLinkOptionName(), serialize($usageFlags), '');
1245 }
1246
1247 private static function deleteLinkUsageOption()
1248 {
1249 Config\Option::delete("sale", array('name' => static::getLinkOptionName()));
1250 }
1251
1252 private static function getLinkOptionName()
1253 {
1254 return 'link_option_'.static::getTableName();
1255 }
1256
1257 #####################################
1258 #### Util
1259 #####################################
1260
1261 private static function checkUpdateLinks($links)
1262 {
1263 $useCodes = static::getUseCodes();
1264 $useGroups = static::getUseGroups();
1265
1266 $locationArgName = '$links['.static::DB_LOCATION_FLAG.']';
1267 $groupArgName = '$links['.static::DB_GROUP_FLAG.']';
1268
1269 if (isset($links[static::DB_LOCATION_FLAG]) && is_array($links[static::DB_LOCATION_FLAG]))
1270 {
1271 if($useCodes)
1272 $links[static::DB_LOCATION_FLAG] = Assert::expectArrayOfUniqueStringNotNull($links[static::DB_LOCATION_FLAG], $locationArgName);
1273 else
1274 $links[static::DB_LOCATION_FLAG] = Assert::expectArrayOfUniqueIntegerNotNull($links[static::DB_LOCATION_FLAG], $locationArgName);
1275 }
1276
1277 if (isset($links[static::DB_GROUP_FLAG]) && is_array($links[static::DB_GROUP_FLAG]))
1278 {
1279 if(!$useGroups)
1280 Assert::announceNotSupported(Loc::getMessage('SALE_LOCATION_CONNECTOR_ENTITY_DOESNT_SUPPORT_GROUPS'));
1281
1282 if($useCodes)
1283 $links[static::DB_GROUP_FLAG] = Assert::expectArrayOfUniqueStringNotNull($links[static::DB_GROUP_FLAG], $groupArgName);
1284 else
1285 $links[static::DB_GROUP_FLAG] = Assert::expectArrayOfUniqueIntegerNotNull($links[static::DB_GROUP_FLAG], $groupArgName);
1286 }
1287
1288 return $links;
1289 }
1290
1300 protected static function queryPage($sql, $limit = 0, $offset = 0)
1301 {
1302 $artificialNav = false;
1303 $limit = intval($limit);
1304 $offset = intval($offset);
1305
1306 // todo: Luke, use Bitrix\Main\DB\Paginator here
1307
1308 $dbConnection = Main\HttpApplication::getConnection();
1309
1310 if($limit)
1311 {
1312 if($dbConnection->getType() == 'mysql')
1313 {
1314 $sql .= ' limit '.($offset ? $offset.', ' : '').$limit;
1315 }
1316 else
1317 {
1318 $artificialNav = true;
1319 }
1320 }
1321
1322 $res = $dbConnection->query($sql);
1323
1324 if($artificialNav)
1325 {
1326 $result = array();
1327
1328 $i = -1;
1329 while($item = $res->fetch())
1330 {
1331 $i++;
1332
1333 if($i < $offset)
1334 continue;
1335
1336 if($i >= $offset + $limit)
1337 break;
1338
1339 $result[] = $item;
1340 }
1341
1342 return new DB\ArrayResult($result);
1343 }
1344 else
1345 return $res;
1346 }
1347
1355 public static function unionize($sqls)
1356 {
1357 if(!is_array($sqls))
1358 return $sqls;
1359
1360 if(count($sqls) == 1)
1361 return array_shift($sqls);
1362
1363 //foreach($sqls as &$sql)
1364 // $sql = '('.$sql.')';
1365
1366 unset($sql);
1367
1368 return implode(' union ', $sqls);
1369 }
1370
1371 // function accepts denormalized list of location IDs on input, then makes it normal and returns list of IDs or CODEs, depending on current entity settings
1372 public static function normalizeLocationList($denormalizedList = array())
1373 {
1374 if(empty($denormalizedList))
1375 return array();
1376
1377 $useCodes = static::getUseCodes();
1378
1379 $denormalizedList = array_flip($denormalizedList);
1380
1381 $query = new Entity\Query('Bitrix\Sale\Location\Location');
1382
1383 $query->setSelect(array(
1384 'PARENT_ID',
1385 'ID',
1386 'CODE',
1387 'LNAME' => 'NAME.NAME' // tmp
1388 ));
1389
1390 $query->setFilter(array('NAME.LANGUAGE_ID' => LANGUAGE_ID)); // tmp
1391 $query->setOrder(array('LEFT_MARGIN' => 'desc')); // important
1392
1393 $res = $query->exec();
1394
1395 // make table of children count for each node
1396
1397 $relations = array();
1398 $selected = array();
1399 while($item = $res->fetch())
1400 {
1401 if(!isset($relations[$item['ID']]['name'])) // tmp
1402 $relations[$item['ID']]['name'] = $item['LNAME'];
1403
1404 if(!isset($relations[$item['ID']]['total']))
1405 $relations[$item['ID']]['total'] = 0;
1406
1407 if(!isset($relations[$item['ID']]['selected']))
1408 $relations[$item['ID']]['selected'] = 0;
1409
1411 if($item['PARENT_ID'] != 0)
1412 {
1413 if(!isset($relations[$item['PARENT_ID']]['total']))
1414 $relations[$item['PARENT_ID']]['total'] = 0;
1415 if(!isset($relations[$item['PARENT_ID']]['selected']))
1416 $relations[$item['PARENT_ID']]['selected'] = 0;
1417 }
1418
1419 $relations[$item['PARENT_ID']]['total'] += (1 + $relations[$item['ID']]['total']);
1420
1421 if(isset($denormalizedList[$item['ID']]))
1422 {
1423 $relations[$item['PARENT_ID']]['selected'] += (1 + $relations[$item['ID']]['selected']);
1424 $selected[$item['ID']] = $item;
1425 }
1426 }
1427
1428 // now make up list of nodes to remove
1429 $removeItems = array();
1430 $removeChildrenOf = array();
1431 foreach($relations as $id => $rel)
1432 {
1433 if($rel['total'] > 0) // item has children
1434 {
1435 if($rel['total'] == $rel['selected']) // selected N of N of all children, remove all those children
1436 {
1437 if(isset($selected[$id])) // item should be selected itself to remove its children
1438 $removeChildrenOf[$id] = true;
1439 }
1440 elseif($rel['selected'] > 0) // selected M of N of children, where M > 0, so remove item itself
1441 $removeItems[$id] = true;
1442 }
1443 }
1444
1445 $normalized = array();
1446 foreach($selected as $id => $item)
1447 {
1448 if(!($item['PARENT_ID'] && $removeChildrenOf[$item['PARENT_ID']]) && !$removeItems[$item['ID']])
1449 $normalized[] = $item[$useCodes ? 'CODE' : 'ID'];
1450 }
1451
1452 return $normalized;
1453 }
1454}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static getLinkStatusForMultipleNodes($nodeInfo, $entityPrimary, $connectors=false)
static getConnectedLocationsInfo($entityPrimary)
static checkLinkUsage($entityPrimary, $linkType=self::DB_LOCATION_FLAG)
static checkConnectionExists($entityPrimary, $locationPrimary, array $behaviour=array('LOCATION_LINK_TYPE'=> 'ID'))
static getLinkedLocations($entityPrimary)
static checkLinkUsageAny($entityPrimary)
static getConnectedEntitiesByCondition($locationPrimary, $linkType='id', $parameters=array())
static updateMultipleForOwner($entityPrimary, $links=array(), $behaviour=array('REMOVE_ABSENT'=> true))
static add(array $data)
static deleteAllForOwner($entityPrimary, $behaviour=array('BATCH_MODE'=> false))
static normalizeLocationList($denormalizedList=array())
static queryPage($sql, $limit=0, $offset=0)
static update($primary, array $data)
$GLOBALS['____1444769544']
Definition license.php:1