| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentForm\App\Api; |
| 4 |
|
| 5 |
use FluentForm\App\Helpers\Helper; |
| 6 |
use FluentForm\App\Models\FormMeta; |
| 7 |
use FluentForm\App\Models\Submission; |
| 8 |
use FluentForm\App\Services\Manager\FormManagerService; |
| 9 |
use FluentForm\Framework\Helpers\ArrayHelper; |
| 10 |
|
| 11 |
class Form |
| 12 |
{ |
| 13 |
public function forms($atts = [], $withFields = false) |
| 14 |
{ |
| 15 |
$defaultAtts = [ |
| 16 |
'search' => '', |
| 17 |
'status' => 'all', |
| 18 |
'sort_column' => 'id', |
| 19 |
'filter_by' => 'all', |
| 20 |
'date_range' => [], |
| 21 |
'sort_by' => 'DESC', |
| 22 |
'per_page' => 10, |
| 23 |
'page' => 1, |
| 24 |
]; |
| 25 |
|
| 26 |
$atts = wp_parse_args($atts, $defaultAtts); |
| 27 |
|
| 28 |
$perPage = (int) ArrayHelper::get($atts, 'per_page', 10); |
| 29 |
$search = sanitize_text_field(ArrayHelper::get($atts, 'search', '')); |
| 30 |
$status = sanitize_text_field(ArrayHelper::get($atts, 'status', 'all')); |
| 31 |
$filter_by = sanitize_text_field(ArrayHelper::get($atts, 'filter_by', 'all')); |
| 32 |
$dateRange = ArrayHelper::get($atts, 'date_range', []); |
| 33 |
$is_filter_by_conv_or_step_form = $filter_by && ('conv_form' == $filter_by || 'step_form' == $filter_by); |
| 34 |
|
| 35 |
$shortColumn = sanitize_sql_orderby(ArrayHelper::get($atts, 'sort_column', 'id')); |
| 36 |
$shortColumn = $shortColumn !== false && $shortColumn !== '' ? $shortColumn : 'id'; |
| 37 |
$sortBy = Helper::sanitizeOrderValue(ArrayHelper::get($atts, 'sort_by', 'DESC')); |
| 38 |
|
| 39 |
$query = \FluentForm\App\Models\Form::orderBy($shortColumn, $sortBy)->getQuery(); |
| 40 |
|
| 41 |
if ($status && 'all' != $status) { |
| 42 |
$query->where('status', $status); |
| 43 |
} |
| 44 |
|
| 45 |
if (false !== ($allowIds = FormManagerService::getUserAllowedFormsScope())) { |
| 46 |
$query->whereIn('id', $allowIds ?: [0]); |
| 47 |
} |
| 48 |
|
| 49 |
if ($filter_by && !$is_filter_by_conv_or_step_form) { |
| 50 |
switch ($filter_by) { |
| 51 |
case 'published': |
| 52 |
$query->where('status', 'published'); |
| 53 |
break; |
| 54 |
case 'unpublished': |
| 55 |
$query->where('status', 'unpublished'); |
| 56 |
break; |
| 57 |
case 'post': |
| 58 |
$query->where('type', 'post'); |
| 59 |
break; |
| 60 |
case 'is_payment': |
| 61 |
$query->where('has_payment', 1); |
| 62 |
break; |
| 63 |
default: |
| 64 |
break; |
| 65 |
} |
| 66 |
} |
| 67 |
|
| 68 |
if ($dateRange) { |
| 69 |
$query->where('created_at', '>=', sanitize_text_field($dateRange[0] . ' 00:00:01')); |
| 70 |
$query->where('created_at', '<=', sanitize_text_field($dateRange[1] . ' 23:59:59')); |
| 71 |
} |
| 72 |
|
| 73 |
if ($search) { |
| 74 |
$query->where(function ($q) use ($search) { |
| 75 |
$q->where('id', 'LIKE', '%' . $search . '%'); |
| 76 |
$q->orWhere('title', 'LIKE', '%' . $search . '%'); |
| 77 |
}); |
| 78 |
} |
| 79 |
|
| 80 |
$currentPage = intval(ArrayHelper::get($atts, 'page', 1)); |
| 81 |
$total = $query->count(); |
| 82 |
$skip = $perPage * ($currentPage - 1); |
| 83 |
|
| 84 |
if ($is_filter_by_conv_or_step_form) { |
| 85 |
$data = $query->select('*')->get()->all(); |
| 86 |
} else { |
| 87 |
if ($total < $skip) { |
| 88 |
$skip = 0; |
| 89 |
} |
| 90 |
$data = $query->select('*')->limit($perPage)->offset($skip)->get()->all(); |
| 91 |
} |
| 92 |
|
| 93 |
// Fetch all counts in bulk before the loop |
| 94 |
$formIds = array_column($data, 'id'); |
| 95 |
|
| 96 |
// Get all submission counts in one query using Submission model |
| 97 |
$submissionCounts = Submission::select([ |
| 98 |
'form_id', |
| 99 |
wpFluent()->raw('COUNT(*) as total_submissions'), |
| 100 |
wpFluent()->raw("SUM(CASE WHEN status = 'unread' THEN 1 ELSE 0 END) as unread_count") |
| 101 |
]) |
| 102 |
->whereIn('form_id', $formIds) |
| 103 |
->where('status', '!=', 'trashed') |
| 104 |
->groupBy('form_id') |
| 105 |
->get(); |
| 106 |
|
| 107 |
// Map counts by form_id for quick lookup |
| 108 |
$countsMap = []; |
| 109 |
foreach ($submissionCounts as $count) { |
| 110 |
$countsMap[$count->form_id] = $count; |
| 111 |
} |
| 112 |
|
| 113 |
// Get all view counts in one query using FormMeta model |
| 114 |
$viewCounts = FormMeta::select(['form_id', 'value']) |
| 115 |
->where('meta_key', '_total_views') |
| 116 |
->whereIn('form_id', $formIds) |
| 117 |
->get(); |
| 118 |
|
| 119 |
// Map view counts by form_id for quick lookup |
| 120 |
$viewsMap = []; |
| 121 |
foreach ($viewCounts as $view) { |
| 122 |
$viewsMap[$view->form_id] = intval($view->value); |
| 123 |
} |
| 124 |
|
| 125 |
$conversationOrStepForms = []; |
| 126 |
foreach ($data as $form) { |
| 127 |
$is_conv_form = Helper::isConversionForm($form->id); |
| 128 |
|
| 129 |
// skip form if filter by conversation form but form is not conversational form |
| 130 |
if ('conv_form' == $filter_by && !$is_conv_form) { |
| 131 |
continue; |
| 132 |
} |
| 133 |
// skip form if filter by step form but form is not step form |
| 134 |
if ('step_form' == $filter_by && !Helper::isMultiStepForm($form)) { |
| 135 |
continue; |
| 136 |
} |
| 137 |
$formInstance = $this->form($form); |
| 138 |
$form->preview_url = Helper::getPreviewUrl($form->id, 'classic'); |
| 139 |
$form->edit_url = Helper::getFormAdminPermalink('editor', $form); |
| 140 |
$form->settings_url = Helper::getFormSettingsUrl($form); |
| 141 |
$form->entries_url = Helper::getFormAdminPermalink('entries', $form); |
| 142 |
$form->analytics_url = Helper::getFormAdminPermalink('analytics', $form); |
| 143 |
|
| 144 |
// Use pre-fetched counts instead of individual queries |
| 145 |
$form->total_views = $viewsMap[$form->id] ?? 0; |
| 146 |
$counts = $countsMap[$form->id] ?? null; |
| 147 |
$form->total_Submissions = $counts ? intval($counts->total_submissions) : 0; |
| 148 |
$form->unread_count = $counts ? intval($counts->unread_count) : 0; |
| 149 |
|
| 150 |
// Calculate conversion rate (same logic as FormProperties::conversionRate()) |
| 151 |
if ($form->total_Submissions && $form->total_views) { |
| 152 |
$form->conversion = ceil(($form->total_Submissions / $form->total_views) * 100); |
| 153 |
} else { |
| 154 |
$form->conversion = 0; |
| 155 |
} |
| 156 |
|
| 157 |
if ($is_conv_form) { |
| 158 |
$form->conversion_preview = Helper::getPreviewUrl($form->id, 'conversational'); |
| 159 |
} |
| 160 |
|
| 161 |
if (!$withFields) { |
| 162 |
unset($form->form_fields); |
| 163 |
} |
| 164 |
if ($is_filter_by_conv_or_step_form) { |
| 165 |
$conversationOrStepForms[] = $form; |
| 166 |
} |
| 167 |
} |
| 168 |
if ($is_filter_by_conv_or_step_form) { |
| 169 |
$total = count($conversationOrStepForms); |
| 170 |
if ($total < $skip) { |
| 171 |
$skip = 0; |
| 172 |
} |
| 173 |
$conversationOrStepForms = array_slice($conversationOrStepForms, $skip, $perPage); |
| 174 |
$dataCount = count($conversationOrStepForms); |
| 175 |
} else { |
| 176 |
$dataCount = count($data); |
| 177 |
} |
| 178 |
|
| 179 |
$from = $dataCount > 0 ? ($currentPage - 1) * $perPage + 1 : null; |
| 180 |
|
| 181 |
$to = $dataCount > 0 ? $from + $dataCount - 1 : null; |
| 182 |
$lastPage = (int) ceil($total / $perPage); |
| 183 |
return [ |
| 184 |
'current_page' => $currentPage, |
| 185 |
'per_page' => $perPage, |
| 186 |
'from' => $from, |
| 187 |
'to' => $to, |
| 188 |
'last_page' => $lastPage, |
| 189 |
'total' => $total, |
| 190 |
'data' => $is_filter_by_conv_or_step_form ? $conversationOrStepForms : $data, |
| 191 |
]; |
| 192 |
} |
| 193 |
|
| 194 |
public function find($formId) |
| 195 |
{ |
| 196 |
return \FluentForm\App\Models\Form::where('id', $formId)->first(); |
| 197 |
} |
| 198 |
|
| 199 |
/** |
| 200 |
* Get Form Properties instance |
| 201 |
* |
| 202 |
* @param int|object $form |
| 203 |
* |
| 204 |
* @return \FluentForm\App\Api\FormProperties |
| 205 |
*/ |
| 206 |
public function form($form) |
| 207 |
{ |
| 208 |
if (is_numeric($form)) { |
| 209 |
$form = $this->find($form); |
| 210 |
} |
| 211 |
|
| 212 |
return (new FormProperties($form)); |
| 213 |
} |
| 214 |
|
| 215 |
public function entryInstance($form) |
| 216 |
{ |
| 217 |
if (is_numeric($form)) { |
| 218 |
$form = $this->find($form); |
| 219 |
} |
| 220 |
|
| 221 |
return (new Entry($form)); |
| 222 |
} |
| 223 |
} |
| 224 |
|