| null> */ public array $tableColumnSearches = []; /** * @var ?string */ public $tableSearch = ''; public function updatedTableSearch(): void { if ($this->getTable()->persistsSearchInSession()) { session()->put( $this->getTableSearchSessionKey(), $this->tableSearch, ); } if ($this->getTable()->shouldDeselectAllRecordsWhenFiltered()) { $this->deselectAllTableRecords(); } $this->resetPage(); } /** * @param string | null $value */ public function updatedTableColumnSearches($value = null, ?string $key = null): void { if (blank($value) && filled($key)) { Arr::forget($this->tableColumnSearches, $key); } if ($this->getTable()->persistsColumnSearchesInSession()) { session()->put( $this->getTableColumnSearchesSessionKey(), $this->tableColumnSearches, ); } if ($this->getTable()->shouldDeselectAllRecordsWhenFiltered()) { $this->deselectAllTableRecords(); } $this->resetPage(); } protected function applySearchToTableQuery(Builder $query): Builder { $this->applyColumnSearchesToTableQuery($query); $this->applyGlobalSearchToTableQuery($query); return $query; } protected function applyColumnSearchesToTableQuery(Builder $query): Builder { foreach ($this->getTableColumnSearches() as $column => $search) { if (blank($search)) { continue; } $column = $this->getTable()->getColumn($column); if (! $column) { continue; } if ($column->isHidden()) { continue; } if (! $column->isIndividuallySearchable()) { continue; } foreach ($this->extractTableSearchWords($search) as $searchWord) { $query->where(function (Builder $query) use ($column, $searchWord) { $isFirst = true; $column->applySearchConstraint( $query, $searchWord, $isFirst, ); }); } } return $query; } /** * @return array */ protected function extractTableSearchWords(string $search): array { return array_filter( str_getcsv(preg_replace('/\s+/', ' ', $search), separator: ' ', escape: '\\'), fn ($word): bool => filled($word), ); } protected function applyGlobalSearchToTableQuery(Builder $query): Builder { $search = $this->getTableSearch(); if (blank($search)) { return $query; } foreach ($this->extractTableSearchWords($search) as $searchWord) { $query->where(function (Builder $query) use ($searchWord) { $isFirst = true; foreach ($this->getTable()->getColumns() as $column) { if ($column->isHidden()) { continue; } if (! $column->isGloballySearchable()) { continue; } $column->applySearchConstraint( $query, $searchWord, $isFirst, ); } }); } return $query; } public function getTableSearch(): ?string { return filled($this->tableSearch) ? trim(strval($this->tableSearch)) : null; } public function hasTableSearch(): bool { return filled($this->tableSearch); } public function resetTableSearch(): void { $this->tableSearch = ''; $this->updatedTableSearch(); } public function resetTableColumnSearch(string $column): void { $this->updatedTableColumnSearches(null, $column); } public function resetTableColumnSearches(): void { $this->tableColumnSearches = []; $this->updatedTableColumnSearches(); } public function getTableSearchIndicator(): Indicator { return Indicator::make(__('filament-tables::table.fields.search.indicator') . ': ' . $this->getTableSearch()) ->removeLivewireClickHandler('resetTableSearch'); } /** * @return array */ public function getTableColumnSearchIndicators(): array { $indicators = []; foreach ($this->getTable()->getColumns() as $column) { if ($column->isHidden()) { continue; } if (! $column->isIndividuallySearchable()) { continue; } $columnName = $column->getName(); $search = Arr::get($this->tableColumnSearches, $columnName); if (blank($search)) { continue; } $indicators[] = Indicator::make("{$column->getLabel()}: {$search}") ->removeLivewireClickHandler("resetTableColumnSearch('{$columnName}')"); } return $indicators; } /** * @param array | null> $searches * @return array | null> */ protected function castTableColumnSearches(array $searches): array { return array_map( fn ($search): array | string => is_array($search) ? $this->castTableColumnSearches($search) : strval($search), $searches, ); } /** * @return array */ public function getTableColumnSearches(): array { // Example input of `$this->tableColumnSearches`: // [ // 'number' => '12345 ', // 'customer' => [ // 'name' => ' john Smith', // ], // ] // The `$this->tableColumnSearches` array is potentially nested. // So, we iterate through it deeply: $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator($this->tableColumnSearches), RecursiveIteratorIterator::SELF_FIRST ); $searches = []; $path = []; foreach ($iterator as $key => $value) { $path[$iterator->getDepth()] = $key; if (is_array($value)) { continue; } // Nested array keys are flattened into `dot.syntax`. $searches[ implode('.', array_slice($path, 0, $iterator->getDepth() + 1)) ] = trim(strval($value)); } return $searches; // Example output: // [ // 'number' => '12345', // 'customer.name' => 'john smith', // ] } public function getTableSearchSessionKey(): string { $table = md5($this::class); return "tables.{$table}_search"; } public function getTableColumnSearchesSessionKey(): string { $table = md5($this::class); return "tables.{$table}_column_search"; } /** * @deprecated Override the `table()` method to configure the table. */ protected function shouldPersistTableSearchInSession(): bool { return false; } /** * @deprecated Override the `table()` method to configure the table. */ protected function shouldPersistTableColumnSearchInSession(): bool { return false; } }