облегчить работу c crud

archcoffe

Новичок
Каждый раз когда появляется необходимость создать новую таблицу для crud, мне приходиться описывать сущности в модели которая наследуется от моего небольшого core. Образовалось большое желание эти сущности описывать не в редакторе, а непосредственно путем формы на экране. Хочется подойти к этому максимально осознанно. Думаю что это уже реализовано, и возможно стоит в лучшем случае перейти и использовать, но так вышло что я не осведомлен :( help..
 

archcoffe

Новичок
автор FCG Tools, так он много чего туда интересного и неинтересного внес - много чего вообще не понял, зачем, что..
я наверно продолжу борьбу с синтаксическими ошибками и подожду лучших времен, просто перепишу чуть удобней и баста =)
 

archcoffe

Новичок
не все же, сделаю сам. Все это время думал, в итоге придумал так:
что бы было проще ориентироваться, под каждый тип поля (input,select,textarea....) - отдельную таблицу где pid и pname таблицы таблиц". Потом собираю все в кучу, создаю таблицы, генерирую модели, контролеры, отображения, обновляю меню. Не уверен что это пригодиться кому то кроме меня, но работает, мне пока что нравится) Раньше 20 минут уходило, теперь 5-10))
 

~WR~

Новичок
Мне кажется, это слегка утопия.

Если админка только для разработчиков, то проще пользоваться любым GUI клиентом для вашей СУБД.
Если админка для юзеров\менджеров, то почти всегда таблица должна иметь хоть чуть-чуть custom-кода. А если есть такой код, то его уже не настроишь в форме.
 

archcoffe

Новичок
Мне кажется, это слегка утопия.
Да слегка. Так почти вдвое увеличивает время на то что бы добавлять новые возможности.. - зазеркалье. Тем больше когда в одиночку заниматься. Но, нравится =)
Если админка для юзеров\ менеджеров
Простенькое, элементарное сродни визитка, может не намного больше - имхо вытянет на ура, для них, правда без "конструктора", он им не пригодиться.
 

Absinthe

жожо
Если админка только для разработчиков, то проще пользоваться любым GUI клиентом для вашей СУБД.
А какой гуй-клиент умеет нормально редактировать пользователя, к которому может быть привязано несколько групп через m2m табличку?

ИМХО такое не должно генерироваться в виде кода, а создаваться на лету. Active Admin.
 

Gas

может по одной?
я так поинмаю, в symfony 2 подобным образом админка сделана.

сам такими вещами не пользовался, больше такими как gii и rails generate scaffold
в Active Admin немного напрягает что нужно учить по сути дополнительный язык (описания админок), который бывает достаточно многословным и как-то пугает "а вдруг я захочу сдеть эдак, но буду ограничен этим инструментом"
 

kkn1960

Новичок
Мне кажется, это слегка утопия.

Если админка только для разработчиков, то проще пользоваться любым GUI клиентом для вашей СУБД.
Если админка для юзеров\менджеров, то почти всегда таблица должна иметь хоть чуть-чуть custom-кода. А если есть такой код, то его уже не настроишь в форме.
С помощью второй версии были созданы админки для нескольких сайтов и именно для юзеров\менеджеров.

Важной частью инструмента является специальное приложение FGS Configurator, предоставляющий веб интерфейс для ввода, вывода и поиска в 26 таблицах. При этом пришлось написать небольшие расширения для вывода значений полей в виде ссылок, три расширения контроллеров из-за специфического ввода спецификаций компонент и три расширения компонента грида для добавления группы записей.

Я хочу подчеркнуть - для стандартного ввода, вывода и поиска записей. Нестандартные ввод, вывод и поиск записей и обработку данных, естественно, нужно кодировать.

В качестве иллюстрации могу привести такие факты:

1) в конфигураторе можно редактировать данные грида в режиме inline edit
2) в конфигураторе используется зависимые select
3) валидация вводимых данных включает проверку значений нескольких полей ввода

И вот все это без кодирования - только путем ввода необходимых атрибутов компонент

Для примера приведу полностью код скрипта list.php, отвечающий за обработку запросов для зависимые select

PHP:
<?php
/*
request script for chained selects
*/
header('Content-type: text/html; charset=utf-8');
include('application.inc');
mqn();
if (!isset($_GET['rowid']))
{
    die();
}
$rowid = $_GET['rowid'];
$file  = ELEMENT_PATH . $rowid . ".php";
if (!file_exists($file))
{
    die();
}
include $file;
//check acces
if (isset($e[$rowid]['acl']) and !$accessHandler->testAccess($e[$rowid]['acl']))
{
    die();
}

foreach ($_GET as $k => $v)
{
    $registry->setGlobal($k, $v);
}
$class = getComponentClass($e[$rowid]['component']);
$obj   = new $class('ajax', 'form', $e[$rowid]);
$buf   = FormKit::render($obj);
print $buf;
Также ввод в поля типа autocomplete не нужно кодировать, а просто ввести необходимые атрибуты.
Для обработки Ajax запросов для autocomplete и inline edit есть универсальные такие же маленькие скрипты:

PHP:
<?php
/*
request script for autocomplete input fields
*/
include('application.inc');
mqn();
if (!isset($_REQUEST['rowid']))
{
    //die();
}
$rowid = $_REQUEST['rowid'];

$file = ELEMENT_PATH . $rowid . ".php";
if (!file_exists($file))
{
    die();
}
include $file;
//check acces
if (isset($e[$rowid]['acl']) and !$accessHandler->testAccess($e[$rowid]['acl']))
{
    die();
}

$class = getComponentClass($e[$rowid]['component']);
$obj   = new $class('ajax', 'form', $e[$rowid]);
$ll    = ListLoader::getInstance();
foreach ($_REQUEST as $k => $v)
{
    $s = $v;
    if ($k == 'term')
    {
        $registry->setGlobal($obj->register, $s);
        $obj->list['lookup_filter'] = $obj->lookupFilter($s);
    }
    else
    {
        $registry->setGlobal($k, $s);
    }
}
$options = $ll->get($obj->list);
if (sizeof($options) > $obj->max_options)
{
    echo '';
}
else
{
    $a = array();
    foreach ($options as $id => $descr)
    {
        $a[] = array(
            "label" => $descr
        );
    }
    $response = json_encode($a);
    echo $response;
}
PHP:
<?php
/*
request script for inline editing
*/
require('system.inc');
if (isset($_REQUEST['fgsw']) and isset($_REQUEST['fgsu']) and isset($_REQUEST['fgsc']) and isset($_REQUEST['fgst']) and (isset($_SESSION[FGS_SESSION_KEY][$_REQUEST['fgsw']][FGS_CONTROLLER_KEY]['controller']) and $_SESSION[FGS_SESSION_KEY][$_REQUEST['fgsw']][FGS_CONTROLLER_KEY]['controller'] == $_REQUEST['fgsc']))
{
    if (isset($_REQUEST['primary_key']) and isset($_REQUEST['field']) and isset($_REQUEST['irow']))
    {
        $pk  = $_SESSION[FGS_SESSION_KEY][$_REQUEST['fgsw']][$_REQUEST['fgsu']][$_REQUEST['fgst']]['apk'][$_REQUEST['irow']];
        $out = array();
        foreach ($pk as $v)
        {
            $out[] = $v;
        }
        if (implode('_', $out) == $_REQUEST['primary_key'])
        {
            $rowid = $_REQUEST['rowid'];
            $file  = COLUMN_PATH . $rowid . ".php";
            if (!file_exists($file))
            {
                die();
            }
            include $file;
            //check acces
            if (isset($column[$rowid]['acl']) and !$accessHandler->testAccess($column[$rowid]['acl']))
            {
                echo 1;
                exit;
            }

            $class = getComponentClass($column[$rowid]['component']);
            $obj   = new $class('ajax', 'grid', $column[$rowid]);
            $obj->ApplyRequestValue('post');
            $obj->validate();
            $table = $column[$rowid]['table'];
            $field = $column[$rowid]['field'];
            foreach ($obj->err as $err)
            {
                debug::write($err['id'], $err['text']);
            }
            if ($field == $_REQUEST['field'] and sizeof($obj->err) == 0)
            {
                $fv         = array();
                $fv[$field] = $db->code($obj->value, $obj->type);
                $result     = $db->update($table, $fv, $pk);
                if ($result['error'] == false and isset($obj->save))
                {
                    if (isset($obj->alias))
                    {
                        $key = $obj->alias;
                    }
                    else
                    {
                        $key = $obj->field;
                    }
                    $_SESSION[FGS_SESSION_KEY][$_REQUEST['fgsw']][$_REQUEST['fgsu']][$_REQUEST['fgst']]['adk'][$_REQUEST['irow']][$key] = $obj->value;
                }
            }
        }
        echo 1;
    }
}
Уважаемый ~WR~!
Если вы приведете примеры этого
чуть-чуть custom-кода
то я постараюсь либо объяснить как это можно выполнить без кодирования либо выполнить с минимумом кодирования
 

Фанат

oncle terrible
Команда форума
Если вы приведете примеры этого то я постараюсь либо объяснить как это можно выполнить без кодирования либо выполнить с минимумом кодирования
Вот тебе пример:
При этом пришлось написать небольшие расширения для вывода значений полей в виде ссылок, три расширения контроллеров из-за специфического ввода спецификаций компонент и три расширения компонента грида для добавления группы записей.
А по поводу стандартного вывода -
Я хочу подчеркнуть, что на практике всегда выясняется, что никакого "стандартного" вывода не существует. Всегда требуются нестандартные подходы.
И в итоге вместо нормальных контроллеров, отвечающих за определённую страницу, мы имеем зоопарк "расширений" единственного Стандартного Контроллера, делающий поддержку приложения адом.
 

kkn1960

Новичок
До веб программирования я работал на C++ Bulder и не помню, чтобы кто-то утверждал, что C++ Bulder не подходит для создания интерфейсов к таблицам баз данных, для чего вообще - то и предназначен FGS Tools .Хочу также напомнить, что C++ Bulder делает ввод и вывод запиисей таблиц именно в виде стандартных форм и гридов и никого это не смущает.

К тому же, по моему мнению, админки в частности и CRUD приложения в целом, именно подразумевают стандартный вид для ввода, вывода или поиска записей.

А теперь по поводу расширений для вывода ссылок.

Для вывода ссылок предназначен специальный компонент с атрибутам href и target. Однако мне нужно было, чтобы при нажатии на ссылку устанавливался фильтр на записи, выводимые в гриде. Поэтому мне требовалось передавать условия фильтрации. Согласитесь, что вряд ли это можно назвать стандартным выводом.
 

Gas

может по одной?
kkn1960
например, хочется вывести grid c книгами, в одной из ячеек показать обложку книги (вызвать допустим $book->getCover()) в другой список прикреплённых файлов со ссылками для download'а (например, HelperMedia::makeLinks($book->getMedia())) - из GUI интерфейса то такое врят-ли получится сделать
 

kkn1960

Новичок
В данный момент для реализации описанного вывода придется кодировать.
Далее просто опишу, как бы я это сделал на вашем месте
1)показать обложку книги (вызвать допустим $book->getCover())

Предположим:
а)при вводе данных по книге файл обложки книги загружается в определенный каталог, а таблице книг хранится только название файла обложки в поле book_cover_file, а путь к файлу в поле book_cover_path
б) Для колонки вывода обложки книги в качестве компонента вывода выберу ColumnText и в качестве атрибута renderer(Визуализатор) введу "image". Установлю алиасы полей: "image_file" для book_cover_file и "image_path" для book_cover_path
Поскольку поле book_cover_path выводить не нужно, для него установлю признак "hidden"(Скрытый?) в 1.
в) Если будет лень писать расширение класса, то в классе GridKit просто добавлю вывод значения поля в виде рисунка:
PHP:
case 'image':
    $src = $row['image_path'].$row['image_file'];
    $s   = "<img src=\"$src\">";
    break;
если хочется потрудиться, то создам класс ColumnImage:
PHP:
<?php
class ColumnImage
{
	static function render($c, $nRow = null, $row = null)
	{
		$src = $row['image_path'].$row['image_file'];
        return  "<img src=\"$src\">";
	}
}
?>
а в качестве атрибута renderer(Визуализатор) введу "ColumnImage"

Если у вас путь к файлам обложки книг хранится в переменной, то еще проще - просто вместо $row['image_path'] подставляете имя этой переменной. Ну и нужно объявить эту переменную глобальной перед использованием.

Ну а я на своем месте сделаю так: добавлю в инструмент компонент вывода ColumnImage, создам спецификацию формы для ввода атрибутов этого компонента и добавлю в таблицу fgs_export экспортируемые атрибуты данного компонента.
Далее буду просто указывать в качестве компонента вывода ColumnImage для такого вывода.

Кстати, вы подали мне прекрасную идею сделать такой вывод стандартным, и я непременно сделаю это в версии 3.1. Так что за идею большое спасибо.

2)в другой список прикреплённых файлов со ссылками для download'а

Здесь все зависит от того, где хранится список прикреплённых файлов. Если хранится в отдельной таблице, то придется кодировать как вывод, так и получение списка. Если же хранится в отдельном поле таблицы книг, то можно поступить, как и первом выводе - просто написать код вывода
 

~WR~

Новичок
Проходил эту стадию.
Беда в том, что очень скоро всех этих case'ов и плагинов станет очень много. И постоянно будут дописываться все новые и новые.

Например, с теми же картинками.
1. Захотели добавить title.
2. Захотели добавить width\height, но чтобы они пересчитывались динамически и не выходили за границу ячейки.
3. Захотели добавить border для облегчения модерирования картинок. Красный для мальчиков, зеленый для девочек, фиолетовый для всех остальных.
4. Захотели добавить fancybox, причем такой, чтобы сразу формировал галерею из картинок одного и того же пользователя.
5. ...
100. ...
9999. ...

Для каждого случая будет свой класс? Или case?
 

~WR~

Новичок
В общем, сам большой кровью пришел к такому варианту:

Делаем небольшой абстрактный класс, который реализует только базовую функциональность: показ грида, постраничный вывод, поиск, сортировка, простейший crud. От него наследуем классы конкретных гридов.

Если грид на 100% стандартный, то указываем только имена колонок и SQL запрос или таблицу. Всё с ходу работает.
Если есть что-то нестандартное, то перегружаем соответствующие функции и пишем это прямо в коде.

Например, если нужно сохранять дополнительный параметр при Insert'е, то перегружаем функцию вставки.
Если нужно как-нибудь хитро форматировать картинку, то перегружаем parseRow и явно это кодим.

Самый главный профит от этого подхода наступает в тот момент, когда у вас становится 100+ гридов.
Во-первых, правки в одном гриде не вызывают изменений в других, как это обязательно будет происходить с плагинами.
Во-вторых, когда вам нужно поправить чужой грид, то не надо думать о том, какой код где лежит. Ибо весь custom code 100% лежит в классе грида и больше нигде.
 

kkn1960

Новичок
Проходил эту стадию.
Беда в том, что очень скоро всех этих case'ов и плагинов станет очень много. И постоянно будут дописываться все новые и новые.

Например, с теми же картинками.
1. Захотели добавить title.
2. Захотели добавить width\height, но чтобы они пересчитывались динамически и не выходили за границу ячейки.
3. Захотели добавить border для облегчения модерирования картинок. Красный для мальчиков, зеленый для девочек, фиолетовый для всех остальных.
4. Захотели добавить fancybox, причем такой, чтобы сразу формировал галерею из картинок одного и того же пользователя.
5. ...
100. ...
9999. ...

Для каждого случая будет свой класс? Или case?
Для начала хочу озвучить очевидный факт: стандарты HTML не предусматривают бесконечного числа атрибутов для html- элементов.

Сегодня я начал добавлять компоненты ColumnImage для вывода картинок в гриде и ColumnImageLink для вывода картинок в виде линка. Поскольку я не помню наизусть все атрибуты HTML-элементов,то я просто поискал какие-же атрибуты есть у элементов "img" и "a" .

Их оказалось не очень много, в частности те, которые вы упоминаете. Причем некоторые из них просто несущественны. Я посчитал существенными следующие атрибуты "alt", "title", "width", "height", "href", "src" + путь к каталогу картинок для ColumnImageLink. Если окажется, что нужны border или другие атрибуты, то добавлю и другие.

Благодаря ограниченности числа атрибутов никакого бесконечного числа не будет - будет только один со всеми атрибутами, которые предусматривает стандарт HTML для этого элемента. Разработчик просто введет нужные ему атрибуты и фреймворк сформирует элемент только с этими атрибутами.

Теперь по поводу динамического пересчета width и height.

Для оценки выражений во время исполнения скрипта у меня есть специальный класс Evaluator. Видимо для динамического пересчета width и height придется написать функцию или класс. Так вот, если нужно будет пересчитывать динамически width и height, то я вместо констант введу указание на использование этой функции или класса. Во время исполнения скрипта, атрибуты width и height будут передаваться в Evaluator для оценки. Если там константы, то вернутся константы, если будет функция, то вернется результат от функции.
 
Сверху