WordPress: вывод записей произвольных типов (Custom Post Types)

Владимир | | PHP, Web разработка, WordPress.

wordpress custom queries

На сегодняшний день движок WordPress довольно интенсивно развивается. Во всяком случае изменения в версиях с 2.8 по 3.1 добавляют WP возможности полноценных CMS. Большинство из этих возможностей должны быть активированы на уровне темы (или плагина), поэтому сразу после установки WP вы работаете с ним как с обычным блоговым движком.

Вроде бы все правильно. Если какие-то возможности вам не нужны, вы их не используете. Но при этом возникает серьёзная проблема с документацией. Дело в том, большинство примеров и в Кодексе, и в других источниках, ориентированы на самый простой случай – дефолтную инсталляцию WP. Как только вы активируете дополнительные возможности ко многим рекомендациям нужно относится очень аккуратно.

Об одном из таких примеров я хочу рассказать в этой статье.

Предположим, вы решили использовать произвольные типы записей (custom post types). Создать их достаточно просто и на эту тему есть масса статей, поэтому подробно останавливаться на этом моменте не будем.

В большинстве случаев достаточно добавить следующий код в файл functions.php вашей темы.

register_post_type('gadgets',
	array(
		'labels' => array(
			'name' => __('Gadgets'),
			'singular_name' => __('Gadget'),
			//остальные переводы
		),
		'public' => true,
		'publicly_queryable' => true,
		'query_var' => true,
		'taxonomies' => array('category','post_tag'),
		'supports' => array('title','editor','author','thumbnail','excerpt','comments'),
		'rewrite' => array('slug' => 'gadgets'),
		//другие настройки
	)
);

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

http://blog.url/gadgets

Теперь обратите внимание, что для этого типа записей мы используем такие же категории, как и для типа Post (строка 11).

Очевидно, что на странице категории мы захотим увидеть записи всех типов, но WordPress покажет только тип Post.

Происходит это потому что WP при формировании запроса к базе данных явно указывает тип поста. И по-умолчанию этот тип – post.

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

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

Решается задача достаточно просто.

function prefix_pre_get_posts($query) {
     if ($query->is_category) {
          $query->set('post_type', 'any');
     }
     return $query;
}

add_action('pre_get_posts', 'prefix_pre_get_posts');

Здесь мы назначаем собственный обработчик события (action) pre_get_posts. Как следует из названия, это событие возникает непосредственно перед отправкой запроса к БД. В первом параметре обработчик получает объект типа WP_Query, который формирует запрос к базе.

Прежде всего, мы проверяем, что в данный момент создаётся страница категории (строка 2). А затем изменяем атрибут post_type. По-умолчанию он равен post. Не забывайте, что обработчик обязательно должен вернуть изменённый (или неизменённый) объект WP_Query.

Вообще, если вы планируете работать с новыми возможностями WP (произвольными типами записей, таксономиями, форматами записей), то научиться работать с WP_Query нужно обязательно. Встроенными функциями движка здесь вы не обойдетесь.

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

Успехов!

Интересно почитать

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

  • igor

    Ого, не знал, что:

    1. query_posts так сильно отличается WP_Query
    2. в кодексе описаны не все особенности функций

    Спасибо, за просвещение.

  • Спасибо, довольно полезно.

  • уууу, дуже корисно, величезне спасибі

  • так и не нашел, как добавить свой список категорий в произвольные записи

  • Тимур

    add_action('init', 'create_post_type');
         
        function create_post_type() {
            register_post_type('video',
                array(
                    'labels' => array(
                        'name' => __('Videos'),
                        'singular_name' => __('Videos'),
                        …
            
                    ),
                    'public' => true,
                    'publicly_queryable' => true,
                    'show_ui' => true,
                    'query_var' => true,
                    'capability_type' => 'post',
                    'hierarchical' => true,
                    'menu_position' => null,
                    //'taxonomies' => array('category','post_tag'),
                    'supports' => array('title','editor','author', 'custom-fields','thumbnail','excerpt','comments'),
                )
            );
             register_taxonomy( 'video', 'video', array( 'hierarchical' => true, 'label' => 'Номенклатура', 'query_var' => true, 'rewrite' => true ) );

        }

    • Тимур

      как сделать вывод категории подобного типа?Пост выводится, но не пойму как вывести категорию

      • Формально вы правы, XSS — тип уязвимости. Но если смотреть шире :), то речь идет об выполнении скриптов с других ресурсов. Эти скрипты не обязательно наносят вред.

      • Используйте WP_Query. Пример установки параметров можно посмотреть здесь.

  • Алексей Большаков

    не совсем в тему, но все таки
    у кого-нибудь есть готовое решение для того чтобы произвольные типы записей и их таксы выводились в sitemap.xml через плагин Google XML Sitemaps или подобный
    и возможно ли такое в принципе?

    • Google (XML) sitemap generator нормально создает карту со ссылками на произвольные типы. Просто укажите в настройках какие типы включить в карту.

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

    • У меня стояла немного другая задача. get_posts вернет те статьи, которые вам нужны. Но мне нужно было получить эти статьи во время основного запроса WP. Если просто использовать get_posts в теме, то сначала будет выполнен стандартный запрос WP, а затем ваш запрос, с параметрами, указанными при вызове get_posts.

  • Asha7771

    Помогите, пожалуйста!

    У меня не работает с Custom Post Types, следующий код:

  • Search Bot

    Вопрос: зарегистрирован custom type post, у него своя независимая категория. Теперь, когда ставлю этот последний кусок кода — pre_get_posts , то пропадают стандартные категории обычных постов (post), но все в порядке с категорией custom type post. Убираю этот кусок кода — всё наоборот: обычные категории возвращаются, кастомная пропадает. Что сделать, чтобы они все были видны?
    Спасибо заранее 🙂

    • Попробую ответить сразу на оба вопроса.

      Когда вы регистрируете новый тип записи, в параметре taxonomies передаётся список таксономий, который можно для этого типа устанавливать. В статье

      'taxonomies' => array('category','post_tag'),

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

      Если у вас отдельная таксономия, укажите

      'taxonomies' => array('ваша_таксономия'),

      С выводом категорий сложнее. Я не совсем понял что значит

      >>пропадают стандартные категории обычных постов

      Т.е. вы не можете зайти на страницы вида
      site.domain/category/category_name ?

      • Зайти могу, в титле пишется название категории («Новости»), но в контенте — «Ничего не найдено. Извините, результатов не найдено. Попробуйте найти нужную запись с помощью поиска.»

        • Теперь понятно 😉

          Между моим и вашим примером есть существенное отличие. В вашем варианте
          if ($query->is_category) {
          $query->set('post_type', 'board');
          }
          Это значит, что на всех страницах категорий будут выведены только посты только типа board. Соответственно, вы получаете пустые страницы.

          Т.е. условие $query->is_category для вашего случая не подходит.

          По-моему, вам вообще не имеет смысла использовать pre_get_posts. Вы можете вы использовать отдельный шаблон для вывода постов вашей новой таксономии (http://codex.wordpress.org/File:Template_Hierarchy.png). Нужно создать в папке темы файлы с названием taxonomy_название.php (проще всего скопировать category.php и потом изменять).

          Кстати, кода, который создает новую таксономию (register_taxonomy) в ваших файлах я не нашел.

        • Мне тут подсказали: если заменить в нежелательном pre_get_posts код
          $query->set('post_type', 'board');
          на:
          $query->set('post_type', array('post', 'board')); — всё работает!!!
          А таксономии у меня и без register_taxonomy работают, достаточно было вот этого:
          function register_post_type_board() {
          register_post_type('board',array(
          'labels' => array(
          'name' => _x('Объявления', 'board'),
          'singular_name' => _x('Объявление', 'board'),
          'add_new' => _x('Добавить новое', 'board'),
          'add_new_item' => __('Добавить новое объявление'),
          'edit_item' => __('Редактировать объявление'),
          'new_item' => __('Новое объявление'),
          'view_item' => __('Смотреть объявление'),
          'search_items' => __('Поиск объявлений'),
          'not_found' => __('Не найдено объявление'),
          'not_found_in_trash' => __('Не найдено объявление в корзине'),
          'parent_item_colon' => »
          //остальные переводы
          ),
          'public' => true,
          'publicly_queryable' => true,
          'query_var' => true,
          'taxonomies' => array('category','post_tag'),
          'supports' => array('title','editor','author','thumbnail','excerpt','comments'),
          'rewrite' => true,
          ));
          }
          add_action('init', 'register_post_type_board');

          Это верно? (работает, во всяком случае)
          Спасибо за помощь!

        • ENEYSolutions
        • Точно. Спасибо большое!

        • Пожалуйста 🙂
          Только если вы использовали
          я не понимаю по$query->set('post_type', array('post', 'board'));
          почему вам не подошел вариант из статьи
          $query->set('post_type', 'any');
          ?

        • Я не понял просто что там вставлять вместо any. Попробовал вставить «board» — не получилось. Ну не программист я 🙂

        • any — соответствует всем типам постов.

          Если нужно выводить только конкретные типы, то их названия нужно перечислить в массиве. Например,

          array('post', 'board') выведет посты типа post и board, но не выведет page.

        • GeFFest

          Так это уже не таксономии, это пользовательский тип записей.

  • Search Bot

    В продолжение: строка 'taxonomies' =>
    array('category','post_tag'), означает что у нас для нового типа записей используются обычные категории. Что это значит?
    Сейчас для пробы добавил обычный пост в кастомную категорию. Он виден в обычной ленте постов (где остальные посты этой кастомной категории не видны), а в своей категории его не видно, там только записи добавленные через свой инструментарий для кастомных постов.
    В общем, что делать, если у меня для кастомного типа записи используется уникальная категория?

  • Денис

    Нашел для себя другое применение, вывод записей в случайном порядке без использования query_posts.
    С query_posts никакие методы не могли заставить работать постраничную навигацию…

    Огромнейшее спасибо! =)))

    function prefix_pre_get_posts($query) {
    $query->set('orderby', 'rand');
    return $query;}
    add_action('pre_get_posts', 'prefix_pre_get_posts');

    • В вашей реализации может возникнуть проблема с производительностью при большом количестве постов. Подробнее читайте здесь.

  • алекс

    а у меня на кастом пост не выводит похожие записи, что длать?

    вот пример http://recepty-salata.ru/listings/calat-zimnij/

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

    • Напишите, пожалуйста, подробнее что вы делаете.
      Стандартный плагин — это какой?

      • Amira

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

        • Amira

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

        • Пожалуйста, сделайте скриншоты страниц с рубриками и списком постов, vladimirsta@yandex.ru

  • Евгений

    наконец-то!!!! то, что надо было для правильной работы плагина Magic Fields 2!!! спасибо огромное!

  • Кто нибудь подскажет, как в админку вывести post-formats записи?

    • Если я правильно понял, вы хотите добавить ещё одну колонку в список постов и указать в ней post-formats?

      Подробная инструкция по добавления колонки есть здесь. В Вашем случае отличие будет только в том, что вместо миниатюры, выводиться будет формат поста, с помощью функции get_post_format.

  • У вас очень полезный сайт!

    Помогите написать цикл для пользовательской таксономии.
    С помощью планина «Сustom Press» создал тип записей «video», далее создал таксономию «video-category», а в них рубрики.

    Для отображения использую шаблон «taxonomy-video-category.php».

    Нужно чтобы эти рубрики отображались, не все записи «video», а по рубрикам, как надо.

    Буду очень благодарен за помощь. Уже два дня мучаюсь.

    • Несколько вопросов:
      1) на странице вы хотите вывести ссылки на вообще все записи «video» или показать часть и сделать постраничную навигацию? Или в каждой рубрике показать, например, первые 10 записей?
      2) WP позволяет устанавливать для одной записи несколько категорий. Т.е. запись должна быть выведена несколько раз?

      В общем случае, можно сделать так:
      1) получить весь список категорий с помощью функции get_categories.
      2) в цикле для каждой категории получить список записей. С помощью примерно такого кода:
      $the_query = new WP_Query(array(
      'post_type' => 'video',
      'tax_query' => array(
      array(
      'taxonomy' => 'category',
      'field' => 'slug',
      'terms' => 'тут нужно указать slug текущей категории',
      ),
      ),
      ));

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

      • Есть тип постов «видео». Нужно чтобы все работало как стандартные рубрики. Чтобы посты выводились на странице соответственно. Если страница рубрики Видео-> Наше видео — там выводятся посты, помеченные «рубрика — наше видео». И так далее.

        У меня же тип поста «Видео». Таксономия «video-cat». В таксономии есть пока две рубрики: «Наше видео» и «Видео партнеров». Нужно чтобы по ссылке на страницу рубрики «Наше видео» там выводились только посты из рубрики «Наше видео» и так далее.

        Вот нужен цикл, чтобы он выводил посты соответствующие терму страницы.

        • Всё-таки не пойму в чем проблема.
          WP автоматически создаёт страницы для каждого термина таксономии. Т.е. у вас должны быть страницы вида /video-cat/nashe-video
          Для их отображения используется шаблон taxonomy.php, а запрос к базе формирует WP и, по-умолчанию, выбираются все посты для которых установлен соответствующий термин.
          Что именно нужно изменить?

        • — Вот есть у меня тип записей «news».

          — Есть таксономия «qwerty», закреплена за типом записей «news». «register_taxonomy('qwerty', array('news', ), $args);». и «'hierarchical' => true,»

          — Захожу я туда и создаю значит рубрики (термы), допустим «odin» и «dva», ссылки у них формируются автоматически «site/qwerty/odin» и «site/qwerty/dva».

          — Перехожу по любой из ссылок (на страницу терма по идее уже). Там отражается то, что написано в файле taxonomy.php. Шапка, футер, все как надо. А там где место для записей данного терма — пустота. Стандартный цикл (который записан в файле taxonomy.php) ВП:

          ничего не выводит там.

          А какой цикл нужен? Или почему этот не работает, если WP такой умный и все самостоятельно должен делать.

        • А для конкретных записей «news» вы указали термины («odin» и «dva»)?

        • Естественно

  • Игорьбай

    Спасибо. Но у меня дилема, которую решить пока не могу. С помощью плагина «Types — Complete Solution for Custom Fields and Types» — созданы такие доп. поля как, «имя», «должность», «текст» и «изображение». Эти поля выводятся на главной. Также в записи присутствует возможность добавления второстепенных (вышеперечисленных) полей, но они не выводятся на главной. То есть есть возможность вывести только одно изображение той или иной записи, но не более. Хотя внутри записи их может быть хоть тысяча. Плагин выводит еще три изображения из трех других записей.
    Внимание вопрос. Как сделать так, что-бы плагин выводил по четыре полея с «именем», «должностью», «текстом» и «изображением» из одной записи. Но не выводя при этом содержимое полей остальных записей. Буду очень благодарен. Заранее спасибо.

    • В шаблоне темы:

      $i = 0;

      ... начало цикла WP ...

      ... вывод заголовка ...

      if ($i === 0) {
      $id = get_the_ID();
      echo get_post_meta($id, 'имя поля 1', true);
      echo get_post_meta($id, 'имя поля 2', true);
      ...
      }

      ... конец цикла ...

      • Игорьбай

        Спасибо большое. Поковырялся, но ни чего не вышло, к сожалению пока что с пхп ооочень плохо знаком. Не понимаю, как открывать цикл и как его закрывать. Скидываю кусок кода, который отвечает за вывод данных из четырех записей. Как его переиначить, что-бы (как я уже писал) выводились данные только из одной записи. Если Вам не трудно, просьба помочь. Заранее ОЧЕНЬ благодарен.

        4, 'category' => 10, 'orderby' => 'post_date', 'order' => 'DESC'); ?>

        ID) ); ?>
        <div class=" m_cor_one-3" style=»margin: 5px 18px 0;»>
        <img src="ID,'wpcf-image',true) ?>» height=»120" />

        ID,'wpcf-name_f',true) ?>

        ID,'wpcf-mnenie',true) ?>

        • Ваш код сильно обрезан, выложите его на какой-нибудь сервис вроде http://pastie.org/

          Открыть и закрыть цикл можно так:

          <?php if ( have_posts() ) : while ( have_posts() ) : ?>

          ... вывод записей ...

          <?php endwhile; else: ?>

          ... сообщение о том, что записи не найдены

          <?php endif; ?>

        • Игорьбай

          Вот мой код:

          http://pastie.org/pastes/9550213/text

          В панели управления, в опции «Редактировать группу» добавил еще одно поле для имени. Теперь их два: «name_f» и «name-f-2». Но вывести их на главную так и не получается. Копаюсь в коде методом тыка. То циклить начинает, то вообще блок пропадает. Буду дальше экспериментировать, конечно с надеждой на Вашу помощь.

        • Подождите, в первом комментарии Вы спрашивали как вывести дополнительные поля для первых четырех записей.
          А в коде, который Вы прислали, для первых четырёх записей только изменяются CSS стили. При этом, в цикле не выводятся ни заголовок, ни анонс записи, только метаданные.
          Если хотите отключить вывод метаполей для всех записей кроме первых 4-х, то нужно заключить их в if блок — http://pastie.org/9551041

        • Игорьбай

          Видимо не совсем правильно объяснил. Мне нужно вывести четыре вида доп. поля из одной записи. То есть на главной должна быть предоставлена инфа только одной записи (остальные, предыдущие игнорируются). А именно, четыре поля с именем, четыре поля с должностью, четыре с изображением и четыре с текстом. Пример. Запись — «Вопрос по Сирии», должна включать в себя четыре мнения экспертов:

          «Иван», «Насяльника», «фото с Иваном», «комментарий с Иваном»
          «Петр», «Эксперт по Сирии», «фото с Петром», «комментарий с Петра»
          «Вадим», «Посол в Сирии», «фото с Вадимом», «комментарий Вадима»
          «Елена», «Историк», «фото с Еленой», «комментарий Елены».

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

          На данный момент выводятся значения одного эксперта с каждой записи, несмотря на то, что в админке есть возможность добавлять в одной записи к полю имя, хоть 100500 значений 🙂

        • Попробуйте внутрь цикла вставить следующий код:

          var_dump(get_post_custom());
          Он должен вывести значения всех метаполей, привязанных к текущему посту.

  • IsmailAl

    Хитро вы код adsence подставили..

  • Добрый день.

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

    Доходит до 10 записи и все.

    вставлял , но все равно не показывает

    • Можете показать код с помощью которого вы выводите записи?

      У объекта WP_Query есть параметр — posts_per_page, с помощью которого можно изменить количество записей на странице.