
Задумка PVTables
- В базе данных, создаются таблицы, которые надо редактировать. С помощью MIGX или другим способом. Или используем существующие.
- В таблице gtsAPI «Таблицы АПИ» создаем краткое описание (иструкции) нужной таблицы.
- При вызове сниппета PVTable указываем название таблицы.
- Затем спиппет подгужает js код PVTables и передает в него название таблицы. PVTables делает запрос к gtsAPI и подгужает описание таблицы, по которому строит таблицу и делает запросы на чтение и запись в gtsAPI.
Быстрый старт
Для самой простой таблицы, в «Таблицы АПИ» нужно только прописать имя таблицы (Если имя совпадает с классом MODX таблицы). Завести и выбрать пакет MODX к которому привязана таблица.
В properties прописать действия доступные для таблицы.
Можно использовать
{
"actions":{
"read":{},
"create":{},
"update":{},
"delete":{},
"subtables":{
"table":"tableName",
"where":{
"field1":1
}
},
"subtabs": {
"test": {
"DocOrderLink": {
"title": "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b",
"table": "DocOrderLink",
"where": {
"type_order_id": 1,
"order_id": "id"
}
},
"OrgsContact": {
"title": "\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u044b",
"table": "OrgsContact",
"where": {
"OrgsContactLink.org_id": "loc_org_id"
}
}
}
}
}
}
При первом запросе к таблице gtsAPI автоматически сгенерирует описания полей к таблице:
{
"actions": {
....
},
"fields": {
"id": {
"type": "view",
"class": "tSkladSmena"
},
"date": {
"type": "date",
"class": "tSkladSmena"
},
"number": {
"type": "number",
"class": "tSkladSmena"
},
"hour": {
"type": "number",
"class": "tSkladSmena"
}
}
}
Или удобнее описания сразу сгенерировать кнопкой:В properties поля можно добавить, удалить и сменить тип. Прописать «label» — название поля и «readonly»:1 — только для чтения. (Потом добавить «filtrer» с кастомным фильтром.)
Преимущества PVTables
1) Можно быстро сделать простую таблицу.
2) Описания таблиц можно, в отличии от getTables, переиспользовать на других страницах сайта или в других таблицах в качестве подтаблиц. Ну и отсюда недостаток ошибка в таблице распространиться везде где она используется.
3) В gtsAPI для таблиц написан стандартный контроллер таблиц, который уже умеет GRUD для произвольных таблиц базы. Нет необходимости для каждой таблицы писать свой контроллер.
4) Можно, в отличии от getTables, переиспользовать описания полей типа autocomplect и select. Что за поля такие ниже в пункте поля PVTables.
5) Часто вместе со стандартным GRUD в таблице еще нужно какое-то кастомное действие. В PVTables можно использовать внешние action — когда PVTables используется в компоненте Vue, сторонний action — когда в gtsAPI действия перенаправляются в сервисный файл компонента, триггеры — до и после стандартный действий можно вызвать функции из сервисного файла компонента, плагины (Думаю внедрить, но еще не определися какие нужны.). Более подробное описание допвозможностей ниже.
6) Для запросов в базу предусмотренны настройки безопасности. В «Таблицы АПИ» можно разрешить доступ только аутенфицированным пользователям, группам пользователей или пользователям у которых есть определенное разрешение MODX. Эти же разрешения можно прописать отдельно для каждого действия в properties:
"actions": {
"create": {
"authenticated":1,
"groups": "ceh_boss,Administrator",
"permitions":"list"
}
}
query в формате pdoFetchПопробовав писать запросы в mysql в формате pdoFetch, я запросы уже по другому и не пишу. gtsAPI по умолчанию формирует запрос в виде:
$default = [
'class' => $rule['class'],
'select' => [
$rule['class'] => '*',
],
'sortby' => [
"{$rule['class']}.id" => 'DESC',
],
'return' => 'data',
'limit' => 0
]
Если в таблице нужно отобразить данные из нескольких таблиц базы данных, то в properties описания таблицы нужно использовать интсрукцию «query»:
{
"loadModels": "ExcelRaschet",
"actions": {
...
},
"query": {
"class": "sraschet",
"where": {
"sraschet.last": 1
},
"select": {
"sraschet": "sraschet.id,sraschet.family_id,sraschet.manager_id,sraschet.raschet_date,sraschet.loc_org_id,sraschet.object,sraschet.price,sraschet.Profit,sraschet.comment,sraschet.status_id,sraschet.contact_date"
},
"sortby": {
"sraschet.id": "DESC"
}
}
}
Удобство pdoFetch в том, что можно на «лету» менять запрос. Например, нужно 2 почти одинаковых запроса с разными where. Написали 1 массив запроса pdoFetch, сделали запрос, затем в массиве переписываем where и делаем второй запрос.В gtsAPI инструкция «query» сливается с запросом по умолчанию через array_merge. И чтобы изменить сортировку по умолчанию достаточно написать:
{
"query": {
"sortby": {
"id": "ASC"
}
}
}
Если в «query» есть «select», то функция автогенерации полей делает описания полей по «select».Типы полей таблиц
Сейчас заведены типы полей text, textarea, boolean, date, autocomplete, select, (нужно еще html, datetime, file.)
autocomplete и select особые поля.
autocomplete позволяет выбрать вставить в таблицу (в status_id например) id записи из другой таблицы базы данных.
Для того чтобы другая таблица данных могла предоставлять autocomplete в ее описании в properties надо вставить инструкцию «autocomplete»:
{
"actions": {
...
},
"autocomplete": {
"select": [
"id",
"name"
],
"where": {
"name:LIKE": "%query%"
},
"tpl": "{$name}",
"limit": 0
}
}
Например, в настройках таблицы gsRaschet в properties задано поле
{
"fields":{
...
"org_id":{
"type":"autocomplete",
"table":"Orgs"
}
}
}
Тогда, чтоб gtsAPI могло выполнять autocomplete инструкцию
"autocomplete": {
"select": [
"id",
"name"
],
"where": {
"name:LIKE": "%query%"
},
"tpl": "{$name}",
"limit": 0
}
Надо вставить в описание таблицы Orgs в properties.select — какие поля запращивать в mysql. where — в поле автокомплект можно вводить текст и поле будет искать этот текст в базе по инструкции в where. tpl — строка отображаемая в поле в формате fenom (вообще хотелось бы чтоб vue формировал эту строку, но vue, как мне сказали, парсит на этапе компиляции компонента и на этапе работы js в vue уже нет ). limit — если в другой таблице всего 10 строк, то имеет смысл подружать их все сразу, а если там например 2000 контрагентов, то лучше поставить «limit»:20 и не загружать их все сразу.
На основе select, where, limit формируется простой запрос в базу. Если нужно, то запрос можно кастомизировать с помощью инструкции «query». Например выборка пользователей из группы 6:
{
"actions": {
"read": []
},
"autocomplete": {
"where": {
"modUserProfile.fullname:LIKE": "%query%"
},
"query": {
"class": "modUser",
"leftJoin": {
"modUserProfile": {
"class": "modUserProfile",
"on": "modUserProfile.internalKey = modUser.id"
},
"modUserGroupMember": {
"class": "modUserGroupMember",
"on": "modUserGroupMember.member = modUser.id"
}
},
"where": {
"modUserGroupMember.user_group": 6,
"modUser.active": 1
},
"select": {
"modUser": "modUser.id",
"modUserProfile": "modUserProfile.fullname"
},
"sortby": {
"fullname": "ASC"
}
},
"tpl": "{$fullname}",
"limit": 20
}
}
Еще в autocomplete можно добавить инструкцию «default_row» — в формате pdoFetch where для определения строки автокомплета по умолчанию. Вернет в ответ сервера «default»:nomer_stroki. (parent позже. Надо найти где он вообще использовался.)
Поле select выдает (почти) обычный селект. Только описания доступных опций селекта забиваются таблице «Селекты» в gtsAPI для того, чтобы их можно было переиспользовать в разных таблицах.
В «Опции в JSON» опции можно забить либо:
Мужчина,ЖенщинаЛибо:
[ [1,"Прямоуголка"], [2,"Кругляк"] ]Для select нужно прописать имя поля.
Для autocomplete можно пописать имя поля автокомплект.
Тогда функция автогенерации описаний полей автоматически по имени поля поймет, что полю нужно поставить тип autocomplect и пропишет нужную таблицу.
Также нужный тип поля select прописывается из имени поля селекта.
Допвозможности. Сторонний action
Часто вместе со стандартным GRUD в таблице еще нужно какое-то кастомное действие. Например перерасчет остатков на складе:
По задумке можно написать такой action:
{
"loadModels":"skladmake",
"actions": {
"skladmake/pereraschet_sklad": {
"groups": "ceh_boss,Administrator",
"head":true,
"icon":"pi pi-trash",
"class":"p-button-rounded p-button-danger"
}
}
}
«head»:true — вывести кнопку в топ таблицы. «row»:true — вывести кнопку в строке таблицы.gtsAPI уже поддерживает такие действия, а PVTables еще нет. В gtsAPI такие действия перенаправляются в сервисный файл компонента. В смысле вызывается фанкция handleRequest из файла "/core/components/skladmake/model/skladmake.class.php".
Допвозможности. Триггеры
В gtsAPI при каждом запросе подгружеется класс сервисного файла пакета. Например, "/core/components/$package/model/$package.class.php". Если такой файл есть. Это либо пакет к которому привязана таблица, либо имена пакетов можно задать в «loadModels».
Если в $package.class.php заданна функция regTriggers:
public function regTriggers()
{
return [
'DocOrderLink'=>[
'gtsfunction2'=>'triggerDocOrderLink',
],
];
}
то тогда когда gtsAPI выполняет какие-то стандартные действия с таблицей класса 'DocOrderLink' gtsAPI вызывает функцию 'triggerDocOrderLink' до и после выполнения этих действий.(В принципе это почти плагины MODX только мне удобней сразу писать их в коде файла компонента.)
Например, такой триггер:
public function triggerDocOrderLink(&$params)
{
if($params['type'] == 'after' and $params['method'] == 'read'){
// при запросе к таблице запрещаем чтение всем кроме администратора
if($this->modx->user->id != 1) return $this->error("Доступ только администратору");
}
if($params['type'] == 'after' and $params['method'] == 'update'){
// при изменение строки таблицы записываем в нее дату изменения
$params['object']->date_update = date('Y-m-d');
$params['object']->save();
}
return $this->success('');
}
Допвозможности. Внешний actionВнешний action используется тогда, когда при нажатии на кнопку нужно показать юзеру какой-то диалог. Тогда пишем компонент vue. Рекомендую сделать копию ExcelRaschet:
npm run copyСтавим пакет PVTables:
npm i pvtablesв src/App.vue что-то типа:
<template>
<PVTables table="sraschet"
:actions="actions"
ref="childComponentRef"
/>
<Toast/>
</template>
<script setup>
import { PVTables } from 'pvtables/pvtables'
import { ref } from 'vue';
import Toast from 'primevue/toast';
import apiCtor from 'pvtables/api'
import { useNotifications } from "pvtables/notify";
const childComponentRef = ref()
const createDocDialog = ref(false)
const updateDocDialog = ref(false)
const api = apiCtor('DocOrderLink')
const { notify } = useNotifications();
const actions = ref({
DocOrderLink:{
create:{
head:true,
icon:"pi pi-plus",
class:"p-button-rounded p-button-success",
head_click: async (event,table,filters,selectedlineItems) => {
...
}
},
update:{
row:true,
icon:"pi pi-pencil",
class:"p-button-rounded p-button-success",
click: async (data, columns,table,filters) => {
...
}
}
},
})
</script>
Более подробный пример в https://github.com/touol/ExcelRaschet. (Но и там запутано скоро будет :-()Для записи в таблицы можно использовать:
import apiCtor from 'pvtables/api'
const api = apiCtor('DocOrderLink')
try {
await api.create(DocLink.value)
} catch (error) {
notify('error', { detail: error.message });
}
Для вызова стореннего action:
const api_sraschet = apiCtor('sraschet',600000)
try {
const resp = await api_sraschet.action('ExcelRaschet/createAccountIn1c',data.value)
} catch (error) {
notify('error', { detail: error.message });
}
В описании таблицы «actions» описывает одновременно и доступные в gtsAPI действия и одновременно описывает какие действия выводить в таблице PVTables. Для того чтобы, действия не показывать, но чтоб они были доступны добавлена инструкция «hide_actions»:
{
"loadModels": "ExcelRaschet",
"actions": {
"read": []
},
"hide_actions": {
"ExcelRaschet\/createAccountIn1c": []
}
}
В если компонент vue на основе ExcelRaschet, то там делаем npm run build и в modx прописываем сниппет:
{'!mixedVue' | snippet:[
'app'=>'ExcelRaschet'
]}
подтаблицы и подтабыНу и остались подтаблицы и подтабы. Я так их называю. В терминологии PrimeVue это expandedRow c таблицей и expandedRow c табами.
Действия вида:
{
"actions":{
"subtables":{
"table":"tableName",
"where":{
"field1":1
}
},
"subtabs": {
"test": {
"DocOrderLink": {
"title": "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b",
"table": "DocOrderLink",
"where": {
"type_order_id": 1,
"order_id": "id"
}
},
"OrgsContact": {
"title": "\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u044b",
"table": "OrgsContact",
"where": {
"OrgsContactLink.org_id": "loc_org_id"
}
}
}
}
}
}
Показывают в строке таблицы кнопки такого вида:При нажатии на них под строкой таблицы показывается, для subtables, другая таблица. Для subtabs набор табов с таблицами:
Инструкция фильтрует в них строки те что связанны с основной таблицей.
"where": {
"type_order_id": 1,
"order_id": "id"
}
«type_order_id»: 1 — фильтровать только документы для расчетов.«order_id»: «id» — id строки основной таблицы. Если кликаем на кнопке в строке с id=41924, то PVTables отфильтрует строки, где order_id — ид расчета равен 41924.
Так же удобно вывести на 1 странице сразу несколько таблиц в табах:
{'!PVTabs' | snippet : [
'tabs'=>[
'DocType'=>[
'title'=>'Типы документов',
'table'=>'DocType',
],
'DocTypeOrder'=>[
'title'=>'Типы заказов',
'table'=>'DocTypeOrder',
],
]
]}
Раскраска строк таблицЧасто надо выделить строки таблиц каким-то цветом по каким-то условиям. Сейчас для этого предусмотренна 1 инструкция (в properties описания таблицы), которая присваивает строкам таблиц классы css:
"row_setting": {
"class": "{switch $status_id}{case 5}canceled{case 4}hit{case 3}outwork{case 1}work{\/switch}"
},
Инструкция снова использует fenom. Хотелось бы прокинуть из properties в js PVTables функцию, но это вроде небезопасно :-(.Ну пока все :-)
Комментарии ()