Различия
Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
abills:docs:development:manual:ru [2018/01/14 16:01] anton [Пошаговое осваивание фреймворка ABillS] |
abills:docs:development:manual:ru [2019/02/21 10:57] (текущий) andriy [Основная часть модуля - вебинтерфейс] |
||
---|---|---|---|
Строка 17: | Строка 17: | ||
* Класса менеджера сущностей в БД. (''Abills/mysql/Example.pm'') | * Класса менеджера сущностей в БД. (''Abills/mysql/Example.pm'') | ||
* Файл с логикой (функциями) ''Abills/modules/Example/webinterface'' | * Файл с логикой (функциями) ''Abills/modules/Example/webinterface'' | ||
- | * Описания меню ''Abills/modules/Example/config'' | + | * Описание меню ''Abills/modules/Example/config'' |
* Дополнительные файлы словарных переменных (необязательно) (''Abills/modules/lng_english.pl'') | * Дополнительные файлы словарных переменных (необязательно) (''Abills/modules/lng_english.pl'') | ||
* Файла описания схемы БД (''db/Example.sql'') | * Файла описания схемы БД (''db/Example.sql'') | ||
Теперь, когда мы рассмотрели какие части должны быть в самом модуле, детально рассмотрим взаимодействие модуля с частями фреймворка.\\ | Теперь, когда мы рассмотрели какие части должны быть в самом модуле, детально рассмотрим взаимодействие модуля с частями фреймворка.\\ | ||
+ | |||
+ | ==== Регистрация модуля в движке === | ||
+ | После добавления имени модуля в массив ''@MODULES'' (**libexec/config.pl**), при инициализации движка читается файл ''config'' из папки модуля и словарь с переменными текущего языка пользователя вебинтерфейса. Функции из ''config'' добавляются в глобальные реестр функций интерфейса, при этом каждой присваивается особый числовой индекс, который позволяет вызвать эту функцию.\\ | ||
+ | |||
+ | Сами функции должны быть доступны в ''webinterface'' (или быть импортированы из других пакетов внутри ''webinterface'')\\ | ||
+ | Поскольку все ''webinterface'' выполняются в глобальной области видимости, к имени каждой функции нужно добавлять имя модуля.\\ | ||
+ | |||
+ | ==== Основная часть модуля - вебинтерфейс ==== | ||
+ | В основном случае, логика вебинтерфейса проста и прозрачна - получить данные, обработать и вывести в каком-то виде (шаблон или таблица).\\ | ||
+ | Фреймворк неявно (через глобальную область видимости) передаёт в ''webinterface'' следующие переменные: | ||
+ | ^ Имя ^ Описание ^ | ||
+ | |%LANG |хеш-масив словаря | | ||
+ | |%FORM |хеш-масив значений переданных на страницу (GET или POST запросом) | | ||
+ | |$html |Обьект визуализации (экземпляр класса ''Abills::HTML'') | | ||
+ | |$users |Менеджер работы с пользователями (экземпляр класса ''Users''). **Использовать только в функциях админ. интерфейса.** | | ||
+ | |$db |Соединение с БД | | ||
+ | |$admin |Менеджер работы с администраторами (экземпляр класса ''Admins'')| | ||
+ | |%conf |хеш-масив конфигурационного файла | | ||
+ | |||
+ | Для примера рассмотрим работу с сущностью ''entity'' в модуле ''Example''\\ | ||
+ | |||
+ | Получить данные можно несколькими способами: | ||
+ | * Из БД (ссылка на работу с БД) | ||
+ | * Из внешнего источника (здесь ссылка на web_request) | ||
+ | * Из файловой системы | ||
+ | * Из других модулей | ||
+ | |||
+ | Для CRUD операций в ABillS принято использовать одну отдельную функцию в которой происходят следующие операции: | ||
+ | * Добавление новой сущности | ||
+ | * Редактирование сущности | ||
+ | * Удаление сущности | ||
+ | * Отображение списка сущностей | ||
+ | * Отображение сущности (совмещено с редактированием) | ||
+ | |||
+ | Если используется работа с БД, то внутри файла ''webinterface'' инициализируется обьект менеджера работы с сущностями. | ||
+ | <code perl> | ||
+ | use Example; # Загрузить файл /usr/abills/Abills/mysql/Example.pm | ||
+ | my $Example = Example->new($db, $admin, \%conf); # Создать объект менеджера | ||
+ | </code> | ||
+ | |||
+ | В коде функция работы с сущностями будет выглядеть так: | ||
+ | <code perl> | ||
+ | #********************************************************** | ||
+ | =head2 entity_example_main() | ||
+ | |||
+ | =cut | ||
+ | #********************************************************** | ||
+ | sub entity_example_main{ | ||
+ | # Хеш для переменных шаблона обьявляется в области видимости функции | ||
+ | my %template_args = (); | ||
+ | | ||
+ | # Флаг отображения шаблона | ||
+ | my $show_template = $FORM{add_form} || 0; | ||
+ | | ||
+ | # Здесь используется глобальный хеш %FORM, | ||
+ | # который доступен в глобальной области видимости | ||
+ | # и включает значения, полученные из GET или POST запроса. | ||
+ | if ($FORM{add}) { | ||
+ | $Example->entity_add({%FORM}); | ||
+ | $show_template = !show_result($Example, $lang{ADDED}); | ||
+ | } | ||
+ | elsif ($FORM{change}) { | ||
+ | $Example->entity_change({%FORM}); | ||
+ | show_result($Example, $lang{CHANGED}); | ||
+ | $show_template = 1; | ||
+ | } | ||
+ | elsif ($FORM{chg}) { | ||
+ | my $entity_info = $Example->entity_info($FORM{chg}); | ||
+ | if (!_error_show($Entity)) { | ||
+ | %template_args = %{$entity_info}; | ||
+ | $show_template = 1; | ||
+ | } | ||
+ | } | ||
+ | elsif ($FORM{del} && $FORM{COMMENTS}) { | ||
+ | $Example->entity_del({ ID => $FORM{del}, COMMENTS => $FORM{COMMENTS} }); | ||
+ | show_result($Example, $lang{DELETED}); | ||
+ | } | ||
+ | | ||
+ | # Использование этой точки выхода | ||
+ | # позволяет использовать эту же функцию | ||
+ | # только для выполнения операции (например AJAX запросом) | ||
+ | return 1 if $FORM{MESSAGE_ONLY}; | ||
+ | |||
+ | # Здесь собрана логика обработки данных для отображения шаблона | ||
+ | if ($show_template) { | ||
+ | # Отображение шаблона | ||
+ | $html->tpl_show( | ||
+ | _include('example_entity', 'Example'), | ||
+ | { | ||
+ | %TEMPLATE_ARGS, | ||
+ | %FORM, | ||
+ | SUBMIT_BTN_ACTION => ($FORM{chg}) ? 'change' : 'add', | ||
+ | SUBMIT_BTN_NAME => ($FORM{chg}) ? $lang{CHANGE} : $lang{ADD}, | ||
+ | } | ||
+ | ); | ||
+ | } | ||
+ | | ||
+ | # Использование этой точки выхода | ||
+ | # позволяет использовать эту же функцию | ||
+ | # для отображения шаблона изменения внутри модального окна | ||
+ | return 1 if ($FORM{TEMPLATE_ONLY}); | ||
+ | | ||
+ | # Использование библиотеки Abills::ResultFormer | ||
+ | # для получения списка из БД (метод $Example->entities_list($attr)) и построения таблицы (Abills::HTML->table($attr)) | ||
+ | my Abills::HTML $table; ($table) = result_former( | ||
+ | { | ||
+ | INPUT_DATA => $Example, | ||
+ | FUNCTION => 'entities_list', | ||
+ | BASE_FIELDS => 0, | ||
+ | DEFAULT_FIELDS => 'ID,NAME,VALUE', | ||
+ | FUNCTION_FIELDS => 'change,del', | ||
+ | SKIP_USER_TITLE => 1, | ||
+ | EXT_FIELDS => 0, | ||
+ | EXT_TITLES => { | ||
+ | id => '#', | ||
+ | name => $lang{NAME}, | ||
+ | value => $lang{VALUE}, | ||
+ | }, | ||
+ | TABLE => { | ||
+ | width => '100%', | ||
+ | caption => $lang{ENTITY}, | ||
+ | ID => 'ENTITIES_TABLE', | ||
+ | EXPORT => 1, | ||
+ | MENU => "$lang{ADD}:index=$index&add_form=1:add" | ||
+ | }, | ||
+ | MAKE_ROWS => 1, | ||
+ | SEARCH_FORMER => 1, | ||
+ | MODULE => 'Example', | ||
+ | } | ||
+ | ); | ||
+ | | ||
+ | # Таблицу нужно выводить отдельно | ||
+ | print $table->show(); | ||
+ | | ||
+ | # Сообщаем движку, что функция завершилась нормально | ||
+ | return 1; | ||
+ | } | ||
+ | </code> | ||
==== Работа с БД ==== | ==== Работа с БД ==== | ||
- | Все классы работы с БД наследуются от ''dbcore''. В таком случае в классе становятся доступны следующие методы:\\ | + | Все классы работы с БД наследуются от ''dbcore''. \\ |
+ | В таком случае в классе становятся доступны следующие методы:\\ | ||
| query($query, $type, $attr) | выполнение запроса к БД ( В основном используется для операции ''SELECT'' )| | | query($query, $type, $attr) | выполнение запроса к БД ( В основном используется для операции ''SELECT'' )| | ||
| changes($table, $data, $attr) | обёртка над query("UPDATE ..."). Сравнивает данные в таблице и изменяет только поля с обновлёнными значениями. Может добавлять в системный лог записи об изменении| | | changes($table, $data, $attr) | обёртка над query("UPDATE ..."). Сравнивает данные в таблице и изменяет только поля с обновлёнными значениями. Может добавлять в системный лог записи об изменении| | ||
Строка 35: | Строка 174: | ||
Все эти методы должны вызываться в объекте с заданными полями ''conf'', ''db'', ''admin'' (''$self->{db}'', ''$self->{conf}'', ''$self->{admin}''). \\ | Все эти методы должны вызываться в объекте с заданными полями ''conf'', ''db'', ''admin'' (''$self->{db}'', ''$self->{conf}'', ''$self->{admin}''). \\ | ||
- | Рассмотрим работу с каждым из методов детальнее. | + | Конструктор в общем случае должен реализовать как минимум этот функционал |
+ | <code perl> | ||
+ | #********************************************************** | ||
+ | =head2 new($db, $admin, \%conf) - Constructor for Example | ||
+ | |||
+ | =cut | ||
+ | #********************************************************** | ||
+ | sub new{ | ||
+ | my ($class, $db, $admin, $CONF) = @_; | ||
+ | my $self = { | ||
+ | db => $db, | ||
+ | admin => $admin, | ||
+ | conf => $CONF | ||
+ | }; | ||
+ | |||
+ | bless($self, $class); | ||
+ | return $self; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Рассмотрим работу с каждым из унаследованных методов детальнее. | ||
<accordion><panel title="**Таблица, которая используется в запросах**"><code mysql> | <accordion><panel title="**Таблица, которая используется в запросах**"><code mysql> | ||
- | CREATE TABLE `example` ( | + | CREATE TABLE `example_entity` ( |
`id` INT UNSIGNED PRIMARY AUTO_INCREMENT, | `id` INT UNSIGNED PRIMARY AUTO_INCREMENT, | ||
`name` VARCHAR(40) NOT NULL, | `name` VARCHAR(40) NOT NULL, | ||
Строка 57: | Строка 216: | ||
Рассмотрим примеры запросов и результат выполнения. | Рассмотрим примеры запросов и результат выполнения. | ||
<code perl> | <code perl> | ||
- | $self->query("SELECT * FROM example"); | + | $self->query("SELECT * FROM example_entity"); |
</code> | </code> | ||
- | Результатом выполнения будет запись в ''$self->{list}'' двумерного масива содержимого таблицы ''example''. | + | Результатом выполнения будет запись в ''$self->{list}'' двумерного масива содержимого таблицы ''example_entity''. |
<code perl> | <code perl> | ||
- | $self->query("SELECT * FROM example", undef, { COLS_NAME => 1 }) | + | $self->query("SELECT * FROM example_entity", undef, { COLS_NAME => 1 }) |
</code> | </code> | ||
Здесь в качестве $type мы указываем ''undef'' для получения данных из базы. ''$attr->{COLS_NAME} => 1'' говорит, что мы хотим получить результаты в виде масива хешей. Результатом выполнения будет запись в $self->{list} масива хешей, где ключами хеша будут названия столбцов таблицы, а значениями - соответственно значения.\\ | Здесь в качестве $type мы указываем ''undef'' для получения данных из базы. ''$attr->{COLS_NAME} => 1'' говорит, что мы хотим получить результаты в виде масива хешей. Результатом выполнения будет запись в $self->{list} масива хешей, где ключами хеша будут названия столбцов таблицы, а значениями - соответственно значения.\\ | ||
Строка 69: | Строка 228: | ||
Следующий пример удобен, когда в коде нам нужно будет сформировать простой список выбора или поисковую таблицу ключ - значение. (Например, по id строки) | Следующий пример удобен, когда в коде нам нужно будет сформировать простой список выбора или поисковую таблицу ключ - значение. (Например, по id строки) | ||
<code perl> | <code perl> | ||
- | $self->query("SELECT id,name FROM example", undef, { LIST2HASH => 'id,name' }); | + | $self->query("SELECT id,name FROM example_entity", undef, { LIST2HASH => 'id,name' }); |
</code> | </code> | ||
Результатом выполнения запроса будет запись в ''$self->{list_hash}'' хеша, где ключ ''id'' строки, а значение ''name''.\\ | Результатом выполнения запроса будет запись в ''$self->{list_hash}'' хеша, где ключ ''id'' строки, а значение ''name''.\\ | ||
Строка 75: | Строка 234: | ||
Теперь рассмотрим ключ ''COLS_UPPER''. | Теперь рассмотрим ключ ''COLS_UPPER''. | ||
<code perl> | <code perl> | ||
- | $self->query("SELECT * FROM example", undef, { COLS_NAME => 1, COLS_UPPER => 1 }) | + | $self->query("SELECT * FROM example_entity", undef, { COLS_NAME => 1, COLS_UPPER => 1 }) |
</code> | </code> | ||
Использование этого ключа связано с системой шаблонов, по утверждённому стандарту, названия столбцом таблицы указываются в __lowercase__, а переменные шаблона указываются в __UPPERCASE__. Таким образом, для передачи данных из БД в шаблон, пришлось бы вручную переназначать переменные при передаче в шаблон. Ключ ''COLS_UPPER'' дублирует ключи в хеше в в виде UPPERCASE, что позволяет передавать строки результата в шаблон без дополнительной логики. | Использование этого ключа связано с системой шаблонов, по утверждённому стандарту, названия столбцом таблицы указываются в __lowercase__, а переменные шаблона указываются в __UPPERCASE__. Таким образом, для передачи данных из БД в шаблон, пришлось бы вручную переназначать переменные при передаче в шаблон. Ключ ''COLS_UPPER'' дублирует ключи в хеше в в виде UPPERCASE, что позволяет передавать строки результата в шаблон без дополнительной логики. | ||
+ | |||
+ | ==== Создание базовой страницы ==== | ||
+ | |||
+ | Создаем базовую страницу сервиса | ||
+ | |||
+ | ''cgi-bin/hello.cgi'' | ||
+ | | ||
+ | <code> | ||
+ | #!/usr/bin/perl | ||
+ | =head1 NAME | ||
+ | |||
+ | Hello world | ||
+ | |||
+ | =cut | ||
+ | |||
+ | use strict; | ||
+ | use warnings; | ||
+ | |||
+ | # Включение нужных путей | ||
+ | BEGIN { | ||
+ | our $libpath = '../'; | ||
+ | my $sql_type = 'mysql'; | ||
+ | unshift(@INC, | ||
+ | $libpath . "Abills/$sql_type/", | ||
+ | $libpath . "Abills/modules/", | ||
+ | $libpath . '/lib/', | ||
+ | $libpath . '/Abills/', | ||
+ | $libpath | ||
+ | ); | ||
+ | } | ||
+ | |||
+ | #Модуль конфигурации | ||
+ | use Conf; | ||
+ | our ( | ||
+ | $libpath, | ||
+ | %conf, | ||
+ | %lang, | ||
+ | $base_dir, | ||
+ | ); | ||
+ | |||
+ | # конфигурационный файл | ||
+ | do "../libexec/config.pl"; | ||
+ | |||
+ | # HTML визуализация | ||
+ | use Abills::HTML; | ||
+ | my $html = Abills::HTML->new( | ||
+ | { | ||
+ | IMG_PATH => 'img/', | ||
+ | NO_PRINT => 1, | ||
+ | CONF => \%conf, | ||
+ | CHARSET => $conf{default_charset}, | ||
+ | } | ||
+ | ); | ||
+ | |||
+ | # Подключение базы | ||
+ | use Abills::SQL; | ||
+ | my $db = Abills::SQL->connect($conf{dbtype}, $conf{dbhost}, $conf{dbname}, $conf{dbuser}, $conf{dbpasswd}, { | ||
+ | CHARSET => ($conf{dbcharset}) ? $conf{dbcharset} : undef | ||
+ | }); | ||
+ | |||
+ | # Включение базовых словарей | ||
+ | if($html->{language} ne 'english') { | ||
+ | do $libpath . "/language/english.pl"; | ||
+ | } | ||
+ | |||
+ | if(-f $libpath . "/language/$html->{language}.pl") { | ||
+ | do $libpath."/language/$html->{language}.pl"; | ||
+ | } | ||
+ | |||
+ | # Подключение модуля работы с шаблонами | ||
+ | require Abills::Templates; | ||
+ | |||
+ | # Включение конфигурационного файла | ||
+ | Conf->new($db, undef, \%conf); | ||
+ | |||
+ | $html->{METATAGS} = templates('metatags_client'); | ||
+ | |||
+ | print $html->header(); | ||
+ | |||
+ | # Диалоговое окно приветсвия | ||
+ | print $html->message('info', $lang{INFO}, "Hello world\nSystem name '$conf{WEB_TITLE}'"); | ||
+ | |||
+ | 1; | ||
+ | </code> |