Equal::class, 'notEqual' => NotEqual::class, 'ilike' => Ilike::class, 'like' => Like::class, 'startWith' => StartWith::class, 'endWith' => EndWith::class, 'gt' => Gt::class, 'lt' => Lt::class, 'ngt' => Ngt::class, 'nlt' => Nlt::class, 'between' => Between::class, 'group' => Group::class, 'where' => Where::class, 'in' => In::class, 'notIn' => NotIn::class, 'date' => Date::class, 'day' => Day::class, 'month' => Month::class, 'year' => Year::class, 'hidden' => Hidden::class, 'newline' => Newline::class, ]; /** * @var Model */ protected $model; /** * @var AbstractFilter[] */ protected $filters = []; /** * Action of search form. * * @var string */ protected $action; /** * @var string */ protected $view; /** * @var string */ protected $filterID; /** * @var string */ protected $name = ''; /** * @var bool */ public $expand = false; /** * @var Collection */ protected $scopes; /** * @var Layout */ protected $layout; /** * Primary key of giving model. * * @var mixed */ protected $primaryKey; /** * @var string */ protected $style = 'padding:0'; /** * @var bool */ protected $disableResetButton = false; /** * @var string */ protected $border = 'border-top:1px solid #f4f4f4;'; /** * @var string */ protected $containerClass = ''; /** * @var bool */ protected $disableCollapse = false; /** * @var array */ protected $inputs; /** * @var string */ protected $mode = self::MODE_RIGHT_SIDE; /** * Create a new filter instance. * * @param Model $model */ public function __construct(Model $model) { $this->model = $model; $this->primaryKey = $model->getKeyName(); $this->filterID = $this->formatFilterId(); $this->initLayout(); $this->scopes = new Collection(); $this->callResolving(); } /** * Initialize filter layout. */ protected function initLayout() { $this->layout = new Filter\Layout\Layout($this); } /** * @return string */ protected function formatFilterId() { $gridName = $this->model->grid()->getName(); return 'filter-box'.($gridName ? '-'.$gridName : ''); } /** * Set action of search form. * * @param string $action * * @return $this */ public function setAction($action) { $this->action = $action; return $this; } /** * @return $this */ public function withoutInputBorder() { $this->containerClass = 'input-no-border'; return $this; } /** * @param bool $disabled * * @return $this */ public function disableCollapse(bool $disabled = true) { $this->disableCollapse = $disabled; return $this; } /** * @param bool $disabled * * @return $this */ public function disableResetButton(bool $disabled = true) { $this->disableResetButton = $disabled; return $this; } /** * Get input data. * * @param string $key * @param null $value * * @return array|mixed */ public function input($key = null, $default = null) { $inputs = $this->inputs(); if ($key === null) { return $inputs; } return Arr::get($inputs, $key, $default); } /** * Get grid model. * * @return Model */ public function model() { return $this->model; } /** * Get grid. * * @return \Dcat\Admin\Grid */ public function grid() { return $this->model->grid(); } /** * Set ID of search form. * * @param string $filterID * * @return $this */ public function setFilterID($filterID) { $this->filterID = $filterID; return $this; } /** * @param string|null $mode * * @return $this|string */ public function mode(string $mode = null) { if ($mode === null) { return $this->mode; } $this->mode = $mode; return $this; } /** * Get filter ID. * * @return string */ public function filterID() { return $this->filterID; } /** * @param string $name * * @return $this */ public function setName($name) { $this->name = $name; $this->setFilterID("{$this->name}-{$this->filterID}"); return $this; } /** * @return string */ public function getName() { return $this->name; } /** * @return $this */ public function withoutBorder() { return $this->withBorder(''); } /** * @return $this */ public function withBorder($border = null) { $this->border = is_null($border) ? 'border-top:1px solid #f4f4f4;' : $border; return $this; } /** * Remove filter by column. * * @param string|array $column */ public function removeFilter($column) { $this->filters = array_filter($this->filters, function (AbstractFilter $filter) use (&$column) { if (is_array($column)) { return ! in_array($filter->column(), $column); } return $filter->column() != $column; }); } /** * @return array */ public function inputs() { if (! is_null($this->inputs)) { return $this->inputs; } $this->inputs = Arr::dot(request()->all()); $this->inputs = array_filter($this->inputs, function ($input) { return $input !== '' && ! is_null($input); }); $this->sanitizeInputs($this->inputs); return $this->inputs; } /** * Get all conditions of the filters. * * @return array */ public function getConditions() { $inputs = $this->inputs(); if (empty($inputs)) { return []; } $params = []; foreach ($inputs as $key => $value) { Arr::set($params, $key, $value); } $conditions = []; foreach ($this->filters() as $filter) { $conditions[] = $filter->condition($params); } return tap(array_filter($conditions), function ($conditions) { if (! empty($conditions)) { $this->expand(); $this->grid()->model()->disableBindTreeQuery(); } }); } /** * @param array $inputs * * @return void */ protected function sanitizeInputs(&$inputs) { if (! $this->name) { return $inputs; } $inputs = collect($inputs)->filter(function ($input, $key) { return Str::startsWith($key, "{$this->name}_"); })->mapWithKeys(function ($val, $key) { $key = str_replace("{$this->name}_", '', $key); return [$key => $val]; })->toArray(); } /** * Add a filter to grid. * * @param AbstractFilter $filter * * @return AbstractFilter */ protected function addFilter(AbstractFilter $filter) { $this->layout->addFilter($filter); $filter->setParent($this); return $this->filters[] = $filter; } /** * Use a custom filter. * * @param AbstractFilter $filter * * @return AbstractFilter */ public function use(AbstractFilter $filter) { return $this->addFilter($filter); } /** * Get all filters. * * @return AbstractFilter[] */ public function filters() { return $this->filters; } /** * @param string $key * @param string $label * * @return Scope */ public function scope($key, $label = '') { $scope = new Scope($this, $key, $label); $this->scopes->push($scope); return $scope; } /** * @return string */ public function getScopeQueryName() { return $this->grid()->getName().'_scope_'; } /** * Get all filter scopes. * * @return Collection */ public function scopes() { return $this->scopes; } /** * Get current scope. * * @return Scope|null */ public function getCurrentScope() { $key = $this->getCurrentScopeName(); return $this->scopes->first(function ($scope) use ($key) { return $scope->key == $key; }); } /** * Get the name of current scope. * * @return string */ public function getCurrentScopeName() { return request($this->getScopeQueryName()); } /** * Get scope conditions. * * @return array */ protected function getScopeConditions() { if ($scope = $this->getCurrentScope()) { return $scope->condition(); } return []; } /** * Expand filter container. * * @param bool $value * * @return $this */ public function expand(bool $value = true) { $this->expand = $value; return $this; } /** * Execute the filter with conditions. * * @param bool $toArray * * @return array|Collection|mixed */ public function execute(bool $toArray = true) { $conditions = array_merge( $this->getConditions(), $this->getScopeConditions() ); return $this->model->addConditions($conditions)->buildData($toArray); } /** * @param string $top * @param string $right * @param string $bottom * @param string $left * * @return Filter */ public function padding($top = '15px', $right = '15px', $bottom = '5px', $left = '') { return $this->style("padding:$top $right $bottom $left"); } /** * @param string $style * * @return $this */ public function style(?string $style) { $this->style = $style; return $this; } /** * @return $this */ public function noPadding() { return $this->style('padding:0;left:-4px;'); } /** * @return $this */ public function hiddenResetButtonText() { Admin::style(".{$this->containerClass} a.reset .d-none d-sm-inline{display:none}"); return $this; } /** * Get the string contents of the filter view. * * @return \Illuminate\View\View|string */ public function render() { if (empty($this->filters)) { return ''; } $this->callComposing(); $this->view = 'admin::filter.right-side-container'; return view($this->view)->with([ 'action' => $this->action ?: $this->urlWithoutFilters(), 'layout' => $this->layout, 'filterID' => $this->disableCollapse ? '' : $this->filterID, 'expand' => $this->expand, 'style' => $this->style, 'border' => $this->border, 'containerClass' => $this->containerClass, 'disableResetButton' => $this->disableResetButton, ])->render(); } /** * Get url without filter queryString. * * @return string */ public function urlWithoutFilters() { $filters = collect($this->filters); /** @var Collection $columns */ $columns = $filters->map->column()->flatten(); $columns->push( $this->grid()->model()->getPageName() ); $groupNames = $filters->filter(function ($filter) { return $filter instanceof Group; })->map(function (AbstractFilter $filter) { return "{$filter->getId()}_group"; }); return Helper::fullUrlWithoutQuery( $columns->merge($groupNames) ); } /** * Get url without scope queryString. * * @return string */ public function urlWithoutScopes() { return Helper::fullUrlWithoutQuery($this->getScopeQueryName()); } /** * Generate a filter object and add to grid. * * @param string $method * @param array $arguments * * @return AbstractFilter|$this */ public function __call($method, $arguments) { if (! empty(static::$supports[$method])) { $class = static::$supports[$method]; if (! is_subclass_of($class, AbstractFilter::class)) { throw new \InvalidArgumentException("The class [{$class}] must be a type of ".AbstractFilter::class.'.'); } return $this->addFilter(new $class(...$arguments)); } if (isset(static::$defaultFilters[$method])) { return $this->addFilter(new static::$defaultFilters[$method](...$arguments)); } return $this; } /** * @param string $name * @param string $filterClass */ public static function extend($name, $filterClass) { static::$supports[$name] = $filterClass; } /** * @return array */ public static function extensions() { return static::$supports; } }