Advertisement
Guest User

Untitled

a guest
Oct 26th, 2019
274
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 46.24 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Bitrix Framework
  4.  * @package bitrix
  5.  * @subpackage main
  6.  * @copyright 2001-2013 Bitrix
  7.  */
  8.  
  9. namespace Bitrix\Main\ORM\Data;
  10.  
  11. use Bitrix\Main;
  12. use Bitrix\Main\ORM\Entity;
  13. use Bitrix\Main\ORM\EntityError;
  14. use Bitrix\Main\ORM\Event;
  15. use Bitrix\Main\ORM\Fields\ExpressionField;
  16. use Bitrix\Main\ORM\Fields\FieldError;
  17. use Bitrix\Main\ORM\Fields\FieldTypeMask;
  18. use Bitrix\Main\ORM\Fields\ScalarField;
  19. use Bitrix\Main\ORM\Objectify\Values;
  20. use Bitrix\Main\ORM\Query\Query;
  21. use Bitrix\Main\ORM\Query\Result as QueryResult;
  22. use Bitrix\Main\Localization\Loc;
  23. use Bitrix\Main\ORM\Query\Filter\ConditionTree as Filter;
  24. use Bitrix\Main\ORM\Objectify\EntityObject;
  25. use Bitrix\Main\ORM\Objectify\Collection;
  26.  
  27. Loc::loadMessages(__FILE__);
  28.  
  29. /**
  30.  * Base entity data manager
  31.  */
  32. abstract class DataManager
  33. {
  34.     const EVENT_ON_BEFORE_ADD = "OnBeforeAdd";
  35.     const EVENT_ON_ADD = "OnAdd";
  36.     const EVENT_ON_AFTER_ADD = "OnAfterAdd";
  37.     const EVENT_ON_BEFORE_UPDATE = "OnBeforeUpdate";
  38.     const EVENT_ON_UPDATE = "OnUpdate";
  39.     const EVENT_ON_AFTER_UPDATE = "OnAfterUpdate";
  40.     const EVENT_ON_BEFORE_DELETE = "OnBeforeDelete";
  41.     const EVENT_ON_DELETE = "OnDelete";
  42.     const EVENT_ON_AFTER_DELETE = "OnAfterDelete";
  43.  
  44.     /** @var Entity[] */
  45.     protected static $entity;
  46.  
  47.     /** @var EntityObject[] Cache of class names */
  48.     protected static $objectClass;
  49.  
  50.     /** @var Collection[] Cache of class names */
  51.     protected static $collectionClass;
  52.  
  53.     /** @var array Restricted words for object class name */
  54.     protected static $reservedWords = [
  55.         // keywords
  56.         'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue',
  57.         'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach',
  58.         'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'finally', 'for', 'foreach', 'function',
  59.         'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset',
  60.         'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return',
  61.         'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'yield',
  62.         // classes
  63.         'self', 'parent',
  64.         // others
  65.         'int', 'float', 'bool', 'string', 'true', 'false', 'null', 'void', 'iterable', 'object', 'resource', 'mixed', 'numeric',
  66.     ];
  67.  
  68.     /**
  69.      * Returns entity object
  70.      *
  71.      * @return Entity
  72.      * @throws Main\ArgumentException
  73.      * @throws Main\SystemException
  74.      */
  75.     public static function getEntity()
  76.     {
  77.         $class = static::getEntityClass()::normalizeEntityClass(get_called_class());
  78.  
  79.         if (!isset(static::$entity[$class]))
  80.         {
  81.             static::$entity[$class] = static::getEntityClass()::getInstance($class);
  82.         }
  83.  
  84.         return static::$entity[$class];
  85.     }
  86.  
  87.     public static function unsetEntity($class)
  88.     {
  89.         $class = static::getEntityClass()::normalizeEntityClass($class);
  90.  
  91.         if (isset(static::$entity[$class]))
  92.         {
  93.             unset(static::$entity[$class]);
  94.         }
  95.     }
  96.  
  97.     /**
  98.      * Returns DB table name for entity
  99.      *
  100.      * @return string
  101.      */
  102.     public static function getTableName()
  103.     {
  104.         return null;
  105.     }
  106.  
  107.     /**
  108.      * Returns connection name for entity
  109.      *
  110.      * @return string
  111.      */
  112.     public static function getConnectionName()
  113.     {
  114.         return 'default';
  115.     }
  116.  
  117.     /**
  118.      * @return string
  119.      */
  120.     public static function getTitle()
  121.     {
  122.         return null;
  123.     }
  124.  
  125.     /**
  126.      * Returns class of Object for current entity.
  127.      *
  128.      * @return string|EntityObject
  129.      */
  130.     public static function getObjectClass()
  131.     {
  132.         if (!isset(static::$objectClass[get_called_class()]))
  133.         {
  134.             static::$objectClass[get_called_class()] = static::getObjectClassByDataClass(get_called_class());
  135.         }
  136.  
  137.         return static::$objectClass[get_called_class()];
  138.     }
  139.  
  140.     /**
  141.      * Returns class name (without namespace) of Object for current entity.
  142.      *
  143.      * @return string
  144.      */
  145.     final public static function getObjectClassName()
  146.     {
  147.         $class = static::getObjectClass();
  148.         return substr($class, strrpos($class, '\\')+1);
  149.     }
  150.  
  151.     protected static function getObjectClassByDataClass($dataClass)
  152.     {
  153.         $objectClass = static::getEntityClass()::normalizeName($dataClass);
  154.  
  155.         // make class name more unique
  156.         $namespace = substr($objectClass, 0, strrpos($objectClass, '\\')+1);
  157.         $className = substr($objectClass, strrpos($objectClass, '\\') + 1);
  158.  
  159.         $className = static::getEntityClass()::getDefaultObjectClassName($className);
  160.  
  161.         return $namespace.$className;
  162.     }
  163.  
  164.     /**
  165.      * Returns class of Object collection for current entity.
  166.      *
  167.      * @return string|Collection
  168.      */
  169.     public static function getCollectionClass()
  170.     {
  171.         if (!isset(static::$collectionClass[get_called_class()]))
  172.         {
  173.             static::$collectionClass[get_called_class()] = static::getCollectionClassByDataClass(get_called_class());
  174.         }
  175.  
  176.         return static::$collectionClass[get_called_class()];
  177.     }
  178.  
  179.     /**
  180.      * Returns class name (without namespace) of Object collection for current entity.
  181.      *
  182.      * @return string
  183.      */
  184.     final public static function getCollectionClassName()
  185.     {
  186.         $class = static::getCollectionClass();
  187.         return substr($class, strrpos($class, '\\')+1);
  188.     }
  189.  
  190.     protected static function getCollectionClassByDataClass($dataClass)
  191.     {
  192.         $objectClass = static::getEntityClass()::normalizeName($dataClass);
  193.  
  194.         // make class name more unique
  195.         $namespace = substr($objectClass, 0, strrpos($objectClass, '\\')+1);
  196.         $className = substr($objectClass, strrpos($objectClass, '\\') + 1);
  197.  
  198.         $className = static::getEntityClass()::getDefaultCollectionClassName($className);
  199.  
  200.         return $namespace.$className;
  201.     }
  202.  
  203.     /**
  204.      * @return EntityObject|string
  205.      */
  206.     public static function getObjectParentClass()
  207.     {
  208.         return EntityObject::class;
  209.     }
  210.  
  211.     /**
  212.      * @return Collection|string
  213.      */
  214.     public static function getCollectionParentClass()
  215.     {
  216.         return Collection::class;
  217.     }
  218.  
  219.     /**
  220.      * @return Query|string
  221.      */
  222.     public static function getQueryClass()
  223.     {
  224.         return Query::class;
  225.     }
  226.  
  227.     /**
  228.      * @return Entity|string
  229.      */
  230.     public static function getEntityClass()
  231.     {
  232.         return Entity::class;
  233.     }
  234.  
  235.     /**
  236.      * @param bool $setDefaultValues
  237.      *
  238.      * @return null Actual type should be annotated by orm:annotate
  239.      * @throws Main\ArgumentException
  240.      * @throws Main\SystemException
  241.      */
  242.     final public static function createObject($setDefaultValues = true)
  243.     {
  244.         return static::getEntity()->createObject($setDefaultValues);
  245.     }
  246.  
  247.     /**
  248.      * @return null Actual type should be annotated by orm:annotate
  249.      * @throws Main\ArgumentException
  250.      * @throws Main\SystemException
  251.      */
  252.     final public static function createCollection()
  253.     {
  254.         return static::getEntity()->createCollection();
  255.     }
  256.  
  257.     /**
  258.      * @see EntityObject::wakeUp()
  259.      *
  260.      * @param $row
  261.      *
  262.      * @return null Actual type should be annotated by orm:annotate
  263.      * @throws Main\ArgumentException
  264.      * @throws Main\SystemException
  265.      */
  266.     final public static function wakeUpObject($row)
  267.     {
  268.         return static::getEntity()->wakeUpObject($row);
  269.     }
  270.  
  271.     /**
  272.      * @see Collection::wakeUp()
  273.      *
  274.      * @param $rows
  275.      *
  276.      * @return null Actual type should be annotated by orm:annotate
  277.      * @throws Main\ArgumentException
  278.      * @throws Main\SystemException
  279.      */
  280.     final public static function wakeUpCollection($rows)
  281.     {
  282.         return static::getEntity()->wakeUpCollection($rows);
  283.     }
  284.  
  285.     /**
  286.      * Returns entity map definition.
  287.      * To get initialized fields @see \Bitrix\Main\ORM\Entity::getFields() and \Bitrix\Main\ORM\Entity::getField()
  288.      */
  289.     public static function getMap()
  290.     {
  291.         return array();
  292.     }
  293.  
  294.     public static function getUfId()
  295.     {
  296.         return null;
  297.     }
  298.  
  299.     public static function isUts()
  300.     {
  301.         return false;
  302.     }
  303.  
  304.     public static function isUtm()
  305.     {
  306.         return false;
  307.     }
  308.  
  309.     /**
  310.      * @param Query $query
  311.      *
  312.      * @return Query
  313.      */
  314.     public static function setDefaultScope($query)
  315.     {
  316.         return $query;
  317.     }
  318.  
  319.     /**
  320.      * @param Entity $entity
  321.      *
  322.      * @return null
  323.      */
  324.     public static function postInitialize(Entity $entity)
  325.     {
  326.         return null;
  327.     }
  328.  
  329.     /**
  330.      * Returns selection by entity's primary key and optional parameters for getList()
  331.      *
  332.      * @param mixed $primary    Primary key of the entity
  333.      * @param array $parameters Additional parameters for getList()
  334.      *
  335.      * @return QueryResult
  336.      * @throws Main\ArgumentException
  337.      * @throws Main\ObjectPropertyException
  338.      * @throws Main\SystemException
  339.      */
  340.     public static function getByPrimary($primary, array $parameters = array())
  341.     {
  342.         static::normalizePrimary($primary);
  343.         static::validatePrimary($primary);
  344.  
  345.         $primaryFilter = array();
  346.  
  347.         foreach ($primary as $k => $v)
  348.         {
  349.             $primaryFilter['='.$k] = $v;
  350.         }
  351.  
  352.         if (isset($parameters['filter']))
  353.         {
  354.             $parameters['filter'] = array($primaryFilter, $parameters['filter']);
  355.         }
  356.         else
  357.         {
  358.             $parameters['filter'] = $primaryFilter;
  359.         }
  360.  
  361.         return static::getList($parameters);
  362.     }
  363.  
  364.     /**
  365.      * Returns selection by entity's primary key
  366.      *
  367.      * @param mixed $id Primary key of the entity
  368.      *
  369.      * @return QueryResult
  370.      * @throws Main\ArgumentException
  371.      * @throws Main\ObjectPropertyException
  372.      * @throws Main\SystemException
  373.      */
  374.     public static function getById($id)
  375.     {
  376.         return static::getByPrimary($id);
  377.     }
  378.  
  379.     /**
  380.      * Returns one row (or null) by entity's primary key
  381.      *
  382.      * @param mixed $id Primary key of the entity
  383.      *
  384.      * @return array|null
  385.      * @throws Main\ArgumentException
  386.      * @throws Main\ObjectPropertyException
  387.      * @throws Main\SystemException
  388.      */
  389.     public static function getRowById($id)
  390.     {
  391.         $result = static::getByPrimary($id);
  392.         $row = $result->fetch();
  393.  
  394.         return (is_array($row)? $row : null);
  395.     }
  396.  
  397.     /**
  398.      * Returns one row (or null) by parameters for getList()
  399.      *
  400.      * @param array $parameters Primary key of the entity
  401.      *
  402.      * @return array|null
  403.      * @throws Main\ArgumentException
  404.      * @throws Main\ObjectPropertyException
  405.      * @throws Main\SystemException
  406.      */
  407.     public static function getRow(array $parameters)
  408.     {
  409.         $parameters['limit'] = 1;
  410.         $result = static::getList($parameters);
  411.         $row = $result->fetch();
  412.  
  413.         return (is_array($row)? $row : null);
  414.     }
  415.  
  416.     /**
  417.      * Executes the query and returns selection by parameters of the query. This function is an alias to the Query object functions
  418.      *
  419.      * @param array $parameters An array of query parameters, available keys are:<br>
  420.      *      "select" => array of fields in the SELECT part of the query, aliases are possible in the form of "alias"=>"field";<br>
  421.      *      "filter" => array of filters in the WHERE/HAVING part of the query in the form of "(condition)field"=>"value";
  422.      *          also could be an instance of Filter;<br>
  423.      *      "group" => array of fields in the GROUP BY part of the query;<br>
  424.      *      "order" => array of fields in the ORDER BY part of the query in the form of "field"=>"asc|desc";<br>
  425.      *      "limit" => integer indicating maximum number of rows in the selection (like LIMIT n in MySql);<br>
  426.      *      "offset" => integer indicating first row number in the selection (like LIMIT n, 100 in MySql);<br>
  427.      *      "runtime" => array of entity fields created dynamically;<br>
  428.      *      "cache => array of cache options:<br>
  429.      *          "ttl" => integer indicating cache TTL;<br>
  430.      *          "cache_joins" => boolean enabling to cache joins, false by default.
  431.      * @see Query::filter()
  432.      *
  433.      * @return QueryResult
  434.      * @throws Main\ArgumentException
  435.      * @throws Main\ObjectPropertyException
  436.      * @throws Main\SystemException
  437.      */
  438.     public static function getList(array $parameters = array())
  439.     {
  440.         $query = static::query();
  441.  
  442.         if(!isset($parameters['select']))
  443.         {
  444.             $query->setSelect(array('*'));
  445.         }
  446.  
  447.         foreach($parameters as $param => $value)
  448.         {
  449.             switch($param)
  450.             {
  451.                 case 'select':
  452.                     $query->setSelect($value);
  453.                     break;
  454.                 case 'filter':
  455.                     $value instanceof Filter ? $query->where($value) : $query->setFilter($value);
  456.                     break;
  457.                 case 'group':
  458.                     $query->setGroup($value);
  459.                     break;
  460.                 case 'order';
  461.                     $query->setOrder($value);
  462.                     break;
  463.                 case 'limit':
  464.                     $query->setLimit($value);
  465.                     break;
  466.                 case 'offset':
  467.                     $query->setOffset($value);
  468.                     break;
  469.                 case 'count_total':
  470.                     $query->countTotal($value);
  471.                     break;
  472.                 case 'runtime':
  473.                     foreach ($value as $name => $fieldInfo)
  474.                     {
  475.                         $query->registerRuntimeField($name, $fieldInfo);
  476.                     }
  477.                     break;
  478.                 case 'data_doubling':
  479.                     if($value)
  480.                     {
  481.                         $query->enableDataDoubling();
  482.                     }
  483.                     else
  484.                     {
  485.                         $query->disableDataDoubling();
  486.                     }
  487.                     break;
  488.                 case 'cache':
  489.                     $query->setCacheTtl($value["ttl"]);
  490.                     if(isset($value["cache_joins"]))
  491.                     {
  492.                         $query->cacheJoins($value["cache_joins"]);
  493.                     }
  494.                     break;
  495.                 default:
  496.                     throw new Main\ArgumentException("Unknown parameter: ".$param, $param);
  497.             }
  498.         }
  499.  
  500.         return $query->exec();
  501.     }
  502.  
  503.     /**
  504.      * Performs COUNT query on entity and returns the result.
  505.      *
  506.      * @param array|Filter $filter
  507.      * @param array $cache An array of cache options
  508.      *      "ttl" => integer indicating cache TTL
  509.      * @return int
  510.      * @throws Main\ObjectPropertyException
  511.      * @throws Main\SystemException
  512.      */
  513.     public static function getCount($filter = array(), array $cache = array())
  514.     {
  515.         $query = static::query();
  516.  
  517.         // new filter
  518.         $query->addSelect(new ExpressionField('CNT', 'COUNT(1)'));
  519.  
  520.         if ($filter instanceof Filter)
  521.         {
  522.             $query->where($filter);
  523.         }
  524.         else
  525.         {
  526.             $query->setFilter($filter);
  527.         }
  528.  
  529.         if(isset($cache["ttl"]))
  530.         {
  531.             $query->setCacheTtl($cache["ttl"]);
  532.         }
  533.  
  534.         $result = $query->exec()->fetch();
  535.  
  536.         return $result['CNT'];
  537.     }
  538.  
  539.     /**
  540.      * Creates and returns the Query object for the entity
  541.      *
  542.      * @return Query
  543.      * @throws Main\ArgumentException
  544.      * @throws Main\SystemException
  545.      */
  546.     public static function query()
  547.     {
  548.         $queryClass = static::getQueryClass();
  549.         return new $queryClass(static::getEntity());
  550.     }
  551.  
  552.     /**
  553.      * @param array $data
  554.      *
  555.      * @return array
  556.      * @throws Main\ArgumentException
  557.      * @throws Main\SystemException
  558.      */
  559.     protected static function replaceFieldName($data = array())
  560.     {
  561.         $entity = static::getEntity();
  562.         foreach ($data as $fieldName => $value)
  563.         {
  564.             /** @var ScalarField $field */
  565.             $field = $entity->getField($fieldName);
  566.             $columnName = $field->getColumnName();
  567.             if($columnName != $fieldName)
  568.             {
  569.                 $data[$columnName] = $data[$fieldName];
  570.                 unset($data[$fieldName]);
  571.             }
  572.         }
  573.  
  574.         return $data;
  575.     }
  576.  
  577.     /**
  578.      * @param       $primary
  579.      * @param array $data
  580.      *
  581.      * @throws Main\ArgumentException
  582.      * @throws Main\SystemException
  583.      */
  584.     protected static function normalizePrimary(&$primary, $data = array())
  585.     {
  586.         $entity = static::getEntity();
  587.         $entity_primary = $entity->getPrimaryArray();
  588.  
  589.         if ($primary === null)
  590.         {
  591.             $primary = array();
  592.  
  593.             // extract primary from data array
  594.             foreach ($entity_primary as $key)
  595.             {
  596.                 /** @var ScalarField $field  */
  597.                 $field = $entity->getField($key);
  598.                 if ($field->isAutocomplete())
  599.                 {
  600.                     continue;
  601.                 }
  602.  
  603.                 if (!isset($data[$key]))
  604.                 {
  605.                     throw new Main\ArgumentException(sprintf(
  606.                         'Primary `%s` was not found when trying to query %s row.', $key, $entity->getName()
  607.                     ));
  608.                 }
  609.  
  610.                 $primary[$key] = $data[$key];
  611.             }
  612.         }
  613.         elseif (is_scalar($primary))
  614.         {
  615.             if (count($entity_primary) > 1)
  616.             {
  617.                 throw new Main\ArgumentException(sprintf(
  618.                     'Require multi primary {`%s`}, but one scalar value "%s" found when trying to query %s row.',
  619.                     join('`, `', $entity_primary), $primary, $entity->getName()
  620.                 ));
  621.             }
  622.  
  623.             $primary = array($entity_primary[0] => $primary);
  624.         }
  625.     }
  626.  
  627.     /**
  628.      * @param $primary
  629.      *
  630.      * @throws Main\ArgumentException
  631.      * @throws Main\SystemException
  632.      */
  633.     protected static function validatePrimary($primary)
  634.     {
  635.         $entity = static::getEntity();
  636.         if (is_array($primary))
  637.         {
  638.             if(empty($primary))
  639.             {
  640.                 throw new Main\ArgumentException(sprintf(
  641.                     'Empty primary found when trying to query %s row.', $entity->getName()
  642.                 ));
  643.             }
  644.  
  645.             $entity_primary = $entity->getPrimaryArray();
  646.  
  647.             foreach (array_keys($primary) as $key)
  648.             {
  649.                 if (!in_array($key, $entity_primary, true))
  650.                 {
  651.                     throw new Main\ArgumentException(sprintf(
  652.                         'Unknown primary `%s` found when trying to query %s row.',
  653.                         $key, $entity->getName()
  654.                     ));
  655.                 }
  656.             }
  657.         }
  658.         else
  659.         {
  660.             throw new Main\ArgumentException(sprintf(
  661.                 'Unknown type of primary "%s" found when trying to query %s row.', gettype($primary), $entity->getName()
  662.             ));
  663.         }
  664.  
  665.         // primary values validation
  666.         foreach ($primary as $key => $value)
  667.         {
  668.             if (!is_scalar($value) && !($value instanceof Main\Type\Date))
  669.             {
  670.                 throw new Main\ArgumentException(sprintf(
  671.                     'Unknown value type "%s" for primary "%s" found when trying to query %s row.',
  672.                     gettype($value), $key, $entity->getName()
  673.                 ));
  674.             }
  675.         }
  676.     }
  677.  
  678.     /**
  679.      * Checks the data fields before saving to DB. Result stores in the $result object
  680.      *
  681.      * @param Result $result
  682.      * @param mixed  $primary
  683.      * @param array  $data
  684.      *
  685.      * @throws Main\ArgumentException
  686.      * @throws Main\SystemException
  687.      */
  688.     public static function checkFields(Result $result, $primary, array $data)
  689.     {
  690.         $entity = static::getEntity();
  691.         //checks required fields
  692.         foreach ($entity->getFields() as $field)
  693.         {
  694.             if ($field instanceof ScalarField && $field->isRequired())
  695.             {
  696.                 $fieldName = $field->getName();
  697.                 if (
  698.                     (empty($primary) && (!isset($data[$fieldName]) || $field->isValueEmpty($data[$fieldName])))
  699.                     || (!empty($primary) && isset($data[$fieldName]) && $field->isValueEmpty($data[$fieldName]))
  700.                 )
  701.                 {
  702.                     $result->addError(new FieldError(
  703.                         $field,
  704.                         Loc::getMessage("MAIN_ENTITY_FIELD_REQUIRED", array("#FIELD#"=>$field->getTitle())),
  705.                         FieldError::EMPTY_REQUIRED
  706.                     ));
  707.                 }
  708.             }
  709.         }
  710.  
  711.         // checks data - fieldname & type & strlen etc.
  712.         foreach ($data as $k => $v)
  713.         {
  714.             if ($entity->hasField($k))
  715.             {
  716.                 $field = $entity->getField($k);
  717.  
  718.             }
  719.             else
  720.             {
  721.                 throw new Main\ArgumentException(sprintf(
  722.                     'Field `%s` not found in entity when trying to query %s row.',
  723.                     $k, $entity->getName()
  724.                 ));
  725.             }
  726.  
  727.             $field->validateValue($v, $primary, $data, $result);
  728.         }
  729.     }
  730.  
  731.     /**
  732.      * @param array $fields
  733.      * @param bool  $setDefaultValues
  734.      * @param array $primary
  735.      *
  736.      * @return EntityObject
  737.      * @throws Main\ArgumentException
  738.      * @throws Main\SystemException
  739.      */
  740.     protected static function convertArrayToObject(&$fields, $setDefaultValues = false, $primary = null)
  741.     {
  742.         // extended data format
  743.         $data = null;
  744.  
  745.         if (isset($fields["fields"]) && is_array($fields["fields"]))
  746.         {
  747.             $data = $fields;
  748.             $fields = $data["fields"];
  749.         }
  750.  
  751.         // convert to object
  752.         if (isset($fields['__object']))
  753.         {
  754.             $object = $fields['__object'];
  755.             unset($fields['__object']);
  756.         }
  757.         else
  758.         {
  759.             $entity = static::getEntity();
  760.  
  761.             /** @var EntityObject $object */
  762.             if ($primary === null)
  763.             {
  764.                 $object = $entity->createObject($setDefaultValues);
  765.  
  766.                 foreach ($fields as $fieldName => $value)
  767.                 {
  768.                     // sometimes data array can be used for storing non-entity data
  769.                     if ($entity->hasField($fieldName))
  770.                     {
  771.                         $object->sysSetValue($fieldName, $value);
  772.                     }
  773.                 }
  774.             }
  775.             else
  776.             {
  777.                 $object = $entity->wakeUpObject($primary);
  778.  
  779.                 foreach ($fields as $fieldName => $value)
  780.                 {
  781.                     // sometimes data array can be used for storing non-entity data
  782.                     if ($entity->hasField($fieldName))
  783.                     {
  784.                         if ($entity->getField($fieldName) instanceof ScalarField && $entity->getField($fieldName)->isPrimary())
  785.                         {
  786.                             // ignore old primary
  787.                             if (array_key_exists($fieldName, $primary) && $primary[$fieldName] == $value)
  788.                             {
  789.                                 unset($fields[$fieldName]);
  790.                                 continue;
  791.                             }
  792.  
  793.                             // but prevent primary changing
  794.                             trigger_error(sprintf(
  795.                                 'Primary of %s %s can not be changed. You can delete this row and add a new one',
  796.                                 static::getObjectClass(), Main\Web\Json::encode($object->primary)
  797.                             ), E_USER_WARNING);
  798.  
  799.                             continue;
  800.                         }
  801.  
  802.                         $object->sysSetValue($fieldName, $value);
  803.                     }
  804.                 }
  805.             }
  806.         }
  807.  
  808.         // auth context
  809.         if (isset($data['auth_context']))
  810.         {
  811.             $object->authContext = $data['auth_context'];
  812.         }
  813.  
  814.         return $object;
  815.     }
  816.  
  817.     /**
  818.      * @param EntityObject                 $object
  819.      * @param                              $ufdata
  820.      * @param \Bitrix\Main\ORM\Data\Result $result
  821.      */
  822.     protected static function checkUfFields($object, $ufdata, $result)
  823.     {
  824.         global $USER_FIELD_MANAGER, $APPLICATION;
  825.  
  826.         $userId = ($object->authContext && $object->authContext->getUserId())
  827.             ? $object->authContext->getUserId()
  828.             : false;
  829.  
  830.         $ufPrimary = ($object->sysGetState() === Main\ORM\Objectify\State::RAW)
  831.             ? false
  832.             : end($object->primary);
  833.  
  834.         if (!$USER_FIELD_MANAGER->CheckFields($object->entity->getUfId(), $ufPrimary, $ufdata, $userId))
  835.         {
  836.             if (is_object($APPLICATION) && $APPLICATION->getException())
  837.             {
  838.                 $e = $APPLICATION->getException();
  839.                 $result->addError(new EntityError($e->getString()));
  840.                 $APPLICATION->resetException();
  841.             }
  842.             else
  843.             {
  844.                 $result->addError(new EntityError("Unknown error while checking userfields"));
  845.             }
  846.         }
  847.     }
  848.  
  849.     /**
  850.      * Adds row to entity table
  851.      *
  852.      * @param array $data An array with fields like
  853.      *  array(
  854.      *      "fields" => array(
  855.      *          "FIELD1" => "value1",
  856.      *          "FIELD2" => "value2",
  857.      *      ),
  858.      *      "auth_context" => \Bitrix\Main\Authentication\Context object
  859.      *  )
  860.      *  or just a plain array of fields.
  861.      *
  862.      * @return AddResult Contains ID of inserted row
  863.      *
  864.      * @throws \Exception
  865.      */
  866.     public static function add(array $data)
  867.     {
  868.         global $USER_FIELD_MANAGER;
  869.  
  870.         // compatibility
  871.         $fields = $data;
  872.  
  873.         // prepare entity object for compatibility with new code
  874.         $object = static::convertArrayToObject($fields, true);
  875.  
  876.         $entity = static::getEntity();
  877.         $result = new AddResult();
  878.  
  879.         try
  880.         {
  881.             static::callOnBeforeAddEvent($object, $fields, $result);
  882.  
  883.             // actualize old-style fields array from object
  884.             $fields = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
  885.  
  886.             // uf values
  887.             $ufdata = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
  888.  
  889.             // check data
  890.             static::checkFields($result, null, $fields);
  891.  
  892.             // check uf data
  893.             if (!empty($ufdata))
  894.             {
  895.                 static::checkUfFields($object, $ufdata, $result);
  896.             }
  897.  
  898.             // check if there is still some data
  899.             if (!count($fields + $ufdata))
  900.             {
  901.                 $result->addError(new EntityError("There is no data to add."));
  902.             }
  903.  
  904.             // return if any error
  905.             if (!$result->isSuccess(true))
  906.             {
  907.                 return $result;
  908.             }
  909.  
  910.             //event on adding
  911.             self::callOnAddEvent($object, $fields, $ufdata);
  912.  
  913.             // use save modifiers
  914.             $fieldsToDb = $fields;
  915.  
  916.             foreach ($fieldsToDb as $fieldName => $value)
  917.             {
  918.                 $field = $entity->getField($fieldName);
  919.                 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
  920.             }
  921.  
  922.             // save data
  923.             $connection = $entity->getConnection();
  924.  
  925.             $tableName = $entity->getDBTableName();
  926.             $identity = $entity->getAutoIncrement();
  927.  
  928.             $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
  929.             $id = $connection->add($tableName, $dataReplacedColumn, $identity);
  930.  
  931.             // build standard primary
  932.             $primary = null;
  933.  
  934.             if (!empty($id))
  935.             {
  936.                 if (strlen($entity->getAutoIncrement()))
  937.                 {
  938.                     $primary = array($entity->getAutoIncrement() => $id);
  939.                     static::normalizePrimary($primary);
  940.                 }
  941.                 else
  942.                 {
  943.                     // for those who did not set 'autocomplete' flag but wants to get id from result
  944.                     $primary = array('ID' => $id);
  945.                 }
  946.             }
  947.             else
  948.             {
  949.                 static::normalizePrimary($primary, $fields);
  950.             }
  951.  
  952.             // fill result
  953.             $result->setPrimary($primary);
  954.             $result->setData($fields);
  955.             $result->setObject($object);
  956.  
  957.             foreach ($primary as $primaryName => $primaryValue)
  958.             {
  959.                 $object->sysSetActual($primaryName, $primaryValue);
  960.             }
  961.  
  962.             // save uf data
  963.             if (!empty($ufdata))
  964.             {
  965.                 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
  966.             }
  967.  
  968.             $entity->cleanCache();
  969.  
  970.             static::callOnAfterAddEvent($object, $fields, $id);
  971.         }
  972.         catch (\Exception $e)
  973.         {
  974.             // check result to avoid warning
  975.             $result->isSuccess();
  976.  
  977.             throw $e;
  978.         }
  979.  
  980.         return $result;
  981.     }
  982.  
  983.     /**
  984.      * @param      $rows
  985.      * @param bool $ignoreEvents
  986.      *
  987.      * @return AddResult
  988.      * @throws Main\ArgumentException
  989.      * @throws Main\SystemException
  990.      */
  991.     public static function addMulti($rows, $ignoreEvents = false)
  992.     {
  993.         global $USER_FIELD_MANAGER;
  994.  
  995.         $rows = array_values($rows);
  996.         $forceSeparateQueries = false;
  997.  
  998.         if (!$ignoreEvents && count($rows) > 1 && strlen(static::getEntity()->getAutoIncrement()))
  999.         {
  1000.             $forceSeparateQueries = true;
  1001.  
  1002.             // change to warning
  1003.             trigger_error(
  1004.                 'Multi-insert doesn\'t work with events as far as we can not get last inserted IDs that we need for the events. '.
  1005.                 'Insert query was forced to multiple separate queries.',
  1006.                 E_USER_NOTICE
  1007.             );
  1008.         }
  1009.  
  1010.         // prepare objects
  1011.         $objects = [];
  1012.  
  1013.         foreach ($rows as $k => &$row)
  1014.         {
  1015.             $objects[$k] = static::convertArrayToObject($row, true);
  1016.         }
  1017.  
  1018.         $entity = static::getEntity();
  1019.         $result = new AddResult();
  1020.  
  1021.         try
  1022.         {
  1023.             // call onBeforeEvent
  1024.             if (!$ignoreEvents)
  1025.             {
  1026.                 foreach ($objects as $k => $object)
  1027.                 {
  1028.                     static::callOnBeforeAddEvent($object, $rows[$k], $result);
  1029.                 }
  1030.             }
  1031.  
  1032.             // collect array data
  1033.             $allFields = [];
  1034.             $allUfData = [];
  1035.  
  1036.             foreach ($objects as $k => $object)
  1037.             {
  1038.                 // actualize old-style fields array from object
  1039.                 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
  1040.  
  1041.                 // uf values
  1042.                 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
  1043.             }
  1044.  
  1045.             // check data and uf
  1046.             foreach ($objects as $k => $object)
  1047.             {
  1048.                 $fields = $allFields[$k];
  1049.                 $ufdata = $allUfData[$k];
  1050.  
  1051.                 // check data
  1052.                 static::checkFields($result, null, $fields);
  1053.  
  1054.                 // check uf data
  1055.                 if (!empty($ufdata))
  1056.                 {
  1057.                     static::checkUfFields($object, $ufdata, $result);
  1058.                 }
  1059.  
  1060.                 // check if there is still some data
  1061.                 if (!count($fields + $ufdata))
  1062.                 {
  1063.                     $result->addError(new EntityError("There is no data to add."));
  1064.                 }
  1065.             }
  1066.  
  1067.             // return if any error in any row
  1068.             if (!$result->isSuccess(true))
  1069.             {
  1070.                 return $result;
  1071.             }
  1072.  
  1073.             //event on adding
  1074.             if (!$ignoreEvents)
  1075.             {
  1076.                 foreach ($objects as $k => $object)
  1077.                 {
  1078.                     $fields = $allFields[$k];
  1079.                     $ufdata = $allUfData[$k];
  1080.  
  1081.                     self::callOnAddEvent($object, $fields, $ufdata);
  1082.                 }
  1083.             }
  1084.  
  1085.             // prepare sql
  1086.             $allSqlData = [];
  1087.  
  1088.             foreach ($allFields as $k => $fields)
  1089.             {
  1090.                 // use save modifiers
  1091.                 $fieldsToDb = $fields;
  1092.  
  1093.                 foreach ($fieldsToDb as $fieldName => $value)
  1094.                 {
  1095.                     $field = $entity->getField($fieldName);
  1096.                     $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
  1097.                 }
  1098.  
  1099.                 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
  1100.  
  1101.                 $allSqlData[$k] = $dataReplacedColumn;
  1102.             }
  1103.  
  1104.             // save data
  1105.             $connection = $entity->getConnection();
  1106.  
  1107.             $tableName = $entity->getDBTableName();
  1108.             $identity = $entity->getAutoIncrement();
  1109.             $ids = [];
  1110.  
  1111.             // multi insert on db level
  1112.             if ($forceSeparateQueries)
  1113.             {
  1114.                 foreach ($allSqlData as $k => $sqlData)
  1115.                 {
  1116.                     // remember all ids
  1117.                     $ids[$k] = $connection->add($tableName, $sqlData, $identity);
  1118.                 }
  1119.             }
  1120.             else
  1121.             {
  1122.                 $id = $connection->addMulti($tableName, $allSqlData, $identity);
  1123.             }
  1124.  
  1125.             if (count($allSqlData) > 1)
  1126.             {
  1127.                 // id doesn't make sense when multiple inserts
  1128.                 $id = null;
  1129.             }
  1130.             else
  1131.             {
  1132.                 $object = $objects[0];
  1133.                 $fields = $allFields[0];
  1134.  
  1135.                 // build standard primary
  1136.                 $primary = null;
  1137.  
  1138.                 if (!empty($id))
  1139.                 {
  1140.                     if (strlen($entity->getAutoIncrement()))
  1141.                     {
  1142.                         $primary = array($entity->getAutoIncrement() => $id);
  1143.                         static::normalizePrimary($primary);
  1144.                     }
  1145.                     else
  1146.                     {
  1147.                         // for those who did not set 'autocomplete' flag but want to get id from result
  1148.                         $primary = array('ID' => $id);
  1149.                     }
  1150.                 }
  1151.                 else
  1152.                 {
  1153.                     static::normalizePrimary($primary, $fields);
  1154.                 }
  1155.  
  1156.                 // fill result
  1157.                 $result->setPrimary($primary);
  1158.                 $result->setData($fields);
  1159.                 $result->setObject($object);
  1160.             }
  1161.  
  1162.             // save uf data
  1163.             foreach ($allUfData as $ufdata)
  1164.             {
  1165.                 if (!empty($ufdata))
  1166.                 {
  1167.                     $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
  1168.                 }
  1169.             }
  1170.  
  1171.             $entity->cleanCache();
  1172.  
  1173.             // after event
  1174.             if (!$ignoreEvents)
  1175.             {
  1176.                 foreach ($objects as $k => $object)
  1177.                 {
  1178.                     $fields = $allFields[$k];
  1179.                     $id = $forceSeparateQueries ? $ids[$k] : null;
  1180.  
  1181.                     static::callOnAfterAddEvent($object, $fields, $id);
  1182.                 }
  1183.             }
  1184.         }
  1185.         catch (\Exception $e)
  1186.         {
  1187.             // check result to avoid warning
  1188.             $result->isSuccess();
  1189.  
  1190.             throw $e;
  1191.         }
  1192.  
  1193.         return $result;
  1194.     }
  1195.  
  1196.     /**
  1197.      * Updates row in entity table by primary key
  1198.      *
  1199.      * @param mixed $primary
  1200.      * @param array $data An array with fields like
  1201.      *  array(
  1202.      *      "fields" => array(
  1203.      *          "FIELD1" => "value1",
  1204.      *          "FIELD2" => "value2",
  1205.      *      ),
  1206.      *      "auth_context" => \Bitrix\Main\Authentication\Context object
  1207.      *  )
  1208.      *  or just a plain array of fields.
  1209.      *
  1210.      * @return UpdateResult
  1211.      *
  1212.      * @throws \Exception
  1213.      */
  1214.     public static function update($primary, array $data)
  1215.     {
  1216.         global $USER_FIELD_MANAGER;
  1217.  
  1218.         // check primary
  1219.         static::normalizePrimary(
  1220.             $primary, isset($fields["fields"]) && is_array($data["fields"]) ? $data["fields"] : $data
  1221.         );
  1222.         static::validatePrimary($primary);
  1223.  
  1224.         // compatibility
  1225.         $fields = $data;
  1226.  
  1227.         /** @var EntityObject $object prepare entity object for compatibility with new code */
  1228.         $object = static::convertArrayToObject($fields, false, $primary);
  1229.  
  1230.         $entity = static::getEntity();
  1231.         $result = new UpdateResult();
  1232.  
  1233.         try
  1234.         {
  1235.             static::callOnBeforeUpdateEvent($object, $fields, $result);
  1236.  
  1237.             // actualize old-style fields array from object
  1238.             $fields = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
  1239.  
  1240.             // uf values
  1241.             $ufdata = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
  1242.  
  1243.             // check data
  1244.             static::checkFields($result, $primary, $fields);
  1245.  
  1246.             // check uf data
  1247.             if (!empty($ufdata))
  1248.             {
  1249.                 static::checkUfFields($object, $ufdata, $result);
  1250.             }
  1251.  
  1252.             // check if there is still some data
  1253.             if (!count($fields + $ufdata))
  1254.             {
  1255.                 return $result;
  1256.             }
  1257.  
  1258.             // return if any error
  1259.             if (!$result->isSuccess(true))
  1260.             {
  1261.                 return $result;
  1262.             }
  1263.  
  1264.             static::callOnUpdateEvent($object, $fields, $ufdata);
  1265.  
  1266.             // use save modifiers
  1267.             $fieldsToDb = $fields;
  1268.  
  1269.             foreach ($fieldsToDb as $fieldName => $value)
  1270.             {
  1271.                 $field = $entity->getField($fieldName);
  1272.                 $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
  1273.             }
  1274.  
  1275.             // save data
  1276.             if (!empty($fieldsToDb))
  1277.             {
  1278.                 $connection = $entity->getConnection();
  1279.                 $helper = $connection->getSqlHelper();
  1280.  
  1281.                 $tableName = $entity->getDBTableName();
  1282.  
  1283.                 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
  1284.                 $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
  1285.  
  1286.                 $replacedPrimary = static::replaceFieldName($primary);
  1287.                 $id = array();
  1288.                 foreach ($replacedPrimary as $k => $v)
  1289.                 {
  1290.                     $id[] = $helper->prepareAssignment($tableName, $k, $v);
  1291.                 }
  1292.                 $where = implode(' AND ', $id);
  1293.  
  1294.                 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
  1295.                 $connection->queryExecute($sql, $update[1]);
  1296.  
  1297.                 $result->setAffectedRowsCount($connection);
  1298.             }
  1299.  
  1300.             $result->setData($fields);
  1301.             $result->setPrimary($primary);
  1302.             $result->setObject($object);
  1303.  
  1304.             // save uf data
  1305.             if (!empty($ufdata))
  1306.             {
  1307.                 $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
  1308.             }
  1309.  
  1310.             $entity->cleanCache();
  1311.  
  1312.             // event after update
  1313.             static::callOnAfterUpdateEvent($object, $fields);
  1314.         }
  1315.         catch (\Exception $e)
  1316.         {
  1317.             // check result to avoid warning
  1318.             $result->isSuccess();
  1319.  
  1320.             throw $e;
  1321.         }
  1322.  
  1323.         return $result;
  1324.     }
  1325.  
  1326.     /**
  1327.      * @param array $primaries
  1328.      * @param array $data
  1329.      * @param bool  $ignoreEvents
  1330.      *
  1331.      * @return UpdateResult
  1332.      * @throws Main\ArgumentException
  1333.      * @throws Main\SystemException
  1334.      */
  1335.     public static function updateMulti($primaries, $data, $ignoreEvents = false)
  1336.     {
  1337.         $entity = static::getEntity();
  1338.         $primaries = array_values($primaries);
  1339.  
  1340.         /** @var EntityObject[] $objects */
  1341.         $objects = [];
  1342.  
  1343.         foreach ($primaries as &$primary)
  1344.         {
  1345.             static::normalizePrimary($primary, $data);
  1346.             static::validatePrimary($primary);
  1347.  
  1348.             /** @var EntityObject $object */
  1349.             $object = $entity->wakeUpObject($primary);
  1350.  
  1351.             foreach ($data as $k => $v)
  1352.             {
  1353.                 $object->set($k, $v);
  1354.             }
  1355.  
  1356.             $objects[] = $object;
  1357.         }
  1358.  
  1359.         $result = new UpdateResult;
  1360.  
  1361.         try
  1362.         {
  1363.             // before event
  1364.             if (!$ignoreEvents)
  1365.             {
  1366.                 foreach ($objects as $object)
  1367.                 {
  1368.                     static::callOnBeforeUpdateEvent($object, $data, $result);
  1369.                 }
  1370.             }
  1371.  
  1372.             // collect array data
  1373.             $allFields = [];
  1374.             $allUfData = [];
  1375.  
  1376.             foreach ($objects as $k => $object)
  1377.             {
  1378.                 // actualize old-style fields array from object
  1379.                 $allFields[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::SCALAR);
  1380.  
  1381.                 // uf values
  1382.                 $allUfData[$k] = $object->collectValues(Values::CURRENT, FieldTypeMask::USERTYPE);
  1383.             }
  1384.  
  1385.             // check data and uf
  1386.             foreach ($objects as $k => $object)
  1387.             {
  1388.                 $fields = $allFields[$k];
  1389.                 $ufdata = $allUfData[$k];
  1390.  
  1391.                 // check data
  1392.                 static::checkFields($result, $object->primary, $fields);
  1393.  
  1394.                 // check uf data
  1395.                 if (!empty($ufdata))
  1396.                 {
  1397.                     static::checkUfFields($object, $ufdata, $result);
  1398.                 }
  1399.  
  1400.                 // check if there is still some data
  1401.                 if (!count($fields + $ufdata))
  1402.                 {
  1403.                     $result->addError(new EntityError("There is no data to add."));
  1404.                 }
  1405.             }
  1406.  
  1407.             // return if any error in any row
  1408.             if (!$result->isSuccess(true))
  1409.             {
  1410.                 return $result;
  1411.             }
  1412.  
  1413.             //event on adding
  1414.             if (!$ignoreEvents)
  1415.             {
  1416.                 foreach ($objects as $k => $object)
  1417.                 {
  1418.                     $fields = $allFields[$k];
  1419.                     $ufdata = $allUfData[$k];
  1420.  
  1421.                     static::callOnUpdateEvent($object, $fields, $ufdata);
  1422.                 }
  1423.             }
  1424.  
  1425.             // prepare sql
  1426.             $allSqlData = [];
  1427.  
  1428.             foreach ($allFields as $k => $fields)
  1429.             {
  1430.                 // use save modifiers
  1431.                 $fieldsToDb = $fields;
  1432.  
  1433.                 foreach ($fieldsToDb as $fieldName => $value)
  1434.                 {
  1435.                     $field = $entity->getField($fieldName);
  1436.                     $fieldsToDb[$fieldName] = $field->modifyValueBeforeSave($value, $fields);
  1437.                 }
  1438.  
  1439.                 $dataReplacedColumn = static::replaceFieldName($fieldsToDb);
  1440.  
  1441.                 $allSqlData[$k] = $dataReplacedColumn;
  1442.             }
  1443.  
  1444.             // check if rows data are equal
  1445.             $areEqual = true;
  1446.  
  1447.             $dataSample = $allSqlData[0];
  1448.             asort($dataSample);
  1449.  
  1450.             foreach ($allSqlData as $data)
  1451.             {
  1452.                 asort($data);
  1453.  
  1454.                 if ($data !== $dataSample)
  1455.                 {
  1456.                     $areEqual = false;
  1457.                     break;
  1458.                 }
  1459.             }
  1460.  
  1461.             // save data
  1462.             $connection = $entity->getConnection();
  1463.             $helper = $connection->getSqlHelper();
  1464.             $tableName = $entity->getDBTableName();
  1465.  
  1466.             // save data
  1467.             if ($areEqual)
  1468.             {
  1469.                 // one query
  1470.                 $update = $helper->prepareUpdate($tableName, $dataSample);
  1471.                 $where = [];
  1472.                 $isSinglePrimary = (count($entity->getPrimaryArray()) == 1);
  1473.  
  1474.                 foreach ($allSqlData as $k => $data)
  1475.                 {
  1476.                     $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
  1477.  
  1478.                     if ($isSinglePrimary)
  1479.                     {
  1480.                         // for single primary IN is better
  1481.                         $primaryName = key($replacedPrimary);
  1482.                         $primaryValue = current($replacedPrimary);
  1483.                         $tableField = $entity->getConnection()->getTableField($tableName, $primaryName);
  1484.  
  1485.                         $where[] = $helper->convertToDb($primaryValue, $tableField);
  1486.                     }
  1487.                     else
  1488.                     {
  1489.                         $id = [];
  1490.  
  1491.                         foreach ($replacedPrimary as $primaryName => $primaryValue)
  1492.                         {
  1493.                             $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
  1494.                         }
  1495.                         $where[] = implode(' AND ', $id);
  1496.                     }
  1497.                 }
  1498.  
  1499.                 if ($isSinglePrimary)
  1500.                 {
  1501.                     $where = $helper->quote($entity->getPrimary()).' IN ('.join(', ', $where).')';
  1502.                 }
  1503.                 else
  1504.                 {
  1505.                     $where = '('.join(') OR (', $where).')';
  1506.                 }
  1507.  
  1508.                 $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
  1509.                 $connection->queryExecute($sql, $update[1]);
  1510.  
  1511.                 $result->setAffectedRowsCount($connection);
  1512.             }
  1513.             else
  1514.             {
  1515.                 // query for each row
  1516.                 foreach ($allSqlData as $k => $dataReplacedColumn)
  1517.                 {
  1518.                     $update = $helper->prepareUpdate($tableName, $dataReplacedColumn);
  1519.  
  1520.                     $replacedPrimary = static::replaceFieldName($objects[$k]->primary);
  1521.  
  1522.                     $id = [];
  1523.  
  1524.                     foreach ($replacedPrimary as $primaryName => $primaryValue)
  1525.                     {
  1526.                         $id[] = $helper->prepareAssignment($tableName, $primaryName, $primaryValue);
  1527.                     }
  1528.                     $where = implode(' AND ', $id);
  1529.  
  1530.                     $sql = "UPDATE ".$helper->quote($tableName)." SET ".$update[0]." WHERE ".$where;
  1531.                     $connection->queryExecute($sql, $update[1]);
  1532.  
  1533.                     $result->setAffectedRowsCount($connection);
  1534.                 }
  1535.             }
  1536.  
  1537.             // doesn't make sense for multiple rows
  1538.             $result->setData($dataSample);
  1539.  
  1540.             if (count($allSqlData) == 1)
  1541.             {
  1542.                 $result->setPrimary($objects[0]->primary);
  1543.                 $result->setObject($objects[0]);
  1544.             }
  1545.  
  1546.             // save uf data
  1547.             foreach ($allUfData as $ufdata)
  1548.             {
  1549.                 if (!empty($ufdata))
  1550.                 {
  1551.                     global $USER_FIELD_MANAGER;
  1552.                     $USER_FIELD_MANAGER->update($entity->getUfId(), end($primary), $ufdata);
  1553.                 }
  1554.             }
  1555.  
  1556.             $entity->cleanCache();
  1557.  
  1558.             // event after update
  1559.             if (!$ignoreEvents)
  1560.             {
  1561.                 foreach ($objects as $k => $object)
  1562.                 {
  1563.                     $fields = $allFields[$k];
  1564.  
  1565.                     static::callOnAfterUpdateEvent($object, $fields);
  1566.                 }
  1567.             }
  1568.         }
  1569.         catch (\Exception $e)
  1570.         {
  1571.             // check result to avoid warning
  1572.             $result->isSuccess();
  1573.  
  1574.             throw $e;
  1575.         }
  1576.  
  1577.         return $result;
  1578.     }
  1579.  
  1580.     /**
  1581.      * Deletes row in entity table by primary key
  1582.      *
  1583.      * @param mixed $primary
  1584.      *
  1585.      * @return DeleteResult
  1586.      *
  1587.      * @throws \Exception
  1588.      */
  1589.     public static function delete($primary)
  1590.     {
  1591.         global $USER_FIELD_MANAGER;
  1592.  
  1593.         // check primary
  1594.         static::normalizePrimary($primary);
  1595.         static::validatePrimary($primary);
  1596.  
  1597.         $entity = static::getEntity();
  1598.         $result = new DeleteResult();
  1599.  
  1600.         try
  1601.         {
  1602.             //event before delete
  1603.             static::callOnBeforeDeleteEvent($primary, $entity, $result);
  1604.  
  1605.             // return if any error
  1606.             if (!$result->isSuccess(true))
  1607.             {
  1608.                 return $result;
  1609.             }
  1610.  
  1611.             //event on delete
  1612.             static::callOnDeleteEvent($primary, $entity);
  1613.  
  1614.             // delete
  1615.             $connection = $entity->getConnection();
  1616.             $helper = $connection->getSqlHelper();
  1617.  
  1618.             $tableName = $entity->getDBTableName();
  1619.  
  1620.             $replacedPrimary = static::replaceFieldName($primary);
  1621.             $id = array();
  1622.             foreach ($replacedPrimary as $k => $v)
  1623.             {
  1624.                 $id[] = $helper->prepareAssignment($tableName, $k, $v);
  1625.             }
  1626.             $where = implode(' AND ', $id);
  1627.  
  1628.             $sql = "DELETE FROM ".$helper->quote($tableName)." WHERE ".$where;
  1629.             $connection->queryExecute($sql);
  1630.  
  1631.             // delete uf data
  1632.             if ($entity->getUfId())
  1633.             {
  1634.                 $USER_FIELD_MANAGER->delete($entity->getUfId(), end($primary));
  1635.             }
  1636.  
  1637.             $entity->cleanCache();
  1638.  
  1639.             //event after delete
  1640.             static::callOnAfterDeleteEvent($primary, $entity);
  1641.         }
  1642.         catch (\Exception $e)
  1643.         {
  1644.             // check result to avoid warning
  1645.             $result->isSuccess();
  1646.  
  1647.             throw $e;
  1648.         }
  1649.  
  1650.         return $result;
  1651.     }
  1652.  
  1653.     /**
  1654.      * @param EntityObject $object
  1655.      * @param              $fields
  1656.      * @param              $result
  1657.      */
  1658.     protected static function callOnBeforeAddEvent($object, $fields, $result)
  1659.     {
  1660.         //event before adding
  1661.         $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
  1662.             'fields' => $fields,
  1663.             'object' => $object
  1664.         ]);
  1665.  
  1666.         $event->send();
  1667.         $event->getErrors($result);
  1668.         $event->mergeObjectFields($object);
  1669.  
  1670.         //event before adding (modern with namespace)
  1671.         $event = new Event($object->entity, self::EVENT_ON_BEFORE_ADD, [
  1672.             'fields' => $fields,
  1673.             'object' => $object
  1674.         ], true);
  1675.  
  1676.         $event->send();
  1677.         $event->getErrors($result);
  1678.         $event->mergeObjectFields($object);
  1679.     }
  1680.  
  1681.     /**
  1682.      * @param $object
  1683.      * @param $fields
  1684.      * @param $ufdata
  1685.      */
  1686.     protected static function callOnAddEvent($object, $fields, $ufdata)
  1687.     {
  1688.         $event = new Event($object->entity, self::EVENT_ON_ADD, [
  1689.             'fields' => $fields + $ufdata,
  1690.             'object' => clone $object
  1691.         ]);
  1692.         $event->send();
  1693.  
  1694.         //event on adding (modern with namespace)
  1695.         $event = new Event($object->entity, self::EVENT_ON_ADD, [
  1696.             'fields' => $fields + $ufdata,
  1697.             'object' => clone $object
  1698.         ], true);
  1699.         $event->send();
  1700.     }
  1701.  
  1702.     /**
  1703.      * @param EntityObject $object
  1704.      * @param array        $fields
  1705.      * @param int          $id
  1706.      */
  1707.     protected static function callOnAfterAddEvent($object, $fields, $id)
  1708.     {
  1709.         //event after adding
  1710.         $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
  1711.             'id' => $id,
  1712.             'fields' => $fields,
  1713.             'object' => clone $object
  1714.         ]);
  1715.         $event->send();
  1716.  
  1717.         //event after adding (modern with namespace)
  1718.         $event = new Event($object->entity, self::EVENT_ON_AFTER_ADD, [
  1719.             'id' => $id,
  1720.             'primary' => $object->primary,
  1721.             'fields' => $fields,
  1722.             'object' => clone $object
  1723.         ], true);
  1724.         $event->send();
  1725.     }
  1726.  
  1727.     /**
  1728.      * @param EntityObject $object
  1729.      * @param              $fields
  1730.      * @param              $result
  1731.      */
  1732.     protected static function callOnBeforeUpdateEvent($object, $fields, $result)
  1733.     {
  1734.         $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
  1735.             'id' => $object->primary,
  1736.             'fields' => $fields,
  1737.             'object' => $object
  1738.         ]);
  1739.  
  1740.         $event->send();
  1741.         $event->getErrors($result);
  1742.         $event->mergeObjectFields($object);
  1743.  
  1744.         //event before update (modern with namespace)
  1745.         $event = new Event($object->entity, self::EVENT_ON_BEFORE_UPDATE, [
  1746.             'id' => $object->primary,
  1747.             'primary' => $object->primary,
  1748.             'fields' => $fields,
  1749.             'object' => $object
  1750.         ], true);
  1751.  
  1752.         $event->send();
  1753.         $event->getErrors($result);
  1754.         $event->mergeObjectFields($object);
  1755.     }
  1756.  
  1757.     /**
  1758.      * @param EntityObject $object
  1759.      * @param              $fields
  1760.      * @param              $ufdata
  1761.      */
  1762.     protected static function callOnUpdateEvent($object, $fields, $ufdata)
  1763.     {
  1764.         $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
  1765.             'id' => $object->primary,
  1766.             'fields' => $fields + $ufdata,
  1767.             'object' => clone $object
  1768.         ]);
  1769.         $event->send();
  1770.  
  1771.         //event on update (modern with namespace)
  1772.         $event = new Event($object->entity, self::EVENT_ON_UPDATE, [
  1773.             'id' => $object->primary,
  1774.             'primary' => $object->primary,
  1775.             'fields' => $fields + $ufdata,
  1776.             'object' => clone $object
  1777.         ], true);
  1778.         $event->send();
  1779.     }
  1780.  
  1781.     /**
  1782.      * @param EntityObject $object
  1783.      * @param              $fields
  1784.      */
  1785.     protected static function callOnAfterUpdateEvent($object, $fields)
  1786.     {
  1787.         $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
  1788.             'id' => $object->primary,
  1789.             'fields' => $fields,
  1790.             'object' => clone $object
  1791.         ]);
  1792.         $event->send();
  1793.  
  1794.         //event after update (modern with namespace)
  1795.         $event = new Event($object->entity, self::EVENT_ON_AFTER_UPDATE, [
  1796.             'id' => $object->primary,
  1797.             'primary' => $object->primary,
  1798.             'fields' => $fields,
  1799.             'object' => clone $object
  1800.         ], true);
  1801.         $event->send();
  1802.     }
  1803.  
  1804.     /**
  1805.      * @param $primary
  1806.      * @param $entity
  1807.      * @param $result
  1808.      */
  1809.     protected static function callOnBeforeDeleteEvent($primary, $entity, $result)
  1810.     {
  1811.         $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $primary));
  1812.         $event->send();
  1813.         $event->getErrors($result);
  1814.  
  1815.         //event before delete (modern with namespace)
  1816.         $event = new Event($entity, self::EVENT_ON_BEFORE_DELETE, array("id" => $primary, "primary" => $primary), true);
  1817.         $event->send();
  1818.         $event->getErrors($result);
  1819.     }
  1820.  
  1821.     /**
  1822.      * @param $primary
  1823.      * @param $entity
  1824.      */
  1825.     protected static function callOnDeleteEvent($primary, $entity)
  1826.     {
  1827.         $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $primary));
  1828.         $event->send();
  1829.  
  1830.         //event on delete (modern with namespace)
  1831.         $event = new Event($entity, self::EVENT_ON_DELETE, array("id" => $primary, "primary" => $primary), true);
  1832.         $event->send();
  1833.     }
  1834.  
  1835.     /**
  1836.      * @param $primary
  1837.      * @param $entity
  1838.      */
  1839.     protected static function callOnAfterDeleteEvent($primary, $entity)
  1840.     {
  1841.         $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $primary));
  1842.         $event->send();
  1843.  
  1844.         //event after delete (modern with namespace)
  1845.         $event = new Event($entity, self::EVENT_ON_AFTER_DELETE, array("id" => $primary, "primary" => $primary), true);
  1846.         $event->send();
  1847.     }
  1848.  
  1849.     /**
  1850.      * Sets a flag indicating crypto support for a field.
  1851.      *
  1852.      * @param string $field
  1853.      * @param string $table
  1854.      * @param bool   $mode
  1855.      *
  1856.      * @throws Main\ArgumentNullException
  1857.      * @throws Main\ArgumentOutOfRangeException
  1858.      */
  1859.     public static function enableCrypto($field, $table = null, $mode = true)
  1860.     {
  1861.         if($table === null)
  1862.         {
  1863.             $table = static::getTableName();
  1864.         }
  1865.         $options = array();
  1866.         $optionString = Main\Config\Option::get("main", "~crypto_".$table);
  1867.         if($optionString <> '')
  1868.         {
  1869.             $options = unserialize($optionString);
  1870.         }
  1871.         $options[strtoupper($field)] = $mode;
  1872.         Main\Config\Option::set("main", "~crypto_".$table, serialize($options));
  1873.     }
  1874.  
  1875.     /**
  1876.      * Returns true if crypto is enabled for a field.
  1877.      *
  1878.      * @param string $field
  1879.      * @param string $table
  1880.      *
  1881.      * @return bool
  1882.      * @throws Main\ArgumentNullException
  1883.      * @throws Main\ArgumentOutOfRangeException
  1884.      */
  1885.     public static function cryptoEnabled($field, $table = null)
  1886.     {
  1887.         if($table === null)
  1888.         {
  1889.             $table = static::getTableName();
  1890.         }
  1891.         $optionString = Main\Config\Option::get("main", "~crypto_".$table);
  1892.         if($optionString <> '')
  1893.         {
  1894.             $field = strtoupper($field);
  1895.             $options = unserialize($optionString);
  1896.             if(isset($options[$field]) && $options[$field] === true)
  1897.             {
  1898.                 return true;
  1899.             }
  1900.         }
  1901.         return false;
  1902.     }
  1903.  
  1904.     /*
  1905.     An inheritor class can define the event handlers for own events.
  1906.     Why? To prevent from rewriting the add/update/delete functions.
  1907.     These handlers are triggered in the Bitrix\Main\ORM\Event::send() function
  1908.     */
  1909.     public static function onBeforeAdd(Event $event){}
  1910.     public static function onAdd(Event $event){}
  1911.     public static function onAfterAdd(Event $event){}
  1912.     public static function onBeforeUpdate(Event $event){}
  1913.     public static function onUpdate(Event $event){}
  1914.     public static function onAfterUpdate(Event $event){}
  1915.     public static function onBeforeDelete(Event $event){}
  1916.     public static function onDelete(Event $event){}
  1917.     public static function onAfterDelete(Event $event){}
  1918. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement