Contacts Query
FluentCrm\App\Services\ContactsQuery
The ContactsQuery class provides a builder-pattern interface for querying contacts with filters, sorting, and pagination. It wraps the Subscriber model and supports both simple and advanced filter modes.
use FluentCrm\App\Services\ContactsQuery;Basic Usage
// Get all subscribed contacts with tag ID 5
$query = new ContactsQuery([
'statuses' => ['subscribed'],
'tags' => [5],
'sort_by' => 'first_name',
'sort_type'=> 'ASC'
]);
$contacts = $query->get();Constructor Arguments
The constructor accepts an $args array with the following keys:
| Key | Type | Default | Description |
|---|---|---|---|
with | array | ['tags', 'lists'] | Eloquent relations to eager-load |
filter_type | string | 'simple' | 'simple' or 'advanced' |
search | string | '' | Full-text search across name, email, and optionally custom fields |
sort_by | string | 'id' | Column to sort by (must be a fillable field or 'id') |
sort_type | string | 'DESC' | Sort direction: 'ASC' or 'DESC' |
tags | array | [] | Filter by tag IDs (simple mode) |
lists | array | [] | Filter by list IDs (simple mode) |
statuses | array | [] | Filter by subscription statuses (validated against allowed statuses) |
sms_statuses | array | [] | Filter by SMS statuses |
company_ids | array | [] | Filter by company IDs |
contact_ids | array | [] | Restrict to specific contact IDs |
contact_status | string | '' | Single status filter (e.g., 'subscribed') |
has_commerce | bool | false | Eager-load commerce relations from the active provider |
custom_fields | bool | false | Append custom field values to each contact in results. Also enables searching custom fields when combined with search |
filters_groups_raw | array | [] | Raw advanced filter groups (from the UI filter builder) |
limit | int|false | false | Limit number of results |
offset | int|false | false | Offset for pagination |
Methods
get()
Executes the query and returns a collection of contacts. Applies limit and offset if set.
- Returns:
CollectionofSubscribermodels
$contacts = (new ContactsQuery([
'tags' => [1, 3],
'limit' => 50,
'offset' => 0
]))->get();
foreach ($contacts as $contact) {
echo $contact->email;
}paginate()
Executes the query with WordPress-style pagination (uses $_GET['page'] and default per-page).
- Returns:
LengthAwarePaginator
$paginated = (new ContactsQuery([
'statuses' => ['subscribed']
]))->paginate();
$contacts = $paginated->items();
$total = $paginated->total();getModel()
Returns the raw underlying Subscriber query builder for custom chaining.
- Returns:
Builder— Eloquent query builder instance
$query = new ContactsQuery(['statuses' => ['subscribed']]);
$model = $query->getModel();
// Add custom conditions
$model->where('created_at', '>=', '2024-01-01');
$results = $model->get();Filter Modes
Simple Filters
In simple mode (default), you pass filter arrays directly:
$query = new ContactsQuery([
'filter_type' => 'simple',
'tags' => [1, 5],
'lists' => [2],
'statuses' => ['subscribed', 'pending'],
'company_ids' => [10]
]);Advanced Filters
In advanced mode, you pass raw filter groups from the UI filter builder. These are processed by registered filter providers via fluentcrm_contacts_filter_{provider} action hooks.
$query = new ContactsQuery([
'filter_type' => 'advanced',
'filters_groups_raw' => [
// Group 1 (conditions within a group are AND)
[
[
'source' => ['contacts', 'status'],
'operator' => 'in',
'value' => ['subscribed']
],
[
'source' => ['contacts', 'created_at'],
'operator' => 'date_after',
'value' => '2024-01-01'
]
],
// Group 2 (groups are OR'd together)
[
[
'source' => ['contacts', 'tags'],
'operator' => 'in_all',
'value' => [1, 5]
]
]
]
]);Real-World Examples
Search with custom fields
$query = new ContactsQuery([
'search' => '[email protected]',
'custom_fields' => true
]);
$contacts = $query->get();
// Each contact has ->custom_fields populatedPaginated list with commerce data
$query = new ContactsQuery([
'statuses' => ['subscribed'],
'has_commerce' => true,
'sort_by' => 'last_activity',
'sort_type' => 'DESC'
]);
$paginated = $query->paginate();Custom query chaining
$query = new ContactsQuery([
'tags' => [5],
'statuses' => ['subscribed']
]);
$model = $query->getModel();
$model->whereNotNull('phone')
->where('created_at', '>=', '2024-06-01');
$contacts = $model->limit(100)->get();