_dbInstance = Database::getInstance(); $this->_dbHandle = $this->_dbInstance->getDbConnection(); } /** * @param $filterArray * @param $rowCount * @param $offset * @return array * Function to allow fetching of facility data. Data objects are created and held in an array * Count of rows for pagination returned alongside data objects. */ public function fetchAll($filterArray, $rowCount, $offset): array { /** * COUNT(DISTINCT ecoFacilities.id) required due to multiple status comments possible. */ $sqlCount = "SELECT COUNT(DISTINCT ecoFacilities.id) AS total FROM ecoFacilities"; /** * DISTINCT used again for prior reasoning, although data is handled properly regardless later. * GROUP_CONCAT is used to handle multiple status comments under one facility. Without this, DISTINCT * drops the additional comment. */ $sqlData = "SELECT DISTINCT ecoFacilities.id, title, GROUP_CONCAT(ecoFacilityStatus.statusComment, ', ') AS status, ecoCategories.name AS category, description, houseNumber, streetName, county, town, postcode, lng, lat, ecoUser.username AS contributor FROM ecoFacilities"; /** * ? Parameters used here over named parameters so logic can be modular, more * columns can be added in the future */ $sqlWhere = " LEFT JOIN ecoCategories ON ecoCategories.id = ecoFacilities.category LEFT JOIN ecoUser ON ecoUser.id = ecoFacilities.contributor LEFT JOIN ecoFacilityStatus ON ecoFacilityStatus.facilityid = ecoFacilities.id WHERE (ecoFacilityStatus.statusComment LIKE ? OR ? IS NULL) AND ecoFacilities.title LIKE ? AND ecoCategories.name LIKE ? AND ecoFacilities.description LIKE ? AND ecoFacilities.streetName LIKE ? AND ecoFacilities.county LIKE ? AND ecoFacilities.town LIKE ? AND ecoFacilities.postcode LIKE ? AND ecoUser.username LIKE ? "; /** * GROUP BY required to ensure status comments are displayed under the same ID * Named parameters used here for prior reasoning, columns can be added above without * effecting the bindIndex. */ $sqlLimits = " GROUP BY ecoFacilities.id LIMIT :limit OFFSET :offset;"; $sqlLimits = " GROUP BY ecoFacilities.id ;"; // Concatenate query snippets for data and row count $dataQuery = $sqlData . $sqlWhere . $sqlLimits; $countQuery = $sqlCount . $sqlWhere . ";"; // Prepare, bind and execute data query $stmt = $this->populateFields($dataQuery, $rowCount, $offset, $filterArray); $stmt->execute(); // Create data objects $dataSet = []; while ($row = $stmt->fetch()) { $dataSet[] = new FacilityData($row); } // Prepare, bind then execute count query $stmt = $this->populateFields($countQuery, null, null, $filterArray); $stmt->execute(); $totalCount = $stmt->fetch()['total']; return [ 'dataset' => $dataSet, 'count' => $totalCount ]; } /** * @param $sqlQuery * @param $rowCount * @param $offset * @param $filterArray * @return false|PDOStatement * Function for fetchAll() to de-dupe code. Performs binding on PDO statements to facilitate * filtering of facilities. Returns a bound PDO statement. */ private function populateFields($sqlQuery, $rowCount, $offset, $filterArray) { $stmt = $this->_dbHandle->prepare($sqlQuery); // Ensures only one value is returned per column name $stmt->setFetchMode(\PDO::FETCH_ASSOC); // Initialize index for binding $bindIndex = 1; // Bind statusComment filter, required due to comments not being so. $statusComment = !empty($filterArray[0]) ? "%" . $filterArray[0] . "%" : null; $stmt->bindValue($bindIndex++, $statusComment ?? "%", \PDO::PARAM_STR); // First ? $stmt->bindValue($bindIndex++, $statusComment, $statusComment === null ? \PDO::PARAM_NULL : \PDO::PARAM_STR); // Second ? // Bind other filters for ($i = 1; $i <= 8; $i++) { // Assuming 8 other filters $value = !empty($filterArray[$i]) ? "%" . $filterArray[$i] . "%" : "%"; $stmt->bindValue($bindIndex++, $value, \PDO::PARAM_STR); } // Bind LIMIT and OFFSET if (!$rowCount == null || !$offset == null) { $stmt->bindValue(':limit', $rowCount, \PDO::PARAM_INT); // LIMIT ? $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); // OFFSET ? } return $stmt; } }