Рубрики
Wordpress

Создание произвольных форм в WordPress

Метод создания произвольных форм реализован с помощью плагина Oi Forms.

Скачать последнюю версию плагина Oi Forms для создания произвольных полей в WordPress можно на странице релизов тут.

Тестовая форма:

Чтобы протестировать форму, распакуйте файл и поместите его в папку oi-forms, предварительно создав ее в папке активной темы. В админке WordPress создайте новую страницу и разместите на ней шорткод:

[form form_id="Theme-PostTitle"]

Убедитесь, что плагин Oi Forms активирован.

Благодаря данному плагину разработчик может быстро и сравнительно легко создать любую форму, которую потом можно использовать на любом сайте, где установлен плагин.

Это поможет облегчить труд разработчика по поддержке клиентских сайтов.

Рубрики
PHP

Как проверить являются ли данные JSON строкой в PHP

Чтобы проверить являются ли данные JSON строкой, необходимо попытаться конвертировать данные в массив с помощью функции json_decode.

Далее следует проверить – возникла ли ошибка конвертации, это делается с помощью функции json_last_error().

Если ошибок не возникло, функция вернет JSON_ERROR_NONE.

Раз ошибок нет, значит конвертация прошла успешно и данные являются строкой в формате JSON.

Если результат проверки с помощью функции json_last_error() отличный от JSON_ERROR_NONE, значит возникли ошибки и строка не является JSON.

Пример функции проверки JSON на PHP

/**
 * @param $data
 *
 * @return bool
 */
function isJson( $data ) {
	json_decode( $data, true );

	return json_last_error() == JSON_ERROR_NONE;
}
Рубрики
PHP

БЭМ в PHP для WordPress

К хорошему привыкаешь быстро, особенно, когда верстаешь страницу в стеке Vue + Pug + Bem!

На мой взгляд это наиболее удобный стек для верстки.

Вот как выглядит код на этом стеке:

+b.product
	+e.inner
		+e.image
		+e.info
			+e.H2.title {{ product.title }}
			+e.price._new {{ product.price }}

А так этот же код выглядит на чистом HTML:

<div class="product">
	<div class="product__inner">
		<div class="product__image"></div>
		<div class="product__info">
			<h2 class="product__title">{{ product.title }}</h2>
			<div class="product__price product__price_new">{{ product.price }}</div>
		</div>
	</div>
</div>

Как видно из примера – вместо каждого +e дописывается название, взятое из предыдущего +b.

Синтаксис таков:

  • Часть названия класса, которая идет после точки компилируется в имя дочернего элемента:+e.inner > product__inner;
  • Часть названия класса, которая идет после точки и начинается с символа нижнего подчеркивания компилируется в имя миксина: +e.price._new > product__price product__price_new;

Работая с WordPress реализовать такую роскошь для создания шаблонов сложно, хотя можно… но кому это вообще надо? )

Делая очередной шаблон страницы на WordPress я очень скучал по простому созданию классов в стиле БЭМ и решил хоть как-тот облегчить себе задачу.

Техническое задание

Без использования шаблонизатора не удастся легко и просто реализовать работу через псевдо-переменные +b и +e, поэтому придется придумать что-то попроще.

Должен быть реализован синтаксис создания классов дочерних элементов и миксинов.

  1. Если указано название класса, не начинающееся с точки, это должен быть родительский класс, например product;
  2. Если за родительским классом идет один или более классов, начинающихся с точки, после которой нет символа нижнего подчеркивания, то должно быть создано столько экземпляров дочерних классов, сколько объявлено, например: product.one.two.three > product__one product__two product__three;
  3. Если после точки идет символ нижнего подчеркивания – это миксин и он должен быть дописан к каждому классу, например: product.one.two.three._first._second > product__one product__two product__three product__one_first product__one_second product__two_first product__two_second product__three_first product__three_second.

Реализация

Я хочу, чтоб это было максимально просто, на сколько это возможно в условиях создания шаблонов в WordPress, то есть в условия написания кода на чистом PHP.

Пусть функция будет называться bem и для начала пусть она принимает один параметр – строку, которая является цепочкой частей классов:

bem('product.one.two.three._first._second');

Объявим функцию:

function bem( $classTrail = '' ) {
	if ( empty( $classTrail ) ) {
		return '';
	}
}

При этом я сразу говорю, что если на вход ничего не пришло, следует вернуть пустую строку.

Далее следует разбить строку на массив:

$classTrail = array_values( array_filter( explode( '.', $classTrail ) ) );

Следом необходимо определить переменные, которые будут хранить промежуточные данные для миксинов и классов, а так же определить начальное значение для составного класса и посчитать число элементов цепочки:

$mixins  = [];
$classes = [];
$block   = '';
$count   = sizeof( $classTrail );

Далее делаем перебор нашего массива $classTrail:

foreach ( $classTrail as $i => $item ) {
    // код
}

Первым делом следует к составному элементу $block приписать название родительского класса, в нашем случае это product. Это следует сделать только в том случае, если мы на первой итерации:

if ( 0 === $i ) {
	$block = $classTrail[ $i ];
}

Вполне может быть, что в нашей цепочке частей классов указан только один класс, поэтому в таком случае нам следует сделать проверку и если так и окажется, добавить этот класс в список:

if ( 0 === $i ) {
	$block = $classTrail[ $i ];

	if ( 1 == $count ) {
		$classes[] = $block;
	}
}

Далее, если итерация не первая приступим к обработке остальных частей цепочки. Начнем с миксинов, проверим наличае символа нижнего подчеркивания:

if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
    // код
}

И если это миксин, сразу добавим его в список миксинов:

if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
	$mixins[] = $classTrail[ $i ];
}

Если при этом в нашей цепочке всего 2 элемента, это означает, что указан родительский класс и миксин: product._new, в этом случае следует часть составного класса добавить в список классов:

if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
	$mixins[] = $classTrail[ $i ];
	if ( 2 == $count ) {
		$classes[] = $block;
	}
}

В ином случае, если это не миксин, мы понимаем, что это дочерний класс, который сразу добавим в список классов:

if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
	$mixins[] = $classTrail[ $i ];
	if ( 2 == $count ) {
		$classes[] = $block;
	}
}
else {
	$classes[] = $block . '__' . $classTrail[ $i ];
}

Таким образом мы составили список классов и миксинов:

$mixins  = [];
$classes = [];
$block   = '';
$count   = sizeof( $classTrail );
foreach ( $classTrail as $i => $item ) {
	if ( 0 === $i ) {
		$block = $classTrail[ $i ];

		if ( 1 == $count ) {
			$classes[] = $block;
		}
	}
	else {

		if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
			$mixins[] = $classTrail[ $i ];
			if ( 2 == $count ) {
				$classes[] = $block;
			}
		}
		else {
			$classes[] = $block . '__' . $classTrail[ $i ];
		}
	}
}

Теперь необходимо перебирая список получившихся классов дописать к каждому миксин из списка миксинов, для этого составим вложенный цикл:

foreach ( $classes as $i => $class ) {
	foreach ( $mixins as $j => $mixin ) {
		$classes[] = $classes[ $i ] . $mixin;
	}
}

Работа практически завершена, но лично у меня есть потребность создавать различные наборы классов. Скажем одна цепочка служит для создания набора классов для описания внешнего вида, вторая для указания селекторов для использования в JS, например:

bem('product._new js.product');
// в результатае будет: product product_new js__product

Для этого нам необходимо строку разбить на массив по пробелам и весь код, написанный выше, поместить в еще один цикл:

$trails = [];

$classTrails = array_values( array_filter( explode( ' ', $classTrail ) ) );

foreach ( $classTrails as $classTrail ) {

	$classTrail = array_values( array_filter( explode( '.', $classTrail ) ) );

	$mixins  = [];
	$classes = [];
	$block   = '';
	$count   = sizeof( $classTrail );
	foreach ( $classTrail as $i => $item ) {
		if ( 0 === $i ) {
			$block = $classTrail[ $i ];

			if ( 1 == $count ) {
				$classes[] = $block;
			}
		}
		else {

			if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
				$mixins[] = $classTrail[ $i ];
				if ( 2 == $count ) {
					$classes[] = $block;
				}
			}
			else {
				$classes[] = $block . '__' . $classTrail[ $i ];
			}
		}
	}

	foreach ( $classes as $i => $class ) {
		foreach ( $mixins as $j => $mixin ) {
			$classes[] = $classes[ $i ] . $mixin;
		}
	}

	$trails = array_merge( $trails, $classes );
}

Теперь осталось вернуть результат. Его можно вернуть в виде:

  • массива;
  • строки;
  • строки вида class="product product_new".

Для этого я ввел еще два входных параметра в функции, мне удобно было именно так:

  • $toAttribute = false – венуть классы внутри атрибута class;
  • $isArray = true – вернуть массив.
bem('product._new js.product', true);
// вернет class="product product_new"

bem('product._new js.product', false, false);
// вернет 'product product_new'

bem('product._new js.product');
// вернет массив

Полный код функции

/**
 * Function that creates the classes chain in BEM style
 *
 * @param string $classTrail
 * @param bool   $toAttribute
 * @param bool   $isArray
 *
 * @return array|string
 */
function bem( $classTrail = '', $toAttribute = false, $isArray = true ) {
	if ( empty( $classTrail ) ) {
		return '';
	}

	$trails = [];

	$classTrails = array_values( array_filter( explode( ' ', $classTrail ) ) );

	foreach ( $classTrails as $classTrail ) {

		$classTrail = array_values( array_filter( explode( '.', $classTrail ) ) );

		$mixins  = [];
		$classes = [];
		$block   = '';
		$count   = sizeof( $classTrail );
		foreach ( $classTrail as $i => $item ) {
			if ( 0 === $i ) {
				$block = $classTrail[ $i ];

				if ( 1 == $count ) {
					$classes[] = $block;
				}
			}
			else {

				if ( 0 === strpos( $classTrail[ $i ], '_' ) ) {
					$mixins[] = $classTrail[ $i ];
					if ( 2 == $count ) {
						$classes[] = $block;
					}
				}
				else {
					$classes[] = $block . '__' . $classTrail[ $i ];
				}
			}
		}

		foreach ( $classes as $i => $class ) {
			foreach ( $mixins as $j => $mixin ) {
				$classes[] = $classes[ $i ] . $mixin;
			}
		}

		$trails = array_merge( $trails, $classes );
	}

	if ( ! empty( $toAttribute ) ) {
		return ' class="' . join( ' ', $trails ) . '" ';
	}

	// if $isArray is false
	if ( empty( $isArray ) ) {
		$trails = join( ' ', $trails );
	}

	// return classes as array
	return $trails;
}

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

Благодаря данной функции наш шаблон станет выглядеть так:

<div <?php echo bem( 'product', true ); ?>>
	<div <?php echo bem( 'product.inner', true ); ?>>
		<div <?php echo bem( 'product.image', true ); ?>></div>
		<div <?php echo bem( 'product.info', true ); ?>>
			<h2 <?php echo bem( 'product.title', true ); ?>>{{ product.title }}</h2>
			<div <?php echo bem( 'product.price', true ); ?>>{{ product.price }}</div>
		</div>
	</div>
</div>

Кому-то может показаться, что стало хуже… но этот только в том случае, если вы не работаете с БЭМ, да еще и в WordPress ))

Могу сказать, что на практике это упростит читабельность классов и сделает их более структурированными.

Рубрики
Backend

Ошибка 500 – решение проблемы

Недавно ко мне обратились из компании, занимающейся созданием сайтов, за консультацией. Были жалобы на нестабильную работу сайта. Нестабильность заключалась в том, что при сохранении публикации сайт вешался. Решение проблемы, когда возникает ошибка 500, я напишу далее. Я представлю как универсальные методы решения проблемы, которые помогут владельцам сайтов с любыми системами управления, так и направленные на решение проблемы пользователей WordPress.

Рубрики
FrontEnd

JavaScript get параметры

В действительности речь пойдет не по теме “JavaScript get параметры”, а о получении значения параметра из строки адреса, эквиваленте глобальной переменной $_GET на PHP.

Представим, что на наш сайт обратились по средствам такого вида ссылки: http://mysite.ru/?text=hello

Наша задача состоит в том, чтобы получить значение параметра text и использовать его в коде JavaScript. Так как в JavaScript нет глобальной переменной типа $_GET необходимо взять всю ссылку из строки адреса и вытащить из нее нужную информацию.

JavaScript get параметры, получение значения:

// получение значения параметра из строки адреса
function get(name){
   if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search))
      return decodeURIComponent(name[1]);
}

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

var text = get('text');
console.log( text ); // вывод значения переменной в консоль

 

Рубрики
Backend

Функция – склонение чисел на PHP

Представим, что задача состоит в следующем: необходимо осуществить склонение чисел в правильном виде. Например: есть два числа – 2 и 10, первое обозначает год, второе месяц, необходимо вывести “2 года, 10 месяцев”.

Для решения данной задачи потребуется написать функцию. Мы будем определять цифру стоящую после десятков и на основании этого указывать нужное наклонение. При этом необходимо передавать в функцию 3 варианта наклонения, когда после десятков стоит единица, когда цифра больше единицы, но меньше пяти и когда больше или равно пяти. Эти данные мы будем передавать в виде массива с тремя значениями, что обеспечит правильное склонение чисел:

array( 'год', 'года', 'лет', )

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

Функция: склонение чисел на PHP

function declension( $num, $vars, $before = '', $after = '' )
{
	if( $num == 0 ) // если число равно нулю
		return; // ничего не возвращаем
	$normal_num = $num; // сохраняем число в исходном виде
	$num = $num % 10; // определяем цифру, стоящую после десятка
	if( $num == 1 ) // если это единица
	{
		$num = $normal_num . ' ' . $vars[0];
	}else if( $num > 1 && $num < 5 ) // если это 2, 3, 4
	{
		$num = $normal_num . ' ' . $vars[1];
	}else
	{
		$num = $normal_num . ' ' . $vars[2];
	}
	return $before . $num . $after; // возвращаем строку
}

Далее идет пример использования данной функции: у второго числа, в качестве значения параметра $before стоит запятая, поэтому, если значение будет не ноль, но получится, что после указания года стоит запятая и далее идет информация о месяцах, а если значение месяцев равно нулю, то после информации о годах ничего не идет.

$time = declension( $year, array( 'год', 'года', 'лет', )) . declension( $month, array( 'месяц', 'месяца', 'месяцев', ), ', ' );

Где $year и $month – это значение лет и месяцев соответственно.

Данная функция позволяет осуществить склонение чисел для любых слов.

 

Рубрики
Backend

Использование PHP для сайта-визитки, разделение на модули

Описанный далее метод безусловно рабочий и может применяться всеми, от новичка до профессионала, тем более, что метод правильный, но данная статья написана специально для тех, кто постигает азы создания сайтов. Тем самым я говорю, что людям с опытом, данная статья может показаться бессмысленной, но все когда-то и с чего-то начинают.

Зачем вообще использовать PHP в создании сайтов? Даже если вы не являетесь программистом и делаете сайт исключительно для себя, то со временем сайт будет расти и шириться. Так бывает всегда и у всех. Все и всегда сталкиваются с одной и той же проблемой – при добавлении нового раздела сайта, а следовательно и нового пункта меню, нужно, что бы измененное меню появилось на всех страницах сайта. Все прекрасно, когда вы используете CMS, измененное меню “само собой” появится на всех страницах сайта, но как быть, если вы, по какой-то причине, использовали статические страницы, сохраненные с расширением *.html? Такое бывает довольно часто, даже в наше время, когда, казалось бы, все используют различные CMS.

Отличным решением проблемы будет разделение страницы сайта на модули.

В этом случае есть два, наиболее простых, варианта решений:

  • использовать SSI
  • использовать JavaScript
  • использовать PHP

Первый способ, с использованием SSI можно обнаружить по следующему адресу:

SSI в примерах

Второй способ будет описан на данном сайте позднее.

Третий способ, с использованием PHP рассмотрим далее.

Использование PHP при разделении страницы сайта на модули:

Во-первых, для того, что бы использовать возможности языка PHP ваш хостинг должен предоставлять такую услугу, во вторых файлы, которые будут содержать инструкции PHP должны иметь расширение *.php и в третьих, инструкции, внутри файла должны быть заключены в конструкцию:

<?php /* ВАШ КОД */ ?>

С помощью PHP можно подключить файл как обычную функцию, при этом содержимое файла будет обработано как если бы он был автономным PHP сценарием. При этом все функции, определенные в подключаемом файле можно использовать как внутри файла в котором происходит подключение, так и в любых других подключенных файлах. И называется эта функция include.
При разработке простого сайта-визитки удобно разделить уже сверстанный макет сайта на логические части/модули. Например можно разделить макет на три части: header, footer и content. В первых двух модулях будут содержаться куски HTML кода, которые на всех страницах сайта изменяться не должны, что логично, ведь дизайн должен быть неизменным во всех разделах сайта. Изменяться будет только та часть, которая содержит контент.
Во-первых данный подход существенно снижает риск допустить ошибку верстки, так как готовые файлы header и footer уже не будут подвергаться изменениям, а во-вторых, при внесении изменений в дизайн достаточно будет изменить только два файла, не затрагивая файлы содержащие контент, это особенно актуально, когда ваш сайт содержит хотя бы более десяти страниц.

В своем примере я разделил сайт на четыре модуля. В одном файле я буду хранить функции, которые стану использовать на сайте, в других двух файлах будет часть до и после контента и третья часть – контент.
При этом я буду загружать нужный контент динамически, точнее за меня это сделает PHP.
Мой основной файл, index.php, будет содержать следующий код:

<?
include 'functions.php';
include 'header.php';
include page_name().'.php';
include 'footer.php';
?>

Сперва я подключил файл содержащий функции, затем все остальное. Поскольку PHP язык линейный, то я подключаю файлы, содержащие части верстки в той последовательности, в которой они были изначально, до разделения.
Вы заметили, что я использую функция page_name(). В данной функции я определяю – какой файл запросил пользователь и подключаю его.
Код данной функции выглядит следующим образом:

function page_name() // функция определяет какой файл подключить в части контента
{
$page = $_GET['p']; // получаем значение переменной из строки адреса
if($page==''){$page = 'main';} // если значение не указано, значение переменной page устанавливается равным main
return $page; // возвращаем значение переменной page
}

 

Сперва функция получает из строки адреса имя файла, который следует подключить в части контента. Эти данные попадают в адресную строку при клике по ссылке, в меню, так как я указываю такую конструкцию:

<a href="/">Главная</a>
<a href="/?p=about">Обо мне</a>
<a href="/?p=contacts">Контакты</a>

 

То есть в строке адреса содержится информация о том, что некая переменная p имеет значение равное тому, что идет после знака “равно”. Соответственно при клике на ссылку “Обо мне” пользователь перейдет в корень сайта(на главную страницу) и в строке адреса будет передан параметр p со значением about, поэтому вместо текста, главной страницы пользователь увидит текст страницы “Обо мне“, а при клике на “Контакты“, значение будет равно contacts и текст страницы будет соответствующим. В первой строке функции как раз говорится, что:  нужно получить значение переменной p из адресной строки и поместить это значение в переменную $page.

Как мы видим, ссылка на главную не содержит никаких параметров, но тем не менее необходимость подгрузить какой-то контент остается и для этой цели применяется условный оператор, который помещает в переменную $page значение равное слову “main“. Затем функция возвращает результат своей работы, а именно возвращает значение переменной $page, которое подставляется в то место, где была вызвана функция, а именно в 4-ю строку файла index.php, в которой загружается соответствующий файл. Для главной, при отсутствии параметров, main.php, для “Обо мне” – about.php , для “Контакты” – contacts.php.

Выделение активного раздела

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

function current_page($link='') // функция опреляет страницу, которая отображается в данный момент
{
$page = $_GET['p']; // получаем значение переменной из строки адреса
if($page==$link) // если значение переменной page равно значению переменной link...
{
return ' class="current"'; // возвращаем указанную строку
}
}

Данная функция точно так же получает из адресной строки значение переменной p, а за тем сравнивает это значение с тем, что было передано в функцию в качестве ее параметров и если эти значения совпадают, то функция возвращает строку, содержащую конструкцию определяющую атрибут класса в html.

<a href="/">Главная</a>
<a href="/?p=about">Обо мне</a>
<a href="/?p=contacts">Контакты</a>


Далее, значение, возвращаемое функцией, выводится в том месте, где эта функция была вызвана.

Предположим, что пользователь просматривает раздел “Обо мне“, значит в адресной строке находится нечто следующее: http://mysite.ru/?p=about и следовательно программа обработчик идя по коду и встречая на своем пути PHP код делает проверку. Когда обработчик встречает функцию без параметра – current_page() он сверяет то, что в адресной строке и то что передано в функцию в качестве параметра. Поскольку в адресной строке значение p чему-то равно, то в данном случае функция ничего не возвращает. Затем следует та же функция, но уже с параметром, равным “about” и сравнивая это значение со значение в адресной строке оказывается, что значения равны и следовательно функция возвращает строку  class="current", таким образом получается, что при просмотре HTML кода страницы мы увидим, что для ссылки на раздел “Обо мне” определен класс – <a href="/?p=about" class="current">Обо мне</a>. Ну и в последнем случае, когда встречается функция current_page('contacts'), она ничего не возвращает, так как значение параметра функции не соответствует значению переменной из адресной строки.

Это простейший вариант решения задачи. Так как в примере используется всего три пункта меню, решение было “в лоб”, но если пунктов меню больше, то удобней использовать цикл, для вывода меню.

Пример такого цикла приведен ниже:

function menu()
{
// формируем массив, состоящий из имени файла и соответствующего названия раздела
$menu = array(
'main' => 'Главная',
'about' => 'Обо мне',
'contacts' => 'Контакты',
);
$out = ''; // обнуляем переменную, которая будет содержать сформированный код меню
foreach($menu as $url=>$name) // начинаем перебор массива
{
if($url==$_GET['p']||($_GET['p']==''&&$url=='main')) // сравниваем значение текущего элемента массива с параметром из адресной строки
{
// значение текущего элемента массива соответствует значению переменной из адресной строки
$current = ' class="current"'; // значение переменной равно строке определяющей класс
}else
{
$current = ''; // значение переменной пусто
}
if($url<>'main') // проверяем, является ли значение текущего элемента массива соответствующим строке 'main'
{
$url = '?p='.$url; // если нет, то ссылка будет иметь указанный вид
}else
{
$url = '?'; // если да, то нужно ссылаться на корень сайта
}
$out .= '<a href="'.$url.'"'.$current.'>'.$name.'</a>'; // формируем ссылку соответствующую текущему элементу массива и дописываем ее к уже сформированной строке
}
return $out; // возвращаем сформированную строку
}
print menu(); // выводим результат работы функции


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

Рубрики
Wordpress

Не работает is_home()

Бывает так, что функция is_home() не работает. Когда я столкнулся с этой проблемой впервые я не стал разбираться в причинах и написал замену этой функции:

function is_home_page()
{
	$a = split( '/', $_SERVER['REQUEST_URI'] );
	if( $a[1] == '' )
		return true;
	return false;
}

Использовать ее можно так же как и is_home().

Рубрики
Wordpress

Склонение чисел

Я применил эту функцию для правильного склонения числа товаров в WooCommerce на движке WordPress.