v1.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. namespace zin;
  3. class sqlBuilder extends wg
  4. {
  5. /**
  6. * @var mixed[]
  7. */
  8. protected static $defineProps = array(
  9. 'class?: string',
  10. 'steps?: array',
  11. 'requiredSteps?: array=["table"]',
  12. 'data?: object',
  13. 'tableList?: array',
  14. 'url?: string',
  15. 'onUpdate?: function',
  16. 'afterUpdate?: function'
  17. );
  18. /**
  19. * @var mixed[]
  20. */
  21. protected static $tablesDesc = array();
  22. public static function getPageCSS()
  23. {
  24. return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
  25. }
  26. public static function getPageJS()
  27. {
  28. return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
  29. }
  30. protected function setSteps()
  31. {
  32. global $lang;
  33. list($steps) = $this->prop(array('steps'));
  34. if(empty($steps))
  35. {
  36. $stepList = $lang->bi->builderStepList;
  37. if(empty($steps)) $this->setProp('steps', array_keys($stepList));
  38. }
  39. }
  40. protected function buildStepBar()
  41. {
  42. global $lang;
  43. list($steps, $requires, $builder) = $this->prop(array('steps', 'requiredSteps', 'data'));
  44. $stepList = $lang->bi->builderStepList;
  45. $lastStep = end($steps);
  46. $selected = $builder->step;
  47. $items = array();
  48. $selectedClass = 'text-primary ring-secondary font-bold selected';
  49. $defaultClass = 'text-gray-900 ring-opacity-0 font-medium';
  50. foreach($steps as $step)
  51. {
  52. if(!isset($stepList[$step])) continue;
  53. $key = $step;
  54. $text = $stepList[$key];
  55. $isSelected = $selected == $key;
  56. $required = in_array($key, $requires);
  57. $classList = array();
  58. $classList[$selectedClass] = $isSelected;
  59. $classList[$defaultClass] = !$isSelected;
  60. $classList['required'] = $required;
  61. $items[] = btn
  62. (
  63. setClass('builder-step-btn relative text-md mx-2 bg-inherit ring', $classList),
  64. set('data-step', $key),
  65. set::type('default'),
  66. $text,
  67. on::click()->do("switchBuilderStep(event)")
  68. );
  69. if($key != $lastStep) $items[] = icon
  70. (
  71. setClass('self-center text-gray-500 text-lg leading-3'),
  72. 'angle-down'
  73. );
  74. }
  75. return div
  76. (
  77. setClass('builder-step-bar flex col justify-evenly basis-40 gap-1 bg-primary-50 h-full'),
  78. $items
  79. );
  80. }
  81. protected function buildJoins()
  82. {
  83. global $lang;
  84. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  85. $joins = $builder->joins;
  86. if(empty($joins)) return null;
  87. $selectTableList = $builder->getSelectTables($tableList);
  88. $items = array();
  89. foreach($joins as $index => $join)
  90. {
  91. $leftTable = $join['table'];
  92. $alias = $join['alias'];
  93. $on = $join['on'];
  94. list($columnA, $columnB) = array($on[0], $on[3]);
  95. $columnAItems = $builder->getTableDescList($columnA);
  96. $columnBItems = $builder->getTableDescList($columnB);
  97. $items[] = formRow
  98. (
  99. setClass('gap-x-4 mt-4'),
  100. sqlBuilderPicker
  101. (
  102. set::name("left_$alias"),
  103. set::label($lang->bi->leftTable),
  104. set::items($tableList),
  105. set::value($leftTable),
  106. set::placeholder($lang->bi->selectTableTip),
  107. set::suffix($alias),
  108. set::width('50'),
  109. set::labelWidth('60px'),
  110. set::onChange('changeBuilderTable(event)'),
  111. set::error($builder->hasError('join', 'table', $alias))
  112. ),
  113. joinCondition
  114. (
  115. set::index($index),
  116. set::name("join_$alias"),
  117. set::values($on),
  118. set::tables($selectTableList),
  119. set::fieldAList($columnAItems),
  120. set::fieldBList($columnBItems),
  121. set::onChange('changeBuilderTable(event)'),
  122. set::onAdd('addJoinTable(event)'),
  123. set::onRemove('removeJoinTable(event)'),
  124. set::columnAError($builder->hasError('join', 'columnA', $alias)),
  125. set::fieldAError($builder->hasError('join', 'fieldA', $alias)),
  126. set::fieldBError($builder->hasError('join', 'fieldB', $alias))
  127. )
  128. );
  129. }
  130. return $items;
  131. }
  132. protected function buildTableStep()
  133. {
  134. global $lang;
  135. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  136. $from = $builder->from;
  137. return array
  138. (
  139. formRow
  140. (
  141. sqlBuilderPicker
  142. (
  143. set::name("from"),
  144. set::label($lang->bi->fromTable),
  145. set::items($tableList),
  146. set::value($from['table']),
  147. set::placeholder($lang->bi->selectTableTip),
  148. set::suffix($from['alias']),
  149. set::width('50'),
  150. set::labelWidth('60px'),
  151. set::onChange('changeBuilderTable(event)'),
  152. set::error($builder->hasError('from', 'table', $from['alias']))
  153. ),
  154. div
  155. (
  156. setClass('flex justify-start items-center'),
  157. btn
  158. (
  159. setID('addJoinTable'),
  160. setClass('ml-2 text-primary'),
  161. set('data-index', -1),
  162. set::type('ghost'),
  163. set::icon('plus'),
  164. $lang->bi->leftTable,
  165. on::click()->do('addJoinTable(event)')
  166. ),
  167. sqlBuilderHelpIcon(set::text($lang->bi->leftTableTip))
  168. )
  169. ),
  170. $this->buildJoins()
  171. );
  172. }
  173. protected function buildFieldStep()
  174. {
  175. global $lang;
  176. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  177. $from = $builder->from;
  178. $joins = $builder->joins;
  179. $tables = array_merge(array($from), $joins);
  180. $panels = array();
  181. foreach($tables as $table)
  182. {
  183. $name = \zget($tableList, $table['table']);
  184. $alias = $table['alias'];
  185. $fields = $builder->getTableDescList($alias);
  186. $select = $table['select'];
  187. $panels[] = fieldSelectPanel
  188. (
  189. set::table($name),
  190. set::alias($alias),
  191. set::fields($fields),
  192. set::values($select),
  193. set::col(count($tables)),
  194. set::onChange('handleSelectFieldChange(event)'),
  195. set::onSelectAll('checkAllField(event)')
  196. );
  197. }
  198. return div
  199. (
  200. setClass('flex row gap-4 h-74'),
  201. $panels
  202. );
  203. }
  204. protected function buildFuncStep()
  205. {
  206. global $lang;
  207. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  208. $funcs = $builder->getFuncs('func');
  209. if(empty($funcs)) return sqlBuilderEmptyContent
  210. (
  211. set::btnClass('add-function'),
  212. set::btnText($lang->bi->addFunc),
  213. set::emptyText($lang->bi->emptyFuncs),
  214. set::onClick('addFunction(event)')
  215. );
  216. $selectTableList = $builder->getSelectTables($tableList);
  217. $items = array();
  218. foreach($funcs as $index => $func)
  219. {
  220. $fieldList = $builder->getTableDescList($func['table']);
  221. if(!isset($fieldList[$func['field']])) $func['field'] = '';
  222. $items[] = sqlBuilderFuncRow
  223. (
  224. set::index($index),
  225. set::tables($selectTableList),
  226. set::fields($fieldList),
  227. set::value($func),
  228. set::onChange('changeBuilderFunc(event)'),
  229. set::onAdd('addFunction(event)'),
  230. set::onRemove('removeFunction(event)'),
  231. set::tableError($builder->hasError('func', 'table', $index)),
  232. set::fieldError($builder->hasError('func', 'field', $index)),
  233. set::functionError($builder->hasError('func', 'function', $index)),
  234. set::aliasError($builder->hasError('func', 'alias', $index)),
  235. set::duplicateError($builder->hasError('func', 'duplicate', $index))
  236. );
  237. }
  238. return $items;
  239. }
  240. protected function buildWhereStep()
  241. {
  242. global $lang;
  243. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  244. $wheres = $builder->wheres;
  245. if(empty($wheres)) return sqlBuilderEmptyContent
  246. (
  247. set::btnClass('add-where'),
  248. set::btnText($lang->bi->addWhere),
  249. set::emptyText($lang->bi->emptyWheres),
  250. set::onClick('addWhereGroup(event)')
  251. );
  252. $selectTableList = $builder->getSelectTables($tableList);
  253. $groups = array();
  254. foreach($wheres as $index => $group)
  255. {
  256. $items = $group['items'];
  257. $operator = $group['operator'];
  258. $isLast = $index == count($wheres) - 1;
  259. $groupItems = array();
  260. foreach($items as $itemIndex => $item)
  261. {
  262. $fieldList = $builder->getTableDescList($item[0]);
  263. $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")));
  264. }
  265. $groups[] = sqlBuilderWhereGroup
  266. (
  267. set::index($index),
  268. set::operator($operator),
  269. set::last($isLast),
  270. set::onChange('changeBuilderWhere(event)'),
  271. set::onAdd('addWhereGroup(event)'),
  272. set::onRemove('removeWhereGroup(event)'),
  273. $groupItems
  274. );
  275. }
  276. return $groups;
  277. }
  278. protected function buildQueryStep()
  279. {
  280. global $lang;
  281. list($builder, $tableList) = $this->prop(array('data', 'tableList'));
  282. $querys = $builder->querys;
  283. if(empty($querys)) return sqlBuilderEmptyContent
  284. (
  285. set::btnClass('add-query'),
  286. set::btnText($lang->bi->addQuery),
  287. set::emptyText($lang->bi->emptyQuerys),
  288. set::onClick('addBuilderQueryFilter(event)')
  289. );
  290. $selectTableList = $builder->getSelectTables($tableList);
  291. $fields = array();
  292. foreach($querys as $query)
  293. {
  294. if(!empty($query['table'])) $fields[$query['table']] = $builder->getTableDescList($query['table']);
  295. }
  296. return sqlBuilderQueryFilter
  297. (
  298. set::querys($querys),
  299. set::tables($selectTableList),
  300. set::fields($fields),
  301. set::defaultItems($builder->queryFilterSelectOptions),
  302. set::onChange('changeBuilderQuery(event)'),
  303. set::onAdd('addBuilderQueryFilter(event)'),
  304. set::onRemove('removeBuilderQueryFilter(event)'),
  305. set::error($builder->error)
  306. );
  307. }
  308. protected function buildGroupStep()
  309. {
  310. global $lang;
  311. list($builder) = $this->prop(array('data'));
  312. $groups = $builder->groups;
  313. $aggs = $builder->getFuncs('agg');
  314. if(empty($groups)) return sqlBuilderEmptyContent
  315. (
  316. set::btnClass('hidden'),
  317. set::emptyText($lang->bi->emptyGroups)
  318. );
  319. return sqlBuilderGroupBy
  320. (
  321. set::groups($groups),
  322. set::aggs($aggs),
  323. set::onChangeAgg('changeAgg(event)'),
  324. set::onChangeType('switchGroupFieldType(event)'),
  325. set::onSort('sortGroupBy(event)')
  326. );
  327. }
  328. protected function buildStepContent()
  329. {
  330. global $lang;
  331. list($builder) = $this->prop(array('data'));
  332. $step = $builder->step;
  333. $groups = $builder->groups;
  334. $ucStep = ucfirst($step);
  335. $contentTitle = $lang->bi->{"step{$ucStep}Title"};
  336. $contentTitleTip = $lang->bi->{"step{$ucStep}Tip"};
  337. $contentFuncName = "build{$ucStep}Step";
  338. return panel
  339. (
  340. setID("builder$step"),
  341. setClass('w-full builder-content'),
  342. set::title($contentTitle),
  343. set::headingClass('justify-start gap-0'),
  344. set::bodyClass('h-80 overflow-auto'),
  345. to::heading
  346. (
  347. sqlBuilderHelpIcon
  348. (
  349. set::text($contentTitleTip)
  350. ),
  351. formGroup
  352. (
  353. setClass('items-center-important', array('hidden' => $step != 'group')),
  354. set::label($lang->bi->enable),
  355. set::labelClass('p-0'),
  356. set::labelWidth('36px'),
  357. switcher
  358. (
  359. setID('useGroup'),
  360. set::name('useGroup'),
  361. set::checked($groups === false ? false : true),
  362. on::change()->do('changeGroupBy(event)')
  363. )
  364. ),
  365. span
  366. (
  367. setClass('text-danger', array('hidden' => !isset($builder->error['select_field']))),
  368. $lang->bi->emptySelect
  369. )
  370. ),
  371. $this->$contentFuncName()
  372. );
  373. }
  374. protected function build()
  375. {
  376. global $lang;
  377. $this->setSteps();
  378. list($class, $data, $url, $onUpdate, $afterUpdate) = $this->prop(array('class', 'data', 'url', 'onUpdate', 'afterUpdate'));
  379. return panel
  380. (
  381. setID('builderPanel'),
  382. set('data-url', $url),
  383. set('data-onupdate', $onUpdate),
  384. setClass('h-96', $class),
  385. set::bodyClass('flex h-96'),
  386. div
  387. (
  388. setID('sqlBuilder'),
  389. setClass('hidden'),
  390. set('data-sqlbuilder', $data),
  391. set('data-afterupdate', $afterUpdate),
  392. div
  393. (
  394. on::init()->do($afterUpdate)
  395. )
  396. ),
  397. $this->buildStepBar(),
  398. $this->buildStepContent()
  399. );
  400. }
  401. }