[HolidaysContent] Вывод контента в определённую дату и/или период времени

Всем привет, поступила банальная на первый взгляд задача, выводить в праздничные дни оформление тематическое… Всё бы ничего, просто написать сниппет который проверяет текущую дату и выводит чанк при совпадении… Но как быть с новым годом? Заказчик просит с 20.12 текущего года, по 10.01 соответственно следующего года показывать снег на сайте, тут уже нужно учитывать период, год начала и конца события.

Не стал искать решения на просторах интернета, решил написать самостоятельно, за одно подтянуть в очередной раз свои знания в 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).
Несколько вызовов не будут мешать друг другу, т.е. вы можете выводить как контент по периоду, так и контент в определённую дату, даже если эта дата попадает в этот период.
Денис Усманов
24 октября 2024, 15:16
modx.pro
4
1 851
+7

Комментарии: 7

Дима Касаткин
24 октября 2024, 20:59
+2
Ух ты! Осенний код подъехал, спасибо! Всегда жара с заявками в праздники, может и пригодится!

Тоже решаю такие квесты регулярно, поэтому позволю себе задать вопрос)

Просто интересно, а ты не пробовал стандартные для Revo планируемые даты публикации прикрутить для этих целей как-то?

Имею в виду перед тем, как решение выработал. Сниппет конечно круче, т.к. он универсальный, один раз поставил, и будет срабатывать каждый год))

Тоже горожу сниппеты, но сейчас подумал, что по-простому кажется можно было типа такого
[[#8.published:is=`1`:then=`[[#8.content]]`]]
выводить в шаблоне страницы, а в стандартных полях ресурса (с id=8 из моего примера) ставить планируемую дату публикации, и снятия с публикации, вот эти:


Как думаешь, @Денис Усманов, рабочая это схема для одноразовых событий?
    Денис Усманов
    26 октября 2024, 14:50
    +1
    Привет! Да, можно вполне было пойти, через ресурсы, но мне нужно было решение в виде сниппета, который выводил бы по датам чанки с CSS/HTML/JS в разных местах… Поэтому, сниппет оказался на мой взгляд, самым верным решением.
      Дима Касаткин
      28 октября 2024, 01:55
      0
      Вообще чем-то похоже на работу пакета BannerY получилось кстати. Только опять же, со срабатыванием каждый год. На самом деле для ежегодных праздников твой вариант подходит конечно идеально!
      Денис Усманов
      26 октября 2024, 14:55
      0
      И добавлю, что предлагаемый тобою вариант, полуавтоматический, т.е. после срабатывания придётся менять дату на следующий год. Мне нужно было сделать решение, которое как говорится «на века» и «без лишней возни») т.к. у заказчика сайтом управляет в основном HR отдел по вакансиям, и там точно разбираться с такими вещами не будут.
      Артур Шевченко
      28 октября 2024, 19:51
      +1
      Немного сократил и добавил поддержку файловых чанков.
      <?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);
        Сергей Шлоков
        30 октября 2024, 11:46
        +3
        Как по мне, чересчур громоздко. Логика с 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 дню.
          Максим
          08 ноября 2024, 14:18
          0
          Раз уж в статье написано про «подтянуть в очередной раз свои знания в PHP», напишу тут свои мысли.

          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 в сниппетах или функциях. Для меня лично тоже так удобнее и нагляднее (единая точка выхода).
            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
            7