Метка: wordpress

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

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

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

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

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

Нашел текущий код тут https://gist.github.com/brendonexus/1def5ec2689b66c2101d969c183042bc

Однако обнаружил, что он не просто заменяет вариации продукта, но и выводит предыдущие значения в select.

Поправил его немного и получилось такое:

// add radio buttons for woocommerce's product
function variation_radio_buttons($html, $args)
{
$args = wp_parse_args(apply_filters('woocommerce_dropdown_variation_attribute_options_args', $args), array(
'options' => false,
'attribute' => false,
'product' => false,
'selected' => false,
'name' => '',
'id' => '',
'class' => '',
'show_option_none' => __('Choose an option', 'woocommerce'),
));

if (false === $args['selected'] && $args['attribute'] && $args['product'] instanceof WC_Product) {
    $selected_key = 'attribute_' . sanitize_title($args['attribute']);
    $args['selected'] = isset($_REQUEST[$selected_key]) ? wc_clean(wp_unslash($_REQUEST[$selected_key])) : $args['product']->get_variation_default_attribute($args['attribute']);
}

$options = $args['options'];
$product = $args['product'];
$attribute = $args['attribute'];
$name = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title($attribute);
$id = $args['id'] ? $args['id'] : sanitize_title($attribute);
$class = $args['class'];
$show_option_none = (bool)$args['show_option_none'];
$show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __('Choose an option', 'woocommerce');

if (empty($options) && !empty($product) && !empty($attribute)) {
    $attributes = $product->get_variation_attributes();
    $options = $attributes[$attribute];
}

$radios = '<div class="variation-radios">';

if (!empty($options)) {
    if ($product && taxonomy_exists($attribute)) {
        $terms = wc_get_product_terms($product->get_id(), $attribute, array(
            'fields' => 'all',
        ));

        foreach ($terms as $term) {
            if (in_array($term->slug, $options, true)) {
                $radios .= '<div><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr($term->slug) . '" ' . checked(sanitize_title($args['selected']), $term->slug, false) . '><label for="' . esc_attr($term->slug) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $term->name)) . '</label></div>';
            }
        }
    } else {
        foreach ($options as $option) {
            $checked = sanitize_title($args['selected']) === $args['selected'] ? checked($args['selected'], sanitize_title($option), false) : checked($args['selected'], $option, false);
            $radios .= '<div><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr($option) . '" id="' . sanitize_title($option) . '" ' . $checked . '><label for="' . sanitize_title($option) . '">' . esc_html(apply_filters('woocommerce_variation_option_name', $option)) . '</label></div>';
        }
    }
}

$radios .= '</div>';

return $radios;

}

add_filter('woocommerce_dropdown_variation_attribute_options_html', 'variation_radio_buttons', 20, 2);
// end add radio buttons for woocommerce's product

Тут представлен код, который подойдет для многих задач. Конкретно свое кастомное решение для моего проекта я не показываю, так как там еще учитывались «побочные» параметры и значени, которых у вас никогда не будет и которые специфичны исключительно для разрабатываемого мною проекта.

Надеюсь, что эта статья будет полезна вам, также как и код.

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

Как вывести записи с непустыми meta_value

При разработке проекта https://foodies.academy возникла задача вывести рецепты, у которых есть видео.

Для унификации задачи сформулируем ее иначе: «необходимо сделать вывод постов, у которых произвольное поле (meta_key) не пустое (meta_value)».

Реализовывать эту задачу мы будем с помощью WP_Query.

В нашем случае мы делаем выборку 5 последних добавленных постов (записей) с post_type равное ‘repice‘ и у которых meta_key равно ‘recipe_video_embed‘ и значение meta_value для этого поля существует и не пустое.

Для этого мы выберем метод сравнение meta_compare NOT IN.

Если буквально говорить, то можно этот код озвучить следующим образом:

делаем выборку 5 постов типа recipe, у которых мета поле (meta_key) равно ‘recipe_video_embed‘ (в нашем случае это означает, что добавлено поле для видео) и оно не равно » (пустой строке) (в нашем случае это означает, что поле для видео содержит какие-то символы.

Собственно код представлен ниже:

<?
$args = [
	'post_type' => 'recipe',
	'posts_per_page' => 5,
	'meta_key'     => 'recipe_video_embed',
	'meta_value'   => [''],
	'meta_compare' => 'NOT IN',
	'orderby' => 'date', 
	'order' => 'DESC', 
];

$content = new WP_Query( $args );
?>

У нас все работает так, как нужно.

Хорошего и чистого кода и вам, друзья!

Свои размеры изображений в WordPress: добавление и удаление

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

add_image_size( string $name, int $width, int $height, bool|array $crop = false )

Функция add_image_size зарегистрирует новый размер изображения.

Параметры

$name — (строка) (Обязательно) Идентификатор размера изображения.

$width — (int) (Необязательно) Ширина изображения в пикселях. По умолчанию 0.

$height — (int) (Необязательно) Высота изображения в пикселях. По умолчанию 0.

$crop — (bool|array) (Необязательно) Обрезка изображения. Если false, изображение будет масштабировано (по умолчанию). Если true, изображение будет обрезано до указанных размеров с использованием центральных положений. Если это массив, изображение будет обрезано с использованием массива, чтобы указать место обрезки. Значения массива должны быть в формате: array(x_crop_position, y_crop_position), где:

     x_crop_position принимает: «слева», «по центру» или «справа».
     y_crop_position принимает: «сверху», «по центру» или «снизу».

Значение по умолчанию: false

В системе есть зарезервированные имена размеров изображений, распознаваемые WordPress: «thumbnail», «medium», «средний», «medium_large», «large» и «post-thumbnail».

Имена «thumb» и «thumbnail» — это просто псевдонимы — они совершенно одинаковы.

Подробное объяснение и «почему» читайте далее в статье image_downsize().

Однако при необходимости вы всегда можете установить параметры самостоятельно:

update_option( 'thumbnail_size_w', 160 );
update_option( 'thumbnail_size_h', 160 );
update_option( 'thumbnail_crop', 1 );

Пример использования функции. Давайте добавим новый размер свой для изображений 330px на 220px.

add_image_size( ‘custom-size’, 330, 220 ); // 330 пикселей в ширину и 220 пикселей в высоту, режим мягкой пропорциональной обрезки.

Если же требуется удалить пользовательский размер для изображений, то существует обратная функция remove_image_size

remove_image_size( string $name )

remove_image_size — удалить новый размер изображения.

Параметры

$имя — (string) (Обязательно) Размер удаляемого изображения.

Возвращает (bool) True если размер изображения был успешно удален, false в случае ошибки.

WordPress pre_get_posts redirect для мультиязычного контента с помощью wpml

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

есть несколько языков на сайте: английский, испанский и китайский. Новости пишут только на английском языке. Для остальных языковых версий нужно сделать тизер для каждой новости (превью / лид-текст новости) и если пользователь кликает по не ней — то переводить его на английскую версию сайта и открывать интересующую новость на английском языке.

Задача не тривиальная. Решение весьма простое.

Был написал следующий функционал в файле functions.php

add_action('pre_get_posts', function(){
if(in_array( get_post_type(), ['event', 'post']) && is_single())
{
    $wpml_permalink = apply_filters( 'wpml_permalink', get_the_permalink(), 'en' );
    if(ICL_LANGUAGE_CODE != 'en' && !empty($wpml_permalink))
    {
        $wpml_permalink = apply_filters( 'wpml_permalink', get_the_permalink(), 'en' );
        echo '<script type="text/javascript">';
        echo 'window.location = "'.$wpml_permalink.'";';
        echo '</script>';
    }
}
});

Здесь мы проверяем

in_array( get_post_type(), [‘event’, ‘post’])

тип нашего поста event или post? Если да, то проходим к дальнейшей проверке:

is_single()

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

Собственно далее мы проверяем, не английский ли у нас язык (используется WPML плагин для мультиязычности) и если это так, то редиректим страницу на английскую версию сайта.

echo '<script type="text/javascript">';
        echo 'window.location = "'.$wpml_permalink.'";';
        echo '</script>';

где $wpml_permalink это ссылка на текущую новость на английском языке:

$wpml_permalink = apply_filters( 'wpml_permalink', get_the_permalink(), 'en' );