Рубрика: Программирование / Back-end

Какие CMS популярны? Как выбрать CMS при разработке сайта?

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

  1. Тип сайта: Важно определить, для чего будет использоваться сайт. Это может быть блог, интернет-магазин, корпоративный сайт, портфолио и т. д.
  2. Удобство использования: CMS должна быть интуитивно понятной для администраторов, чтобы они могли легко добавлять, редактировать и удалять контент.
  3. Безопасность: CMS должна обеспечивать надежную защиту от хакерских атак и иметь регулярные обновления безопасности.
  4. Экосистема плагинов и тем: Важно, чтобы CMS имела богатую экосистему дополнений, которые могут добавить необходимый функционал без необходимости в разработке с нуля.
  5. Сообщество и поддержка: Чем больше сообщество вокруг CMS, тем больше вероятность получения помощи и решения проблем.
  6. Скорость и производительность: CMS должна обеспечивать хорошую производительность сайта даже при высоких нагрузках.
  7. Расширяемость и масштабируемость: CMS должна позволять легко масштабировать сайт с ростом бизнеса.
  8. Стоимость: Некоторые CMS бесплатны, в то время как другие могут требовать лицензионные платежи или плату за дополнительный функционал.

Популярные CMS в мире:

  1. WordPress: Одна из самых популярных и распространённых CMS. Имеет большое сообщество пользователей и разработчиков, богатую библиотеку плагинов и тем.
  2. Joomla: Еще одна из популярных CMS, более подходящая для создания корпоративных сайтов и онлайн-журналов.
  3. Drupal: Мощная CMS, которая обладает расширенными возможностями по управлению контентом и поддерживает создание сложных сайтов.
  4. Magento: Специализированная CMS для интернет-магазинов, обладает широкими возможностями по управлению товарами, заказами и платежами.
  5. Shopify: Еще одна CMS, специализированная на интернет-магазинах, но отличается от Magento тем, что предлагает готовые решения в облаке, что облегчает настройку и управление.

В СНГ популярность могут иметь те же системы, но также стоит обратить внимание на локальные CMS, которые могут быть более адаптированы к специфике регионального рынка.

Как отправить email в формате html с помощью WordPress правильно

Вы можете отправлять электронные письма с помощью WordPress и PHP-скриптов. Вот пример простого PHP-скрипта, который можно использовать для отправки электронной почты через WordPress:

// Устанавлием возможность отправки письма в виде html
add_filter('wp_mail_content_type', function( $content_type ) {
            return 'text/html';
});
// Отправляем письмо
$mail_sent = wp_mail($to, $subject, $body, $headers);

// Проверяем результат отправки письма
if ($mail_sent) {
    echo 'Письмо успешно отправлено!';
} else {
    echo 'Ошибка при отправке письма.';
}

Можно отправить и просто php функцией mail(). Однако, для этого в WordPress ест своя функция — wp_mail();

Вот ее параметры:

wp_mail( string|string[] $to, string $subject, string $message, string|string[] $headers = ”, string|string[] $attachments = array() ): bool

Sends an email, similar to PHP’s mail function. — перевод Отправляет электронное письмо, аналогично функции почты PHP.

Возвращаемое значение true не означает автоматически, что пользователь получил электронное письмо успешно. Это всего лишь означает, что используемый метод смог обработать запрос без каких-либо ошибок.

Тип контента по умолчанию — text/plain, что не позволяет использовать HTML.
Однако вы можете установить тип контента электронного письма, используя фильтр «wp_mail_content_type».

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

Передаваемые параметры

$to string|string[] required
Массив или список адресов электронной почты, разделенных запятыми, для отправки сообщения.
$subject string required
Тема письма.
$message string required
Содержание сообщения.
$headers string|string[] optional
Дополнительные заголовки. По умолчанию: »
$attachments string|string[] optional
Пути к файлам для прикрепления. По умолчанию: array[]

Возвращаться
bool Указывает, было ли письмо отправлено успешно.

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

База индексов почтовых отделений Укрпочты

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

Так как сроки были сжатые — решили сделать решение «на коленке» и вот что получилось:

<?
ini_set("max_execution_time", "6000");
global $wpdb;
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );

set_regions();
function set_regions()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_regions";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `REGION_UA` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	// get regions data
	// get_regions_by_region_ua
	// receive data:
	// ["REGION_ID"]=>
	//   string(3) "262"
	//   ["REGION_UA"]=>
	//   string(18) "Вінницька"
	//   ["REGION_EN"]=>
	//   string(9) "Vinnytska"
	//   ["REGION_KATOTTG"]=>
	//   string(17) "05000000000010236"
	//   ["REGION_KOATUU"]=>
	//   string(9) "500000000"
	//   ["REGION_RU"]=>
	//   NULL
	
	// $region->REGION_UA - region name
	// $region->REGION_ID - region ID
	$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_regions_by_region_ua',
	[
		'headers' => [ 
			'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
			'Accept'=> 'application/json'
		]
	]);
	if(wp_remote_retrieve_response_code($data)==200)
	{
		$entries = json_decode($data['body']);
		$delete = $wpdb->query("TRUNCATE TABLE $table_name");
		foreach($entries->Entries->Entry as $entry)
		{
			$wpdb->insert( 
				$table_name, 
				[
					'REGION_UA' => $entry->REGION_UA, 
					'REGION_ID' => $entry->REGION_ID
				]
			);
		}
		echo 'regions scrapper - finish<br />';
		set_districts();
	}
	else
	{
		$data = json_decode($data['body']);
		var_dump($data->fault->code);
		var_dump($data->fault->message);
		die('error');
	}
}
// set_districts();
function set_districts()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_districts";
	$table_regions_name = $wpdb->prefix . "ukrposhta_regions";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `DISTRICT_ID` int(11) NOT NULL,
	  `DISTRICT_UA` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$result = $wpdb->get_results ( "SELECT * FROM $table_regions_name ORDER BY `REGION_ID`;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $region)
	{
		// get districts data
		// $district->DISTRICT_UA name of district
		// $district->DISTRICT_ID - district ID
		// sleep(1);
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_districts_by_region_id_and_district_ua?region_id='.
		$region->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			foreach($data->Entries->Entry as $entry)
			{
				$wpdb->insert( 
					$table_name, 
					[
						'REGION_ID' => $region->REGION_ID,
						'DISTRICT_UA' => $entry->DISTRICT_UA, 
						'DISTRICT_ID' => $entry->DISTRICT_ID
					]
				);
			}
			echo 'districts scrapper for region '.$region->REGION_UA.' - finish<br />';
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$region->REGION_UA);
		}
	}
	set_cities();
}
// set_cities();
function set_cities()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_cities";
	$table_districts_name = $wpdb->prefix . "ukrposhta_districts";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `DISTRICT_ID` int(11) NOT NULL,
	  `CITY_UA` text,
	  `CITY_ID` int(11) NOT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$result = $wpdb->get_results ( "SELECT * FROM $table_districts_name ORDER BY `DISTRICT_ID`;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $district)
	{
		// get cities data
		// $city->CITY_UA - city name
		// $city->CITY_ID - city ID
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_city_by_region_id_and_district_id_and_city_ua?district_id='.$district->DISTRICT_ID.'&region_id='.$district->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			foreach($data->Entries->Entry as $entry)
			{
				$wpdb->insert( 
					$table_name, 
					[
						'REGION_ID' => $district->REGION_ID,
						'DISTRICT_ID' => $district->DISTRICT_ID,
						'CITY_UA' => $entry->CITY_UA,
						'CITY_ID' => $entry->CITY_ID
					]
				);
			}
			echo 'cities scrapper for district '.$district->DISTRICT_UA.' - finish<br />';
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$district->DISTRICT_UA);
		}
	}
	set_postoffices()
}
function set_postoffices()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_post_offices";
	$table_districts_name = $wpdb->prefix . "ukrposhta_cities";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_UA` text,
	  `DISTRICT_UA` text,
	  `CITY_UA` text,
	  `POSTINDEX` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$value = 500*54;
	$result = $wpdb->get_results ( "SELECT * FROM $table_districts_name ORDER BY `CITY_ID` LIMIT $value, 500;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $city)
	{
		// get cities data
		// $city->CITY_UA - city name
		// $city->CITY_ID - city ID
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_postoffices_by_city_id?city_id='.$city->CITY_ID.'&district_id='.$city->DISTRICT_ID.'&region_id='.$city->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			if(isset($data->Entries->Entry))
			{
				$temp_index = '';	
				foreach($data->Entries->Entry as $entry)
				{
					if($temp_index!=$entry->POSTINDEX)
					{
						$wpdb->insert( 
							$table_name, 
							[
								'REGION_UA' => $entry->REGION_UA,
								'DISTRICT_UA' => $entry->DISTRICT_UA,
								'CITY_UA' => $entry->CITY_UA,
								'POSTINDEX' => $entry->POSTINDEX
							]
						);
					}
					$temp_index = $entry->POSTINDEX;
				}
				echo 'postoffices scrapper for city '.$entry->CITY_UA.' - finish<br />';
			}
			else
			{
				echo $city->CITY_UA.'- no post offices<br />';
			}
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$district->DISTRICT_UA);
		}
	}
}
die();
?>

Реализация текущая для CMS WordPress. Однако, ее можно легко использовать где угодно, заменив <?
ini_set("max_execution_time", "6000");
global $wpdb;
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );

set_regions();
function set_regions()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_regions";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `REGION_UA` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	// get regions data
	// get_regions_by_region_ua
	// receive data:
	// ["REGION_ID"]=>
	//   string(3) "262"
	//   ["REGION_UA"]=>
	//   string(18) "Вінницька"
	//   ["REGION_EN"]=>
	//   string(9) "Vinnytska"
	//   ["REGION_KATOTTG"]=>
	//   string(17) "05000000000010236"
	//   ["REGION_KOATUU"]=>
	//   string(9) "500000000"
	//   ["REGION_RU"]=>
	//   NULL
	
	// $region->REGION_UA - region name
	// $region->REGION_ID - region ID
	$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_regions_by_region_ua',
	[
		'headers' => [ 
			'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
			'Accept'=> 'application/json'
		]
	]);
	if(wp_remote_retrieve_response_code($data)==200)
	{
		$entries = json_decode($data['body']);
		$delete = $wpdb->query("TRUNCATE TABLE $table_name");
		foreach($entries->Entries->Entry as $entry)
		{
			$wpdb->insert( 
				$table_name, 
				[
					'REGION_UA' => $entry->REGION_UA, 
					'REGION_ID' => $entry->REGION_ID
				]
			);
		}
		echo 'regions scrapper - finish<br />';
		set_districts();
	}
	else
	{
		$data = json_decode($data['body']);
		var_dump($data->fault->code);
		var_dump($data->fault->message);
		die('error');
	}
}
// set_districts();
function set_districts()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_districts";
	$table_regions_name = $wpdb->prefix . "ukrposhta_regions";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `DISTRICT_ID` int(11) NOT NULL,
	  `DISTRICT_UA` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$result = $wpdb->get_results ( "SELECT * FROM $table_regions_name ORDER BY `REGION_ID`;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $region)
	{
		// get districts data
		// $district->DISTRICT_UA name of district
		// $district->DISTRICT_ID - district ID
		// sleep(1);
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_districts_by_region_id_and_district_ua?region_id='.
		$region->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			foreach($data->Entries->Entry as $entry)
			{
				$wpdb->insert( 
					$table_name, 
					[
						'REGION_ID' => $region->REGION_ID,
						'DISTRICT_UA' => $entry->DISTRICT_UA, 
						'DISTRICT_ID' => $entry->DISTRICT_ID
					]
				);
			}
			echo 'districts scrapper for region '.$region->REGION_UA.' - finish<br />';
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$region->REGION_UA);
		}
	}
	set_cities();
}
// set_cities();
function set_cities()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_cities";
	$table_districts_name = $wpdb->prefix . "ukrposhta_districts";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_ID` int(11) NOT NULL,
	  `DISTRICT_ID` int(11) NOT NULL,
	  `CITY_UA` text,
	  `CITY_ID` int(11) NOT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$result = $wpdb->get_results ( "SELECT * FROM $table_districts_name ORDER BY `DISTRICT_ID`;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $district)
	{
		// get cities data
		// $city->CITY_UA - city name
		// $city->CITY_ID - city ID
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_city_by_region_id_and_district_id_and_city_ua?district_id='.$district->DISTRICT_ID.'&region_id='.$district->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			foreach($data->Entries->Entry as $entry)
			{
				$wpdb->insert( 
					$table_name, 
					[
						'REGION_ID' => $district->REGION_ID,
						'DISTRICT_ID' => $district->DISTRICT_ID,
						'CITY_UA' => $entry->CITY_UA,
						'CITY_ID' => $entry->CITY_ID
					]
				);
			}
			echo 'cities scrapper for district '.$district->DISTRICT_UA.' - finish<br />';
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$district->DISTRICT_UA);
		}
	}
	set_postoffices()
}
function set_postoffices()
{
	global $wpdb;
	$table_name = $wpdb->prefix . "ukrposhta_post_offices";
	$table_districts_name = $wpdb->prefix . "ukrposhta_cities";
	$sql = "CREATE TABLE `$table_name` (
	  `REGION_UA` text,
	  `DISTRICT_UA` text,
	  `CITY_UA` text,
	  `POSTINDEX` text
	) ENGINE=InnoDB DEFAULT CHARSET=utf8;
	COMMIT;";
	dbDelta( $sql );
	
	$value = 500*54;
	$result = $wpdb->get_results ( "SELECT * FROM $table_districts_name ORDER BY `CITY_ID` LIMIT $value, 500;");
	$delete = $wpdb->query("TRUNCATE TABLE $table_name");
	foreach($result as $city)
	{
		// get cities data
		// $city->CITY_UA - city name
		// $city->CITY_ID - city ID
		$data = wp_remote_get('https://www.ukrposhta.ua/address-classifier-ws/get_postoffices_by_city_id?city_id='.$city->CITY_ID.'&district_id='.$city->DISTRICT_ID.'&region_id='.$city->REGION_ID,
		[
			'headers' => [ 
				'Authorization' => 'Bearer КОД_АПИ_ДЛЯ_ПОДКЛЮЧЕНИЯ',
				'Accept'=> 'application/json'
			]
		]);
		if(wp_remote_retrieve_response_code($data)==200)
		{
			$data = json_decode($data['body']);
			if(isset($data->Entries->Entry))
			{
				$temp_index = '';	
				foreach($data->Entries->Entry as $entry)
				{
					if($temp_index!=$entry->POSTINDEX)
					{
						$wpdb->insert( 
							$table_name, 
							[
								'REGION_UA' => $entry->REGION_UA,
								'DISTRICT_UA' => $entry->DISTRICT_UA,
								'CITY_UA' => $entry->CITY_UA,
								'POSTINDEX' => $entry->POSTINDEX
							]
						);
					}
					$temp_index = $entry->POSTINDEX;
				}
				echo 'postoffices scrapper for city '.$entry->CITY_UA.' - finish<br />';
			}
			else
			{
				echo $city->CITY_UA.'- no post offices<br />';
			}
		}
		else
		{
			$data = json_decode($data['body']);
			var_dump($data->fault->code);
			var_dump($data->fault->message);
			die('error '.$district->DISTRICT_UA);
		}
	}
}
die();
?>

Реализация текущая для CMS WordPress. Однако, ее можно легко использовать где угодно, заменив wp_remote_get на команды cURL и заменив запросы к БД и ее таблицам на соответствующие команды, которые будут работать в конкретном вашем случае.

Таблицы у нас 4:

  • данные с областями
  • данные с районами
  • данные с городами
  • данные с индексами почтовых отделений.

Также, если кому нужна уже собранная база — напишите — вышлем базу со всеми данными.

Баг с пагинацией главной страницы в WordPress

Нашли в системе WordPress такой баг, что если у вас главная страница указана шаблоном страницы или кастомная (хотя на многих других сайтах проверяли — и во всех случаях эта ошибка встречалась), то введя в строку поиска /page/2 (вместо 2 — можете ввести любое число) — то вы увидите, что откроется главная страница. Хотя должна открываться 404-я страница.

Мы решили для своего проекта эту проблему таким образом:

add_action('template_redirect', 'is_home_page', 10);
function is_home_page(){
    if(get_page_template_slug()==='page-home.php' && !empty(get_query_var('paged')))
    {
        global $wp_query;
        $wp_query->set_404();
        status_header( 404 );
        get_template_part( 404 );
        exit();
    }
}

Мы использовали конструкцию get_page_template_slug()===’page-home.php’, потому как у нас очень кастомная верстка.

В обычном случае же, думаю, достаточно будет проверки is_home() / is_front_page().

Соответственно, что происходит?

Мы добавили экшн, который проверяет есть ли у вас переменная paged и какое у нас название шаблона страницы. Если мы понимаем, что это главная страница, то показываем страницу 404 и пишем, что такой страницы не существует.

Все просто)

Надеюсь, что кому-то пригодится это решение так же, как оно пригодилось и нам!

Вывод тегов для категории (включая подкатегории)

Добавляем в файл functions.php следующую функцию (код):

// get tags for category (with children)
function get_category_tags($args) {
	global $wpdb;
	
	$tags = $wpdb->get_results
	("
		SELECT DISTINCT terms2.term_id as tag_id, terms2.name as tag_name, null as tag_link
		FROM
			".$wpdb->prefix."posts as p1
			LEFT JOIN ".$wpdb->prefix."term_relationships as r1 ON p1.ID = r1.object_ID
			LEFT JOIN ".$wpdb->prefix."term_taxonomy as t1 ON r1.term_taxonomy_id = t1.term_taxonomy_id
			LEFT JOIN ".$wpdb->prefix."terms as terms1 ON t1.term_id = terms1.term_id,

			".$wpdb->prefix."posts as p2
			LEFT JOIN ".$wpdb->prefix."term_relationships as r2 ON p2.ID = r2.object_ID
			LEFT JOIN ".$wpdb->prefix."term_taxonomy as t2 ON r2.term_taxonomy_id = t2.term_taxonomy_id
			LEFT JOIN ".$wpdb->prefix."terms as terms2 ON t2.term_id = terms2.term_id
		WHERE
			t1.taxonomy = 'category' AND p1.post_status = 'publish' AND terms1.term_id IN (".$args['categories'].") AND
			t2.taxonomy = 'post_tag' AND p2.post_status = 'publish'
			AND p1.ID = p2.ID
		ORDER by tag_name
	");
	$count = 0;
	foreach ($tags as $tag) {
		$tags[$count]->tag_link = get_tag_link($tag->tag_id);
		$count++;
	}
	return $tags;
}
// get tags for category (with children)

Вызываем эту функцию после в нужном шаблоне и нужном месте следующим образом:

$args = array(
        'categories'                => '12,13,14'
    );

$tags = get_category_tags($args);

12, 13, 14 — меняете на свои IDшники категорий, которые нужно вам показать.

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

// get tags for category (with children)
function get_category_tags($args) {
	global $wpdb;
	if(array_key_exists('post_type', $args)===false)
	{
		$args['post_type'] = "'post'";
	}
	
	$tags = $wpdb->get_results
	("
		SELECT DISTINCT terms2.term_id as tag_id, terms2.name as tag_name, null as tag_link
		FROM
			".$wpdb->prefix."posts as p1
			LEFT JOIN ".$wpdb->prefix."term_relationships as r1 ON p1.ID = r1.object_ID
			LEFT JOIN ".$wpdb->prefix."term_taxonomy as t1 ON r1.term_taxonomy_id = t1.term_taxonomy_id
			LEFT JOIN ".$wpdb->prefix."terms as terms1 ON t1.term_id = terms1.term_id,

			".$wpdb->prefix."posts as p2
			LEFT JOIN ".$wpdb->prefix."term_relationships as r2 ON p2.ID = r2.object_ID
			LEFT JOIN ".$wpdb->prefix."term_taxonomy as t2 ON r2.term_taxonomy_id = t2.term_taxonomy_id
			LEFT JOIN ".$wpdb->prefix."terms as terms2 ON t2.term_id = terms2.term_id
		WHERE
			t1.taxonomy = 'category' AND p1.post_status = 'publish' AND p1.post_type IN (".$args['post_type'].") AND terms1.term_id IN (".$args['categories'].") AND
			t2.taxonomy = 'post_tag' AND p2.post_status = 'publish' AND p2.post_type IN (".$args['post_type'].")
			AND p1.ID = p2.ID
		ORDER by tag_name
	");
	$count = 0;
	foreach ($tags as $tag) {
		$tags[$count]->tag_link = get_tag_link($tag->tag_id);
		$count++;
	}
	return $tags;
}
// get tags for category (with children)

Вызов функции теперь будет следующий для вывод тегой для кастомных постов:

<?$providers = get_category_tags(['categories' => $category_id, 'post_type' => '"game"']);?>

Использовать все это в теме можно следующим образом:

<?$providers = get_category_tags(['categories' => $category_id, 'post_type' => '"game"']);?>
		<?if(count($providers)):?>
		<nav class="providers">
			<?foreach($providers as $provider):?>
			<a href="<?=$provider->tag_link;?>"><?=$provider->tag_name;?></a>
			<?endforeach;?>
		</nav>
		<?endif;?>

Вывод тегов (tags) для кастомных постов (custom post type)

Всем привет.

У нас на проекте появилась задача сделать страницу в WordPress, где будут отображаться дополнительные параметры для кастомных типов записей. Грубо говоря — вывести все сущности одного типа, по которым фильтруются кастомные типы записей.

К примеру: у вас есть типа записей games. Условно говоря — это запись с игрой. Для каждой игры, помимо категории своей, есть также такой параметр, как «Провайдер» (Provider). Вот его мы решили хранить в базе, как тег для нашего custom_post_type => ‘game’.

И наша задача состоит в том, что нужно вывести на странице все провайдеры, у которых есть хоть одна запись типа «game». Получился код у нас следующий:

function get_terms_by_custom_post_type( $post_type, $taxonomy ){
	$args = array( 'post_type' => $post_type);
	$loop = new WP_Query( $args );
	$postids = array();
	// build an array of post IDs
	while ( $loop->have_posts() ) : $loop->the_post();
		array_push($postids, get_the_ID());
	endwhile;
	// get taxonomy values based on array of IDs
	$taxonomies = wp_get_object_terms( $postids,  $taxonomy );
	return $taxonomies;
}

Этот код работает. Все хорошо. Однако те, кто хорошо знает WordPress, заметит, что если эту функцию использовать, то сбиваются настройки для обьекта $post.

Для того, чтобы все работало как должно, нужно добавить еще одну строку:

	wp_reset_postdata();
В итоге у нас получился следующий код, который нужно добавить в файл functions.php для вашей темы:
function get_terms_by_custom_post_type( $post_type, $taxonomy ){
	$args = array( 'post_type' => $post_type);
	$loop = new WP_Query( $args );
	$postids = array();
	// build an array of post IDs
	while ( $loop->have_posts() ) : $loop->the_post();
		array_push($postids, get_the_ID());
	endwhile;
	// get taxonomy values based on array of IDs
	$taxonomies = wp_get_object_terms( $postids,  $taxonomy );
	wp_reset_postdata();
	return $taxonomies;
}

Использовать эту функцию необходимо следующим образом:

<?$providers = get_terms_by_custom_post_type('game', 'post_tag');?>

Наш код в итоге для вывода провайдеров получился следующим:

<?$providers = get_terms_by_custom_post_type('game', 'post_tag');?>
	
	<?if(count($providers)):?>
	<div class="games__body">
		<h1 class="entry-title text-center"><?=the_title();?></h1>
		<div class="games-providers__grid">
			<?foreach($providers as $provider):?>
			<a href="<?=get_term_link($provider, 'post_tag');?>" class="games-providers__item" id="<?=$provider->name;?>">
			</a>
			<?endforeach;?>
		</div>
	</div>
	<?endif;?>

Всем спасибо. Надеюсь будет полезна эта функция.

Скрываем админпанель для пользователей

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

Для своих сайтов и сайтов своих клиентов я всегда скрываю эту панель.

В этой статье расскажу как это делается.

Открываем файл functions.php в корне своей темы.

Далее вставляем следующий код:

add_filter('show_admin_bar', '__return_false');

Все. Теперь ваши авторизованные пользователи сайта WordPress не будут видеть админпанель вверху сайта.

Что мы сделали в итоге: мы добавили фильтр, который возвращает false для параметра show_admin_bar.

Если же вы хотите отключить ее только для определенного пользователя, то необходимо зайти во вкладку «Пользователи» в админпанели /wp-admin/users.php. Выбираем необходимого пользователя и у него снимаем флаг напротив Show Toolbar when viewing site.

Нажимаем синюю кнопку обновить. Готово!

Замена вариаций продукта с Select на Radio buttons для WooCommerce (вариант 2)

В одном из статей был описан метод, как можно заменить стандарный вывод выбора вариаций с помощью select на radio. Эта статья находится тут.

Сегодня же будет показан еще один, альтернативный, вариант решения данной проблемы.

Открываем файл /ваша_тема/woocommerce/single-product/add-to-cart/variable.php (если такого файла нет, то нужно создать его.

В папке /ваша_тема/woocommerce/ находятся шаблоны woocommerce, которые изменяются для темы. Исходники их находятся в папке с плагином woocommerce в папке /templates.

В этом файле мы перед функцией вывода select выбора вариаций товара добавляем

<?foreach($options as $index => $item):?>
<label for="<?=sanitize_title( $attribute_name );?>_<?=$item;?>" <?if($index===0):?>class="active"<?endif;?>><?=$item;?></label>
<?endforeach;?>

и оборачиваем все это (вместе с выводом select) в обертку

<div class="radio-input-container"></div>

В итоге у нас получится следующая конструкция:

<div class="radio-input-container">
<?foreach($options as $index => $item):?>
<label for="<?=sanitize_title( $attribute_name );?>_<?=$item;?>" <?if($index===0):?>class="active"<?endif;?>><?=$item;?></label>
<?endforeach;?>
<?php
wc_dropdown_variation_attribute_options(
array(
'options'   => $options,
'attribute' => $attribute_name,
'product'   => $product,
)
);
?>
</div>

в js файл со скриптами добавляем:

$('.radio-input-container label').on('click', function(){
var Value = $(this).text();
var Label = $(this);
$('.radio-input-container label').removeClass('active');
Label.addClass('active');
Label.parent().find('select').val(Value).change();
});

Я также добавил очистку наших «лейблов» по клику на ссылку «Очистить»:

$('.reset_variations').on('click', function(){
$('.radio-input-container label').removeClass('active');
});

и следующие стили получились:

.radio-input-container {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 15px;
label {
width: 20%;
text-align: center;
border: 1px solid @colorGrey;
border-radius: 5px;
padding: 6px 15px 5px 15px;
cursor: pointer;
font-weight: 500;
&:hover, &.active {
border-color: @colorRed;
background: @colorWhite;
}
}
select {
display: none;
}
}

Select я спрятал намеренно, что не портило дизайн и не путало пользователя.

В итоге получилась такая вот красота:

Отключение уведомлений о новом пользователе на почту администратора WordPress

Наверное, многие из владельцев сайтов на системе WordPress сталкивались с неимоверным количеством спама на почту… Где рассказывает система о том, что зарегистрирован новый пользователь или что пользователь сменил себе пароль.

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

Чтобы отключить уведомления по электронной почте WordPress по умолчанию для администраторов нужно сделать следующие действия…

Пожалуйста, отредактируйте файл function.php (в папке с вашей текущей темой) и добавьте следующий код

<?php
//Отключить уведомление о новом пользователе, отправленное администратору сайта
function wp_disable_new_user_notifications() {
//Удаление исходного использования созданных писем
remove_action( 'register_new_user', 'wp_send_new_user_notifications' );
remove_action( 'edit_user_created_user', 'wp_send_new_user_notifications', 10, 2 );

}
add_action( 'init', 'wp_disable_new_user_notifications' );

Ссылка на перевод страницы WPML/WordPress

Очень часто я в проектах сталкиваюсь с тем, что на страницах в шаблонах присутствуют статические кнопки, ссылки, изображения-ссылки на какие-то страницы (типа контакты, услуги и т.п.). Для них нужно учитывать тот момент, что если проставить просто статическую ссылку, то при переходе на другой язык — ссылка будет вести на ту страницу, которую вы хотите открыть кликая на нее, но на языке, который указан в самой ссылке. Это не правильно. Вот по этому для всех тех, кто пользуется плагином WPML (WordPress) я написал краткий мануал о том, как правильно проставлять такие ссылки корректно.

Конструкция

<?=apply_filters( 'wpml_get_translated_slug', '_post_slug_', '_post_type_', ICL_LANGUAGE_CODE);?>

используется для создания ссылок на переведенные страницы в WordPress, когда используется плагин WPML (WordPress Multilingual). Она позволяет получить переведенный слаг (часть URL-адреса после домена) страницы для заданного типа записи и языка.

Для использования этой конструкции необходимо заменить «post_slug» на слаг страницы, которую вы хотите перевести, «_post_type_» на тип записи (например, post или page), а ICL_LANGUAGE_CODE на языковой код, соответствующий языку перевода.

Например, если вы хотите создать ссылку на переведенную страницу с заголовком «About Us» (тип записи «page») на испанском языке (код языка «es»), то конструкция будет выглядеть следующим образом:

<?=apply_filters( 'wpml_get_translated_slug', 'about-us', 'page', 'es');?>

Такая ссылка будет указывать на страницу с переведенным заголовком «Acerca de nosotros».

Использование конструкции <?=apply_filters( ‘wpml_get_translated_slug’, ‘_post_slug_’, ‘_post_type_’, ICL_LANGUAGE_CODE);?> позволяет создавать ссылки на переведенные страницы без необходимости знать их фактический URL-адрес, что может быть полезно при изменении структуры сайта или переносе страниц на другие адреса.