Your IP : 3.22.68.127


Current Path : /home/bitrix/ext_www/dev.easy-comfort.com.ua/bitrix/modules/sale/lib/location/
Upload File :
Current File : /home/bitrix/ext_www/dev.easy-comfort.com.ua/bitrix/modules/sale/lib/location/location.php

<?php
/**
 * Bitrix Framework
 * @package Bitrix\Sale\Location
 * @subpackage sale
 * @copyright 2001-2014 Bitrix
 */
namespace Bitrix\Sale\Location;

use Bitrix\Main;
use Bitrix\Main\DB;
use Bitrix\Main\Entity;
use Bitrix\Main\Localization\Loc;

use Bitrix\Sale\Location\Name;
use Bitrix\Sale\Location\Util\Assert;
use Bitrix\Sale\Location\Search;
use Bitrix\Sale\Location\DB\Helper;

Loc::loadMessages(__FILE__);

final class LocationTable extends Tree
{
	public static function getFilePath()
	{
		return __FILE__;
	}

	public static function getTableName()
	{
		return 'b_sale_location';
	}

	/**
	* Returns location with the specified code.
	*
	* @param string $code location code to search for
	*
	* @throws Bitrix\Main\ArgumentNullException
	*
	* @return Bitrix\Main\DB\Result location
	*/
	public static function getByCode($code = '', $parameters = array())
	{
		$code = Assert::expectStringNotNull($code, '$code');

		if(!is_array($parameters))
			$parameters = array();

		$parameters['filter']['=CODE'] = $code;
		$parameters['limit'] = 1;

		return self::getList($parameters);
	}

	public static function checkFields(Entity\Result $result, $primary, array $data)
	{
		parent::checkFields($result, $primary, $data);

		foreach(static::getEntity()->getFields() as $field)
		{
			$error = false;

			if($field->getName() == 'LATITUDE' && strlen($data['LATITUDE']))
			{
				// latitude is set in data and not empty, it must lay between -90 and 90
				if(!is_numeric($data['LATITUDE']))
					$error = Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LATITUDE_TYPE_ERROR');
				elseif(($latitude = floatval($data['LATITUDE'])) && ($latitude < -90 || $latitude > 90))
					$error = Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LATITUDE_RANGE_ERROR');
			}

			if($field->getName() == 'LONGITUDE' && strlen($data['LONGITUDE']))
			{
				// longitude is set in data and not empty, it must lay between -180 and 180
				if(!is_numeric($data['LONGITUDE']))
					$error = Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LONGITUDE_TYPE_ERROR');
				elseif(($longitude = floatval($data['LONGITUDE'])) && ($longitude < -180 || $longitude > 180))
					$error = Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LONGITUDE_RANGE_ERROR');
			}

			if($error !== false)
			{
				$result->addError(new Entity\FieldError(
					$field,
					$error,
					Entity\FieldError::INVALID_VALUE
				));
			}
		}
	}

	public static function add(array $data)
	{
		return self::addExtended($data);
	}

	/**
	* Adds a new location
	*
	* @param mixed[] $data to be added. Additional data keys could be passed:
	*
	*	<ul>
	*		<li>
	*			NAME string[] : add name string to a newly created location
	*		</li>
	*		<li>
	*			EXTERNAL string[] : add external data records to a newly created location
	*		</li>
	*	</ul>
	*
	* @param mixed[] $additional an additional behaviour flags:
	*
	*	<ul>
	*		<li>
	*			REBALANCE boolean (default: true) : do rebalance after add
	*		</li>
	*	</ul>
	*
	* @return \Bitrix\Main\Entity\AddResult the result of add operation
	*/
	public static function addExtended(array $data, array $additional = array())
	{
		$resetLegacy = !isset($additional['RESET_LEGACY']) || $additional['RESET_LEGACY'] !== false;

		if(isset($data['EXTERNAL']))
		{
			$external = $data['EXTERNAL'];
			unset($data['EXTERNAL']);
		}

		if(isset($data['NAME']))
		{
			$name = $data['NAME'];
			unset($data['NAME']);
		}

		// force code to lowercase
		if(isset($data['CODE']))
			$data['CODE'] = ToLower($data['CODE']);

		// you are not allowed to modify tree data over LocationTable::add()
		self::applyRestrictions($data);

		// store tree data and basic
		$addResult = parent::addExtended($data, $additional);

		// add connected data
		if($addResult->isSuccess())
		{
			$primary = $addResult->getId();

			// external
			if(isset($external))
				ExternalTable::addMultipleForOwner($primary, $external);

			// names
			if(isset($name))
				Name\LocationTable::addMultipleForOwner($primary, $name);

			if(intval($data['TYPE_ID']) > 0 && $resetLegacy)
				self::resetLegacy(intval($data['TYPE_ID']));

			Search\Finder::setIndexInvalid();
			$GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
		}

		return $addResult;
	}

	protected static function resetLegacy($typeId)
	{
		$type = TypeTable::getList(array('filter' => array('=ID' => $typeId), 'select' => array('CODE')))->fetch();
		if(strlen($type['CODE']) && in_array($type['CODE'], array('COUNTRY', 'REGION', 'CITY')))
			static::resetLegacyPath();
	}

	public static function update($primary, array $data)
	{
		return self::updateExtended($primary, $data);
	}
	/**
	* Updates an existed location
	*
	* @param integer $primary location primary key of a element being updated
	* @param mixed[] $data new data to set. Additional data keys could be passed:
	*
	*	<ul>
	*		<li>
	*			NAME string[] : update name string for specified location
	*		</li>
	*		<li>
	*			EXTERNAL string[] : update external data records for specified location
	*		</li>
	*	</ul>
	*
	* @param mixed[] $additional an additional behaviour flags:
	*
	*	<ul>
	*		<li>
	*			REBALANCE boolean (default: true) : do rebalancing after add
	*		</li>
	*	</ul>
	*
	* @return \Bitrix\Main\Entity\UpdateResult the result of update operation
	*/
	public static function updateExtended($primary, array $data, array $additional = array())
	{
		$primary = Assert::expectIntegerPositive($primary, '$primary');
		$resetLegacy = !isset($additional['RESET_LEGACY']) || $additional['RESET_LEGACY'] !== false;

		// first update parent, and if it succeed, do updates of the connected data

		if(isset($data['EXTERNAL']))
		{
			$external = $data['EXTERNAL'];
			unset($data['EXTERNAL']);
		}

		if(isset($data['NAME']))
		{
			$name = $data['NAME'];
			unset($data['NAME']);
		}

		// force code to lowercase
		if(isset($data['CODE']))
			$data['CODE'] = ToLower($data['CODE']);

		// you are not allowed to modify tree data over LocationTable::update()
		self::applyRestrictions($data);

		$updResult = parent::updateExtended($primary, $data, $additional);

		// update connected data
		if($updResult->isSuccess())
		{
			// external
			if(isset($external))
				ExternalTable::updateMultipleForOwner($primary, $external);

			// names
			if(isset($name))
				Name\LocationTable::updateMultipleForOwner($primary, $name);

			if($resetLegacy && (intval($data['TYPE_ID']) > 0 || isset($data['PARENT_ID'])))
				self::resetLegacy(intval($data['TYPE_ID']));

			$GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');

			if(isset($name) || isset($data['PARENT_ID']))
				Search\Finder::setIndexInvalid();
		}

		return $updResult;
	}

	public static function delete($primary)
	{
		return self::deleteExtended($primary);
	}
	/**
	 * Deletes location from the tree
	 *
	 * @param $primary
	 * @param array $additional
	 * @return Entity\DeleteResult
	 * @throws Main\ArgumentException
	 * @throws Main\SystemException
	 * @throws Tree\NodeNotFoundException
	 */
	public static function deleteExtended($primary, array $additional = array())
	{
		$primary = Assert::expectIntegerPositive($primary, '$primary');
		$resetLegacy = !isset($additional['RESET_LEGACY']) || $additional['RESET_LEGACY'] !== false;
		$deleteSubtree = !isset($additional['DELETE_SUBTREE']) || $additional['DELETE_SUBTREE'] !== false;

		// delete connected data of sub-nodes
		if($deleteSubtree)
		{
			$rangeSql = parent::getSubtreeRangeSqlForNode($primary);
			Name\LocationTable::deleteMultipleByParentRangeSql($rangeSql);
			ExternalTable::deleteMultipleByParentRangeSql($rangeSql);
		}

		if($resetLegacy)
			$data = static::getList(array('filter' => array('=ID' => $primary), 'select' => array('TYPE_ID')))->fetch();

		$delResult = parent::deleteExtended($primary, $additional);

		// delete connected data
		if($delResult->isSuccess())
		{
			Name\LocationTable::deleteMultipleForOwner($primary);
			ExternalTable::deleteMultipleForOwner($primary);

			if($resetLegacy && intval($data['TYPE_ID']))
			{
				$type = TypeTable::getList(array('filter' => array('=ID' => $data['TYPE_ID']), 'select' => array('CODE')))->fetch();
				if(strlen($type['CODE']) && in_array($type['CODE'], array('COUNTRY', 'REGION', 'CITY')))
					static::resetLegacyPath();
			}

			$GLOBALS['CACHE_MANAGER']->ClearByTag('sale-location-data');
			Search\Finder::setIndexInvalid();
		}

		return $delResult;
	}

	/**
	*
	*
	*
	*/
	public static function getExternalData($primary, $parameters = array())
	{
		$primary = Assert::expectIntegerPositive($primary, '$primary');

		if(!is_array($parameters) || empty($parameters))
			$parameters = array();

		$parameters['filter']['LOCATION_ID'] = $primary;

		return ExternalTable::getList($parameters);
	}

	// todo: make getList with SITE_ID parameter to have an ability to filter by SITE_ID using orm (even slowly)

	/**
	 * Fetches a parent chain of a specified node, using its code
	 * 
	 * Available keys in $behaviour
	 * SHOW_LEAF : if set to true, return node itself in the result
	 */
	public static function getPathToNodeByCode($code, $parameters, $behaviour = array('SHOW_LEAF' => true))
	{
		$code = Assert::expectStringNotNull($code, '$code');

		return self::getPathToNodeByCondition(array('=CODE' => $code), $parameters, $behaviour);
	}

	public static function checkNodeIsParentOfNode($primary, $childPrimary, $behaviour = array('ACCEPT_CODE' => false, 'CHECK_DIRECT' => false))
	{
		if(!$behaviour['ACCEPT_CODE'])
			return static::checkNodeIsParentOfNodeById($primary, $childPrimary, $behaviour);

		$primary = Assert::expectStringNotNull($primary, '$primary');
		$childPrimary = Assert::expectStringNotNull($childPrimary, '$childPrimary');

		return static::checkNodeIsParentOfNodeByFilters(array('=CODE' => $primary), array('=CODE' => $childPrimary), $behaviour);
	}

	public static function resetLegacyPath()
	{
		$dbConnection = Main\HttpApplication::getConnection();
		$locTable = static::getTableName();

		$types = array();
		$res = TypeTable::getList(array(
			'filter' => array('CODE' => array('COUNTRY', 'REGION', 'CITY')),
			'select' => array('ID', 'CODE')
		));
		while($item = $res->fetch())
			$types[$item['CODE']] = $item['ID'];

		if(!empty($types))
		{
			if(!$dbConnection->isTableExists('b_sale_loc_rebind'))
				$dbConnection->query("create table b_sale_loc_rebind (TARGET_ID ".Helper::getSqlForDataType('int').", LOCATION_ID ".Helper::getSqlForDataType('int').")");
			else
				$dbConnection->query("truncate table b_sale_loc_rebind");

			$sqlWhere = array();
			foreach($types as $code => $id)
				$sqlWhere[] = "'".intval($id)."'";

			$dbConnection->query("update ".$locTable." set COUNTRY_ID = NULL, REGION_ID = NULL, CITY_ID = NULL where TYPE_ID in (".implode(', ', $sqlWhere).")");

			if(intval($types['REGION']) && intval($types['COUNTRY']))
			{
				// countries for regions
				$dbConnection->query("insert into b_sale_loc_rebind (TARGET_ID, LOCATION_ID) select A.ID as ONE, B.ID as TWO from ".$locTable." A inner join ".$locTable." B on A.TYPE_ID = '".intval($types['REGION'])."' and B.TYPE_ID = '".intval($types['COUNTRY'])."' and B.LEFT_MARGIN <= A.LEFT_MARGIN and B.RIGHT_MARGIN >= A.RIGHT_MARGIN");
				Helper::mergeTables($locTable, 'b_sale_loc_rebind', array('COUNTRY_ID' => 'LOCATION_ID'), array('ID' => 'TARGET_ID'));
				$dbConnection->query("truncate table b_sale_loc_rebind");
			}

			if(intval($types['REGION']) && intval($types['CITY']))
			{
				// regions for cities
				$dbConnection->query("insert into b_sale_loc_rebind (TARGET_ID, LOCATION_ID) select A.ID as ONE, B.ID as TWO from ".$locTable." A inner join ".$locTable." B on A.TYPE_ID = '".intval($types['CITY'])."' and B.TYPE_ID = '".intval($types['REGION'])."' and B.LEFT_MARGIN <= A.LEFT_MARGIN and B.RIGHT_MARGIN >= A.RIGHT_MARGIN");
				Helper::mergeTables($locTable, 'b_sale_loc_rebind', array('REGION_ID' => 'LOCATION_ID'), array('ID' => 'TARGET_ID'));
				$dbConnection->query("truncate table b_sale_loc_rebind");
			}

			if(intval($types['COUNTRY']) && intval($types['CITY']))
			{
				// countries for cities
				$dbConnection->query("insert into b_sale_loc_rebind (TARGET_ID, LOCATION_ID) select A.ID as ONE, B.ID as TWO from ".$locTable." A inner join ".$locTable." B on A.TYPE_ID = '".intval($types['CITY'])."' and B.TYPE_ID = '".intval($types['COUNTRY'])."' and B.LEFT_MARGIN <= A.LEFT_MARGIN and B.RIGHT_MARGIN >= A.RIGHT_MARGIN");
				Helper::mergeTables($locTable, 'b_sale_loc_rebind', array('COUNTRY_ID' => 'LOCATION_ID'), array('ID' => 'TARGET_ID'));
			}

			Helper::dropTable('b_sale_loc_rebind');

			if(intval($types['COUNTRY']))
				$dbConnection->query("update ".$locTable." set COUNTRY_ID = ID where TYPE_ID = '".intval($types['COUNTRY'])."'");

			if(intval($types['REGION']))
				$dbConnection->query("update ".$locTable." set REGION_ID = ID where TYPE_ID = '".intval($types['REGION'])."'");

			if(intval($types['CITY']))
				$dbConnection->query("update ".$locTable." set CITY_ID = ID where TYPE_ID = '".intval($types['CITY'])."'");
		}
	}

	public static function getCodeValidators()
	{
		return array(
			new Entity\Validator\Unique(),
		);
	}

	public static function getMap()
	{
		return array(

			'ID' => array(
				'data_type' => 'integer',
				'primary' => true,
				'autocomplete' => true,
				'title' => 'ID'
			),
			'CODE' => array(
				'data_type' => 'string',
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_CODE_FIELD'),
				'required' => true,
				'validation' => array(__CLASS__, 'getCodeValidators')
			),

			'LEFT_MARGIN' => array(
				'data_type' => 'integer',
			),
			'RIGHT_MARGIN' => array(
				'data_type' => 'integer',
			),
			'DEPTH_LEVEL' => array(
				'data_type' => 'integer',
			),
			'SORT' => array(
				'data_type' => 'integer',
				'default' => 100,
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_SORT_FIELD')
			),
			'PARENT_ID' => array(
				'data_type' => 'integer',
				'default' => 0,
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_PARENT_ID_FIELD')
			),
			'TYPE_ID' => array(
				'data_type' => 'integer',
				'required' => true,
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_TYPE_ID_FIELD')
			),
			'LATITUDE' => array(
				'data_type' => 'float',
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LATITUDE_FIELD')
			),
			'LONGITUDE' => array(
				'data_type' => 'float',
				'title' => Loc::getMessage('SALE_LOCATION_LOCATION_ENTITY_LONGITUDE_FIELD')
			),

			// virtual
			'TYPE' => array(
				'data_type' => 'Bitrix\Sale\Location\Type',
				'reference' => array(
					'=this.TYPE_ID' => 'ref.ID'
				),
				'join_type' => "inner"
			),
			'NAME' => array(
				'data_type' => 'Bitrix\Sale\Location\Name\Location',
				'reference' => array(
					'=this.ID' => 'ref.LOCATION_ID'
				),
				'join_type' => "inner"
			),
			'PARENT' => array(
				'data_type' => 'Bitrix\Sale\Location\Location',
				'reference' => array(
					'=this.PARENT_ID' => 'ref.ID'
				)
			),
			'PARENTS' => array(
				'data_type' => 'Bitrix\Sale\Location\Location',
				'reference' => array(
					'<=ref.LEFT_MARGIN' => 'this.LEFT_MARGIN',
					'>=ref.RIGHT_MARGIN' => 'this.RIGHT_MARGIN'
				)
			),
			'CHILDREN' => array(
				'data_type' => 'Bitrix\Sale\Location\Location',
				'reference' => array(
					'=this.ID' => 'ref.PARENT_ID'
				)
			),
			'EXTERNAL' => array(
				'data_type' => 'Bitrix\Sale\Location\External',
				'reference' => array(
					'=this.ID' => 'ref.LOCATION_ID'
				)
			),
			'DEFAULT_SITE' => array(
				'data_type' => 'Bitrix\Sale\Location\DefaultSite',
				'reference' => array(
					'=this.CODE' => 'ref.LOCATION_CODE'
				)
			),

			'CNT' => array(
				'data_type' => 'integer',
				'expression' => array(
					'count(*)'
				)
			),
			'CHILDREN_CNT' => array(
				'data_type' => 'integer',
				'expression' => array(
					'count(%s)', 
					'CHILD.ID'
				)
			),
			'IS_PARENT' => array(
				'data_type' => 'boolean',
				'expression' => array(
					'case when count(%s) > 0 then 1 else 0 end',
					'CHILD.ID'
				)
			),

			// do not remove unless you want migrator to be dead
			'COUNTRY_ID' => array(
				'data_type' => 'integer',
			),
			'REGION_ID' => array(
				'data_type' => 'integer',
			),
			'CITY_ID' => array(
				'data_type' => 'integer',
			),
			'LOC_DEFAULT' => array(
				'data_type' => 'string',
			),

			// deprecated aliases
			'CHILD' => array(
				'data_type' => 'Bitrix\Sale\Location\Location',
				'reference' => array(
					'=this.ID' => 'ref.PARENT_ID'
				)
			),
			'CHILD_CNT' => array(
				'data_type' => 'integer',
				'expression' => array(
					'count(%s)', 
					'CHILD.ID'
				)
			),
			'DEFAULT_SORT' => array(
				'data_type' => 'Bitrix\Sale\Location\DefaultSiteTable',
				'reference' => array(
					'=this.CODE' => 'ref.LOCATION_CODE'
				)
			),

		);
	}

	/**
	 * @deprecated
	 */
	public static function getListFast($parameters = array())
	{
		// here $parameters conversion required

		if(isset($parameters['filter']['NAME']))
		{
			$parameters['filter']['PHRASE'] = $parameters['filter']['NAME'];
			unset($parameters['filter']['NAME']);
		}

		if(isset($parameters['filter']['LANGUAGE_ID']))
		{
			$parameters['filter']['NAME.LANGUAGE_ID'] = $parameters['filter']['LANGUAGE_ID'];
			unset($parameters['filter']['LANGUAGE_ID']);
		}

		return \Bitrix\Sale\Location\Search\Finder::find($parameters, array('USE_INDEX' => false, 'USE_ORM' => false));
	}
}