[HolidaysContent] Вывод контента в определённую дату и/или период времени
        Всем привет, поступила банальная на первый взгляд задача, выводить в праздничные дни оформление тематическое… Всё бы ничего, просто написать сниппет который проверяет текущую дату и выводит чанк при совпадении… Но как быть с новым годом? Заказчик просит с 20.12 текущего года, по 10.01 соответственно следующего года показывать снег на сайте, тут уже нужно учитывать период, год начала и конца события.
Не стал искать решения на просторах интернета, решил написать самостоятельно, за одно подтянуть в очередной раз свои знания в PHP… Как сделал, всё же посмотрел на modstore.pro, есть ли какие ни будь подобные компоненты, оказывается есть My_holidays, почитал описание, показалось, что он не совсем мне удобен возможно.
В общем и целом, надеюсь кому нибудь пригодится мой сниппет)
Сниппет HolidaysContent
Использование:
В определённую дату, например 8 марта
Новый год, с 20.12 текущего года, по 10.01 следующего года
Обратите внимание, что параметр date — приоритетнее параметров периода (start/end).
Несколько вызовов не будут мешать друг другу, т.е. вы можете выводить как контент по периоду, так и контент в определённую дату, даже если эта дата попадает в этот период.
    
    
                                                                                
            Не стал искать решения на просторах интернета, решил написать самостоятельно, за одно подтянуть в очередной раз свои знания в PHP… Как сделал, всё же посмотрел на modstore.pro, есть ли какие ни будь подобные компоненты, оказывается есть My_holidays, почитал описание, показалось, что он не совсем мне удобен возможно.
В общем и целом, надеюсь кому нибудь пригодится мой сниппет)
Сниппет HolidaysContent
<?php
$date = $modx->getOption('date', $scriptProperties, '');
$start = $modx->getOption('start', $scriptProperties, '');
$end = $modx->getOption('end', $scriptProperties, '');
$chunk = $modx->getOption('tpl', $scriptProperties, '');
$currentTimestamp = time();
$currentYear = date('Y');
// Проверяем, совпадает ли текущая дата с указанной датой
if (!empty($date)) {
    $dateWithYear = $date . '.' . $currentYear;
    $dateTimestamp = strtotime($dateWithYear);
    if ($dateTimestamp !== false && date('d.m', $currentTimestamp) == date('d.m', $dateTimestamp)) {
        return $modx->getChunk($chunk);
    }
}
// Проверяем, попадает ли текущая дата в указанный период
if (!empty($start) && !empty($end)) {
    // Определяем год для начала и конца периода
    list($startDay, $startMonth, $startYear) = explode('.', $start);
    list($endDay, $endMonth, $endYear) = explode('.', $end);
    // Определяем год для начала периода
    if ($startYear == 'this') {
        $startYear = $currentYear;
    } elseif ($startYear == 'next') {
        $startYear = $currentYear + 1;
    } else {
        $startYear = $currentYear;
    }
    // Определяем год для конца периода
    if ($endYear == 'this') {
        $endYear = $currentYear;
    } elseif ($endYear == 'next') {
        $endYear = $currentYear + 1;
    } else {
        $endYear = $currentYear;
    }
    $startWithYear =  $startDay . '.' . $startMonth . '.' . $startYear;
    $endWithYear = $endDay . '.' . $endMonth . '.' . $endYear;
    $startTimestamp = strtotime($startWithYear);
    $endTimestamp = strtotime($endWithYear);
    // Если период пересекает новый год, нужно учесть это
    if ($startTimestamp > $endTimestamp) {
        // Проверяем, если текущая дата попадает в период пересекающий год
        if ($currentTimestamp >= $startTimestamp || $currentTimestamp < $endTimestamp) {
            return $modx->getChunk($chunk);
        }
    } else {
        if ($currentTimestamp >= $startTimestamp && $currentTimestamp <= $endTimestamp) {
            return $modx->getChunk($chunk);
        }
    }
}Использование:
В определённую дату, например 8 марта
[[!HolidaysContent? date=`08.03` &tpl=`8march`]]Новый год, с 20.12 текущего года, по 10.01 следующего года
[[!HolidaysContent? start=`20.12.this` &end=`10.01.next` &tpl=`newyear`]]Обратите внимание, что параметр date — приоритетнее параметров периода (start/end).
Несколько вызовов не будут мешать друг другу, т.е. вы можете выводить как контент по периоду, так и контент в определённую дату, даже если эта дата попадает в этот период.
Комментарии: 7
                Ух ты! Осенний код подъехал, спасибо! Всегда жара с заявками в праздники, может и пригодится!
Тоже решаю такие квесты регулярно, поэтому позволю себе задать вопрос)
Просто интересно, а ты не пробовал стандартные для Revo планируемые даты публикации прикрутить для этих целей как-то?
Имею в виду перед тем, как решение выработал. Сниппет конечно круче, т.к. он универсальный, один раз поставил, и будет срабатывать каждый год))
Тоже горожу сниппеты, но сейчас подумал, что по-простому кажется можно было типа такого

Как думаешь, @Денис Усманов, рабочая это схема для одноразовых событий?
                    Тоже решаю такие квесты регулярно, поэтому позволю себе задать вопрос)
Просто интересно, а ты не пробовал стандартные для Revo планируемые даты публикации прикрутить для этих целей как-то?
Имею в виду перед тем, как решение выработал. Сниппет конечно круче, т.к. он универсальный, один раз поставил, и будет срабатывать каждый год))
Тоже горожу сниппеты, но сейчас подумал, что по-простому кажется можно было типа такого
[[#8.published:is=`1`:then=`[[#8.content]]`]]выводить в шаблоне страницы, а в стандартных полях ресурса (с id=8 из моего примера) ставить планируемую дату публикации, и снятия с публикации, вот эти:
Как думаешь, @Денис Усманов, рабочая это схема для одноразовых событий?
                Привет! Да, можно вполне было пойти, через ресурсы, но мне нужно было решение в виде сниппета, который выводил бы по датам чанки с CSS/HTML/JS в разных местах… Поэтому, сниппет оказался на мой взгляд, самым верным решением.            
                    
                Вообще чем-то похоже на работу пакета BannerY получилось кстати. Только опять же, со срабатыванием каждый год. На самом деле для ежегодных праздников твой вариант подходит конечно идеально!            
                    
                И добавлю, что предлагаемый тобою вариант, полуавтоматический, т.е. после срабатывания придётся менять дату на следующий год. Мне нужно было сделать решение, которое как говорится «на века» и «без лишней возни») т.к. у заказчика сайтом управляет в основном HR отдел по вакансиям, и там точно разбираться с такими вещами не будут.            
                    
                Немного сократил и добавил поддержку файловых чанков.
                    <?php
$start = $modx->getOption('start', $scriptProperties, '');
$end = $modx->getOption('end', $scriptProperties, '');
$chunk = $modx->getOption('tpl', $scriptProperties, '');
// определяем в каких случаях мы ничего не будем делать, нам важно не перетрудиться.
if((!$start && !$end) || !$chunk){
    return '';
}
// завозим поддержку файловых чанков
if(!$parser = $modx->getService('pdoTools')){
    $parser = $modx;
}
$now = strtotime('d.m.Y');
$currentYear = date('Y');
// предполагается что дада передаётся строкой вида "d.m"
$startTime = strtotime($start . '.' . $currentYear);
// если задана только дата начала, считае, что оформление будет активно только в эту дату
if(!$end){
    if($now === $startTime){
        return $parser->getChunk($chunk);
    }
}
$startParts = explode('.', $start);
$endParts = explode('.', $end);
// проверяем больше месяц даты начала месяца даты окончания
if((int)$startParts[1] > (int)$endParts[1]){
    $endTime = strtotime($end . '.' . ($currentYear + 1));
}else{
    $endTime = strtotime($end . '.' . $currentYear);
}
if ($now < $startTime || $now > $endTime) {
   return '';
}
return $parser->getChunk($chunk);            
                Как по мне, чересчур громоздко. Логика с date и start/end не очевидна. Да и в коде слишком много лишней логики. Я бы предложил вариант с датой и длительностью (просто в целях разминки полушарий)
                    $date = $modx->getOption('date', $scriptProperties, '');
$duration = $modx->getOption('duration', $scriptProperties, '1');
$chunk = $modx->getOption('tpl', $scriptProperties, '');
if (empty($date) || empty($chunk)) {
  return '';
}
[$day, $month, $year] = sscanf($date, "%d.%d.%d");
$year ??= date('Y');
$date = "$day.$month.$year";
$currentTimestamp = time();
$startTimestamp = strtotime($date);
$endTimestamp = strtotime($date . " + $duration days");
if ($currentTimestamp >= $startTimestamp && $currentTimestamp < $endTimestamp) {
  $pdoTools = $modx->getService('pdoTools');
  return $pdoTools->getChunk($chunk);
}Указал год — сработает только для него. Не указал, ежегодно. Если длительность не указана, то она равна 1 дню.            
                Раз уж в статье написано про «подтянуть в очередной раз свои знания в PHP», напишу тут свои мысли.
1. Работать с датами можно через объект DateTime. Насколько помню там даже можно просто сравнивать даты обычным оператором $myDate > $yourDate
2. Может быть имеет смысл не привязываться к году, а просто выводить за столько то дней до и после определённой даты. Новый год всегда 1 января не важно какого года, другие праздники тоже.
3. На мой взгляд удобнее и нагляднее вместо:
                    1. Работать с датами можно через объект DateTime. Насколько помню там даже можно просто сравнивать даты обычным оператором $myDate > $yourDate
2. Может быть имеет смысл не привязываться к году, а просто выводить за столько то дней до и после определённой даты. Новый год всегда 1 января не важно какого года, другие праздники тоже.
3. На мой взгляд удобнее и нагляднее вместо:
$start = $modx->getOption('start', $scriptProperties, '');
$end = $modx->getOption('end', $scriptProperties, '');
$chunk = $modx->getOption('tpl', $scriptProperties, '');писать в таком стиле:$options = array_merge([
        'start'   => '',
        'end'    => '',
        'tpl'      => "@INLINE {$var}"
    ], $scriptProperties);4. Я всегда стараюсь делать один return в сниппетах или функциях. Для меня лично тоже так удобнее и нагляднее (единая точка выхода).            
                            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.