container = $container; $this->em = $em; $this->im = $im; $this->hub_distributor = $hub_distributor; $this->hub_filter_logger = $hub_filter_logger; $this->trans = $trans; $this->rt = $rt; } // TODO: move this to env or something so we can enable and disable filters without code changes protected function getActiveFilters(): array { return [ 'App\Service\HubFilter\Filters\DateAndTimeHubFilter', 'App\Service\HubFilter\Filters\JoTypeHubFilter', 'App\Service\HubFilter\Filters\PaymentMethodHubFilter', 'App\Service\HubFilter\Filters\RiderAvailabilityHubFilter', 'App\Service\HubFilter\Filters\InventoryHubFilter', 'App\Service\HubFilter\Filters\RoundRobinHubFilter', 'App\Service\HubFilter\Filters\MaxResultsHubFilter', ]; } public function find(HubCriteria $criteria) { $point = $criteria->getPoint(); $limit_results = $criteria->getLimitResults(); $limit_distance = $criteria->getLimitDistance(); $jo_type = $criteria->getJoType(); $flag_inventory_check = $criteria->hasInventoryCheck(); $flag_riders_check = $criteria->hasRidersCheck(); $items = $criteria->getItems(); $date_time = $criteria->getDateTime(); $payment_method = $criteria->getPaymentMethod(); $flag_emergency = $criteria->isEmergency(); $flag_round_robin = $criteria->isRoundRobin(); $jo_id = $criteria->getJobOrderId(); $customer_id = $criteria->getCustomerId(); $customer_class = $criteria->getCustomerClass(); // needed for JORejection records and SMS notifs $order_date = $criteria->getOrderDate(); $service_type = $criteria->getServiceType(); // error_log('payment methods ' . $payment_method); // error_log('distance limit ' . $limit_distance); // error_log('emergency flag ' . $flag_emergency); // get all the hubs within distance $filtered_hubs = $this->getClosestHubs($point, $limit_distance, $jo_id, $customer_id); // build param list $params = [ 'date_time' => $date_time, 'flag_inventory_check' => $flag_inventory_check, 'customer_class' => $customer_class, 'jo_type' => $jo_type, 'order_date' => $order_date, 'service_type' => $service_type, 'items' => $items, 'flag_emergency' => $flag_emergency, 'limit_results' => $limit_results, 'payment_method' => $payment_method, 'flag_riders_check' => $flag_riders_check, 'flag_round_robin' => $flag_round_robin, ]; // loop through all enabled filters foreach ($this->getActiveFilters() as $hub_filter) { // no hubs left to filter if (empty($filtered_hubs)) { break; } $f = $this->container->get($hub_filter); // check if supported area is exempted from this filter if ($this->isExemptedByArea($f->getID(), $point)) { continue; } $f->setJOID($jo_id); $f->setCustomerID($customer_id); // get requested params only $req_params = array_intersect_key($params, array_flip($f->getRequestedParams())); // filter hub list $filtered_hubs = $f->filter($filtered_hubs, $req_params); // error_log($f->getID() . ' hubs ' . json_encode($filtered_hubs)); } // error_log('final hub list ' . json_encode($filtered_hubs)); return $filtered_hubs; } protected function getClosestHubs(Point $point, $limit_distance, $jo_id, $customer_id) { // get closest hubs based on st_distance function from db $query_string = 'SELECT h, st_distance(h.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Hub h WHERE h.status_open = true ORDER BY dist'; $query = $this->em->createQuery($query_string) ->setParameter('lat', $point->getLatitude()) ->setParameter('lng', $point->getLongitude()); // error_log($query->getSql()); $result = $query->getResult(); $hubs = []; $hubs_data = []; foreach ($result as $row) { $hubs[] = $row[0]; // get coordinates of hub $hub_coordinates = $row[0]->getCoordinates(); $cust_lat = $point->getLatitude(); $cust_lng = $point->getLongitude(); $hub_lat = $hub_coordinates->getLatitude(); $hub_lng = $hub_coordinates->getLongitude(); // get distance in kilometers from customer point to hub point $dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng); if ($dist < $limit_distance) { $hubs_data[] = [ 'hub' => $row[0], 'db_distance' => $row['dist'], 'distance' => $dist, 'duration' => 0, 'jo_count' => 0, 'inventory' => 0, ]; } else { $this->hub_filter_logger->logFilteredHub($row[0], 'not_within_distance', $jo_id, $customer_id); } } return $hubs_data; } // convert db distance to kilometers protected function distance($lat1, $lon1, $lat2, $lon2) { if (($lat1 == $lat2) && ($lon1 == $lon2)) return 0; $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; return round(($miles * 1.609344), 1); } protected function isExemptedByArea(string $filter_id, Point $coordinates): bool { $long = $coordinates->getLongitude(); $lat = $coordinates->getLatitude(); // get supported area given a set of coordinates $query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true'); $area = $query->setParameter('long', $long) ->setParameter('lat', $lat) ->setMaxResults(1) ->getOneOrNullResult(); if ($area !== null) { // get all exceptions $exceptions = $area->getHubFilterExceptions(); if (isset($exceptions[$filter_id])) { error_log("FILTER " . $filter_id . " DISABLED FOR AREA: " . $area->getName()); // disable this filter for this area return true; } } // filter is in place return false; } }