Created
August 16, 2012 16:26
-
-
Save jippi/3371460 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| App::uses('CalendarDate', 'Lib'); | |
| /** | |
| * Support class for working with recurring entries | |
| * | |
| */ | |
| class RecurringSupport { | |
| protected static $_modelName; | |
| /** | |
| * Calculate recurrence based on a list of inputs | |
| * | |
| * Settings can be the following: | |
| * - limit: The max number of expansions to do on each entry (Must be integer) - default is 10 expansions | |
| * - skipBefore: Skip all expansions before this date (Must be a CalendarDate object) | |
| * - dateInterval: Number of "time" to skip for each expansion test (Must be a DateInterval object) - default is 1 day | |
| * | |
| * @param array $entries A list of recurring entries that should be expanded | |
| * @param array $settings A list of extra configurations for the calculations | |
| * @return array The expanded list | |
| */ | |
| public static function calculateRecurrence($entries, $settings = array()) { | |
| $defaults = array( | |
| 'limit' => 10, | |
| 'skipBefore' => null, | |
| 'dateInterval' => new DateInterval('P1D'), | |
| ); | |
| $settings = array_merge($defaults, $settings); | |
| $return = array(); | |
| foreach ($entries as $entry) { | |
| if (!Hash::get($entry, static::_prefixKey('is_recurrent'))) { | |
| $return[] = $entry; | |
| continue; | |
| } | |
| $start = new CalendarDate(Hash::get($entry, static::_prefixKey('start_time'))); | |
| $stop = new CalendarDate(Hash::get($entry, static::_prefixKey('end_time'))); | |
| $expansions = 0; | |
| for ($date = $start; $date <= $stop; $date->add($settings['dateInterval'])) { | |
| if ($settings['skipBefore'] && $date < $settings['skipBefore']) { | |
| continue; | |
| } | |
| if (!static::ruleIsTrue($entry, $date)) { | |
| continue; | |
| } | |
| $expansions++; | |
| $return[] = static::renderEventForDate($entry, clone $date); | |
| if (is_numeric($settings['limit']) && $expansions >= $settings['limit']) { | |
| break; | |
| } | |
| } | |
| } | |
| return $return; | |
| } | |
| /** | |
| * Get the next recurring date for an entry or list of entries | |
| * | |
| * This method will only expand forward in time, and only find the next recurring date | |
| * | |
| * The list must be bare, not containing any model alias key | |
| * | |
| * @param array $entries | |
| * @return array | |
| */ | |
| public static function getNextDate($entries, $model = null) { | |
| static::_setModelName($model); | |
| if (isset($entries[0])) { | |
| $entries = array($entries); | |
| } | |
| $result = static::calculateRecurrence($entries, array('limit' => 1, 'skipBefore' => new CalendarDate())); | |
| if (empty($result)) { | |
| return $entries; | |
| } | |
| return $entries; | |
| } | |
| /** | |
| * Renders a recurring entry for the given date. Does not check to make sure the | |
| * event occurs on this date (use RecurrenceRule::ruleIsTrue for that). | |
| * | |
| * @param array $entry Contains data formatted the same as a retrieved entry from a find. | |
| * @param DateTime $date A DateTime object in UTC time. This is the date to render the event for. | |
| * @return array The instance rendered for this recurring event | |
| */ | |
| public static function renderEventForDate($entry, DateTime $date) { | |
| $start_date = new CalendarDate(Hash::get($entry, static::_prefixKey('start_time'))); | |
| $end_date = new CalendarDate(Hash::get($entry, static::_prefixKey('end_time'))); | |
| $interval = $start_date->diff($end_date); | |
| $interval = new DateInterval("PT{$interval->h}H{$interval->i}M"); | |
| $floating_start_hour = $start_date->format('H'); | |
| $date->setTime($floating_start_hour, $date->format('i'), $date->format('s')); | |
| $entry = Hash::insert($entry, static::_prefixKey('start_time'), $date->format()); | |
| $date->add($interval); | |
| $entry = Hash::insert($entry, static::_prefixKey('end_time'), $date->format()); | |
| return $entry; | |
| } | |
| /** | |
| * Test if a expansion rule is true | |
| * | |
| * It will check the frequency key and check if should be recurring on the date provided as 2nd argument | |
| * | |
| * @param array $entry | |
| * @param DateTime $date | |
| * @return boolean | |
| */ | |
| public static function ruleIsTrue($entry, DateTime $date) { | |
| $itemStart = new CalendarDate(Hash::get($entry, static::_prefixKey('start_time'))); | |
| $itemEnd = new CalendarDate(Hash::get($entry, static::_prefixKey('end_time'))); | |
| $frequency = Hash::get($entry, static::_prefixKey('frequency')); | |
| if ($frequency == 'weekly') { | |
| $dayOfWeek = strtolower($itemStart->format('l')); | |
| return $dayOfWeek == strtolower($date->format('l')); | |
| } | |
| if ($frequency === 'daily') { | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Optionally prefix a key with a modelName | |
| * | |
| * Returns the original key if no prefix is specified | |
| * | |
| * @param string $key | |
| * @return string | |
| */ | |
| protected static function _prefixKey($key) { | |
| if (empty(static::$_modelName)) { | |
| return $key; | |
| } | |
| return sprintf('%s.%s', static::$_modelName, $key); | |
| } | |
| /** | |
| * Set the prefix for _prefixKey calls | |
| * | |
| * @param string $model | |
| * @return void | |
| */ | |
| protected static function _setModelName($model) { | |
| static::$_modelName = $model; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment