Pull to refresh

Мои плагины для Smarty

PHP *
Недавно в блоге PHP пролетала статья про наследование шаблонов в Smarty, которая навела меня на мысль: я уже на протяжении нескольких лет использую Smarty в качестве основного шаблонизатора и у меня накопилось множество написанных мною плагинов для расширения базового функционала. Почему бы не поделиться с сообществом своими наработками и послушать других? Я думаю, у многих есть чем поделиться по этой теме…

0. Disclaimer


  1. Эта статья написана не для того, чтобы убедить/разубедить в использовании Smarty. Используете — может быть найдёте здесь что-то интересное, нет — воздержитесь, пожалуйста, от холиваров. Я прекрасно и без вас знаю, какой гадкий, мерзкий и тормознутый Smarty по сравнению с [сюда вставить название вашего любимого шаблонизатора]. Статья не о том.
  2. Плагины создавались на протяжении нескольких лет, поэтому стиль несколько разношёрстный, в некоторых ситуациях они не очень дружат друг с другом. Я делюсь идеями, которые весьма просты в реализации, поэтому придираться к моей реализации не стоит. Тем не менее, за разумную критику буду благодарен.
  3. Плагины зачастую крутятся в составе самопального микро-фреймворка, который поддерживает многоязычность. В приведённых исходниках я заменил все вызовы функций фреймворка на переменные, поэтому код моментами выглядит дебильно из-за переменных, global и прочих корректив.


1. Модификаторы (modifiers)


Стандартная архитектура Model-View-Controller (MVC) подразумевает разделение логики, данных и внешнего вида. В теории всё красиво. А на практике как быть, скажем, с датой рождения, которая в базе данных лежит в виде «1980-01-19», а пользователю надо вывести «19 января 1980»? Кто этим должен заниматься? Вот тут у многих известных мне разработчиков и начинает ломаться логика. Это самое преобразование внешнего вида куда только не всовывают (да-да, доводилось встречать и уникумов, хранящих это в БД в виде текстовой строки).

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

Итак, приступим…

1.1. Вывод дат

Казалось бы — есть стандартный модификатор date_format, что ещё нужно? А вот такой я зануда, и мне его мало:
  • Во-первых, он непригоден для уже описанного ранее случая с днём рождения, хранящегося в виде date или datetime в БД.
  • Во-вторых, я не должен в каждом месте набивать всякие иероглифы вида: {$yesterday|date_format:'%A, %B %e, %Y'} (пример из справки).
  • И, наконец, в-третьих, большинство разрабатываемых мною сайтов — многоязычные. Т.е. для русского языка я должен использовать формат '%d %B %Y', для английского '%B %d, %Y' и т.д.


1.1.1. Модификатор date

Внешний вид: {$variable|date:'формат'}. В качестве необязательного формата используется 'date', 'datetime' либо 'time'. Все описанные ранее проблемы модификатор решает сам.

Файл smarty/plugins/modifier.date.php (скачать)
/**
* Модификатор date: unix_timestamp => дата
*
* param string $string
* param string $format
* return string
*/
function smarty_modifier_date($string, $format='datetime')
{
   global $Language;
   if(!is_numeric($string))
   {
      trigger_error('Modifier date: invalid input data!');
   }

   if ($format == 'date')
   {
      $format = $Language['global']['date_format'];
   }
   elseif ($format == 'datetime')
   {
      $format = $Language['global']['datetime_format'];
   }
   elseif ($format == 'time')
   {
      $format = $Language['global']['time_format'];
   }
   else
      // Клинический случай
      trigger_error('Modifier date: invalid format specified!');

   $format = str_replace('%B', $Language['months'][date('n', $string)], $format);

   return strftime($format, $string);
}


Обращаю внимание на волшебную глобальную переменную $Language, которая хранит форматы вывода дат для разных языков и нормальные названия месяцев на родном посетителю языке.

Т.е. для английского языка:
$Language['global']['date_format']   =   '%B %d, %Y';
$Language['global']['time_format']   =   '%H:%M';
$Language['global']['datetime_format']   =   '%B %d, %Y %H:%M';

А для русского:
$Language['global']['date_format']   =   '%d %B %Y';
$Language['global']['time_format']   =   '%H:%M';
$Language['global']['datetime_format']   =   '%d %B %Y %H:%M';


Содержимое $Language['months'] оставлю для самостоятельного сочинения ;)

В некоторых проектах на этот модификатор дополнительно ложится задача пересчёта часовой зоны, в зависимости от настроек пользователя. Пустячок, а приятно — не надо делать резких телодвижений для этого в коде модулей.

1.1.2. Модификатор date_sql

Полностью аналогичен предыдущему случаю. Разница лишь во входящих данных. Этот ожидает на входе не UNIX_TIMESTAMP, а MySQL'ные DATETIME или DATE.

Файл smarty/plugins/modifier.date_sql.php (скачать)
/**
* Модификатор date_sql: Преобразование sql date/datetime в дату
*
* param string $string
* param string $format
* return string
*/
function smarty_modifier_date_sql($string, $format='datetime')
{
   global $Language;

   // Это date?
   if (!preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $string, $result))
      // Это datetime?
      if (!preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/', $string, $result))
         // Я понял! Это невесть что!
         trigger_error('Invalid data for date_sql modifier!');

   $replace = array(
      '%d' => $result[3],
      '%B' => $Language['months'][(int)$result[2]],
      '%Y' => $result[1],
      '%H' => $result[4],
      '%M' => $result[5],
      '%S' => $result[6],
   );

   if ($format == 'date')
   {
      $format = $Language['global']['date_format'];
   }
   elseif ($format == 'datetime')
   {
      $format = $Language['global']['datetime_format'];
   }
   elseif ($format == 'time')
   {
      $format = $Language['global']['time_format'];
   }
   else
      // Клинический случай
      trigger_error('Invalid date format for date_sql modifier!');

   return strtr($format, $replace);
}


UPDATE: У ряда комментаторов возник вопрос: почему я вместо strtotime() использую жуткую комбинацию strtr() и регулярных выражений? Ответ простой: для корректной обработки неполных дат вида «1980-00-00» (все знают мой возраст, но не день роджения) или «0000-01-19» (все поздравляют меня с днём рождения, но никто не знает, сколько мне лет).

Тут возникает закономерный вопрос: нафига вообще 2 модификатора? Пусть будет один, который внутри себя разбирается во входных данных. Я считаю это интересным решением, которое я пока не могу реализовать по ряду технических особенностей фреймворка (а может это просто лень?).

UPDATE: Ряд толковых комментариев сподвиг меня склеить и упростить эти оба плагина, как я и собирался, но по куче причин не решался. Получилось вот что:

Файл smarty/plugins/modifier.date.php (скачать)
<?php
/**
* Модификатор date: unix_timestamp, date, datetime => дата на человеческом языке
*
* param string $string
* param string $format — 'date', 'time', 'datetime'
* return string
*/
function smarty_modifier_date($string, $format='datetime')
{
   global $Language;

   if (!isset($Language['global'][$format.'_format']))
   {
      trigger_error('Modifier date: invalid format specified!');
      return '';
   }

   $format = $Language['global'][$format.'_format'];

   if (!is_numeric($string))
   {
      // Попытаемся прочтитать date и datetime
      $timestamp = strtotime($string);
      if ($timestamp!==FALSE && $timestamp!=-1 /*PHP<5.1*/)
      {
         $string = $timestamp;
      }
   }

   // Это похоже на unix_timestamp?
   if (is_numeric($string))
   {
      // Месяц на человеческом языке
      $format = str_replace('%B', $Language['Month'][date('n', $string)], $format);   

      return strftime($format, $string);
   }

   // Мы ещё здесь? Что-то неладно...
   trigger_error('Invalid data for date modifier!');
   return '';
}
?>


Благодарю за помощь.

На этом моменте хочется прерваться, хотя есть ещё пяток интересных плагинов. Поскольку времени на написание я потратил изрядно (чукча не писатель), а интерес сообщества к этой теме мне неизвестен: полетят ли в меня тухлые помидоры или статья просто затеряется невостребованной — я не знаю.

UPDATE: Уже написана и выложена вторая часть.

P.S. Сильно не ругайте, ибо ППНХ ;)
Tags:
Hubs:
Total votes 41: ↑29 and ↓12 +17
Views 3.8K
Comments Comments 21