Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
96.97% |
64 / 66 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
| CalendarEventQueryBuilder | |
96.92% |
63 / 65 |
|
75.00% |
3 / 4 |
15 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| tableExists | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
4.18 | |||
| buildCalendarQueryParts | |
100.00% |
50 / 50 |
|
100.00% |
1 / 1 |
9 | |||
| buildEventDetailsSql | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | /** @license For full copyright and license information, please see the LICENSE.md file. */ |
| 6 | |
| 7 | namespace App\Modules\Dashboard\Application\Service\Query; |
| 8 | |
| 9 | defined('AMMONLY_APP') || exit('Direct script access is forbidden.'); |
| 10 | |
| 11 | use PDO; |
| 12 | use Throwable; |
| 13 | |
| 14 | /** |
| 15 | * Enterprise Query Builder for Calendar Event queries and relational joins. |
| 16 | * |
| 17 | * Provides shared SELECT/JOIN clauses and table existence checks across calendar |
| 18 | * widgets and interactive event detail modal services. |
| 19 | */ |
| 20 | final readonly class CalendarEventQueryBuilder |
| 21 | { |
| 22 | /** |
| 23 | * CalendarEventQueryBuilder constructor. |
| 24 | * |
| 25 | * @param string $tablePrefix Database table prefix. |
| 26 | */ |
| 27 | public function __construct( |
| 28 | private string $tablePrefix = 'a_' |
| 29 | ) { |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Checks if a database table exists in the target database. |
| 34 | * |
| 35 | * @param PDO $pdo Active PDO handle. |
| 36 | * @param string $tableName Target table name. |
| 37 | * @return bool True if table exists, false otherwise. |
| 38 | */ |
| 39 | public function tableExists(PDO $pdo, string $tableName): bool |
| 40 | { |
| 41 | try { |
| 42 | $driver = (string) $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); |
| 43 | if ($driver === 'sqlite') { |
| 44 | $stmt = $pdo->prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = :t"); |
| 45 | $stmt->execute([':t' => $tableName]); |
| 46 | return $stmt->fetchColumn() !== false; |
| 47 | } |
| 48 | |
| 49 | $stmt = $pdo->query('SHOW TABLES LIKE ' . $pdo->quote($tableName)); |
| 50 | return $stmt !== false && $stmt->fetchColumn() !== false; |
| 51 | } catch (Throwable) { |
| 52 | return false; |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Builds SELECT and JOIN clauses for calendar queries including company, process, and subprocess relations. |
| 58 | * |
| 59 | * @param PDO $pdo Database connection. |
| 60 | * @param bool $includeAttendees Whether to include attendees payload column. |
| 61 | * @return array{0: string, 1: string} [selectSql, joinSql] |
| 62 | */ |
| 63 | public function buildCalendarQueryParts(PDO $pdo, bool $includeAttendees = false): array |
| 64 | { |
| 65 | $compTable = $this->tablePrefix . 'mod_companies_records'; |
| 66 | $contTable = $this->tablePrefix . 'mod_contacts_records'; |
| 67 | $projTable = $this->tablePrefix . 'mod_projects_records'; |
| 68 | $ctrTable = $this->tablePrefix . 'mod_contracts_records'; |
| 69 | $tckTable = $this->tablePrefix . 'mod_tickets_records'; |
| 70 | $tskTable = $this->tablePrefix . 'mod_project_tasks_records'; |
| 71 | $stgTable = $this->tablePrefix . 'mod_project_stages_records'; |
| 72 | |
| 73 | $selects = [ |
| 74 | "cal.`id`", "cal.`subject`", "cal.`start_date`", "cal.`end_date`", |
| 75 | "cal.`status`", "cal.`event_type`", "cal.`priority`", "cal.`company_id`", |
| 76 | "cal.`contact_id`", "cal.`project_id`", "cal.`contract_id`", |
| 77 | "cal.`ticket_id`", "cal.`task_id`", "cal.`description`", "cal.`location`", |
| 78 | "cal.`meeting_url`", "cal.`owner`", |
| 79 | ]; |
| 80 | |
| 81 | if ($includeAttendees) { |
| 82 | $selects[] = "cal.`attendees`"; |
| 83 | } |
| 84 | |
| 85 | $joins = []; |
| 86 | |
| 87 | if ($this->tableExists($pdo, $compTable)) { |
| 88 | $selects[] = "comp.`name` AS `company_name`"; |
| 89 | $joins[] = "LEFT JOIN `{$compTable}` comp ON comp.`id` = cal.`company_id`"; |
| 90 | } else { |
| 91 | $selects[] = "NULL AS `company_name`"; |
| 92 | } |
| 93 | |
| 94 | if ($this->tableExists($pdo, $contTable)) { |
| 95 | $selects[] = "CONCAT_WS(' ', cont.`first_name`, cont.`last_name`) AS `contact_name`"; |
| 96 | $joins[] = "LEFT JOIN `{$contTable}` cont ON cont.`id` = cal.`contact_id`"; |
| 97 | } else { |
| 98 | $selects[] = "NULL AS `contact_name`"; |
| 99 | } |
| 100 | |
| 101 | $processExprs = []; |
| 102 | if ($this->tableExists($pdo, $projTable)) { |
| 103 | $processExprs[] = "proj.`project_name`"; |
| 104 | $joins[] = "LEFT JOIN `{$projTable}` proj ON proj.`id` = cal.`project_id`"; |
| 105 | } |
| 106 | if ($this->tableExists($pdo, $ctrTable)) { |
| 107 | $processExprs[] = "ctr.`contract_name`"; |
| 108 | $joins[] = "LEFT JOIN `{$ctrTable}` ctr ON ctr.`id` = cal.`contract_id`"; |
| 109 | } |
| 110 | $processExprs[] = "cal.`process_ref`"; |
| 111 | $selects[] = "COALESCE(" . implode(', ', $processExprs) . ", '') AS `process_name`"; |
| 112 | |
| 113 | $subprocessExprs = []; |
| 114 | if ($this->tableExists($pdo, $tckTable)) { |
| 115 | $subprocessExprs[] = "tck.`subject`"; |
| 116 | $joins[] = "LEFT JOIN `{$tckTable}` tck ON tck.`id` = cal.`ticket_id`"; |
| 117 | } |
| 118 | if ($this->tableExists($pdo, $tskTable)) { |
| 119 | $subprocessExprs[] = "tsk.`task_name`"; |
| 120 | $joins[] = "LEFT JOIN `{$tskTable}` tsk ON tsk.`id` = cal.`task_id`"; |
| 121 | } |
| 122 | if ($this->tableExists($pdo, $stgTable)) { |
| 123 | $subprocessExprs[] = "stg.`stage_name`"; |
| 124 | $joins[] = "LEFT JOIN `{$stgTable}` stg ON stg.`id` = cal.`stage_id`"; |
| 125 | } |
| 126 | $subprocessExprs[] = "cal.`subprocess_ref`"; |
| 127 | $selects[] = "COALESCE(" . implode(', ', $subprocessExprs) . ", '') AS `subprocess_name`"; |
| 128 | |
| 129 | return [ |
| 130 | implode(",\n ", $selects), |
| 131 | implode("\n ", $joins), |
| 132 | ]; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Builds full SQL statement for fetching single event modal details. |
| 137 | * |
| 138 | * @param PDO $pdo Database connection. |
| 139 | * @param string $calTable Calendar table name. |
| 140 | * @return string Formatted SQL query. |
| 141 | */ |
| 142 | public function buildEventDetailsSql(PDO $pdo, string $calTable): string |
| 143 | { |
| 144 | [$selectSql, $joinSql] = $this->buildCalendarQueryParts($pdo, true); |
| 145 | |
| 146 | return "SELECT {$selectSql} |
| 147 | FROM `{$calTable}` cal |
| 148 | {$joinSql} |
| 149 | WHERE cal.`id` = :id |
| 150 | AND cal.`special_access` = 1 |
| 151 | LIMIT 1"; |
| 152 | } |
| 153 | } |