true, 'filter' => true, 'actions' => true, 'quick_edit_button' => false, 'edit_button' => true, 'view_button' => true, 'delete_button' => true, 'row_selector' => true, 'create_button' => true, 'bordered' => false, 'table_collapse' => true, 'toolbar' => true, 'create_mode' => self::CREATE_MODE_DEFAULT, 'dialog_form_area' => ['700px', '670px'], 'table_class' => ['table', 'custom-data-table', 'data-table'], 'scrollbar_x' => false, 'actions_class' => null, 'batch_actions_class' => null, 'paginator_class' => null, ]; /** * @var \Illuminate\Http\Request */ protected $request; /** * @var bool */ protected $show = true; /** * @var bool */ protected $async = false; /** * Create a new grid instance. * * Grid constructor. * * @param Repository|\Illuminate\Database\Eloquent\Model|Builder|null $repository * @param null|\Closure $builder */ public function __construct($repository = null, ?\Closure $builder = null, $request = null) { $this->model = new Model(request(), $repository); $this->columns = new Collection(); $this->allColumns = new Collection(); $this->rows = new Collection(); $this->builder = $builder; $this->request = $request ?: request(); $this->resourcePath = url($this->request->getPathInfo()); if ($repository = $this->model->repository()) { $this->setKeyName($repository->getKeyName()); } $this->model->setGrid($this); $this->setUpTools(); $this->setUpFilter(); $this->callResolving(); } /** * Get table ID. * * @return string */ public function getTableId() { return $this->tableId; } /** * Set primary key name. * * @param string|array $name * @return $this */ public function setKeyName($name) { $this->keyName = $name; return $this; } /** * Get or set primary key name. * * @return string|array */ public function getKeyName() { return $this->keyName ?: 'id'; } /** * Add column to Grid. * * @param string $name * @param string $label * @return Column */ public function column($name, $label = '') { return $this->addColumn($name, $label); } /** * Add number column. * * @param null|string $label * @return Column */ public function number(?string $label = null) { return $this->addColumn('#', $label ?: '#'); } /** * 启用异步渲染功能. * * @param bool $async * @return $this */ public function async(bool $async = true) { $this->async = $async; if ($async) { $this->view('admin::grid.async-table'); } return $this; } public function getAsync() { return $this->async; } /** * 判断是否允许查询数据. * * @return bool */ public function buildable() { return ! $this->async || $this->isAsyncRequest(); } /** * @return bool */ public function isAsyncRequest() { return $this->request->get(static::ASYNC_NAME); } /** * Batch add column to grid. * * @example * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]); * 2.$grid->columns('name', 'email' ...) * * @param array $columns * @return Collection|Column[]|void */ public function columns($columns = null) { if ($columns === null) { return $this->columns; } if (func_num_args() == 1 && is_array($columns)) { foreach ($columns as $column => $label) { $this->column($column, $label); } return; } foreach (func_get_args() as $column) { $this->column($column); } } /** * @return Collection|Column[] */ public function allColumns() { return $this->allColumns; } /** * 删除列. * * @param string|Column $column * @return $this */ public function dropColumn($column) { if ($column instanceof Column) { $column = $column->getName(); } $this->columns->offsetUnset($column); $this->allColumns->offsetUnset($column); return $this; } /** * Add column to grid. * * @param string $field * @param string $label * @return Column */ protected function addColumn($field = '', $label = '') { $column = $this->newColumn($field, $label); $this->columns->put($field, $column); $this->allColumns->put($field, $column); return $column; } /** * @param string $field * @param string $label * @return Column */ public function prependColumn($field = '', $label = '') { $column = $this->newColumn($field, $label); $this->columns->prepend($column, $field); $this->allColumns->prepend($column, $field); return $column; } /** * @param string $field * @param string $label * @return Column */ public function newColumn($field = '', $label = '') { $column = new Column($field, $label); $column->setGrid($this); return $column; } /** * Get Grid model. * * @return Model */ public function model() { return $this->model; } /** * @return array */ public function getColumnNames() { return $this->columnNames; } /** * Apply column filter to grid query. */ protected function applyColumnFilter() { $this->columns->each->bindFilterQuery($this->model()); } /** * @param string|array $class * @return $this */ public function addTableClass($class) { $this->options['table_class'] = array_merge((array) $this->options['table_class'], (array) $class); return $this; } public function formatTableClass() { if ($this->options['bordered']) { $this->addTableClass(['table-bordered', 'complex-headers', 'data-table']); } return implode(' ', array_unique((array) $this->options['table_class'])); } /** * Build the grid. * * @return void */ public function build() { if (! $this->buildable()) { $this->callBuilder(); $this->handleExportRequest(); $this->prependRowSelectorColumn(); $this->appendActionsColumn(); $this->sortHeaders(); return; } if ($this->built) { return; } $collection = clone $this->processFilter(); $this->prependRowSelectorColumn(); $this->appendActionsColumn(); Column::setOriginalGridModels($collection); $this->columns->map(function (Column $column) use (&$collection) { $column->fill($collection); $this->columnNames[] = $column->getName(); }); $this->buildRows($collection); $this->sortHeaders(); } /** * @return void */ public function callBuilder() { if ($this->builder && ! $this->built) { call_user_func($this->builder, $this); } $this->built = true; } /** * Build the grid rows. * * @param Collection $data * @return void */ protected function buildRows($data) { $this->rows = $data->map(function ($row) { return new Row($this, $row); }); foreach ($this->rowsCallbacks as $callback) { $callback($this->rows); } } /** * Set grid row callback function. * * @return Collection|$this */ public function rows(\Closure $callback = null) { if ($callback) { $this->rowsCallbacks[] = $callback; return $this; } return $this->rows; } /** * Get create url. * * @return string */ public function getCreateUrl() { return $this->urlWithConstraints($this->resource().'/create'); } /** * @param string $key * @return string */ public function getEditUrl($key) { return $this->urlWithConstraints("{$this->resource()}/{$key}/edit"); } /** * @param string $url * @return string */ public function urlWithConstraints(?string $url) { $queryString = ''; if ($constraints = $this->model()->getConstraints()) { $queryString = http_build_query($constraints); } return $url.($queryString ? ('?'.$queryString) : ''); } /** * @param \Closure $closure * @return Grid\Tools\RowSelector */ public function rowSelector() { return $this->rowSelector ?: ($this->rowSelector = new Grid\Tools\RowSelector($this)); } /** * Prepend checkbox column for grid. * * @return void */ protected function prependRowSelectorColumn() { if (! $this->options['row_selector']) { return; } $rowSelector = $this->rowSelector(); $keyName = $this->getKeyName(); $this->prependColumn( Grid\Column::SELECT_COLUMN_NAME )->setLabel($rowSelector->renderHeader())->display(function () use ($rowSelector, $keyName) { return $rowSelector->renderColumn($this, $this->{$keyName}); }); } /** * @param string $width * @param string $height * @return $this */ public function setDialogFormDimensions(string $width, string $height) { $this->options['dialog_form_area'] = [$width, $height]; return $this; } /** * Render create button for grid. * * @return string */ public function renderCreateButton() { if (! $this->options['create_button']) { return ''; } return (new Tools\CreateButton($this))->render(); } /** * @param bool $value * @return $this */ public function withBorder(bool $value = true) { $this->options['bordered'] = $value; return $this; } /** * @param bool $value * @return $this */ public function tableCollapse(bool $value = true) { $this->options['table_collapse'] = $value; return $this; } /** * 显示横轴滚动条. * * @param bool $value * @return $this */ public function scrollbar(bool $value = true) { $this->options['table_scrollbar'] = $value; return $this; } /** * Set grid header. * * @param Closure|string|Renderable $content * @return $this */ public function header($content) { $this->header[] = $content; return $this; } /** * Render grid header. * * @return string */ public function renderHeader() { if (! $this->header) { return ''; } return <<{$this->renderHeaderOrFooter($this->header)} HTML; } protected function renderHeaderOrFooter($callbacks) { $target = [$this->processFilter(), $this]; $content = []; foreach ($callbacks as $callback) { $content[] = Helper::render($callback, $target); } if (empty($content)) { return ''; } return implode('
', $content); } /** * Set grid footer. * * @param Closure|string|Renderable $content * @return $this */ public function footer($content) { $this->footer[] = $content; return $this; } /** * Render grid footer. * * @return string */ public function renderFooter() { if (! $this->footer) { return ''; } return <<{$this->renderHeaderOrFooter($this->footer)} HTML; } /** * Get or set option for grid. * * @param string|array $key * @param mixed $value * @return $this|mixed */ public function option($key, $value = null) { if (is_null($value)) { return $this->options[$key] ?? null; } if (is_array($key)) { $this->options = array_merge($this->options, $key); } else { $this->options[$key] = $value; } return $this; } protected function setUpOptions() { if ($this->options['bordered']) { $this->tableCollapse(false); } } /** * Disable row selector. * * @return $this */ public function disableRowSelector(bool $disable = true) { $this->tools->disableBatchActions($disable); return $this->option('row_selector', ! $disable); } /** * Show row selector. * * @return $this */ public function showRowSelector(bool $val = true) { return $this->disableRowSelector(! $val); } /** * Remove create button on grid. * * @return $this */ public function disableCreateButton(bool $disable = true) { return $this->option('create_button', ! $disable); } /** * Show create button. * * @return $this */ public function showCreateButton(bool $val = true) { return $this->disableCreateButton(! $val); } /** * If allow creation. * * @return bool */ public function allowCreateButton() { return $this->options['create_button']; } /** * @param string $mode * @return $this */ public function createMode(string $mode) { return $this->option('create_mode', $mode); } /** * @return $this */ public function enableDialogCreate() { return $this->createMode(self::CREATE_MODE_DIALOG); } /** * Get or set resource path. * * @return string */ public function resource() { return $this->resourcePath; } /** * Create a grid instance. * * @param mixed ...$params * @return $this */ public static function make(...$params) { return new static(...$params); } /** * @param Closure $closure * @return $this; */ public function wrap(\Closure $closure) { $this->wrapper = $closure; return $this; } /** * @return bool */ public function hasWrapper() { return $this->wrapper ? true : false; } /** * Add variables to grid view. * * @param array $variables * @return $this */ public function with(array $variables) { return $this->addVariables($variables); } /** * Get all variables will used in grid view. * * @return array */ protected function defaultVariables() { return [ 'grid' => $this, 'tableId' => $this->getTableId(), ]; } /** * Set a view to render. * * @param string $view * @return $this */ public function view($view) { $this->view = $view; return $this; } /** * Set grid title. * * @param string $title * @return $this */ public function title($title) { $this->variables['title'] = $title; return $this; } /** * Set grid description. * * @param string $description * @return $this */ public function description($description) { $this->variables['description'] = $description; return $this; } /** * Set resource path for grid. * * @param string $path * @return $this */ public function setResource($path) { $this->resourcePath = admin_url($path); return $this; } /** * 设置是否显示. * * @param bool $value * @return $this */ public function show(bool $value = true) { $this->show = $value; return $this; } /** * 是否显示横向滚动条. * * @param bool $value * @return $this */ public function scrollbarX(bool $value = true) { $this->options['scrollbar_x'] = $value; return $this; } /** * @return string */ public function formatTableParentClass() { $tableCollaps = $this->option('table_collapse') ? 'table-collapse' : ''; $scrollbarX = $this->option('scrollbar_x') ? 'table-scrollbar-x' : ''; return "table-responsive table-wrapper complex-container table-middle mt-1 {$tableCollaps} {$scrollbarX}"; } /** * Get the string contents of the grid view. * * @return string */ public function render() { $this->callComposing(); $this->build(); $this->applyFixColumns(); $this->setUpOptions(); $this->addFilterScript(); $this->addScript(); return $this->doWrap(); } public function getView() { if ($this->async && $this->hasFixColumns()) { return 'admin::grid.async-fixed-table'; } return $this->view; } protected function addScript() { if ($this->async && ! $this->isAsyncRequest()) { $query = static::ASYNC_NAME; $url = Helper::fullUrlWithoutQuery(['_pjax']); $url = Helper::urlWithQuery($url, [static::ASYNC_NAME => 1]); $options = [ 'selector' => ".async-{$this->getTableId()}", 'queryName' => $query, 'url' => $url, ]; if ($this->hasFixColumns()) { $options['loadingStyle'] = 'height:140px;'; } $options = json_encode($options); Admin::script( <<show) { return; } $view = view($this->getView(), $this->variables()); if (! $wrapper = $this->wrapper) { return $view->render(); } return Helper::render($wrapper($view)); } /** * Add column to grid. * * @param string $name * @return Column */ public function __get($name) { return $this->addColumn($name); } /** * Dynamically add columns to the grid view. * * @param $method * @param $arguments * @return Column */ public function __call($method, $arguments) { if (static::hasMacro($method)) { return $this->macroCall($method, $arguments); } return $this->addColumn($method, $arguments[0] ?? null); } public function __toString() { return (string) $this->render(); } }