diff --git a/src/Database.php b/src/Database.php index 8a261fb..469304b 100644 --- a/src/Database.php +++ b/src/Database.php @@ -7,12 +7,14 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ namespace InitPHP\Database; +use InitPHP\Database\Utils\{Pagination, + Datatables}; use \InitPHP\Database\Exceptions\{WritableException, ReadableException, UpdatableException, @@ -83,6 +85,15 @@ public function __construct(array $credentials = []) $this->_validation = new Validation($this->_credentials['validation']['methods'], $this->_credentials['validation']['messages'], $this->_credentials['validation']['labels'], $this); } + public function __call($name, $arguments) + { + if(Helper::str_starts_with($name, 'findBy') === FALSE){ + throw new \RuntimeException('There is no "' . $name . '" method.'); + } + $this->where(Helper::camelCaseToSnakeCase(\substr($name, 6)), \current($arguments)); + return $this; + } + final public function newInstance(array $credentials = []): Database { return new self(empty($credentials) ? $this->_credentials : \array_merge($this->_credentials, $credentials)); @@ -625,6 +636,31 @@ public function all(int $limit = 100, int $offset = 0) ->read(); } + public function group(\Closure $group, string $logical = 'AND'): self + { + $logical = \str_replace(['&&', '||'], ['AND', 'OR'], \strtoupper($logical)); + if(!\in_array($logical, ['AND', 'OR'], true)){ + throw new \InvalidArgumentException('Logical operator OR, AND, && or || it could be.'); + } + + $clone = clone $this; + $clone->reset(); + + \call_user_func_array($group, [$clone]); + + $where = $clone->_whereQuery(); + if($where !== ''){ + $this->_STRUCTURE['where'][$logical][] = '(' . $where . ')'; + } + + $having = $clone->_havingQuery(); + if($having !== ''){ + $this->_STRUCTURE['having'][$logical][] = '(' . $having . ')'; + } + unset($clone); + return $this; + } + public function onlyDeleted(): self { $this->_isOnlyDeletes = true; @@ -637,17 +673,57 @@ public function onlyUndeleted(): self return $this; } + /** + * QueryBuilder resetlemeden SELECT cümlesi kurar ve satır sayısını döndürür. + * + * @return int + */ + public function count(): int + { + $select = $this->_STRUCTURE['select']; + $this->_STRUCTURE['select'][] = 'COUNT(*) AS row_count'; + $this->_deleteFieldBuild(false); + $parameters = Parameters::get(false); + $res = $this->query($this->_readQuery()); + $count = $res->toArray()['row_count'] ?? 0; + unset($res); + Parameters::merge($parameters); + $this->_STRUCTURE['select'] = $select; + return $count; + } + + public function pagination(int $page = 1, int $per_page_limit = 10, string $link = '?page={page}'): Pagination + { + $total_row = $this->count(); + $this->offset(($page - 1) * $per_page_limit) + ->limit($per_page_limit); + $res = $this->query($this->_readQuery()); + $this->reset(); + + return new Pagination($res, $page, $per_page_limit, $total_row, $link); + } + + public function datatables(array $columns, int $method = Datatables::GET_REQUEST): string + { + return (new Datatables($this, $columns, $method))->__toString(); + } + public function _readQuery(): string { if($this->getSchema() !== null){ $this->table($this->getSchema()); } - + $where = $this->_whereQuery(); + if($where !== ''){ + $where = ' WHERE ' . $where; + }else{ + $where = ' WHERE 1'; + } return 'SELECT ' . (empty($this->_STRUCTURE['select']) ? '*' : \implode(', ', $this->_STRUCTURE['select'])) . ' FROM ' . \implode(', ', $this->_STRUCTURE['table']) . (!empty($this->_STRUCTURE['join']) ? ' ' . \implode(', ', $this->_STRUCTURE['join']) : '') - . $this->_whereQuery() + . $where . $this->_havingQuery() . (!empty($this->_STRUCTURE['group_by']) ? ' GROUP BY ' . \implode(', ', $this->_STRUCTURE['group_by']) : '') . (!empty($this->_STRUCTURE['order_by']) ? ' ORDER BY ' . \implode(', ', $this->_STRUCTURE['order_by']) : '') @@ -734,11 +810,17 @@ public function _updateQuery(array $data): string if($schemaID !== null && isset($data[$schemaID])){ $this->where($schemaID, $data[$schemaID]); } + $where = $this->_whereQuery(); + if($where !== ''){ + $where = ' WHERE ' . $where; + }else{ + $where = ' WHERE 1'; + } return 'UPDATE ' . (empty($this->_STRUCTURE['table']) ? $this->getSchema() : end($this->_STRUCTURE['table'])) . ' SET ' . \implode(', ', $update) - . $this->_whereQuery() + . $where . $this->_havingQuery() . $this->_limitQuery(); } @@ -787,22 +869,33 @@ public function _updateBatchQuery(array $data, $referenceColumn): string $update[] = $syntax; } $this->in($referenceColumn, $where); - + $where = $this->_whereQuery(); + if($where !== ''){ + $where = ' WHERE ' . $where; + }else{ + $where = ' WHERE 1'; + } return 'UPDATE ' . (empty($this->_STRUCTURE['table']) ? $this->getSchema() : end($this->_STRUCTURE['table'])) . ' SET ' . \implode(', ', $update) - . $this->_whereQuery() + . $where . $this->_havingQuery() . $this->_limitQuery(); } public function _deleteQuery(): string { + $where = $this->_whereQuery(); + if($where !== ''){ + $where = ' WHERE ' . $where; + }else{ + $where = ' WHERE 1'; + } return 'DELETE FROM' . ' ' . (empty($this->_STRUCTURE['table']) ? $this->getSchema() : end($this->_STRUCTURE['table'])) - . $this->_whereQuery() + . $where . $this->_havingQuery() . $this->_limitQuery(); } @@ -840,10 +933,9 @@ private function _whereQuery(): string $isAndEmpty = empty($this->_STRUCTURE['where']['AND']); $isOrEmpty = empty($this->_STRUCTURE['where']['OR']); if($isAndEmpty && $isOrEmpty){ - return ' WHERE 1'; + return ''; } - return ' WHERE ' - . (!$isAndEmpty ? \implode(' AND ', $this->_STRUCTURE['where']['AND']) : '') + return (!$isAndEmpty ? \implode(' AND ', $this->_STRUCTURE['where']['AND']) : '') . (!$isAndEmpty && !$isOrEmpty ? ' AND ' : '') . (!$isOrEmpty ? \implode(' OR ', $this->_STRUCTURE['where']['OR']) : ''); } diff --git a/src/Entity.php b/src/Entity.php index 870b1fa..54fbfd3 100644 --- a/src/Entity.php +++ b/src/Entity.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ConnectionException.php b/src/Exceptions/ConnectionException.php index cad8cb7..964e820 100644 --- a/src/Exceptions/ConnectionException.php +++ b/src/Exceptions/ConnectionException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/DeletableException.php b/src/Exceptions/DeletableException.php index 0ebf8b5..f509327 100644 --- a/src/Exceptions/DeletableException.php +++ b/src/Exceptions/DeletableException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ModelCallbacksException.php b/src/Exceptions/ModelCallbacksException.php index 8f09ec2..e6531cc 100644 --- a/src/Exceptions/ModelCallbacksException.php +++ b/src/Exceptions/ModelCallbacksException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ModelException.php b/src/Exceptions/ModelException.php index bcb7179..aae3d79 100644 --- a/src/Exceptions/ModelException.php +++ b/src/Exceptions/ModelException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ModelRelationsException.php b/src/Exceptions/ModelRelationsException.php index 8e7711f..a886a05 100644 --- a/src/Exceptions/ModelRelationsException.php +++ b/src/Exceptions/ModelRelationsException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ReadableException.php b/src/Exceptions/ReadableException.php index 5861b44..9eeebe2 100644 --- a/src/Exceptions/ReadableException.php +++ b/src/Exceptions/ReadableException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/SQLQueryExecuteException.php b/src/Exceptions/SQLQueryExecuteException.php index 5c0fc1a..6019f64 100644 --- a/src/Exceptions/SQLQueryExecuteException.php +++ b/src/Exceptions/SQLQueryExecuteException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/UpdatableException.php b/src/Exceptions/UpdatableException.php index 3821993..29f2e8b 100644 --- a/src/Exceptions/UpdatableException.php +++ b/src/Exceptions/UpdatableException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/ValidationException.php b/src/Exceptions/ValidationException.php index 36ac06e..037c698 100644 --- a/src/Exceptions/ValidationException.php +++ b/src/Exceptions/ValidationException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Exceptions/WritableException.php b/src/Exceptions/WritableException.php index bb390f9..ccfc273 100644 --- a/src/Exceptions/WritableException.php +++ b/src/Exceptions/WritableException.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Facade/DB.php b/src/Facade/DB.php index 1c9a220..5e1dc07 100644 --- a/src/Facade/DB.php +++ b/src/Facade/DB.php @@ -7,15 +7,13 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ namespace InitPHP\Database\Facade; -use \InitPHP\Database\{Database, - Raw, - Result}; +use InitPHP\Database\{Database, Raw, Result, Utils\Pagination}; /** * @mixin Database @@ -45,6 +43,8 @@ * @method static Result get(?string $table = null) * @method static bool create(array $set) * @method static bool createBatch(array $set) + * @method static int count() + * @method static Pagination pagination(int $page = 1, int $per_page_limit = 10, string $link = '?page={page}') * @method static Result read(array $selector = [], array $conditions = [], array $parameters = []) * @method static Result readOne(array $selector = [], array $conditions = [], array $parameters = []) * @method static bool update(array $set) diff --git a/src/Helpers/Helper.php b/src/Helpers/Helper.php index c919341..1ec9589 100644 --- a/src/Helpers/Helper.php +++ b/src/Helpers/Helper.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Helpers/Parameters.php b/src/Helpers/Parameters.php index 61212ef..9d0bbc0 100644 --- a/src/Helpers/Parameters.php +++ b/src/Helpers/Parameters.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Helpers/Validation.php b/src/Helpers/Validation.php index 2ca6ec1..631fb2f 100644 --- a/src/Helpers/Validation.php +++ b/src/Helpers/Validation.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Init.php b/src/Init.php index d554b99..9b99194 100644 --- a/src/Init.php +++ b/src/Init.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ @@ -33,3 +33,5 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . 'Model.php'; require_once __DIR__ . DIRECTORY_SEPARATOR . 'Raw.php'; require_once __DIR__ . DIRECTORY_SEPARATOR . 'Result.php'; +require_once __DIR__ . DIRECTORY_SEPARATOR . 'Utils/Pagination.php'; +require_once __DIR__ . DIRECTORY_SEPARATOR . 'Utils/Datatables.php'; diff --git a/src/Model.php b/src/Model.php index 7f8d217..4cda054 100644 --- a/src/Model.php +++ b/src/Model.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 715aa45..ec00a3c 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ @@ -43,6 +43,20 @@ public function reset(): void $this->_STRUCTURE = self::STRUCTURE; } + public function importQB(array $structure): self + { + $this->_STRUCTURE = $structure; + + return $this; + } + + public function exportQB(): array + { + return $this->_STRUCTURE; + } + + + /** * @param string|Raw ...$columns * @return $this diff --git a/src/Raw.php b/src/Raw.php index 0b57fd8..60cc92d 100644 --- a/src/Raw.php +++ b/src/Raw.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Result.php b/src/Result.php index 55a34c7..20da38d 100644 --- a/src/Result.php +++ b/src/Result.php @@ -7,7 +7,7 @@ * @author Muhammet ŞAFAK * @copyright Copyright © 2022 Muhammet ŞAFAK * @license ./LICENSE MIT - * @version 2.0.5 + * @version 2.0.6 * @link https://www.muhammetsafak.com.tr */ diff --git a/src/Utils/Datatables.php b/src/Utils/Datatables.php new file mode 100644 index 0000000..1d60677 --- /dev/null +++ b/src/Utils/Datatables.php @@ -0,0 +1,185 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0.6 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Database\Utils; + +use InitPHP\Database\Database; + +final class Datatables +{ + + public const GET_REQUEST = 0; + + public const POST_REQUEST = 1; + + private Database $db; + + private array $request = []; + + private array $columns = []; + + private int $total_row = 0; + + private int $total_filtered_row = 0; + + private array $results; + + private int $draw = 0; + + public function __construct(Database $db, $columns, $method = self::GET_REQUEST) + { + $this->db = $db; + + switch ($method) { + case self::GET_REQUEST: + $this->request = $_GET ?? []; + break; + case self::POST_REQUEST: + $this->request = $_POST ?? []; + break; + } + $this->columns = $columns; + + $this->total_row = $this->db->count(); + + $this->filterQuery(); + $this->orderQuery(); + $this->total_filtered_row = $this->db->count(); + $this->limitQuery(); + $this->results = $this->db->get()->toArray(); + + if(isset($this->request['draw'])){ + $this->draw = (int)$this->request['draw']; + } + } + + public function __toString(): string + { + return \json_encode($this->getResults()); + } + + public function getResults(): array + { + return [ + 'draw' => $this->draw, + 'recordsTotal' => $this->total_row, + 'recordsFiltered' => $this->total_filtered_row, + 'data' => $this->output_prepare() + ]; + } + + + private function orderQuery() + { + $columns = $this->columns; + if(!isset($this->request['order'])){ + return; + } + $count = \count($this->request['order']); + $dtColumns = $this->pluck($columns, 'dt'); + for($i = 0; $i < $count; ++$i){ + $columnId = \intval($this->request['order'][$i]['column']); + $reqColumn = $this->request['columns'][$columnId]; + $columnId = \array_search($reqColumn['data'], $dtColumns); + $column = $columns[$columnId]; + if(($reqColumn['orderable'] ?? 'false') != 'true' || !isset($column['db'])){ + continue; + } + $dir = ($this->request['order'][$i]['dir'] ?? 'asc') === 'asc' ? 'ASC' : 'DESC'; + $this->db->orderBy($column['db'], $dir); + } + } + + private function filterQuery() + { + $columns = $this->columns; + $dtColumns = $this->pluck($columns, 'dt'); + $str = $this->request['search']['value'] ?? ''; + if($str === '' || !isset($this->request['columns'])){ + return; + } + $columnsCount = \count($this->request['columns']); + $this->db->group(function (Database $db) use ($str, $dtColumns, $columns, $columnsCount) { + for ($i = 0; $i < $columnsCount; ++$i) { + $reqColumn = $this->request['columns'][$i]; + $columnId = \array_search($reqColumn['data'], $dtColumns); + $column = $columns[$columnId]; + if(empty($column['db'])){ + continue; + } + $db->orLike($column['db'], $str); + } + }); + if(isset($this->request['columns'])){ + for ($i = 0; $i < $columnsCount; ++$i) { + $reqColumn = $this->request['columns'][$i]; + $columnId = \array_search($reqColumn['data'], $dtColumns); + $column = $columns[$columnId]; + $str = $reqColumn['search']['value'] ?? ''; + if(($reqColumn['searchable'] ?? 'false') != 'true' || $str == '' || empty($column['db'])){ + continue; + } + $this->db->like($column['db'], $str); + } + } + } + + private function limitQuery() + { + if(isset($this->request['start']) && $this->request['length'] != -1){ + $this->db->offset((int)$this->request['start']) + ->limit((int)$this->request['length']); + } + } + + private function output_prepare(): array + { + $out = []; + $columns = $this->columns; + $data = $this->results; + $dataCount = \count($data); + $columnCount = \count($columns); + + for ($i = 0; $i < $dataCount; ++$i) { + $row = []; + + for ($y = 0; $y < $columnCount; ++$y) { + $column = $columns[$y]; + if(isset($column['formatter'])){ + $row[$column['dt']] = \call_user_func_array($column['formatter'], (empty($column['db']) ? [$data[$i]] : [$data[$i][$column['db']], $data[$i]])); + }else{ + $row[$column['dt']] = !empty($column['db']) ? $data[$i][$column['db']] : ''; + } + } + + $out[] = $row; + } + + + return $out; + } + + private function pluck(array $array, string $prop): array + { + $out = []; + $len = \count($array); + for ($i = 0; $i < $len; ++$i) { + if(empty($array[$i][$prop])){ + continue; + } + $out[$i] = $array[$i][$prop]; + } + return $out; + } + +} diff --git a/src/Utils/Pagination.php b/src/Utils/Pagination.php new file mode 100644 index 0000000..a72dfa5 --- /dev/null +++ b/src/Utils/Pagination.php @@ -0,0 +1,230 @@ + + * @copyright Copyright © 2022 Muhammet ŞAFAK + * @license ./LICENSE MIT + * @version 2.0.6 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Database\Utils; + +use InitPHP\Database\Result; + +/** + * @mixin Result + */ +final class Pagination +{ + + private Result $result; + + protected int $page = 1; + protected int $perPageLimit = 10; + protected int $totalPage = 0; + protected int $totalRow = 0; + protected int $howDisplayedPage = 8; + protected string $linkTemplate = ''; + + public function __construct(Result &$res, int $page, int $perPageLimit, int $totalRow, string $linkTemplate) + { + $this->result = &$res; + + $this->page = $page; + $this->perPageLimit = $perPageLimit; + $this->totalRow = $totalRow; + $this->linkTemplate = $linkTemplate; + + $this->totalPage = \ceil(($this->totalRow / $this->perPageLimit)); + } + + public function __call($name, $arguments) + { + return $this->getResult()->{$name}(...$arguments); + } + + public function getResult(): Result + { + return $this->result; + } + + public function getPage(): int + { + return $this->page; + } + + public function getTotalPage(): int + { + return $this->totalPage; + } + + public function getTotalRow(): int + { + return $this->totalRow; + } + + public function setDisplayedPage(int $displayedPage): self + { + $this->howDisplayedPage = ($displayedPage % 2 !== 0) ? $displayedPage + 1 : $displayedPage; + + return $this; + } + + public function getDisplayedPage(): int + { + return $this->howDisplayedPage; + } + + public function setBaseLink($link): self + { + $this->linkTemplate = $link; + + return $this; + } + + public function getPages(): array + { + $pages = []; + $beforeAfter = $this->howDisplayedPage / 2; + $beforeLimit = ($beforeAfter > $this->page) ? ($beforeAfter - ($beforeAfter - $this->page)) : $beforeAfter; + $afterLimit = $this->howDisplayedPage - $beforeLimit; + + for ($i = ($this->page - 1); $i >= ($this->page - $beforeLimit); --$i) { + $pages[] = [ + 'url' => $this->_linkGenerator($i), + 'page' => $i, + 'active' => false, + ]; + } + $pages = \array_reverse($pages, false); + + $pages[] = [ + 'url' => $this->_linkGenerator($this->page), + 'page' => $this->page, + 'active' => true, + ]; + + for ($i = ($this->page + 1); $i <= ($this->page + $afterLimit); ++$i) { + if($i > $this->totalPage){ + break; + } + $pages[] = [ + 'url' => $this->_linkGenerator($i), + 'page' => $i, + 'active' => false, + ]; + } + + return $pages; + } + + public function nextPage(): ?array + { + if($this->page < $this->totalPage && $this->totalPage > 1){ + $page = $this->page + 1; + return [ + 'url' => $this->_linkGenerator($page), + 'page' => $page, + ]; + } + return null; + } + + public function prevPage(): ?array + { + if($this->page > 1){ + $page = $this->page - 1; + return [ + 'url' => $this->_linkGenerator($page), + 'page' => $page, + ]; + } + return null; + } + + public function toHTML(array $attrs = []): string + { + $pages = $this->getPages(); + $prev = $this->prevPage(); + $next = $this->nextPage(); + $li_attr_prepare = ''; + $li_a_attr_prepare = ''; + $li_attr_prepare_active = ' class="active"'; + if(isset($attrs['li'])){ + $li_attr_prepare = $this->_attrGenerator($attrs['li']); + $li_a_attr_prepare = $this->_attrGenerator(($attrs['li']['a'] ?? [])); + if(isset($attrs['li']['class'])){ + $attrs['li']['class'] .= ' active'; + }else{ + $attrs['li']['class'] = 'active'; + } + $li_attr_prepare_active = $this->_attrGenerator($attrs['li']); + } + + $html = '_attrGenerator(($attrs['nav'] ?? [])) + . '>_attrGenerator(($attrs['ul'] ?? [])) + . '>'; + if($prev !== null){ + $html .= '' . ($attrs['prev_label'] ?? '«') . ''; + } + + foreach ($pages as $page) { + $html .= '' + . $page['page'] + . ''; + } + + if($next !== null){ + $html .= '' . ($attrs['next_label'] ?? '»') . ''; + } + + $html .= ''; + return $html; + } + + + private function _attrGenerator(array $assoc): string + { + if($assoc === []){ + return ''; + } + $res = ''; + foreach ($assoc as $name => $value) { + if(\is_array($value)){ + continue; + } + $res .= ' ' . $name . '="' . $value . '"'; + } + return $res; + } + + + private function _linkGenerator($page) + { + if(empty($this->linkTemplate)){ + return $page; + } + return \strtr($this->linkTemplate, [ + '{page}' => $page, + ]); + } + +} diff --git a/tests/QueryBuilderUnitTest.php b/tests/QueryBuilderUnitTest.php index 47f4f99..929dcd0 100644 --- a/tests/QueryBuilderUnitTest.php +++ b/tests/QueryBuilderUnitTest.php @@ -5,6 +5,7 @@ use InitPHP\Database\Database; use InitPHP\Database\Helpers\Parameters; +use InitPHP\Database\QueryBuilder; class QueryBuilderUnitTest extends \PHPUnit\Framework\TestCase { @@ -368,4 +369,35 @@ public function testTableAliasSQLStatementBuild() $this->db->reset(); } + public function testWhereGroupStatement() + { + Parameters::reset(); + $this->db->select('id, title, content, url') + ->from('posts') + ->where('status', 1) + ->group(function (Database $db) { + $db->where('user_id', 1) + ->where('datetime', date("Y-m-d"), '>='); + }, 'or') + ->group(function (Database $db) { + $db->group(function (Database $db) { + $db->where('id', 1) + ->where('status', 0); + }, 'or') + ->group(function (Database $db) { + $db->where('id', 2) + ->where('status', 1); + }, 'or'); + }, 'or'); + + + + $expected = 'SELECT id, title, content, url FROM posts WHERE status = :status AND (user_id = :user_id AND datetime >= :datetime) OR ((id = :id AND status = :status_1) OR (id = :id_1 AND status = :status_2))'; + + $this->assertEquals($expected, $this->db->_readQuery()); + $this->db->reset(); + + Parameters::reset(); + } + }