current_user_serial = $_SESSION[self::USER_SERIAL] ?? 0; $this->current_user_id = $_SESSION[self::USER_ID] ?? ""; $this->current_user_name = $_SESSION[self::USER_NAME] ?? ""; $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? ""; $this->pagination_page = $_SESSION[self::PAGINATION_PAGE] ?? 1; } // ===================== // Search Business Facts // ===================== public function searchBusinessFacts() { $subscription_serial = $_SESSION['subscription_serial']; $usersubscription_serial = $_SESSION['usersubscription_serial']; $subscription = (new Subscriptions())->getSubscription($subscription_serial); if ($subscription->count() === 0) { $this->logError("Invalid Subscription ({$subscription_serial}): " . __METHOD__); } $userssubscription = (new Users())->getUserSubscription($usersubscription_serial); $user_subscription_start_date = $userssubscription->record->usersubscription_start_date; $user_subscription_end_date = $userssubscription->record->usersubscription_end_date; $searchhistory = (new SearchHistory())->getUsersPreviousSearchParameters($this->current_user_serial, $subscription_serial); // Update Search History Record if (empty($searchhistory)) { (new SearchHistory())->saveUserSearchParameters($_POST); } else { (new SearchHistory())->updateUserSearchParameters($_POST); } $businessfacts = $this->getBusinessFactsPage($user_subscription_start_date, $user_subscription_end_date,); $searchhistory = (new SearchHistory())->getUsersPreviousSearchParameters($this->current_user_serial, $subscription_serial); // Populate Entry Dates with what was searched for if provided or users entry dates if no entry dates were provided $searchhistory->record->searchhistory_parameters->businessfact_entry_date_from = $businessfacts->record->businessfact_entry_date_from; $searchhistory->record->searchhistory_parameters->businessfact_entry_date_to = $businessfacts->record->businessfact_entry_date_to; $popovers = (new Popovers())->getPopovers(); $dropdowns = (new Dropdowns())->getDropdowns(); $siccodes = (new SICCodes())->getSICCodes(); $cities = (new Cities())->getSubscriptionCities($subscription_serial); $counties = (new Counties())->getSubscriptionCounties($subscription_serial); $availablerecordcount = $this->userTotalAvailableRecordCount($user_subscription_start_date, $user_subscription_end_date); $XML = $this->mergeXML($businessfacts, ""); $XML = $this->mergeXML($subscription, $XML); $XML = $this->mergeXML($popovers, $XML); $XML = $this->mergeXML($dropdowns, $XML); $XML = $this->mergeXML($cities, $XML); $XML = $this->mergeXML($counties, $XML); $XML = $this->mergeXML($siccodes, $XML); $XML = $this->mergeXML($searchhistory, $XML); $XML = $this->mergeXML($availablerecordcount, $XML); $content = $this->applyXSL($XML, $this->getXSL("searchBusinessFacts")); $this->displayContent($content); } // =================================== // Return a Business Facts Page Object // =================================== public function getBusinessFactsPage($user_subscription_start_date, $user_subscription_end_date) { // Pagination settings $pagination = filter_input(INPUT_POST, "pagination") ?: ""; $this->pagination_size = filter_input(INPUT_POST, "businessfacts_per_page", FILTER_SANITIZE_NUMBER_INT) ?: $this->pagination_size; // Connect to DB $connection = $this->connect(); $filters = $this->buildBusinessFactsFilters($user_subscription_start_date, $user_subscription_end_date); // Record Count $SQL = "select count(*) as count from view_businessfacts {$filters['where']}"; $statement = $connection->prepare($SQL); $this->bindBusinessFactsParams($statement, $filters['params']); $statement->execute(); $recordset = $statement->fetch(PDO::FETCH_ASSOC); $count = $recordset ? (int) $recordset["count"] : 0; // === PAGINATION LOGIC === switch ($pagination) { case "first": $this->pagination_page = 1; break; case "previous": $this->pagination_page = max($this->pagination_page - 1, 1); break; case "next": $this->pagination_page = min($this->pagination_page + 1, ceil($count / $this->pagination_size)); break; case "last": $this->pagination_page = (int) ceil($count / $this->pagination_size); break; } $offset = max(0, ($this->pagination_page - 1) * $this->pagination_size); $start = $offset + 1; $end = min($count, $start + $this->pagination_size - 1); // === DATA QUERY === $SQL = "select view_businessfacts.* from view_businessfacts {$filters['where']}"; if ($pagination !== 'off') { $SQL .= " limit :limit offset :offset"; } // echo $SQL; // die(); $statement = $connection->prepare($SQL); $this->bindBusinessFactsParams($statement, $filters['params']); if ($pagination !== 'off') { $statement->bindValue(":limit", $this->pagination_size, PDO::PARAM_INT); $statement->bindValue(":offset", $offset, PDO::PARAM_INT); } $statement->execute(); $businessfacts = $this->getTableXML("businessfacts", $statement); // Add searched entry_dates for search history $businessfacts->record->businessfact_entry_date_from = !empty($filters['entry_from']) ? date('m/d/Y', strtotime($filters['entry_from'])) : date('m/d/Y', strtotime($filters['subscription_start'])); $businessfacts->record->businessfact_entry_date_to = !empty($filters['entry_to']) ? date('m/d/Y', strtotime($filters['entry_to'])) : date('m/d/Y', strtotime($filters['subscription_end'])); // Insert Pagination Attributes $businessfacts->addAttribute("count", $count); $businessfacts->addAttribute("start", $start); $businessfacts->addAttribute("end", $end); $businessfacts->addAttribute("page", $this->pagination_page); $_SESSION[self::PAGINATION_PAGE] = $this->pagination_page; return $businessfacts; } private function buildBusinessFactsFilters($user_subscription_start_date, $user_subscription_end_date) { $businessfact_company = trim((string) filter_input(INPUT_POST, "businessfact_company")) ?: ""; $businessfact_zip = trim((string) filter_input(INPUT_POST, "businessfact_zip")) ?: ""; $businessfact_home_based_business = trim((string) filter_input(INPUT_POST, "businessfact_home_based_business")) ?: ""; $businessfact_employee_count_from = trim((string) filter_input(INPUT_POST, "businessfact_employee_count_from")) ?: ""; $businessfact_employee_count_to = trim((string) filter_input(INPUT_POST, "businessfact_employee_count_to")) ?: ""; $businessfact_entry_date_from = trim((string) filter_input(INPUT_POST, "businessfact_entry_date_from")) ?: ""; $businessfact_entry_date_to = trim((string) filter_input(INPUT_POST, "businessfact_entry_date_to")) ?: ""; $businessfact_lease_end_from = trim((string) filter_input(INPUT_POST, "businessfact_lease_end_from")) ?: ""; $businessfact_lease_end_to = trim((string) filter_input(INPUT_POST, "businessfact_lease_end_to")) ?: ""; // Determine real date range based upon users subscription start date $oneYearAgo = date('Y-m-d', strtotime('-1 year')); if ($user_subscription_start_date <= $oneYearAgo) { $user_subscription_start_date = $oneYearAgo; } if ($user_subscription_end_date < $user_subscription_start_date) { $this->displayNotice("Your authorized access period is invalid (end date is before start date)."); return; } $entryFrom = $this->normalizeBusinessFactsDate($businessfact_entry_date_from); $entryTo = $this->normalizeBusinessFactsDate($businessfact_entry_date_to); $leaseEndFrom = $this->normalizeBusinessFactsDate($businessfact_lease_end_from); $leaseEndTo = $this->normalizeBusinessFactsDate($businessfact_lease_end_to); $clauses = []; $params = []; $clauses[] = "view_businessfacts.businessfact_entry_date BETWEEN :subscription_start AND :subscription_end"; $params[':subscription_start'] = $user_subscription_start_date; $params[':subscription_end'] = $user_subscription_end_date; $allowedCountyIds = $this->getBusinessFactsSubscriptionCountyIds(); $requestedCountyIds = $this->getBusinessFactsPostIds("businessfact_county"); $countyIds = $requestedCountyIds ? array_values(array_intersect($requestedCountyIds, $allowedCountyIds)) : $allowedCountyIds; $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_county", $countyIds, "county_"); if ($businessfact_company !== '') { $clauses[] = "view_businessfacts.businessfact_company REGEXP :businessfact_company"; $params[':businessfact_company'] = $businessfact_company; } if ($businessfact_zip !== '') { $clauses[] = "view_businessfacts.businessfact_zip REGEXP :businessfact_zip"; $params[':businessfact_zip'] = $businessfact_zip; } $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_city", $this->getBusinessFactsPostIds("businessfact_city"), "city_"); $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_business_class", $this->getBusinessFactsPostIds("businessfact_business_class"), "class_"); $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_action", $this->getBusinessFactsPostIds("businessfact_action"), "action_"); $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_sic_code", $this->getBusinessFactsPostValues("businessfact_sic_code"), "sic_"); if ($businessfact_home_based_business === 'Y' || $businessfact_home_based_business === 'N') { $clauses[] = "view_businessfacts.businessfact_home_based_business = :businessfact_home_based_business"; $params[':businessfact_home_based_business'] = $businessfact_home_based_business; } if ($businessfact_employee_count_from !== '' && ctype_digit($businessfact_employee_count_from)) { $businessfact_employee_count_from = (int) $businessfact_employee_count_from; } else { $businessfact_employee_count_from = null; } if ($businessfact_employee_count_to !== '' && ctype_digit($businessfact_employee_count_to)) { $businessfact_employee_count_to = (int) $businessfact_employee_count_to; } else { $businessfact_employee_count_to = null; } if ($businessfact_employee_count_from !== null && $businessfact_employee_count_to !== null) { if ($businessfact_employee_count_from > $businessfact_employee_count_to) { $tmp = $businessfact_employee_count_from; $businessfact_employee_count_from = $businessfact_employee_count_to; $businessfact_employee_count_to = $tmp; } $clauses[] = "view_businessfacts.businessfact_employee_count BETWEEN :employee_count_from AND :employee_count_to"; $params[':employee_count_from'] = $businessfact_employee_count_from; $params[':employee_count_to'] = $businessfact_employee_count_to; } elseif ($businessfact_employee_count_from !== null) { $clauses[] = "view_businessfacts.businessfact_employee_count = :employee_count"; $params[':employee_count'] = $businessfact_employee_count_from; } elseif ($businessfact_employee_count_to !== null) { $clauses[] = "view_businessfacts.businessfact_employee_count = :employee_count"; $params[':employee_count'] = $businessfact_employee_count_to; } if ($entryFrom !== null || $entryTo !== null) { if ($entryFrom !== null && $entryTo === null) { $entryTo = $entryFrom; } if ($entryTo !== null && $entryFrom === null) { $entryFrom = $entryTo; } if ($entryFrom > $entryTo) { $tmp = $entryFrom; $entryFrom = $entryTo; $entryTo = $tmp; } if ($entryFrom < $user_subscription_start_date || $entryTo > $user_subscription_end_date) { $this->displayNotice("The Entry date or date range {$this->current_user_name} selected is outside your authorized access period."); return; } $clauses[] = "view_businessfacts.businessfact_entry_date BETWEEN :entry_from AND :entry_to"; $params[':entry_from'] = $entryFrom; $params[':entry_to'] = $entryTo; } if ((string) ($_SESSION['subscription_serial'] ?? '') === '10' && ($leaseEndFrom !== null || $leaseEndTo !== null)) { if ($leaseEndFrom !== null && $leaseEndTo === null) { $leaseEndTo = $leaseEndFrom; } if ($leaseEndTo !== null && $leaseEndFrom === null) { $leaseEndFrom = $leaseEndTo; } if ($leaseEndFrom > $leaseEndTo) { $tmp = $leaseEndFrom; $leaseEndFrom = $leaseEndTo; $leaseEndTo = $tmp; } $clauses[] = "view_businessfacts.businessfact_lease_end BETWEEN :lease_end_from AND :lease_end_to"; $params[':lease_end_from'] = $leaseEndFrom; $params[':lease_end_to'] = $leaseEndTo; } return [ 'where' => "where " . implode(" and ", $clauses), 'params' => $params, 'entry_from' => $entryFrom, 'entry_to' => $entryTo, 'subscription_start' => $user_subscription_start_date, 'subscription_end' => $user_subscription_end_date, ]; } private function getBusinessFactsSubscriptionCountyIds() { $counties = (new Counties())->getSubscriptionCounties($_SESSION['subscription_serial']); $countyIds = []; foreach ($counties as $county) { $countyId = trim((string) $county->subscriptioncounty_county); if (ctype_digit($countyId)) { $countyIds[] = (int) $countyId; } } return array_values(array_unique($countyIds)); } private function getBusinessFactsPostIds($name) { $values = $this->getBusinessFactsPostValues($name); $ids = []; foreach ($values as $value) { if (ctype_digit((string) $value)) { $ids[] = (int) $value; } } return array_values(array_unique($ids)); } private function getBusinessFactsPostValues($name) { $value = $_POST[$name] ?? $_POST[$name . "_"] ?? ""; if (is_array($value)) { $values = $value; } else { $values = explode(',', (string) $value); } $clean = []; foreach ($values as $item) { $item = trim((string) $item); if ($item !== '' && preg_match('/^[A-Za-z0-9_-]+$/', $item)) { $clean[] = $item; } } return array_values(array_unique($clean)); } private function addBusinessFactsInClause(&$clauses, &$params, $field, $values, $paramPrefix) { if (!$values) { if ($field === "view_businessfacts.businessfact_county") { $clauses[] = "1 = 0"; } return; } $placeholders = []; foreach ($values as $index => $value) { $placeholder = ":{$paramPrefix}{$index}"; $placeholders[] = $placeholder; $params[$placeholder] = $value; } $clauses[] = "{$field} IN (" . implode(',', $placeholders) . ")"; } private function bindBusinessFactsParams($statement, $params) { foreach ($params as $name => $value) { $type = is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR; $statement->bindValue($name, $value, $type); } } private function normalizeBusinessFactsDate($date) { if ($date === '') { return null; } $timestamp = strtotime($date); return $timestamp === false ? null : date("Y-m-d", $timestamp); } // ================================== // Users Total Available Record Count // ================================== public function userTotalAvailableRecordCount($user_subscription_start_date, $user_subscription_end_date) { // determine real date range based upon users subscription start date if ($user_subscription_start_date <= date('Y-m-d', strtotime('-1 year'))) { $user_subscription_start_date = date('Y-m-d', strtotime('-1 year')); } $clauses = [ "view_businessfacts.businessfact_entry_date between :subscription_start and :subscription_end", ]; $params = [ ':subscription_start' => $user_subscription_start_date, ':subscription_end' => $user_subscription_end_date, ]; $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_county", $this->getBusinessFactsSubscriptionCountyIds(), "county_"); $SQL = "select count(*) as count from view_businessfacts where " . implode(" and ", $clauses); $statement = $this->connect()->prepare($SQL); $this->bindBusinessFactsParams($statement, $params); $statement->execute(); return $this->getTableXML("userstotalavailablerecords", $statement); } // ===================== // Search Business Facts // ===================== public function businessFactsBasic() { $step = filter_input(INPUT_POST, "step") ?: "prompt"; switch ($step) { case "prompt": $dateranges = $this->getDateRangeXML(); $content = $this->applyXSL($dateranges, $this->getXSL("businessFactsBasic")); $this->displayContent($content); break; case "results": $week_start_raw = trim((string) filter_input(INPUT_POST, 'week_start')); $week_end_raw = trim((string) filter_input(INPUT_POST, 'week_end')); $week_start_db = $week_start_raw !== '' ? date('Y-m-d', strtotime($week_start_raw)) : null; $week_end_db = $week_end_raw !== '' ? date('Y-m-d', strtotime($week_end_raw)) : null; $businessfacts = $this->getBusinessFactsBasic($week_start_db, $week_end_db); $dateranges = $this->getDateRangeXML(); $searched = $dateranges->addChild('searchedranges'); $searched->addChild('week_start', $week_start_db ? date('m/d/Y', strtotime($week_start_db)) : ''); $searched->addChild('week_end', $week_end_db ? date('m/d/Y', strtotime($week_end_db)) : ''); $xml = $this->mergeXML($businessfacts, ''); $xml = $this->mergeXML($dateranges, $xml); $content = $this->applyXSL($xml, $this->getXSL("businessFactsBasic")); $this->displayContent($content); break; default: $this->displayHome(); } } public function getDateRangeXML() { $ranges = $this->getFridayThursdayRanges(2, new DateTimeImmutable('today')); $xml = new SimpleXMLElement(''); $labels = [ 0 => 'week_prior', 1 => 'two_weeks_prior', ]; foreach ($ranges as $i => $range) { $name = $labels[$i] ?? 'prior_' . ($i + 1); $node = $xml->addChild($name); $start = date('m/d/Y', strtotime($range['start'])); $end = date('m/d/Y', strtotime($range['end'])); $node->addChild('start', $start); $node->addChild('end', $end); } $xml->subscription_serial = $_SESSION['subscription_serial']; return $xml; } public function getFridayThursdayRanges(int $weeks = 2, ?DateTimeInterface $today = null): array { $today = $today ? DateTimeImmutable::createFromInterface($today) : new DateTimeImmutable('today'); // "last thursday" gives the most recent Thursday strictly before today. // If today IS Thursday, we want today as the end date. $end = ($today->format('N') === '4') ? $today : $today->modify('last thursday'); $ranges = []; for ($i = 0; $i < $weeks; $i++) { $start = $end->modify('-6 days'); $ranges[] = [ 'start' => $start->format('Y-m-d'), 'end' => $end->format('Y-m-d'), ]; $end = $start->modify('-1 day'); // move to previous block } return $ranges; } // =============================== // Return an Business Facts Object // =============================== public function getBusinessFactsBasic($week_start, $week_end) { $SQL = "select view_businessfacts.* from view_businessfacts where view_businessfacts.businessfact_entry_date between '{$week_start}' and '{$week_end}' order by view_businessfacts.businessfact_county_name_verbose, view_businessfacts.businessfact_zip, view_businessfacts.businessfact_company"; return $this->getTable("businessfacts", $SQL); } } ?>