454 lines
15 KiB
PHP
454 lines
15 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace zin;
|
|
|
|
class sqlBuilder extends wg
|
|
{
|
|
protected static array $defineProps = array(
|
|
'class?: string',
|
|
'steps?: array',
|
|
'requiredSteps?: array=["table"]',
|
|
'data?: object',
|
|
'tableList?: array',
|
|
'url?: string',
|
|
'onUpdate?: function',
|
|
'afterUpdate?: function'
|
|
);
|
|
|
|
protected static array $tablesDesc = array();
|
|
|
|
public static function getPageCSS(): ?string
|
|
{
|
|
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
|
}
|
|
|
|
public static function getPageJS(): ?string
|
|
{
|
|
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
|
}
|
|
|
|
protected function setSteps()
|
|
{
|
|
global $lang;
|
|
list($steps) = $this->prop(array('steps'));
|
|
|
|
if(empty($steps))
|
|
{
|
|
$stepList = $lang->bi->builderStepList;
|
|
if(empty($steps)) $this->setProp('steps', array_keys($stepList));
|
|
}
|
|
}
|
|
|
|
protected function buildStepBar()
|
|
{
|
|
global $lang;
|
|
list($steps, $requires, $builder) = $this->prop(array('steps', 'requiredSteps', 'data'));
|
|
|
|
$stepList = $lang->bi->builderStepList;
|
|
$lastStep = end($steps);
|
|
$selected = $builder->step;
|
|
$items = array();
|
|
|
|
$selectedClass = 'text-primary ring-secondary font-bold selected';
|
|
$defaultClass = 'text-gray-900 ring-opacity-0 font-medium';
|
|
|
|
foreach($steps as $step)
|
|
{
|
|
if(!isset($stepList[$step])) continue;
|
|
|
|
$key = $step;
|
|
$text = $stepList[$key];
|
|
|
|
$isSelected = $selected == $key;
|
|
$required = in_array($key, $requires);
|
|
|
|
$classList = array();
|
|
$classList[$selectedClass] = $isSelected;
|
|
$classList[$defaultClass] = !$isSelected;
|
|
$classList['required'] = $required;
|
|
|
|
$items[] = btn
|
|
(
|
|
setClass('builder-step-btn relative text-md mx-2 bg-inherit ring', $classList),
|
|
set('data-step', $key),
|
|
set::type('default'),
|
|
$text,
|
|
on::click()->do("switchBuilderStep(event)")
|
|
);
|
|
|
|
if($key != $lastStep) $items[] = icon
|
|
(
|
|
setClass('self-center text-gray-500 text-lg leading-3'),
|
|
'angle-down'
|
|
);
|
|
}
|
|
|
|
return div
|
|
(
|
|
setClass('builder-step-bar flex col justify-evenly basis-40 gap-1 bg-primary-50 h-full'),
|
|
$items
|
|
);
|
|
}
|
|
|
|
protected function buildJoins()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$joins = $builder->joins;
|
|
if(empty($joins)) return null;
|
|
|
|
$selectTableList = $builder->getSelectTables($tableList);
|
|
|
|
$items = array();
|
|
foreach($joins as $index => $join)
|
|
{
|
|
$leftTable = $join['table'];
|
|
$alias = $join['alias'];
|
|
$on = $join['on'];
|
|
|
|
list($columnA, $columnB) = array($on[0], $on[3]);
|
|
|
|
$columnAItems = $builder->getTableDescList($columnA);
|
|
$columnBItems = $builder->getTableDescList($columnB);
|
|
|
|
$items[] = formRow
|
|
(
|
|
setClass('gap-x-4 mt-4'),
|
|
sqlBuilderPicker
|
|
(
|
|
set::name("left_$alias"),
|
|
set::label($lang->bi->leftTable),
|
|
set::items($tableList),
|
|
set::value($leftTable),
|
|
set::placeholder($lang->bi->selectTableTip),
|
|
set::suffix($alias),
|
|
set::width('50'),
|
|
set::labelWidth('60px'),
|
|
set::onChange('changeBuilderTable(event)'),
|
|
set::error($builder->hasError('join', 'table', $alias))
|
|
),
|
|
joinCondition
|
|
(
|
|
set::index($index),
|
|
set::name("join_$alias"),
|
|
set::values($on),
|
|
set::tables($selectTableList),
|
|
set::fieldAList($columnAItems),
|
|
set::fieldBList($columnBItems),
|
|
set::onChange('changeBuilderTable(event)'),
|
|
set::onAdd('addJoinTable(event)'),
|
|
set::onRemove('removeJoinTable(event)'),
|
|
set::columnAError($builder->hasError('join', 'columnA', $alias)),
|
|
set::fieldAError($builder->hasError('join', 'fieldA', $alias)),
|
|
set::fieldBError($builder->hasError('join', 'fieldB', $alias))
|
|
)
|
|
);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
protected function buildTableStep()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$from = $builder->from;
|
|
|
|
return array
|
|
(
|
|
formRow
|
|
(
|
|
sqlBuilderPicker
|
|
(
|
|
set::name("from"),
|
|
set::label($lang->bi->fromTable),
|
|
set::items($tableList),
|
|
set::value($from['table']),
|
|
set::placeholder($lang->bi->selectTableTip),
|
|
set::suffix($from['alias']),
|
|
set::width('50'),
|
|
set::labelWidth('60px'),
|
|
set::onChange('changeBuilderTable(event)'),
|
|
set::error($builder->hasError('from', 'table', $from['alias']))
|
|
),
|
|
div
|
|
(
|
|
setClass('flex justify-start items-center'),
|
|
btn
|
|
(
|
|
setID('addJoinTable'),
|
|
setClass('ml-2 text-primary'),
|
|
set('data-index', -1),
|
|
set::type('ghost'),
|
|
set::icon('plus'),
|
|
$lang->bi->leftTable,
|
|
on::click()->do('addJoinTable(event)')
|
|
),
|
|
sqlBuilderHelpIcon(set::text($lang->bi->leftTableTip))
|
|
)
|
|
),
|
|
$this->buildJoins()
|
|
);
|
|
}
|
|
|
|
protected function buildFieldStep()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$from = $builder->from;
|
|
$joins = $builder->joins;
|
|
|
|
$tables = array_merge(array($from), $joins);
|
|
$panels = array();
|
|
foreach($tables as $table)
|
|
{
|
|
$name = \zget($tableList, $table['table']);
|
|
$alias = $table['alias'];
|
|
$fields = $builder->getTableDescList($alias);
|
|
$select = $table['select'];
|
|
|
|
$panels[] = fieldSelectPanel
|
|
(
|
|
set::table($name),
|
|
set::alias($alias),
|
|
set::fields($fields),
|
|
set::values($select),
|
|
set::col(count($tables)),
|
|
set::onChange('handleSelectFieldChange(event)'),
|
|
set::onSelectAll('checkAllField(event)')
|
|
);
|
|
}
|
|
return div
|
|
(
|
|
setClass('flex row gap-4 h-70'),
|
|
$panels
|
|
);
|
|
}
|
|
|
|
protected function buildFuncStep()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$funcs = $builder->getFuncs('func');
|
|
if(empty($funcs)) return sqlBuilderEmptyContent
|
|
(
|
|
set::btnClass('add-function'),
|
|
set::btnText($lang->bi->addFunc),
|
|
set::emptyText($lang->bi->emptyFuncs),
|
|
set::onClick('addFunction(event)')
|
|
);
|
|
|
|
$selectTableList = $builder->getSelectTables($tableList);
|
|
$items = array();
|
|
foreach($funcs as $index => $func)
|
|
{
|
|
$fieldList = $builder->getTableDescList($func['table']);
|
|
if(!isset($fieldList[$func['field']])) $func['field'] = '';
|
|
$items[] = sqlBuilderFuncRow
|
|
(
|
|
set::index($index),
|
|
set::tables($selectTableList),
|
|
set::fields($fieldList),
|
|
set::value($func),
|
|
set::onChange('changeBuilderFunc(event)'),
|
|
set::onAdd('addFunction(event)'),
|
|
set::onRemove('removeFunction(event)'),
|
|
set::tableError($builder->hasError('func', 'table', $index)),
|
|
set::fieldError($builder->hasError('func', 'field', $index)),
|
|
set::functionError($builder->hasError('func', 'function', $index)),
|
|
set::aliasError($builder->hasError('func', 'alias', $index)),
|
|
set::duplicateError($builder->hasError('func', 'duplicate', $index))
|
|
);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
protected function buildWhereStep()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$wheres = $builder->wheres;
|
|
if(empty($wheres)) return sqlBuilderEmptyContent
|
|
(
|
|
set::btnClass('add-where'),
|
|
set::btnText($lang->bi->addWhere),
|
|
set::emptyText($lang->bi->emptyWheres),
|
|
set::onClick('addWhereGroup(event)')
|
|
);
|
|
|
|
$selectTableList = $builder->getSelectTables($tableList);
|
|
$groups = array();
|
|
foreach($wheres as $index => $group)
|
|
{
|
|
$items = $group['items'];
|
|
$operator = $group['operator'];
|
|
$isLast = $index == count($wheres) - 1;
|
|
|
|
$groupItems = array();
|
|
foreach($items as $itemIndex => $item)
|
|
{
|
|
$fieldList = $builder->getTableDescList($item[0]);
|
|
$groupItems[] = sqlBuilderWhereItem
|
|
(
|
|
set::first($itemIndex === 0),
|
|
set::index("{$index}_{$itemIndex}"),
|
|
set::tables($selectTableList),
|
|
set::fields($fieldList),
|
|
set::value($item),
|
|
set::onChange('changeBuilderWhere(event)'),
|
|
set::onAdd('addWhereItem(event)'),
|
|
set::onRemove('removeWhereItem(event)'),
|
|
set::tableError($builder->hasError('where', "{$index}_{$itemIndex}_0")),
|
|
set::fieldError($builder->hasError('where', "{$index}_{$itemIndex}_1")),
|
|
set::valueError($builder->hasError('where', "{$index}_{$itemIndex}_4")),
|
|
);
|
|
}
|
|
|
|
$groups[] = sqlBuilderWhereGroup
|
|
(
|
|
set::index($index),
|
|
set::operator($operator),
|
|
set::last($isLast),
|
|
set::onChange('changeBuilderWhere(event)'),
|
|
set::onAdd('addWhereGroup(event)'),
|
|
set::onRemove('removeWhereGroup(event)'),
|
|
$groupItems
|
|
);
|
|
}
|
|
|
|
return $groups;
|
|
}
|
|
|
|
protected function buildQueryStep()
|
|
{
|
|
global $lang;
|
|
list($builder, $tableList) = $this->prop(array('data', 'tableList'));
|
|
$querys = $builder->querys;
|
|
if(empty($querys)) return sqlBuilderEmptyContent
|
|
(
|
|
set::btnClass('add-query'),
|
|
set::btnText($lang->bi->addQuery),
|
|
set::emptyText($lang->bi->emptyQuerys),
|
|
set::onClick('addBuilderQueryFilter(event)')
|
|
);
|
|
|
|
$selectTableList = $builder->getSelectTables($tableList);
|
|
$fields = array();
|
|
foreach($querys as $query)
|
|
{
|
|
if(!empty($query['table'])) $fields[$query['table']] = $builder->getTableDescList($query['table']);
|
|
}
|
|
return sqlBuilderQueryFilter
|
|
(
|
|
set::querys($querys),
|
|
set::tables($selectTableList),
|
|
set::fields($fields),
|
|
set::defaultItems($builder->queryFilterSelectOptions),
|
|
set::onChange('changeBuilderQuery(event)'),
|
|
set::onAdd('addBuilderQueryFilter(event)'),
|
|
set::onRemove('removeBuilderQueryFilter(event)'),
|
|
set::error($builder->error)
|
|
);
|
|
}
|
|
|
|
protected function buildGroupStep()
|
|
{
|
|
global $lang;
|
|
list($builder) = $this->prop(array('data'));
|
|
$groups = $builder->groups;
|
|
$aggs = $builder->getFuncs('agg');
|
|
if(empty($groups)) return sqlBuilderEmptyContent
|
|
(
|
|
set::btnClass('hidden'),
|
|
set::emptyText($lang->bi->emptyGroups)
|
|
);
|
|
return sqlBuilderGroupBy
|
|
(
|
|
set::groups($groups),
|
|
set::aggs($aggs),
|
|
set::onChangeAgg('changeAgg(event)'),
|
|
set::onChangeType('switchGroupFieldType(event)'),
|
|
set::onSort('sortGroupBy(event)')
|
|
);
|
|
}
|
|
|
|
protected function buildStepContent()
|
|
{
|
|
global $lang;
|
|
list($builder) = $this->prop(array('data'));
|
|
$step = $builder->step;
|
|
$groups = $builder->groups;
|
|
|
|
$ucStep = ucfirst($step);
|
|
$contentTitle = $lang->bi->{"step{$ucStep}Title"};
|
|
$contentTitleTip = $lang->bi->{"step{$ucStep}Tip"};
|
|
$contentFuncName = "build{$ucStep}Step";
|
|
return panel
|
|
(
|
|
setID("builder$step"),
|
|
setClass('w-full builder-content'),
|
|
set::title($contentTitle),
|
|
set::headingClass('justify-start gap-0'),
|
|
set::bodyClass('h-80 overflow-auto'),
|
|
to::heading
|
|
(
|
|
sqlBuilderHelpIcon
|
|
(
|
|
set::text($contentTitleTip)
|
|
),
|
|
formGroup
|
|
(
|
|
setClass('items-center-important', array('hidden' => $step != 'group')),
|
|
set::label($lang->bi->enable),
|
|
set::labelClass('p-0'),
|
|
set::labelWidth('36px'),
|
|
switcher
|
|
(
|
|
setID('useGroup'),
|
|
set::name('useGroup'),
|
|
set::checked($groups === false ? false : true),
|
|
on::change()->do('changeGroupBy(event)')
|
|
)
|
|
),
|
|
span
|
|
(
|
|
setClass('text-danger', array('hidden' => !isset($builder->error['select_field']))),
|
|
$lang->bi->emptySelect
|
|
)
|
|
),
|
|
$this->$contentFuncName()
|
|
);
|
|
}
|
|
|
|
protected function build()
|
|
{
|
|
global $lang;
|
|
$this->setSteps();
|
|
|
|
list($class, $data, $url, $onUpdate, $afterUpdate) = $this->prop(array('class', 'data', 'url', 'onUpdate', 'afterUpdate'));
|
|
|
|
return panel
|
|
(
|
|
setID('builderPanel'),
|
|
set('data-url', $url),
|
|
set('data-onupdate', $onUpdate),
|
|
setClass('h-96', $class),
|
|
set::bodyClass('flex h-96'),
|
|
div
|
|
(
|
|
setID('sqlBuilder'),
|
|
setClass('hidden'),
|
|
set('data-sqlbuilder', $data),
|
|
set('data-afterupdate', $afterUpdate),
|
|
div
|
|
(
|
|
on::init()->do($afterUpdate)
|
|
)
|
|
),
|
|
$this->buildStepBar(),
|
|
$this->buildStepContent()
|
|
);
|
|
}
|
|
}
|