| Total Complexity | 43 |
| Total Lines | 287 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like CallDetailRecordsController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use CallDetailRecordsController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 14 | class CallDetailRecordsController extends BaseController |
||
| 15 | { |
||
| 16 | |||
| 17 | |||
| 18 | /** |
||
| 19 | * Выборка записей из журнала звонков |
||
| 20 | * |
||
| 21 | */ |
||
| 22 | public function indexAction(): void |
||
| 23 | { |
||
| 24 | } |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Запрос нового пакета истории разговоров для DataTable JSON |
||
| 28 | */ |
||
| 29 | public function getNewRecordsAction(): void |
||
| 30 | { |
||
| 31 | $currentPage = $this->request->getPost('draw'); |
||
| 32 | $position = $this->request->getPost('start'); |
||
| 33 | $recordsPerPage = $this->request->getPost('length'); |
||
| 34 | $searchPhrase = $this->request->getPost('search'); |
||
| 35 | $this->view->draw = $currentPage; |
||
| 36 | $this->view->recordsTotal = 0; |
||
| 37 | $this->view->recordsFiltered = 0; |
||
| 38 | $this->view->data = []; |
||
| 39 | |||
| 40 | // Посчитаем количество уникальных звонков без учета фильтров |
||
| 41 | $parameters = []; |
||
| 42 | $parameters['columns'] = 'COUNT(DISTINCT(linkedid)) as rows'; |
||
| 43 | $recordsTotalReq = CallDetailRecords::findFirst($parameters); |
||
| 44 | if ($recordsTotalReq) { |
||
| 45 | $recordsTotal = $recordsTotalReq->rows; |
||
| 46 | $this->view->recordsTotal = $recordsTotal; |
||
| 47 | } else { |
||
| 48 | return; |
||
| 49 | } |
||
| 50 | // Посчитаем количество уникальных звонков с учетом фильтров |
||
| 51 | if ( ! empty($searchPhrase['value'])) { |
||
| 52 | $this->prepareConditionsForSearchPhrases($searchPhrase['value'], $parameters); |
||
| 53 | // Если мы не смогли расшифровать строку запроса вернем пустой результата |
||
| 54 | if (empty($parameters['conditions'])) { |
||
| 55 | return; |
||
| 56 | } |
||
| 57 | } |
||
| 58 | $recordsFilteredReq = CallDetailRecords::findFirst($parameters); |
||
| 59 | if ($recordsFilteredReq) { |
||
| 60 | $recordsFiltered = $recordsFilteredReq->rows; |
||
| 61 | $this->view->recordsFiltered = $recordsFiltered; |
||
| 62 | } |
||
| 63 | |||
| 64 | // Найдем все LinkedID подходящих под заданный фильтр |
||
| 65 | $parameters['columns'] = 'DISTINCT(linkedid) as linkedid'; |
||
| 66 | $parameters['order'] = ['start desc']; |
||
| 67 | $parameters['limit'] = $recordsPerPage; |
||
| 68 | $parameters['offset'] = $position; |
||
| 69 | |||
| 70 | $selectedLinkedIds = CallDetailRecords::find($parameters); |
||
| 71 | $arrIDS = []; |
||
| 72 | foreach ($selectedLinkedIds as $item) { |
||
| 73 | $arrIDS[] = $item->linkedid; |
||
| 74 | } |
||
| 75 | |||
| 76 | if (count($arrIDS) === 0) { |
||
| 77 | return; |
||
| 78 | } |
||
| 79 | |||
| 80 | // Получим все детальные записи для обработки и склеивания |
||
| 81 | if (count($arrIDS) === 1) { |
||
| 82 | $parameters = [ |
||
| 83 | 'conditions' => 'linkedid = :ids:', |
||
| 84 | 'columns' => 'id, disposition, start, src_num, dst_num, billsec, recordingfile, did, dst_chan, linkedid, is_app', |
||
| 85 | 'bind' => [ |
||
| 86 | 'ids' => $arrIDS[0], |
||
| 87 | ], |
||
| 88 | 'order' => ['linkedid desc', 'start asc'], |
||
| 89 | ]; |
||
| 90 | } else { |
||
| 91 | $parameters = [ |
||
| 92 | 'conditions' => 'linkedid IN ({ids:array})', |
||
| 93 | 'columns' => 'id, disposition, start, src_num, dst_num, billsec, recordingfile, did, dst_chan, linkedid, is_app', |
||
| 94 | 'bind' => [ |
||
| 95 | 'ids' => $arrIDS, |
||
| 96 | ], |
||
| 97 | 'order' => ['linkedid desc', 'start asc'], |
||
| 98 | ]; |
||
| 99 | } |
||
| 100 | |||
| 101 | $selectedRecords = CallDetailRecords::find($parameters); |
||
| 102 | |||
| 103 | $arrCdr = []; |
||
| 104 | |||
| 105 | $objectLinkedCallRecord = (object)[ |
||
| 106 | 'linkedid' => '', |
||
| 107 | 'disposition' => '', |
||
| 108 | 'start' => '', |
||
| 109 | 'src_num' => '', |
||
| 110 | 'dst_num' => '', |
||
| 111 | 'billsec' => 0, |
||
| 112 | 'answered' => [], |
||
| 113 | 'detail' => [], |
||
| 114 | ]; |
||
| 115 | |||
| 116 | foreach ($selectedRecords as $record) { |
||
| 117 | if ( ! array_key_exists($record->linkedid, $arrCdr)) { |
||
| 118 | $arrCdr[$record->linkedid] = clone $objectLinkedCallRecord; |
||
| 119 | } |
||
| 120 | if ($record->is_app !== '1' |
||
| 121 | && $record->billsec > 0 |
||
| 122 | && ($record->disposition === 'ANSWER' || $record->disposition === 'ANSWERED')) { |
||
| 123 | $disposition = 'ANSWERED'; |
||
| 124 | } else { |
||
| 125 | $disposition = 'NOANSWER'; |
||
| 126 | } |
||
| 127 | $linkedRecord = $arrCdr[$record->linkedid]; |
||
| 128 | $linkedRecord->linkedid = $record->linkedid; |
||
| 129 | $linkedRecord->disposition = $linkedRecord->disposition !== 'ANSWERED' ? $disposition : 'ANSWERED'; |
||
| 130 | $linkedRecord->start = $linkedRecord->start === '' ? $record->start : $linkedRecord->start; |
||
| 131 | $linkedRecord->src_num = $linkedRecord->src_num === '' ? $record->src_num : $linkedRecord->src_num; |
||
| 132 | if ( ! empty($record->did)) { |
||
| 133 | $linkedRecord->dst_num = $record->did; |
||
| 134 | } else { |
||
| 135 | $linkedRecord->dst_num = $linkedRecord->dst_num === '' ? $record->dst_num : $linkedRecord->dst_num; |
||
| 136 | } |
||
| 137 | $linkedRecord->billsec += (int)$record->billsec; |
||
| 138 | if ($disposition === 'ANSWERED') { |
||
| 139 | $linkedRecord->answered[] = [ |
||
| 140 | 'id' => $record->id, |
||
| 141 | 'src_num' => $record->src_num, |
||
| 142 | 'dst_num' => $record->dst_num, |
||
| 143 | 'recordingfile' => $record->recordingfile, |
||
| 144 | ]; |
||
| 145 | } |
||
| 146 | $linkedRecord->detail[] = $record; |
||
| 147 | } |
||
| 148 | $output = []; |
||
| 149 | foreach ($arrCdr as $cdr) { |
||
| 150 | $timing = gmdate($cdr->billsec < 3600 ? 'i:s' : 'G:i:s', $cdr->billsec); |
||
| 151 | $output[] = [ |
||
| 152 | date('d-m-Y H:i:s', strtotime($cdr->start)), |
||
| 153 | $cdr->src_num, |
||
| 154 | $cdr->dst_num, |
||
| 155 | $timing === '00:00' ? '' : $timing, |
||
| 156 | $cdr->answered, |
||
| 157 | $cdr->disposition, |
||
| 158 | 'DT_RowId' => $cdr->linkedid, |
||
| 159 | 'DT_RowClass' => 'NOANSWER' === $cdr->disposition ? 'ui negative' : 'detailed', |
||
| 160 | ]; |
||
| 161 | } |
||
| 162 | |||
| 163 | $this->view->data = $output; |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * |
||
| 168 | * Подготовка параметров запроса для фильтрации CDR записей |
||
| 169 | * |
||
| 170 | * @param string $searchPhrase поисковая фраза, которую ввел пользователь |
||
| 171 | * @param array $parameters параметры запроса CDR |
||
| 172 | */ |
||
| 173 | private function prepareConditionsForSearchPhrases(string &$searchPhrase, array &$parameters): void |
||
| 301 | } |
||
| 302 | } |
||
| 303 | } |
||
| 304 | } |