<?php
namespace App\BackendBundle\Helper;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManagerInterface;
use App\BackendBundle\Interfaces\LocationInterface;
use App\BackendBundle\Helper\GoogleApiHelper;
use App\BackendBundle\Model\Address\AddressParameter;
use App\Entity\Address;
use App\Entity\AddressCity;
use App\Entity\AddressDistrict;
use App\Entity\AddressType;
use App\Entity\AddressLatLon;
class AddressHelper {
private EntityManagerInterface $em;
private Connection $connection;
private GoogleApiHelper $googleApiHelper;
public function __construct(EntityManagerInterface $em, GoogleApiHelper $googleApiHelper) {
$this->em = $em;
$this->connection = $this->em->getConnection();
$this->googleApiHelper = $googleApiHelper;
}
public function createAddress($addressData, $withLatLon = false) {
$address = new Address();
if (array_key_exists('street', $addressData)) {
$address->setStreet($addressData['street']);
}
if (array_key_exists('number', $addressData)) {
$address->setNumber($addressData['number']);
}
if (array_key_exists('floor', $addressData)) {
$address->setFloor($addressData['floor']);
}
if (array_key_exists('stair', $addressData)) {
$address->setStair($addressData['stair']);
}
if (array_key_exists('type', $addressData)) {
$address->setAddressType($addressData['type']);
}
if (array_key_exists('city', $addressData)) {
$address->setAddressCity($addressData['city']);
}
$this->em->persist($address);
$this->em->flush();
return $address;
}
public function createAddressLatLon(Address $address, $lat, $lon) {
$addressLatLon = new AddressLatLon();
$addressLatLon->setAddress($address);
$addressLatLon->setLat($lat);
$addressLatLon->setLon($lon);
$this->em->persist($addressLatLon);
$this->em->flush();
return $addressLatLon;
}
private function updateAddressLatLon(Address $address, AddressParameter $addressParameter) {
if ($addressParameter->hasLatLon()) {
$this->updateAddressLatLonValues($address, $addressParameter->getLat(), $addressParameter->getLon());
return;
}
if ($addressParameter->getUseGoogleApi()) {
$this->updateAddressLatLonGoogle($address);
return;
}
}
private function updateAddressLatLonValues(Address $address, $lat, $lon) {
$addressLatLon = $address->getAddressLatLon();
if (empty($addressLatLon)) {
$this->createAddressLatLon($address, $lat, $lon);
return;
}
$addressLatLon->setLat($lat);
$addressLatLon->setLon($lon);
$this->em->persist($addressLatLon);
$this->em->flush();
}
private function updateAddressLatLonGoogle(Address $address) {
[$lat, $lon] = $this->googleApiHelper->getLatLon($address);
if (empty($lat) || empty($lon)) {
return;
}
$addressLatLon = $address->getAddressLatLon();
if (empty($addressLatLon)) {
$this->createAddressLatLon($address, $lat, $lon);
return;
}
$addressLatLon->setLat($lat);
$addressLatLon->setLon($lon);
$this->em->persist($addressLatLon);
$this->em->flush();
}
public function deleteAddressLatLon(Address $address) {
$addressLatLon = $address->getAddressLatLon();
if (!empty($addressLatLon)) {
$this->em->remove($addressLatLon);
$this->em->flush();
}
}
public function createAddressCity($zip, $name) {
$city = new AddressCity();
$city->setZip($zip);
$city->setName($name);
$this->em->persist($city);
return $city;
}
public function updateAddress(Address $address, $locationData, $useGoogleAPI = false) {
$cityString = $locationData['city'];
$zipString = $locationData['zip'];
// First check if the city already exists in our DB
$city = $this->getAddressCityByName($cityString);
if (is_null($city)) {
// if not then create a new entry
$city = $this->createAddressCity($zipString, $cityString);
}
$streetParts = $this->getStreetParts($locationData['street']);
if (!empty($streetParts['street'] && !empty($streetParts['number']))) {
$address->setStreet($streetParts['street']);
$address->setNumber($streetParts['number']);
} else {
$address->setStreet($locationData['street']);
}
$address->setAddressCity($city);
$this->em->persist($address);
$this->em->flush();
$lat = $locationData['lat'];
$lon = $locationData['lon'];
$this->updateAddressLatLon($address, $lat, $lon, $useGoogleAPI);
return $address;
}
public function updateAddressAdmin($addressData) {
$address = $addressData['address'];
if (array_key_exists('street', $addressData)) {
$address->setStreet($addressData['street']);
}
if (array_key_exists('number', $addressData)) {
$address->setNumber($addressData['number']);
}
if (array_key_exists('floor', $addressData)) {
$address->setFloor($addressData['floor']);
}
if (array_key_exists('stair', $addressData)) {
$address->setStair($addressData['stair']);
}
if (array_key_exists('latLon', $addressData)) {
$address->setAddressLatLon($addressData['latLon']);
}
if (array_key_exists('type', $addressData)) {
$address->setAddressType($addressData['type']);
}
if (array_key_exists('city', $addressData)) {
$address->setAddressCity($addressData['city']);
}
if (array_key_exists('latLon', $addressData)) {
$addressData['latLon']->setAddress($address);
}
$this->em->persist($address);
if (array_key_exists('latLon', $addressData)) {
$this->em->persist($addressData['latLon']);
}
return $address;
}
public function createAddressByData(AddressParameter $addressParameter) {
$city = $this->getAddressCity($addressParameter->getCity(), $addressParameter->getZip());
if (empty($addressParameter->getAddressType())) {
$addressParameter->setAddressType($this->getAddressTypeByShortName('STD'));
}
// our street string contains both name and number, so we split it here
// and store it as an array in result
$streetParts = $this->getStreetParts($addressParameter->getStreet());
//preg_match('/^([^\d]*[^\d\s]) *(\d.*)$/', $street, $streetAndNumber);
$addressData = array();
$addressData['type'] = $addressParameter->getAddressType();
$addressData['city'] = $city;
$address = null;
if (!empty($streetParts['street'] && !empty($streetParts['number']))) {
// create a new address
$addressData['street'] = $streetParts['street'];
$addressData['number'] = $streetParts['number'];
$address = $this->createAddress($addressData);
} else {
// create a new address
$addressData['street'] = $addressParameter->getStreet();
$address = $this->createAddress($addressData);
}
if (!empty($address)) {
$this->updateAddressLatLon($address, $addressParameter);
}
return $address;
}
public function getAddressCity($cityString, $zipString) {
// first check if city exists by name
$cityByName = $this->getAddressCityByName($cityString);
if (!empty($cityByName)) {
return $cityByName;
}
// second check if city exists by zip
$cityByZip = $this->getAddressCityByZip($zipString);
if (!empty($cityByZip)) {
return $cityByZip;
}
// check if city and zip has wrongly be changed
if (is_numeric($cityString)) {
$cityByZip = $this->getAddressCityByZip($cityString);
if (!empty($cityByZip)) {
return $cityByZip;
}
}
// if no case matches then create a new entry
$newCity = $this->createAddressCity($zipString, $cityString);
return $newCity;
}
public function getAddressCityByID($id) {
return $this->em->getRepository(AddressCity::class)->findOneBy(array('id' => $id));
}
public function getAddressByID($id) {
return $this->em->getRepository(Address::class)->findOneBy(array('id' => $id));
}
public function getAddressesByCommaIds($string) {
$idArray = explode(",", $string);
$addressAdded = array();
$addresses = array();
foreach ($idArray as $id) {
if (array_key_exists($id, $addressAdded)) {
continue;
}
$address = $this->getAddressByID($id);
if (empty($address)) {
continue;
}
array_push($addresses, $address);
$addressAdded[$id] = 1;
}
return $addresses;
}
public function getAddressCityByName($name) {
$dqlString = 'SELECT city FROM App\Entity\AddressCity city
WHERE city.name = :name';
$query = $this->em->createQuery($dqlString);
$query->setParameter('name', $name);
$cities = $query->getResult();
// if exactly one result return it
if (count($cities) == 1) {
return $cities[0];
}
// if more then one result - return first
if (count($cities) > 1) {
return $cities[0];
}
// default
return null;
}
public function searchAddressCitiesByName($searchText) {
$sql = "SELECT id as id, name as text "
. "FROM address_city as ac "
. "WHERE ac.name LIKE :searchText";
$stmt = $this->connection->prepare($sql);
$stmt->bindValue('searchText', "%$searchText%");
$stmtResult = $stmt->executeQuery();
return $stmtResult->fetchAllAssociative();
}
public function getAddressCityByZip($zip) {
$sqlText = 'SELECT city FROM App\Entity\AddressCity city
WHERE city.zip = :zip';
$query = $this->em->createQuery($sqlText);
$query->setParameter('zip', $zip);
$cities = $query->getResult();
// if exactly one result return it
if (count($cities) == 1) {
return $cities[0];
}
// if more then one result - return first
if (count($cities) > 1) {
return $cities[0];
}
// default
return null;
}
// used in autocomplete controller
public function getAutocompleteCities($searchText) {
if (!empty($searchText)) {
$sqlText = "SELECT a FROM App\Entity\AddressCity a WHERE a.name like :searchText OR a.zip like :searchNumber";
$query = $this->em->createQuery($sqlText);
$query->setParameter('searchNumber', $searchText);
$query->setParameter('searchText', '%' . $searchText . '%');
return $query->getResult();
}
return array();
}
public function getAddressCitiesByZipArray() {
$cities = $this->em->getRepository(AddressCity::class)->findAll();
$result = array();
/* @var $city AddressCity */
foreach ($cities as $city) {
$zip = $city->getZip();
$result[$zip] = $city;
}
return $result;
}
public function getAddressTypesByShortNameArray() {
$types = $this->em->getRepository(AddressType::class)->findAll();
$result = array();
/* @var $type AddressType */
foreach ($types as $type) {
$shortName = $type->getShortName();
$result[$shortName] = $type;
}
return $result;
}
public function getAddressTypeByShortName($shortName) {
$result = $this->em->getRepository(AddressType::class)->findOneBy(array('shortName' => $shortName));
return $result;
}
public function getAddressDistrict(Address $address = null) {
if ($address == null) {
return null;
}
$city = $address->getAddressCity();
if (empty($city)) {
return null;
}
return $city->getAddressDistrict();
}
public function getAddressDistrictByID($id) {
return $this->em->getRepository(AddressDistrict::class)->findOneBy(array('id' => $id));
}
public function getAddressDistrictByName($name) {
return $this->em->getRepository(AddressDistrict::class)->findOneBy(array('name' => $name));
}
public function getAddressDistrictByStateID($stateID) {
return $this->em->getRepository(AddressDistrict::class)->findBy(array('addressState' => $stateID), array('name' => 'ASC'));
}
public function getAddressDistrictByStateName($stateName) {
$sqlText = 'SELECT district FROM App\Entity\AddressDistrict district
JOIN district.addressState state
WHERE state.name = :stateName
ORDER BY district.name ASC';
$query = $this->em->createQuery($sqlText);
$query->setParameter('stateName', $stateName);
return $query->getResult();
}
public function getLocationDistricts($locations) {
$districts = array();
/* @var $location LocationInterface */
foreach ($locations as $location) {
$address = $location->getAddress();
if (empty($address)) {
continue;
}
/* @var $addressDistrict AddressDistrict */
$addressDistrict = $this->getAddressDistrict($address);
if (empty($addressDistrict)) {
continue;
}
// check if the district already is in the array
if (!in_array($addressDistrict, $districts)) {
// append it to the array
array_push($districts, $addressDistrict);
}
}
return $districts;
}
public function getStreetParts($streetName): array
{
$result = array();
$result['street'] = null;
$result['number'] = null;
if (empty($streetName)) {
return $result;
}
$spaceLast = strrpos($streetName, " ");
if (!$spaceLast) {
$result['street'] = $streetName;
return $result;
}
$result['street'] = trim(substr($streetName, 0, $spaceLast));
$result['number'] = trim(substr($streetName, $spaceLast));
return $result;
}
public function sortLocations(Collection $locations) {
if (count($locations) < 2) {
return $locations;
}
$arrLocations = $locations->toArray();
uasort($arrLocations, function ($location1, $location2) {
$address1 = $location1->getAddress();
$address2 = $location2->getAddress();
if ($address1->getStreet() > $address2->getStreet()) {
return 1;
} else {
return -1;
}
});
return $arrLocations;
}
}