phplibrary/src/Controller/Component.php

499 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace ctiso\Controller;
use ctiso\HttpRequest;
use ctiso\ComponentRequest;
use ctiso\Arr;
use ctiso\Path;
use ctiso\File;
use ctiso\Form\Form;
use ctiso\View\View;
use ctiso\Database;
use ctiso\Collection;
use ctiso\Registry;
use ctiso\Controller\SiteInterface;
use ctiso\Database\PDOStatement;
use ctiso\View\Json;
use PHPTAL;
use PHPTAL_PreFilter_Normalize;
use ctiso\View\Template;
use ctiso\View\TalView;
/**
* Класс компонента
*/
class Component
{
/** @var string[] */
public $viewPath = [];
/** @var string[] */
public $webPath = [];
/** @var ?string */
public $template = null;
public string $templatePath;
/** @var int */
public $component_id;
/** @var string */
public $component_title;
/** @var string */
public $COMPONENTS_WEB;
public Registry $config;
public Database $db;
public Collection $parameter;
/** @var string */
public $output = 'html';
/** @var string */
public $module;
/** @var string */
public $item_module;
/**
* @var SiteInterface $site
*/
public $site;
function before(): void {
}
/**
* @param string $match
* @return string
*/
static function replaceContent($match) {
return \ctiso\Tales::phptal_component(htmlspecialchars_decode($match[3]));
}
/**
* @param string $text
* @return string
*/
static function applyComponents($text) {
$callback = fn($x) => self::replaceContent($x);
return preg_replace_callback('/<(\w+)(\s+[a-zA-Z\-]+=\"[^\"]*\")*\s+tal:replace="structure\s+component:([^\"]*)"[^>]*>/u',
$callback, $text);
}
/**
* Выполняет запрос компонента и возвращает результат
* Результат может быть строкой или View для обычных компонентов, или массивом для использования в сервисах
*
* @param HttpRequest $request
* @param bool $has_id
* @return mixed
*/
function execute(HttpRequest $request, $has_id = true) {
$crequest = new ComponentRequest($this->component_id, $request);
$_action = $request->get('action', 'index');
if (is_array($_action)) {
$action = 'action' . ucfirst(Arr::get($_action, $this->component_id, 'index'));
} else {
$action = 'action' . ucfirst($_action);
}
$this->before();
$actionMethod = [$this, $action];
if (is_callable($actionMethod)) {
return call_user_func($actionMethod, $crequest);
}
return $this->actionIndex($crequest);
}
/**
* Получить имя шаблона
* @param Registry $_registry
* @return string
*/
public function getTemplateName($_registry) {
return (isset($_COOKIE['with_template']) && preg_match('/^[\w\d-]{3,20}$/', $_COOKIE['with_template']))
? $_COOKIE['with_template'] : ($_registry ? $_registry->get('site', 'template') : 'modern');
}
/**
* Получить шаблон
* @param string $name
* @return Template
*/
public function getView($name)
{
if ($this->output === 'json') {
return new Json($name);
}
/** @var Registry $config */
$config = $this->config;
$default = $config->get('site', 'template');
$template = ($this->template) ? $this->template : $this->getTemplateName($config);
$selected = null;
$tpl = null;
foreach ($this->viewPath as $index => $viewPath) {
// Загружать шаблон по умолчанию если не найден текущий
$dir = Path::join($this->viewPath[$index], 'templates', $template);
if(is_dir($dir)) {
$tpl = new TalView(Path::join($this->viewPath[$index], 'templates', $template, $name));
$selected = $index;
break;
}
}
if ($selected === null) {
// Последний вариант viewPath, путь к папке компонента
$selected = count($this->viewPath) - 1;
$tpl = new TalView(Path::join($this->viewPath[$selected], 'templates', 'modern', $name));
$template = 'modern';
}
// $tpl->addPreFilter(new \PHPTAL_PreFilter_Normalize());
$tpl->set('common', Path::join($this->config->get('system', 'web'), '../', 'common'));
$tpl->set('script', Path::join($this->config->get('system', 'web'), 'js'));
$tpl->set('media', Path::join($this->config->get('system', 'templates.web'), $template));
if ($default) {
$tpl->set('site_template', Path::join($this->config->get('site', 'templates.web'), $default));
}
$tpl->set('base', $this->config->get('site', 'web'));
$tpl->set('component_base', $this->webPath[$selected]);
$tpl->set('component', Path::join($this->webPath[$selected], 'templates', $template));
$tpl->set('component_id', $this->component_id);
return $tpl;
}
/**
* Возвращает путь к шаблону по умолчанию
* @return string
*/
function _getDefaultPath() {
return $this->viewPath[count($this->viewPath) - 1];
}
/**
* Возвращает путь к шаблону
* @param string $name
* @return string
*/
public function getTemplatePath($name) {
$registry = $this->config;
// Брать настройки из куков если есть
$template = ($this->template) ? $this->template : $this->getTemplateName($registry);
foreach ($this->viewPath as $index => $_) {
if(is_dir(Path::join($this->viewPath[$index], 'templates', $template))) {
return Path::join($this->viewPath[$index], 'templates', $template, $name);
}
}
return Path::join($this->viewPath[count($this->viewPath) - 1], 'templates', 'modern', $name);
}
/**
* Возвращает путь к шаблонам
* @return string
*/
public function getTemplateWebPath()
{
return Path::join($this->webPath[count($this->webPath) - 1], 'templates', 'modern');
}
/**
* Создает модель
*
* @template T
* @param class-string<T> $modelName
* @return T
*/
public function getModel($modelName)
{
$model = new $modelName();
$model->config = $this->config;
$model->db = $this->db;
return $model;
}
/**
* @param string $key
* @param string $val
* @param PDOStatement $res
* @return array{value: string, name: string}[]
*/
public function options(string $key, string $val, $res) {
$result = [];
while($res->next()) {
$result[] = ['value' => $res->getString($key), 'name' => $res->getString($val)];
}
return $result;
}
/**
* @param array $list
* @param bool $selected
* @return array{value: string, name: string, selected: bool}[]
*/
public function optionsPair(array $list, $selected = false) {
$result = [];
foreach ($list as $key => $value) {
$result [] = ['value' => $key, 'name' => $value, 'selected' => $key == $selected];
}
return $result;
}
/**
* Найти файл по пути
* @param string[] $pathList
* @param string $name
* @return string|null
*/
function findFile(array $pathList, string $name) {
foreach($pathList as $item) {
$filename = Path::join($item, $name);
if (file_exists($filename)) {
return $filename;
}
}
return null;
}
/**
* Получить информацию о параметрах
* @return array<mixed>
*/
function getInfo() {
$filename = Path::join($this->viewPath[count($this->viewPath) - 1], 'install.json');
if (file_exists($filename)) {
$settings = json_decode(File::getContents($filename), true);
if ($settings) {
return $settings;
}
}
return ['parameter' => []];
}
/**
* Генерация интерфейса для выбора галлереи фотографии
* @param View $view
* @param ?\ctiso\Form\OptionsFactory $options
*/
public function setParameters(View $view, $options = null): void
{
$form = new Form();
$settings = $this->getInfo();
$form->addFieldList($settings['parameter'], $options);
$view->set('form', $form);
$view->set('component', $settings['component']);
$view->set('component_title', $settings['title']);
}
/**
* @param \ctiso\Form\OptionsFactory $options
* @return array
*/
public function getFields($options = null) {
$form = new Form();
$settings = $this->getInfo();
$form->addFieldList($settings['parameter'], $options);
return $form->field;
}
/**
* Обьеденить с ComponentFactory
* @param string $expression
* @param SiteInterface $site
* @return Component
*/
static function loadComponent(string $expression, $site)
{
$expression = htmlspecialchars_decode($expression);
$offset = strpos($expression, '?');
$url = parse_url($expression);
$arguments = [];
$path = $expression;
if (is_int($offset)) {
$path = substr($expression, 0, $offset);
$query = substr($expression, $offset + 1);
parse_str($query, $arguments);
}
$name = $path;
$config = $site->getConfig();
// FIXME: Если имя для компонента не задано то возвращаем пустой компонент
// Нужно дополнительно проверить и файл или в autoloader просто не найдет файл копонента
if (!$name) {
return new Component();
}
$filename = ucfirst($name);
$path = Path::join ($config->get('site', 'components'), $name, $filename . '.php');
$className = implode("\\", ['Components', ucfirst($name), $filename]);
$component = null;
if (file_exists($path)) {
/** @var Component $component */
$component = new $className();
$component->viewPath = [$config->get('site', 'components') . '/' . $name . '/'];
$component->webPath = [$config->get('site', 'components.web') . '/' . $name];
$component->COMPONENTS_WEB = $config->get('site', 'web') . '/components/';
} else {
/** @var Component $component */
$component = new $className();
$template = $component->getTemplateName($site->getConfig());
$component->viewPath = [
// Сначало ищем локально
$config->get('site', 'templates') . '/'. $template . '/_components/' . $name . '/',
$config->get('site', 'components') . '/' . $name . '/',
// Потом в общем хранилище
$config->get('system', 'templates'). '/' . $template . '/_components/' . $name . '/',
$config->get('system', 'components') . '/' . $name . '/',
];
if (defined('COMPONENTS_WEB')) {
$component->webPath = [
// Сначало локально
$config->get('site', 'templates.web') . '/' . $template . '/_components/' . $name,
$config->get('site', 'components.web') . '/' . $name,
// Потом в общем хранилище
$config->get('system', 'templates.web') . '/' . $template . '/_components/' . $name,
$config->get('system', 'components.web') . '/' . $name,
];
$component->COMPONENTS_WEB = $config->get('system', 'components.web');
} else {
$component->webPath = ['', $config->get('site', 'components.web') . '/' . $name, '', ''];
}
}
// Вынести в отдельную функцию
$db = $site->getDatabase();
$component->db = $db;
$component->config = $site->getConfig();
$component->site = $site;
$stmt = $db->prepareStatement("SELECT * FROM component WHERE code = ?");
$stmt->setString(1, $expression);
$cid = $stmt->executeQuery();
if ($cid->next()) {
$component->component_id = $cid->getInt('id_component');
} else {
$last = $db->getIdGenerator();
$result = null;
if ($last->isBeforeInsert()) {
$result = $last->getId('component_id_component_seq');
$stmt = $db->prepareStatement("INSERT INTO component (id_component, code) VALUES ($result, ?)");
$stmt->setString(1, $expression);
$stmt->executeQuery();
}
if ($last->isAfterInsert()) {
$stmt = $db->prepareStatement("INSERT INTO component (code) VALUES (?)");
$stmt->setString(1, $expression);
$stmt->executeQuery();
$result = $last->getId('component_id_component_seq');
}
$component->component_id = $result;
}
$params = new Collection();
$params->import(array_merge($_GET, $arguments));
$component->parameter = $params;
$component->template = $params->get('template', false);
$editor = $component->getEditUrl();
if ($editor) {
$site->addComponentConfig($editor);
}
return $component;
}
/**
* @return ?array{name: string, url: string}
*/
function getEditUrl() {
return null;
}
/**
* @param ComponentRequest $request
* @return array
*/
function raw_query($request)
{
$arr = $request->r->export('get');
$param = [];
$parameter = $this->parameter;
foreach($parameter->export() as $key => $value) {
$param[$key] = $value;
}
$data = [];
foreach($arr as $key => $value) {
if (is_array($value)) {
$data[$key] = Arr::get($value, $this->component_id);
} else {
$data[$key] = $value;
}
}
$data['param'] = $param;
return $data;
}
/**
* @param ComponentRequest $request
* @param array $list
* @return string
*/
function query($request, $list)
{
$arr = $request->r->export('get');
foreach($list as $key => $val) {
$arr[$key] [$this->component_id] = $val;
}
unset($arr['active_page']);
return '?' . http_build_query($arr);
}
/**
* @param string $name
* @param string $path
* @param array $shim
*/
function addRequireJsPath($name, $path, $shim = null): void {
$this->site->addRequireJsPath($name, $path, $shim);
}
/**
* @param ComponentRequest $request
* @return mixed
*/
function actionIndex($request) {
return "";
}
/**
* @param HttpRequest $request
* @return array
*/
function getDefaultPageEnvironment($request) {
return [];
}
}