Milk Admin
SearchBuilder Extensions
SearchBuilder Extensions allow you to add custom search fields, filters, and action lists to the search interface without modifying the SearchBuilder class itself. They extend the AbstractSearchBuilderExtension class.
Creating a SearchBuilder Extension
namespace Extensions\MyExtension;
use App\Abstracts\AbstractSearchBuilderExtension;
class SearchBuilder extends AbstractSearchBuilderExtension
{
// Configuration parameters
protected string $label = 'Status:';
protected string $filter_name = 'status_filter';
// Hook called during builder configuration
public function configure(object $builder): void
{
// Add search fields and filters
$builder->actionList(
$this->filter_name,
$this->label,
['active' => 'Active', 'inactive' => 'Inactive'],
'active'
);
}
}
Accessing the Model
Get the model instance from the SearchBuilder:
public function configure(object $builder): void
{
// Get the model
$model = $builder->getModel();
// Access model properties
$fields = $model->getFields();
// Get loaded model extensions
$soft_del_ext = $model->getLoadedExtension('SoftDelete');
if ($soft_del_ext) {
$field_name = $soft_del_ext->field_name;
}
}
Available Hooks
| Hook | Parameters | Return | Description |
|---|---|---|---|
configure() |
object $builder |
void |
Add search fields, filters, and action lists |
SearchBuilder Methods
Common methods available in the SearchBuilder:
| Method | Parameters | Description |
|---|---|---|
actionList() |
string $name, string $label, array $options, mixed $default |
Add a dropdown filter with predefined options |
addSearchField() |
string $name, string $label, string $type = 'text' |
Add a search input field |
getModel() |
- | Get the associated model instance |
Extension Parameters
Define configurable parameters as protected properties:
class SearchBuilder extends AbstractSearchBuilderExtension
{
// Default values
protected string $label = 'Status:';
protected string $filter_name = 'show_deleted';
}
// Override in module configuration
$rule_builder->addExtension('SoftDelete', [
'label' => 'Record Status:',
'filter_name' => 'custom_filter'
]);
Example 1: SoftDelete SearchBuilder Extension
Adds a filter to show active or deleted records:
namespace Extensions\SoftDelete;
use App\Abstracts\AbstractSearchBuilderExtension;
class SearchBuilder extends AbstractSearchBuilderExtension
{
protected string $label = 'Status:';
protected string $filter_name = 'show_deleted';
public function configure(object $builder): void
{
// Add action list filter
$builder->actionList(
$this->filter_name,
$this->label,
[
'active' => 'Active',
'deleted' => 'Deleted'
],
'active'
);
}
}
Example 2: Status Filter Extension
Add a filter for record status (published, draft, archived):
namespace Extensions\PublishStatus;
use App\Abstracts\AbstractSearchBuilderExtension;
class SearchBuilder extends AbstractSearchBuilderExtension
{
protected string $label = 'Status:';
protected string $filter_name = 'status';
public function configure(object $builder): void
{
$builder->actionList(
$this->filter_name,
$this->label,
[
'all' => 'All',
'published' => 'Published',
'draft' => 'Draft',
'archived' => 'Archived'
],
'all'
);
}
}
Example 3: Category Filter Extension
Add a dynamic filter based on database categories:
namespace Extensions\CategoryFilter;
use App\Abstracts\AbstractSearchBuilderExtension;
use App\Get;
class SearchBuilder extends AbstractSearchBuilderExtension
{
protected string $label = 'Category:';
protected string $filter_name = 'category_id';
public function configure(object $builder): void
{
// Get categories from database
$categories = $this->getCategories();
if (empty($categories)) {
return;
}
// Add filter with dynamic options
$builder->actionList(
$this->filter_name,
$this->label,
$categories,
'all'
);
}
private function getCategories(): array
{
$categories = ['all' => 'All Categories'];
try {
$db = Get::db();
$results = $db->select('categories')
->fields(['id', 'name'])
->where('status = ?', ['active'])
->order('name ASC')
->getResults();
foreach ($results as $cat) {
$categories[$cat->id] = $cat->name;
}
} catch (\Exception $e) {
// Fallback to empty list
}
return $categories;
}
}
Example 4: Date Range Filter Extension
Add search fields for filtering by date range:
namespace Extensions\DateRange;
use App\Abstracts\AbstractSearchBuilderExtension;
class SearchBuilder extends AbstractSearchBuilderExtension
{
protected bool $add_date_from = true;
protected bool $add_date_to = true;
public function configure(object $builder): void
{
if ($this->add_date_from) {
$builder->addSearchField(
'date_from',
'From Date:',
'date'
);
}
if ($this->add_date_to) {
$builder->addSearchField(
'date_to',
'To Date:',
'date'
);
}
}
}
Example 5: User Filter Extension
Add a filter to show records by specific user (author, assignee, etc.):
namespace Extensions\UserFilter;
use App\Abstracts\AbstractSearchBuilderExtension;
class SearchBuilder extends AbstractSearchBuilderExtension
{
protected string $label = 'Created By:';
protected string $filter_name = 'created_by';
protected bool $show_all_option = true;
public function configure(object $builder): void
{
// Get list of users
$users = $this->getUsersList();
if (empty($users)) {
return;
}
$builder->actionList(
$this->filter_name,
$this->label,
$users,
'all'
);
}
private function getUsersList(): array
{
$users = [];
if ($this->show_all_option) {
$users['all'] = 'All Users';
}
try {
$userModel = new \Modules\Auth\UserModel();
$allUsers = $userModel->order('username')->getResults();
foreach ($allUsers as $user) {
$users[$user->id] = $user->username ?? "User #{$user->id}";
}
} catch (\Exception $e) {
// Fallback to empty list
}
return $users;
}
}
Common Use Cases
- Status filters - Filter by record status (active/inactive, published/draft)
- Soft delete filters - Show/hide deleted records
- Category filters - Filter by categories or tags
- User filters - Filter by author, assignee, or related user
- Date filters - Filter by date ranges or specific periods
- Priority filters - Filter by priority levels (high, medium, low)
- Type filters - Filter by content type or record type
Best Practices
- Use descriptive labels that clearly indicate the filter purpose
- Provide sensible default values (often 'all' or 'active')
- Use protected properties for configurable options
- Keep filter names consistent with field names when possible
- Handle database errors gracefully when loading dynamic options
- Consider performance when loading large option lists
See Also
Loading...