Files
gustavoo-me/wp-content/plugins/fluent-crm/app/Services/ContactsQuery.php
T
2026-07-23 22:56:30 -03:00

230 lines
7.8 KiB
PHP

<?php
namespace FluentCrm\App\Services;
use FluentCrm\App\Models\Subscriber;
use FluentCrm\Framework\Support\Arr;
class ContactsQuery
{
private $args = [];
private $model = null;
public function __construct($args = [])
{
$this->args = wp_parse_args($args, [
'with' => ['tags', 'lists'],
'filter_type' => 'simple',
'filters_groups' => [],
'filters_groups_raw' => [],
'search' => '',
'sort_by' => 'id',
'sort_type' => 'DESC',
'tags' => [],
'lists' => [],
'statuses' => [],
'sms_statuses' => [],
'has_commerce' => false,
'custom_fields' => false,
'limit' => false,
'offset' => false,
'contact_status' => '',
'company_ids' => [],
'contact_ids' => []
]);
$this->setupQuery();
}
private function setupQuery()
{
if ($this->args['filters_groups_raw']) {
$this->formatAdvancedFilters();
}
$subscribersQuery = Subscriber::with($this->args['with']);
if ($search = $this->args['search']) {
$subscribersQuery->searchBy($search, $this->args['custom_fields']);
}
if ($shortBy = $this->args['sort_by']) {
// Allowlist of every real fc_subscribers column. The framework
// rewrite (May 2025) made orderBy() throw LogicException for
// names that don't match ^[a-zA-Z0-9_\.]+$, and unknown columns
// would produce SQL errors — so the check has to happen before
// we hand the value to the builder. getFillable() used to be
// the gate but it omits id, contact_owner and a few others,
// so legitimate sorts (e.g., contact_owner) silently dropped.
$allowedSortBy = [
'id', 'user_id', 'hash', 'contact_owner', 'company_id', 'prefix',
'first_name', 'last_name', 'email', 'timezone', 'address_line_1',
'address_line_2', 'postal_code', 'city', 'state', 'country', 'ip',
'latitude', 'longitude', 'total_points', 'life_time_value', 'phone',
'status', 'contact_type', 'source', 'avatar', 'date_of_birth',
'created_at', 'last_activity', 'updated_at',
];
if (in_array($shortBy, $allowedSortBy, true)) {
$subscribersQuery->orderBy($shortBy, $this->args['sort_type']);
}
}
if ($this->args['filter_type'] == 'advanced') {
$filtersGroups = $this->args['filters_groups'];
$subscribersQuery->where(function ($subscribersQueryGroup) use ($filtersGroups) {
foreach ($filtersGroups as $groupIndex => $group) {
$method = 'orWhere';
if ($groupIndex == 0) {
$method = 'where';
}
$subscribersQueryGroup->{$method}(function ($q) use ($group) {
foreach ($group as $providerName => $items) {
do_action_ref_array('fluentcrm_contacts_filter_' . $providerName, [&$q, $items]);
}
});
}
});
} else {
if ($tags = $this->args['tags']) {
$subscribersQuery->filterByTags($tags);
}
if ($lists = $this->args['lists']) {
$subscribersQuery->filterByLists($lists);
}
if ($company_ids = $this->args['company_ids']) {
$subscribersQuery->filterByCompanies($company_ids);
}
if ($statuses = $this->args['statuses']) {
$statuses = (array) $statuses;
$statuses = array_intersect($statuses, fluentcrm_subscriber_statuses());
$subscribersQuery->filterByStatues($statuses);
}
if ($sms_statuses = $this->args['sms_statuses']) {
$sms_statuses = (array) $sms_statuses;
$subscribersQuery->where(function ($query) use ($sms_statuses) {
foreach ($sms_statuses as $sms_status) {
$query->orWhere('sms_status', $sms_status);
}
});
}
}
if ($this->args['has_commerce']) {
/**
* Filter the commerce provider for quering contacts in FluentCRM.
*
* This filter allows you to modify the commerce provider used in the Contact Query.
*
* @since 2.5.1
*
* @param string The commerce provider.
*/
$commerceProvider = apply_filters('fluentcrm_commerce_provider', '');
if ($commerceProvider) {
$subscribersQuery->with(['commerce_by_provider' => function ($query) use ($commerceProvider) {
$query->where('provider', $commerceProvider);
}]);
}
}
if ($this->args['contact_status']) {
$subscribersQuery->where('status', $this->args['contact_status']);
}
if ($this->args['company_ids']) {
$subscribersQuery->filterByCompanies($this->args['company_ids']);
}
if ($this->args['contact_ids'] && is_array($this->args['contact_ids']) && !empty($this->args['contact_ids'])) {
$subscribersQuery->whereIn('id', $this->args['contact_ids']);
}
$this->model = $subscribersQuery;
}
public function get()
{
$subscriberModel = $this->model;
if ($limit = $this->args['limit']) {
$subscriberModel = $subscriberModel->limit($limit);
}
if ($offset = $this->args['offset']) {
$subscriberModel = $subscriberModel->offset($offset);
}
return $this->returnSubscribers($subscriberModel->get());
}
public function paginate()
{
return $this->returnSubscribers($this->model->paginate());
}
public function getModel()
{
return $this->model;
}
private function returnSubscribers($subscribers)
{
if ($this->args['custom_fields']) {
// we have to include custom fields
foreach ($subscribers as $subscriber) {
$subscriber->custom_fields = $subscriber->custom_fields();
}
}
return $subscribers;
}
private function formatAdvancedFilters()
{
$filters = $this->args['filters_groups_raw'];
$groups = [];
foreach ($filters as $filterGroup) {
$group = [];
foreach ($filterGroup as $filterItem) {
if (count($filterItem['source']) != 2 || empty($filterItem['source'][0]) || empty($filterItem['source'][1]) || empty($filterItem['operator'])) {
continue;
}
$provider = $filterItem['source'][0];
if (!isset($group[$provider])) {
$group[$provider] = [];
}
$property = $filterItem['source'][1];
if ($property == 'purchased_groups') {
$property = 'purchased_items';
}
$group[$provider][] = [
'property' => $property,
'operator' => $filterItem['operator'],
'value' => $filterItem['value'],
'extra_value' => Arr::get($filterItem, 'extra_value')
];
}
if ($group) {
$groups[] = $group;
}
}
$this->args['filters_groups'] = $groups;
}
}