Далеко не секрет что битрикс плохо работает с ajax'ом. И вот настала необходимость написать интерфейс для ExtJS.Версия сыровата но в процессе
Задача:
Продолжение следует....
Задача:
- Написать удобный инструмент для работы с ExtJS
- Он должен работать в двух режимах(аякс хтмл)
use Bitrix\Main,
Inter\Olsc,
Bitrix\Main\IO;
/**
* Class Component
*/
abstract class Component extends \CBitrixComponent
{
/**@var \CMain*/
protected $globalApplication;
/**@var \Inter\Olsc\Component\Events */
protected $eventObj = null;
protected $isAjaxResponse = false,//Флаг "ответ аяксом"
$postJson = array(),//Массив из POST json'a
$entryPointPrefix = 'action_';//префикс методов эвентов
public $data = array(),
$Response = array();
public function __construct($component = null)
{
parent::__construct($component);
}
/**
* Подготовка компонента к работе
*
* @param $arParams
*
* @throws \Bitrix\Main\ArgumentTypeException if $APPLICATION isn't instanceof \CMain
* @return array
*/
final public function onPrepareComponentParams(&$arParams)
{
$server = Main\Application::getInstance()->getContext()->getServer();
$ajax = $server->get('HTTP_X_REQUESTED_WITH');
$content_type = $server->get('CONTENT_TYPE');
if($ajax && strtolower($ajax) === 'xmlhttprequest' && strpos($content_type, 'multipart/form-data') === false)//На основе хедеров выясняем аякс ли это
{
$this->isAjaxResponse = true;
define("PUBLIC_AJAX_MODE", true);
}
try
{
$arParams['SEF_URL_TEMPLATES']['classOnly'] = '#CLASS#/';
global $APPLICATION;
if(!($APPLICATION instanceof \CMain))
{
throw new Main\ArgumentTypeException('APPLICATION', '\CMain object');
}
$this->globalApplication = $APPLICATION;
if($this->isAjaxResponse)
{
$this->postJson = $this->getPostJsonFromInput();//Получение данных пришедших json'ом
$this->globalApplication->RestartBuffer();
}
$this->setVariablesSef($arParams);//Используем ЧПУ битрикса бля определения класса и метода
//Подключение классов формы
if(!empty($this->data['arVariables']['CLASS']))
{
$this->eventObj = $this->initClassForm($this->data['arVariables']['CLASS']);
//Вызов события компонента
$this->eventObj->onPrepareComponentParams($arParams);
}
}
catch(\Exception $e)
{
$this->catchExeption($e);
}
//Так положено
return $arParams;
}
/**
* Главный исполняймый метод
*/
final public function executeComponent()
{
try
{
if($this->arParams['NO_CLASS_INIT'] !== 'Y' && !$this->inDeveloping())
{
return false;
}
$this->executeComponentEx();
//don't remove
if($this->isAjaxResponse)//Если аякс то выплёвываем json
{
$this->ajaxResponseSend();
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/epilog_after.php");
exit;
}
$this->includeComponentTemplate();
}catch (\Exception $e)
{
$this->catchExeption($e);
}
unset($this->data['arVariables']);
return $this->data;
}
abstract public function executeComponentEx();
private function catchExeption(\Exception $e)
{
if($this->isAjaxResponse)
{
$this->setErrMesExption($e);
$this->Response['success'] = false;
$this->ajaxResponseSend();
exit;
}
$errorMsg = '<span style="color:red">System failure!';
if($e->getCode()>0)
{
$errorMsg .= ' Code:'.$e->getCode();
}
$errorMsg .= '</span>';
echo $errorMsg;
throw $e;
}
final private function inDeveloping()
{
if(!is_object($this->eventObj) || !method_exists($this->eventObj, $this->entryPointPrefix.$this->data['arVariables']['METHOD']))
{
in_developing();//Заглушка
return false;
}
return true;
}
private function setErrMesExption(\Exception $e)
{
if($ex = $e->getPrevious())
{
$this->setErrMesExption($ex);
}
$arConfig = Main\Config\Configuration::getValue('exception_handling');
if($e instanceof Olsc\Exception\MessagesForUser || $arConfig['debug'])
{
$this->Response['error']['message'][] = $e->getMessage().' , '.'Code: '.$e->getCode();
}
else
{
$this->Response['error']['message'][] = 'Code: '.$e->getCode();
}
}
final private function setVariablesSef(&$arParams)
{
if (!($arParams['SEF_MODE'] === 'Y')) {
$this->data['arVariables'] = $arParams['arVariables'];
} else {
//тут магия копипасты с форума битрикса. Что тут происходит - никому не известно.)))
$arDefaultUrlTemplates404 = array();
$arDefaultVariableAliases404 = array();
$arVariables = array('METHOD' => 'default');
if (!empty($arParams['DEFAULT_ROUTE'])) {
$arVariables['CLASS'] = $arParams['DEFAULT_ROUTE'];
}
$arComponentVariables = array('CLASS', "METHOD");
$arUrlTemplates = \CComponentEngine::MakeComponentUrlTemplates(
$arDefaultUrlTemplates404,
$arParams["SEF_URL_TEMPLATES"]
);
$arVariableAliases = \CComponentEngine::MakeComponentVariableAliases(
$arDefaultVariableAliases404,
$arParams["VARIABLE_ALIASES"]
);
$componentPage = \CComponentEngine::ParseComponentPath(
$arParams['SEF_FOLDER'],
$arUrlTemplates,
$arVariables
);
\CComponentEngine::InitComponentVariables(
$componentPage,
$arComponentVariables,
$arVariableAliases,
$arVariables
);
$this->data['arVariables'] = $arVariables;
}
}
final private function getPostJsonFromInput()
{
global $HTTP_RAW_POST_DATA;
return json_decode($HTTP_RAW_POST_DATA, true);
}
/**
* Метод собирает и отправляет Response
*/
final private function ajaxResponseSend()
{
if(!is_bool($this->Response['success']))
{
$this->Response['success'] = false;
}
header('Content-Type: text/x-json; charset=' . LANG_CHARSET, true);
//echo \CUtil::PhpToJSObject($this->Response);
$data = $this->ajaxObjectToString($this->Response); // Преобразуем DateTime в string
echo json_encode($data);
}
/**
* Преобразование спец. данных Битрикс в строку методом toString
* @data array $data Массив данных для преобразования
*
* @param $data
*
* @return array Массив данных с преобразованными данными
*/
final private function ajaxObjectToString($data)
{
foreach ($data as $k => $v)
{
if (is_array($v))
{
$data[$k] = $this->ajaxObjectToString($v);
}
else if (is_object($v) && method_exists($v, 'toString'))
{
$data[$k] = $v->toString();
}
}
return $data;
}
/**
* Инициализация вспомогательного объекта формы
*
* @param string $className массив классов из папки /components/*/component/class.route/
*
* @throws \Bitrix\Main\NotImplementedException
* @return Olsc\Component\Events
*/
final private function initClassForm($className)
{
$classRoutePath = Main\IO\Path::convertSiteRelativeToAbsolute(
array(
$this->getPath(),
'class.route'
)
);
$subClass = '*\*\Component\Events';
$classPath = $classRoutePath.IO\Path::DIRECTORY_SEPARATOR.strtolower($className).'.php';
$classPath = IO\Path::normalize($classPath);
$className = get_called_class().'\\'.$className;
$this->includeFormClass($classPath);
if (!is_subclass_of($className, $subClass))
{
throw new Main\NotImplementedException($className.' not subclass of '.$subClass);
}
return new $className($this);
}
/**
* Метод подключает файл
*
* @param $file
*
* @return mixed
* @return mixed
* @throws \Bitrix\Main\IO\FileNotFoundException
*/
final private function includeFormClass($file)
{
if(!Main\IO\File::isFileExists($file))
{
throw new Main\IO\FileNotFoundException($file);
}
return require_once($file);
}
/**
* Вызов событий
*/
final protected function evalEvent($evalName, &$param = null)
{
$method = $this->entryPointPrefix.$evalName;
if(method_exists($this->eventObj, $method))
{
$this->eventObj->$method($param);
}
}
final public function getGlobalApplication()
{
return $this->globalApplication;
}
final public function getPostJson()
{
return $this->postJson;
}
final public function isAjax()
{
return $this->isAjaxResponse;
}
final public function execEventMethod($class, $method)
{
$obj = $this->initClassForm($class);
$method = $this->entryPointPrefix.$method;
if(method_exists($obj, $method))
{
$arParams = $this->arParams;
$obj->onPrepareComponentParams($arParams);
return $obj->$method();
//return call_user_func(array($obj, $method));
}
return false;
}
}
|