%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/catalog/lib/model/ |
Current File : //home/bitrix/www/bitrix/modules/catalog/lib/model/entity.php |
<?php namespace Bitrix\Catalog\Model; use Bitrix\Main, Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); abstract class Entity { const PREFIX_OLD = 'OLD_'; private static $entity = null; /** @var null|Main\Entity\DataManager Tablet object */ private $tablet = null; /** @var array Table scalar fields list */ private $tabletFields = array(); /** @var null|Main\DB\Result Database result object */ private $result = null; /** @var array Entity cache */ private $cache = array(); /** @var array internal */ private $cacheModifyed = array(); private $fields = array(); private $fieldsCount = 0; private $aliases = array(); private $fieldMask = array(); private $fetchCutMask = array(); public function __construct() { $this->createEntityTablet(); $this->initEntityCache(); $this->result = null; $this->fetchCutMask = array(); } /** * @return Entity */ public static function getEntity() { $className = get_called_class(); if (empty(self::$entity[$className])) { /** @var Entity $entity */ $entity = new static; self::$entity[$className] = $entity; } return self::$entity[$className]; } public static function getList(array $parameters) { $entity = static::getEntity(); $parameters = $entity->prepareTabletQueryParameters($parameters); $entity->result = $entity->getTablet()->getList($parameters); return $entity; } public function fetch(Main\Text\Converter $converter = null) { if ($this->result === null) return false; $row = $this->result->fetch($converter); if (!$row) { $this->result = null; $this->fetchCutMask = array(); return false; } if (empty($this->fields)) return $row; if (!isset($row['ID'])) return $row; $this->setEntityCacheItem((int)$row['ID'], $row, true); if (!empty($this->fetchCutMask)) $row = array_diff_key($row, $this->fetchCutMask); return $row; } public static function clearCache() { static::getEntity()->clearEntityCache(); } public static function add(array $data) { $result = new Main\Entity\AddResult(); $entity = static::getEntity(); static::normalize($data); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_BEFORE_ADD)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_BEFORE_ADD, $data ); $event->send(); $event->mergeData($data); if ($event->getErrors($result)) return $result; } static::prepareForAdd($result, null, $data); if (!$result->isSuccess()) return $result; unset($result); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_ADD)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_ADD, $data ); $event->send(); unset($event); } $result = $entity->getTablet()->add($data['fields']); $success = $result->isSuccess(); if ($success) { $data['fields'] = $result->getData(); if ($entity->fieldsCount > 0) $entity->setEntityCacheItem((int)$result->getId(), $result->getData(), false); } if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_AFTER_ADD)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_AFTER_ADD, array( 'id' => $result->getId(), 'fields' => $data['fields'], 'external_fields' => $data['external_fields'], 'actions' => $data['actions'], 'success' => $success ) ); $event->send(); unset($event); } if ($success && !empty($data['actions'])) static::runAddExternalActions($result->getId(), $data); unset($success, $entity); return $result; } public static function update($id, array $data) { $result = new Main\Entity\UpdateResult(); $entity = static::getEntity(); static::normalize($data); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_BEFORE_UPDATE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_BEFORE_UPDATE, array( 'id' => $id, 'fields' => $data['fields'], 'external_fields' => $data['external_fields'], 'actions' => $data['actions'] ) ); $event->send(); $event->mergeData($data); if ($event->getErrors($result)) return $result; } static::prepareForUpdate($result, $id, $data); if (!$result->isSuccess()) return $result; unset($result); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_UPDATE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_UPDATE, array( 'id' => $id, 'fields' => $data['fields'], 'external_fields' => $data['external_fields'], 'actions' => $data['actions'] ) ); $event->send(); unset($event); } $result = $entity->getTablet()->update($id, $data['fields']); $success = $result->isSuccess(); if ($success) { $data['fields'] = $result->getData(); if ($entity->fieldsCount > 0) $entity->modifyEntityCacheItem($id, $data['fields']); } if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_AFTER_UPDATE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_AFTER_UPDATE, array( 'id' => $id, 'fields' => $data['fields'], 'external_fields' => $data['external_fields'], 'actions' => $data['actions'], 'success' => $success ) ); $event->send(); unset($event); } if ($success && !empty($data['actions'])) static::runUpdateExternalActions($id, $data); unset($success, $entity); return $result; } public static function delete($id) { $result = new Main\Entity\DeleteResult(); $entity = static::getEntity(); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_BEFORE_DELETE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_BEFORE_DELETE, array('id' => $id) ); $event->send(); if ($event->getErrors($result)) return $result; } if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_DELETE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_DELETE, array('id' => $id) ); $event->send(); unset($event); } if ($entity->fieldsCount > 0 && !isset($entity->cache[$id])) $entity->loadEntityCacheItem($id); $result = $entity->getTablet()->delete($id); $success = $result->isSuccess(); if ($success) $entity->expireEntityCacheItem((int)$id); if (Event::existEventHandlers($entity, Main\Entity\DataManager::EVENT_ON_AFTER_DELETE)) { $event = new Event( $entity, Main\Entity\DataManager::EVENT_ON_AFTER_DELETE, array('id' => $id, 'success' => $success) ); $event->send(); unset($event); } if ($success) static::runDeleteExternalActions($id); unset($success, $entity); return $result; } public static function setCacheItem($id, array $row) { $id = (int)$id; if ($id <= 0 || empty($row)) return; static::getEntity()->setEntityCacheItem($id, $row, false); } public static function getCacheItem($id, $load = false) { $id = (int)$id; if ($id <= 0) return null; return static::getEntity()->getEntityCacheItem($id, $load); } public static function clearCacheItem($id) { $id = (int)$id; if ($id <= 0) return; static::getEntity()->clearEntityCacheItem($id); } /** * @return string */ public static function getTabletClassName() { return ''; } /** * @return array */ public static function getCachedFieldList() { return array(); } protected function getTablet() { if (!($this->tablet instanceof Main\Entity\DataManager)) throw new Main\ObjectNotFoundException(sprintf( 'Tablet not found in entity `%s`', get_class($this) )); return $this->tablet; } protected static function prepareForAdd(Main\Entity\AddResult $result, $id, array &$data) { $data = static::getEntity()->checkTabletWhiteList($data); if (empty($data)) { $result->addError(new Main\Entity\EntityError(sprintf( 'Empty data fo add in entity `%s`', get_called_class() ))); } } protected static function prepareForUpdate(Main\Entity\UpdateResult $result, $id, array &$data) { $data = static::getEntity()->checkTabletWhiteList($data); if (empty($data)) { $result->addError(new Main\Entity\EntityError(sprintf( 'Empty data fo update in entity `%s`', get_called_class() ))); } } protected static function deleteNoDemands($id) { $entity = static::getEntity(); if ($entity->fieldsCount > 0 && !isset($entity->cache[$id])) $entity->loadEntityCacheItem($id); $result = $entity->getTablet()->delete($id); if ($result->isSuccess()) { if ($entity->fieldsCount > 0) $entity->expireEntityCacheItem((int)$id); static::runDeleteExternalActions($id); } unset($entity); return $result; } /** * @param array $data * @return void */ protected static function normalize(array &$data) { $result = array( 'fields' => array(), 'external_fields' => array(), 'actions' => array() ); if (isset($data['fields']) && is_array($data['fields'])) { $result['fields'] = $data['fields']; if (isset($data['external_fields']) && is_array($data['external_fields'])) $result['external_fields'] = $data['external_fields']; if (isset($data['actions']) && is_array($data['actions'])) $result['actions'] = $data['actions']; } else { $result['fields'] = $data; } $data = $result; unset($result); } protected static function runAddExternalActions($id, array $data){} protected static function runUpdateExternalActions($id, array $data){} protected static function runDeleteExternalActions($id){} /** * @return void */ private function createEntityTablet() { $tabletClassName = static::getTabletClassName(); $this->tablet = new $tabletClassName; $this->tabletFields = $this->tablet->getEntity()->getScalarFields(); } private function initEntityCache() { $this->clearEntityCache(); $this->aliases = array(); $this->fieldMask = array(); $this->fields = static::getCachedFieldList(); if (!empty($this->fields)) { foreach ($this->fields as $alias => $field) { if (is_numeric($alias)) { $this->fieldMask[$field] = true; } else { $this->fieldMask[$alias] = true; $this->aliases[$alias] = $field; } } unset($alias, $field); } $this->fieldsCount = count($this->fields); } private function clearEntityCache() { $this->cache = array(); $this->cacheModifyed = array(); } private function prepareTabletQueryParameters(array $parameters) { $this->fetchCutMask = array(); if (empty($this->fields)) return $parameters; if (!isset($parameters['select'])) return $parameters; if (in_array('*', $parameters['select'])) return $parameters; if (isset($parameters['group'])) return $parameters; $select = $parameters['select']; foreach ($this->fields as $field) { $existField = false; $index = array_search($field, $select); if ($index !== false && is_numeric($index)) $existField = true; if ($existField) continue; $parameters['select'][] = $field; $this->fetchCutMask[$field] = true; } unset($index, $existField, $field); return $parameters; } private function replaceFieldToAlias(array &$row) { if (empty($this->aliases)) return; foreach ($this->aliases as $alias => $field) { $row[$alias] = $row[$field]; unset($row[$field]); } unset($alias, $field); } private function checkTabletWhiteList(array $fields) { return array_intersect_key($fields, $this->tabletFields); } /* entity cache item tools */ private function loadEntityCacheItem($id) { if (isset($this->cache[$id])) return; if (empty($this->fields)) return; $row = $this->getTablet()->getList(array( 'select' => array_values($this->fields), 'filter' => array('=ID' => $id) ))->fetch(); if (!empty($row)) $this->setEntityCacheItem($id, $row, true); unset($row); } private function getEntityCacheItem($id, $load = false) { $load = ($load === true); $result = array(); if (!isset($this->cache[$id]) && $load && !empty($this->fields)) $this->loadEntityCacheItem($id); if (isset($this->cache[$id])) $result = $this->cache[$id]; return $result; } private function setEntityCacheItem($id, array $row, $replaceAliases = false) { if (empty($this->fieldMask)) return; if (isset($this->cache[$id])) return; if ($replaceAliases) $this->replaceFieldToAlias($row); $data = array_intersect_key($row, $this->fieldMask); if (!empty($data) && count($data) == $this->fieldsCount) $this->cache[$id] = $data; unset($data); } private function modifyEntityCacheItem($id, array $row) { if (empty($this->fieldMask)) return; $data = array_intersect_key($row, $this->fieldMask); if (!empty($data)) { if (!isset($this->cache[$id])) $this->loadEntityCacheItem($id); if (isset($this->cache[$id])) { $this->expireEntityCacheItem($id, true); $this->cache[$id] = array_merge($this->cache[$id], $data); } } unset($data); } private function expireEntityCacheItem($id, $copy = false) { if (empty($this->fields)) return; if (!isset($this->cache[$id])) return; if (isset($this->cacheModifyed[$id])) return; $oldData = array(); foreach (array_keys($this->fieldMask) as $field) $oldData[self::PREFIX_OLD.$field] = $this->cache[$id][$field]; unset($field); if ($copy) $this->cache[$id] = array_merge($oldData, $this->cache[$id]); else $this->cache[$id] = $oldData; unset($oldData); $this->cacheModifyed[$id] = true; } private function clearEntityCacheItem($id) { if (isset($this->cache[$id])) unset($this->cache[$id]); if (isset($this->cacheModifyed[$id])) unset($this->cacheModifyed[$id]); } /* entity cache item tools end */ }