em = $entityManager; } private function daysInMonth($month, $year) { return $month == 2 ? ($year % 4 ? 28 : ($year % 100 ? 29 : ($year % 400 ? 28 : 29))) : (($month - 1) % 7 % 2 ? 30 : 31); } //$months = number of Month to the last season public function getCalendarWithPadding($months) { $calendar = null; $currentMonth = date('n'); $currentYear = date('Y'); $weekDays = ['So','Mo','Di','Mi','Do','Fr','Sa']; setlocale(LC_TIME, "german"); $yearTurned = false; $yearsTurned = 0; $now = new \DateTime(); for($m = $currentMonth; $m < ($currentMonth + $months); $m++) { $actualMonth = $m % 12; if($actualMonth == 0) { $actualMonth = 12; $yearTurned = true; } $actualYear = $currentYear + $yearsTurned; if($yearTurned) { $yearsTurned = $yearsTurned + 1; $yearTurned = false; } $date = getdate(mktime(0,0,0, $actualMonth, 1, $actualYear)); $weekDay = $weekDays[$date['wday']]; $numberDays = $this->daysInMonth($actualMonth, $actualYear); $data = null; $actualStartingWeekDay = $date['wday']; if($actualStartingWeekDay == 0) { $actualStartingWeekDay = 7; } //first empty days from the last month for($fwd = 0; $fwd < $actualStartingWeekDay - 1; $fwd++) { $day = new CalendarDayState(); $day->setDay(0); $day->setIsBookable(false); $day->setIsInSeason(false); $day->setIsReserved(false); $day->setIsReservationBegin(false); $day->setIsReservationEnd(false); $day->setIsEmpty(true); $day->setIsPastDate(false); $data[] = $day; } //days of month for($d = 1; $d <= $numberDays; $d++) { $day = new CalendarDayState(); $day->setDay($d); $datetime = new \DateTime(); $datetime->setTimestamp(mktime(0,0,0, $actualMonth, $d, $actualYear)); $day->setDate($datetime); $day->setIsBookable(false); $day->setIsInSeason(false); $day->setIsReserved(false); $day->setIsReservationBegin(false); $day->setIsReservationEnd(false); $day->setIsEmpty(false); if($datetime < $now){ $day->setIsPastDate(true); }else{ $day->setIsPastDate(false); } $data[] = $day; } $startIndex = count($data); //last empty days from the next month for($lwd = $startIndex; $lwd < 42; $lwd++) { $day = new CalendarDayState(); $day->setDay(0); $day->setIsBookable(false); $day->setIsInSeason(false); $day->setIsReserved(false); $day->setIsReservationBegin(false); $day->setIsReservationEnd(false); $day->setIsEmpty(true); $day->setIsPastDate(false); $data[] = $day; } $calendar[$actualYear."-".$actualMonth] = [ 'numberDays' => $numberDays, 'monthNumber' => $actualMonth, 'monthName' => utf8_encode(strftime("%B", mktime(0, 0, 0, $actualMonth, 1, $actualYear))), 'year' => $actualYear, 'startWeekDay' => $weekDay, 'data' => $data, 'marked' => 0 ]; } return $calendar; } public function getCalendar($lodging) { $months = $this->getMinCalendarEntriesByLodging($lodging); $calendar = null; $currentMonth = date('n'); $currentYear = date('Y'); $weekDays = ['So','Mo','Di','Mi','Do','Fr','Sa']; setlocale(LC_TIME, "german"); $yearTurned = false; $yearsTurned = 0; for($m = $currentMonth; $m < ($currentMonth + $months); $m++) { $actualMonth = $m % 12; if($actualMonth == 0) { $actualMonth = 12; $yearTurned = true; } $actualYear = $currentYear + $yearsTurned; if($yearTurned) { $yearsTurned = $yearsTurned + 1; $yearTurned = false; } $date = getdate(mktime(0,0,0, $actualMonth, 1, $actualYear)); $weekDay = $weekDays[$date['wday']]; $numberDays = $this->daysInMonth($actualMonth, $actualYear); $data = null; // alle Wochentage for($d = 1; $d <= $numberDays; $d++) { // standard-vorbelegung $day = new CalendarDayState(); $day->setDay($d); $day->setIsBookable(false); $day->setIsInSeason(false); $day->setIsReserved(false); $day->setIsReservationBegin(false); $day->setIsReservationEnd(false); $datetime = new \DateTime(); $datetime->setTimestamp(mktime(0,0,0, $actualMonth, $d, $actualYear)); $day->setDate($datetime); $data[] = $day; } $calendar[] = [ 'numberDays' => $numberDays, 'monthNumber' => $actualMonth, 'monthName' => utf8_encode(strftime("%B", mktime(0, 0, 0, $actualMonth, 1, $actualYear))), 'year' => $actualYear, 'startWeekDay' => $weekDay, 'data' => $data, 'marked' => 0 ]; } return $calendar; } /** * Retrieve needed number of months to generate a calendar which includes all available seasons * * @param FewoLodging $lodging * @return int */ public function getMinCalendarEntriesByLodging($lodging) { $latestSeason = $this->em->getRepository('AppBundle:FewoLodging')->findLatestSeasonEndForLodging($lodging); $curMonthDate = new \DateTime(); $curMonthDate->setDate($curMonthDate->format('Y'), $curMonthDate->format('n'), 1); $seasonMonthDate = new \DateTime($latestSeason->getToDate()->format('Y-m-d')); $seasonMonthDate->setDate($seasonMonthDate->format('Y'), $seasonMonthDate->format('n'), 1); $dateInterval = $seasonMonthDate->diff($curMonthDate); $month = intval($dateInterval->format('%m')) + (intval($dateInterval->format('%y')) * 12); return 2 + $month; } /** * @param FewoLodging $lodging * @return array|null */ public function getReservations(FewoLodging $lodging) { $reservations = $lodging->getReservations(); $ret = null; for($i = 0; $i < count($reservations); $i++) { $fromDate = $reservations[$i]->getFromDate(); $toDate = $reservations[$i]->getToDate(); $ret[] = [ 'startDay' => $fromDate->format('j'), 'startMonth' => $fromDate->format('n'), 'startYear' => $fromDate->format('Y'), 'endDay' => $toDate->format('j'), 'endMonth' => $toDate->format('n'), 'endYear' => $toDate->format('Y') ]; } return $ret; } private function getMonthIndex($calendar, $month, $year) { $result = 0; for($i = 0; $i < count($calendar); $i++) { if($calendar[$i]['monthNumber'] == $month && $calendar[$i]['year'] == $year) { $result = $i; break; } } return $result; } /** * @param $calendar * @param \DateTime $fromDate * @param \DateTime $toDate * @param $appendix * @return mixed */ private function markCalendarDays($calendar, \DateTime $fromDate, \DateTime $toDate, $reservationMode = false, FewoPrice $price = null, FewoReservation $reservation = null) { $startDay = $fromDate->format('j'); $startMonth = $fromDate->format('n'); $startYear = $fromDate->format('Y'); $endDay = $toDate->format('j'); $endMonth = $toDate->format('n'); $endYear = $toDate->format('Y'); $today = new \DateTime(); $todayDay = $today->format('j'); $todayMonth = $today->format('n'); $todayYear = $today->format('Y'); if ($endMonth >= $todayMonth || ($todayMonth == 12 && $endMonth >= 1 && ($todayYear < $endYear))) //($toDate > $today) { if($fromDate < $today) { $startDay = 1; $startMonth = $today->format('n'); $startYear = $today->format('Y'); } // wenn sich der Kalender über mehrere Jahre zieht, könnte es hier zu Problemen kommen $startCalendarIndex = $this->getMonthIndex($calendar, $startMonth, $startYear); $endCalendarIndex = $this->getMonthIndex($calendar, $endMonth, $endYear); // anfang und ende innerhalb eines monats if($startCalendarIndex == $endCalendarIndex) { $data = $calendar[$startCalendarIndex]['data']; $calendar[$startCalendarIndex]['marked'] = 1; // hier geht man von ungepaddeten Kalendermonaten aus $startIndex = $startDay - 1; $endIndex = $endDay - 1; for ($i = $startIndex; $i <= $endIndex; $i++) { if ($reservationMode) { if ($i == $startIndex) { $data[$i]->setIsReserved(true); $data[$i]->setIsReservationBegin(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } elseif ($i == $endIndex) { $data[$i]->setIsReserved(true); $data[$i]->setIsReservationEnd(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); //$data[$i]->setIsBookable(true); } else { $data[$i]->setIsReserved(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } else { $data[$i]->setIsReserved(false); $data[$i]->setPrice($price); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } $calendar[$startCalendarIndex]['data'] = $data; } else { // erster monat $data = $calendar[$startCalendarIndex]['data']; $calendar[$startCalendarIndex]['marked'] = 1; $startIndex = $startDay - 1; $endIndex = count($data) - 1; for($i = $startIndex; $i <= $endIndex; $i++) { if ($reservationMode) { if ($i == $startIndex) { $data[$i]->setIsReserved(true); $data[$i]->setIsReservationBegin(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } else { $data[$i]->setIsReserved(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } else { $data[$i]->setIsReserved(false); $data[$i]->setPrice($price); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } $calendar[$startCalendarIndex]['data'] = $data; // letzter monat $data = $calendar[$endCalendarIndex]['data']; $calendar[$endCalendarIndex]['marked'] = 1; $startIndex = 0; $endIndex = $endDay - 1; for($i = $startIndex; $i <= $endIndex; $i++) { if ($reservationMode) { if ($i == $endIndex) { $data[$i]->setIsReserved(true); $data[$i]->setIsReservationEnd(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); //$data[$i]->setIsBookable(true); } else { $data[$i]->setIsReserved(true); $data[$i]->setReservation($reservation); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } else { $data[$i]->setIsReserved(false); $data[$i]->setPrice($price); $data[$i]->setIsInSeason(true); $data[$i]->setIsBookable(false); } } $calendar[$endCalendarIndex]['data'] = $data; // alle monate dazwischen for($i = $startCalendarIndex + 1; $i < $endCalendarIndex; $i++) { $data = $calendar[$i]['data']; $calendar[$i]['marked'] = 1; $startIndex = 0; $endIndex = count($data) - 1; for($j = $startIndex; $j <= $endIndex; $j++) { if ($reservationMode) { $data[$j]->setIsReserved(true); $data[$j]->setReservation($reservation); $data[$j]->setIsInSeason(true); $data[$j]->setIsBookable(false); } else { $data[$j]->setIsReserved(false); $data[$j]->setPrice($price); $data[$j]->setIsInSeason(true); $data[$j]->setIsBookable(false); } } $calendar[$i]['data'] = $data; } } } return $calendar; } // in der Regel: calendar = pricesCalendar; extensionCalendar = reservationsCalendar private function mergeCalendars($calendar, $extensionCalendar) { for($i = 0; $i < count($calendar); $i++) { $calendarData = $calendar[$i]['data']; $extensionCalendarData = $extensionCalendar[$i]['data']; $endIndex = count($calendarData); for($j = 0; $j < $endIndex; $j++) { if ($extensionCalendarData[$j]->getIsReserved()) { $price = $calendarData[$j]->getPrice(); //$isInSeason = $calendarData[$j]->getIsInSeason(); $reservation = $calendarData[$j]->getReservation(); $calendarData[$j] = $extensionCalendarData[$j]; if ($calendarData[$j]->getIsReservationEnd() && $extensionCalendarData[$j]->getIsReservationBegin()) { $calendarData[$j]->setReservation($reservation); $calendarData[$j]->setIsReservationBegin(false); $calendarData[$j]->setIsReservationEnd(false); } $calendarData[$j]->setIsInSeason(true); $calendarData[$j]->setPrice($price); } } $calendar[$i]['data'] = $calendarData; } return $calendar; } /** @var CalendarDayState[] $calendarMonth */ private function findCalendarDayIndexInMonth($dayNumber, $calendarMonth) { $result = 0; for ($i = 0; $i < count($calendarMonth); $i++) { if ($calendarMonth[$i]->getDay() == $dayNumber) { $result = $i; break; } } return $result; } public function mergeWithPaddedCalendar($calendar, $paddedCalendar) { $i = 0; foreach ($paddedCalendar as $key => $data){ $calendarDataCurrMonth = $calendar[$i]['data']; $paddedCalendarDataCurrMonth = $paddedCalendar[$key]['data']; $startIndex = $this->findCalendarDayIndexInMonth(1, $paddedCalendarDataCurrMonth); //$startIndex = array_search(1, $paddedCalendarDataCurrMonth); for($j = 0; $j < count($calendarDataCurrMonth); $j++, $startIndex++) { $paddedCalendarDataCurrMonth[$startIndex] = $calendarDataCurrMonth[$j]; } $paddedCalendar[$key]['data'] = $paddedCalendarDataCurrMonth; $i++; } /*for($i = 0; $i < count($paddedCalendar); $i++) { $calendarDataCurrMonth = $calendar[$i]['data']; $paddedCalendarDataCurrMonth = $paddedCalendar[$i]['data']; $startIndex = $this->findCalendarDayIndexInMonth(1, $paddedCalendarDataCurrMonth); //$startIndex = array_search(1, $paddedCalendarDataCurrMonth); for($j = 0; $j < count($calendarDataCurrMonth); $j++, $startIndex++) { $paddedCalendarDataCurrMonth[$startIndex] = $calendarDataCurrMonth[$j]; } $paddedCalendar[$i]['data'] = $paddedCalendarDataCurrMonth; }*/ return $paddedCalendar; } private function isNextDay($current, $next) { $result = false; $calculatedNextDate = date('d.m.Y', strtotime($current." + 1 day")); if($calculatedNextDate == $next) { $result = true; } return $result; } private function getMonthFromCalendar($calendar, $monthNumber) { $result = []; for($i = 0; $i < count($calendar); $i++) { if($calendar[$i]['monthNumber'] == $monthNumber) { $result = $calendar[$i]; } } return $result; } /** * @param FewoLodging $lodging * @param $calendar * @return array|null calendars */ private function filterReservableDays(FewoLodging $lodging, $calendar) { $potentiallyReservableDays = []; for ($currMonthIndex = 0; $currMonthIndex < count($calendar); $currMonthIndex++) { $currMonth = $calendar[$currMonthIndex]; $data = $currMonth['data']; for ($currDayIndex = 0; $currDayIndex < count($data); $currDayIndex++) { $currDay = $data[$currDayIndex]; // We passed the season end? => All days in the stack are not reservable, as their minimum stay // requirement would mean their reservation end is outside a season if (!$currDay->getIsInSeason()) { while (!empty($potentiallyReservableDays)) { $potentiallyReservableDay = array_pop($potentiallyReservableDays); $potentiallyReservableDay->setIsBookable(false); } $currDay->setIsBookable(false); continue; } $currSeason = $currDay->getPrice()->getSeason(); // Process the stack of reservable days and set reservable=true, if their minimum stay requirement could // be fulfilled. Set reservable=false if there is no chance for the stack entry to be reservable. // Remove potentially reservable days from the stack for which a final decision has been made. if (!$currDay->getIsReserved() || $currDay->getIsReservationBegin()) { foreach ($potentiallyReservableDays as $k => $potentiallyReservableDay) { $nights = intval($potentiallyReservableDay->getDate()->diff($currDay->getDate())->format('%a')); if ($nights >= $currSeason->getMinimumStay()) { $potentiallyReservableDay->setIsBookable(true); unset($potentiallyReservableDays[$k]); } elseif ($currDay->getIsReservationBegin()) { // Reservation begin => We are absolutely sure that this day is not reservable $potentiallyReservableDay->setIsBookable(false); unset($potentiallyReservableDays[$k]); } } } // Add the current calendar day to stack, as we need to analyse the following days in order to be able // to decide if the day is reservable or not. if ((!$currDay->getIsReserved() || $currDay->getIsReservationEnd()) && ($currSeason->getOnlyWeekday() == null || $currSeason->getOnlyWeekday() == $currDay->getDate()->format('w'))) { $potentiallyReservableDays[] = $currDay; } else { $currDay->setIsBookable(false); } } } // Remaining entries in the stack are definitely not reservable. Otherwise they would have been processed before. foreach ($potentiallyReservableDays as $potentiallyReservableDay) { $potentiallyReservableDay->setIsBookable(false); } return $calendar; } private function findCalendarDayKey($calendarMonth, $dayNumber){ foreach ($calendarMonth['data'] as $key => $data) { if($data->getDay() == $dayNumber){ return $key; } } } public function calendarAndFillDayStates($paddedCalendar, FewoLodging $lodging){ $startMonth = array_values($paddedCalendar)[0]['monthNumber']; $startYear = array_values($paddedCalendar)[0]['year']; $endMonth = array_values($paddedCalendar)[count($paddedCalendar)-1]['monthNumber']; $endYear = array_values($paddedCalendar)[count($paddedCalendar)-1]['year']; $endDay = array_values($paddedCalendar)[count($paddedCalendar)-1]['numberDays']; $startDate = new \DateTime(date( "Y-m-d", mktime(0,0,0, $startMonth, 1, $startYear))); $endDate = new \DateTime(date( "Y-m-d", mktime(0,0,0, $endMonth, $endDay, $endYear))); //reservations in the Calender Time $reservations = $lodging->getReservationsFilter($startDate, $endDate); foreach($reservations as $reservation) { $setFromIntervall = clone $reservation->getFromDate(); $fromDate = $reservation->getFromDate(); $toDate = $reservation->getToDate(); // $diff = $fromDate->diff($toDate); $interval = \DateInterval::createFromDateString('1 day'); $period = new \DatePeriod($setFromIntervall->modify('+1 day'), $interval, $toDate); //first if(isset($paddedCalendar[$fromDate->format("Y-n")])){ $key = $this->findCalendarDayKey($paddedCalendar[$fromDate->format("Y-n")], $fromDate->format("d")); $paddedCalendar[$fromDate->format("Y-n")]['data'][$key]->setIsReservationBegin(true); $paddedCalendar[$fromDate->format("Y-n")]['data'][$key]->setReservation($reservation); } //last if(isset($paddedCalendar[$toDate->format("Y-n")])) { $key = $this->findCalendarDayKey($paddedCalendar[$toDate->format("Y-n")], $toDate->format("d")); $paddedCalendar[$toDate->format("Y-n")]['data'][$key]->setIsReservationEnd(true); } //days foreach ($period as $dt) { if(isset($paddedCalendar[$dt->format("Y-n")])) { $key = $this->findCalendarDayKey($paddedCalendar[$dt->format("Y-n")], $dt->format("d")); $paddedCalendar[$dt->format("Y-n")]['data'][$key]->setIsReserved(true); $paddedCalendar[$dt->format("Y-n")]['data'][$key]->setReservation($reservation); } } } $prices = $lodging->getPricesFilter($startDate, $endDate); foreach ($prices as $price) { /** @var FewoSeason $season */ $season = $price->getSeason(); $setFromIntervall = clone $season->getFromDate(); //$fromDate = $season->getFromDate(); $setToIntervall = clone $season->getToDate(); //$toDate = $season->getToDate(); $interval = \DateInterval::createFromDateString('1 day'); $period = new \DatePeriod($setFromIntervall, $interval, $setToIntervall->modify('+1 day')); //days foreach ($period as $dt) { if(isset($paddedCalendar[$dt->format("Y-n")])) { $key = $this->findCalendarDayKey($paddedCalendar[$dt->format("Y-n")], $dt->format("d")); $currDay = $paddedCalendar[$dt->format("Y-n")]['data'][$key]; $currDay->setPrice($price); if(!$currDay->getIsReserved() && $season->getOnlyWeekday() != null && $season->getOnlyWeekday() == $currDay->getDate()->format('w')){ $currDay->setIsBookable(true); } if(!$currDay->getIsReserved() && $season->getOnlyWeekday() == null){ $currDay->setIsBookable(true); } if($currDay->getIsReservationBegin() == true && $currDay->getIsReservationEnd() == true){ $currDay->setIsBookable(false); } //check for the min sty days. if ($currDay->getIsReservationBegin() == true) { //days $minDays = $season->getMinimumStay(); $setMinStayFrom = clone $currDay->getDate(); $setMinStayTo = clone $currDay->getDate(); //$minStayInterval = \DateInterval::createFromDateString('-1 day'); // $minStayPeriod = new \DatePeriod($setMinStayTo->modify('+1 day'), $minStayInterval, $setMinStayFrom->modify('-'.($minDays-1).' days')); $setMinStayTo->modify('-'.($minDays).' days'); $counter = 0; $last = $minDays; for($mSdt = $setMinStayFrom; $mSdt >= $setMinStayTo; $mSdt->modify('-1 day')){ if(isset($paddedCalendar[$mSdt->format("Y-n")])) { $key = $this->findCalendarDayKey($paddedCalendar[$mSdt->format("Y-n")], $mSdt->format("d")); $checkDay = $paddedCalendar[$mSdt->format("Y-n")]['data'][$key]; if($minDays != $checkDay->getPrice()->getSeason()->getMinimumStay()){ if($minDays > $checkDay->getPrice()->getSeason()->getMinimumStay()){ $last = $counter+$checkDay->getPrice()->getSeason()->getMinimumStay(); $setMinStayTo->modify('-'.($last).' days'); } if($minDays < $checkDay->getPrice()->getSeason()->getMinimumStay()){ $last = $counter+$checkDay->getPrice()->getSeason()->getMinimumStay(); $setMinStayTo->modify('-'.($last).' days'); } $minDays = $checkDay->getPrice()->getSeason()->getMinimumStay(); } $counter++; if($counter <= $last) { $checkDay->setIsBookable(false); } } } } } } //$season is end } return $paddedCalendar; } /** * @param FewoLodging $lodging * @param $calendar * @return mixed * * createCalendarAndFillDayStates */ public function createCalendarAndFillDayStates(FewoLodging $lodging) { $pricesCalendar = $this->getCalendar($lodging); $reservationsCalendar = $this->getCalendar($lodging); $reservations = $lodging->getReservations(); $prices = $lodging->getPrices(); foreach ($prices as $price) { $season = $price->getSeason(); $pricesCalendar = $this->markCalendarDays($pricesCalendar, $season->getFromDate(), $season->getToDate(), false, $price); } foreach($reservations as $reservation) { $fromDate = $reservation->getFromDate(); $toDate = $reservation->getToDate(); $reservationsCalendar = $this->markCalendarDays($reservationsCalendar, $fromDate, $toDate, true, null, $reservation); //TODO fast fertig } $mergedPricesAndReservationsCalendar = $this->mergeCalendars($pricesCalendar, $reservationsCalendar); $actuallyReservableDaysCalendar = $this->filterReservableDays($lodging, $mergedPricesAndReservationsCalendar); //$resultCalendar = $this->mergeCalendars($actuallyReservableDaysCalendar, $reservationsCalendar, true); return $actuallyReservableDaysCalendar; //return $resultCalendar; } public function convertDate($date) { $result = substr($date, 0, 2).'.'.substr($date, 2, 2).'.'.substr($date, 4, 4); return $result; } }