everything you need to know about wp_query, wordcamp russia 2014
DESCRIPTION
http://2014.russia.wordcamp.org/TRANSCRIPT
Всё, что нужно знать о WP_Query
Сергей Бирюков
WordCamp Russia 2014
Обо мне
Сергей Бирюков● Разработчик ядра WordPress
http://core.trac.wordpress.org● Локализатор WP в России
http://ru.wordpress.org
http://sergeybiryukov.ru@flash_usb
Что мы знаем о WP_Query?
Условные теги
is_author(), is_category(), is_home() и т. д.
Кто сталкивался с query_posts()?
Способы получения записей
● query_posts()● new WP_Query()● get_posts()
Цикл WordPress
while ( have_posts() ) :
the_post();
endwhile;
Вторичный цикл
$query = new WP_Query( … );
while ( $query->have_posts() ) :
$query->the_post();
endwhile;
Массив записей
$result = get_posts( … );
foreach ( $result as $post_obj ) {
...
}
Что мы не знаем?
У каждого объекта запроса есть методы
is_author() — то же самое, что $wp_query->is_author().
function is_author() {
global $wp_query;
return $wp_query->is_author();
}
Обычный цикл
while ( have_posts() ) :
the_post();
if ( is_author() )
echo 'Страница автора';
endwhile;
Обычный цикл
while ( have_posts() ) :
the_post();
if ( $wp_query->is_author() )
echo 'Страница автора';
endwhile;
Вторичный цикл
$query = new WP_Query( … );
while ( $query->have_posts() ) :
$query->the_post();
if ( $query->is_author() )
echo 'Страница автора.';
endwhile;
Вторичный цикл
$query = new WP_Query( … );
while ( $query->have_posts() ) :
$query->the_post();
if ( $query->is_author() )
echo 'Страница автора.';
endwhile;
Вторичный цикл
$query = new WP_Query( … );
while ( $query->have_posts() ) :
$query->the_post();
if ( $query->is_author() )
echo 'Страница автора.';
endwhile;
Если мы создаём новый объект:
$my_query = new WP_Query( $query );
то можем вызывать его методы:
while ( $my_query->have_posts() ) :
$my_query->the_post();
endwhile;
wp_reset_postdata();
● Зачем нужны wp_reset_postdata() и wp_reset_query()?● Что насчёт query_posts()?● Как изменить запрос?● Как изменить основной запрос?
Что такое основной запрос,и почему это важно?
wp-blog-header.php
// Загружаем окружение WordPress
require './wp-load.php';
// Определяем, какие файлы шаблонов подключить
require WPINC . '/template-loader.php';
Что происходит при загрузке?
$wp_the_query = new WP_Query();
$wp_query =& $wp_the_query;
Немного о ссылках в PHP
$a = 4;
$b =& $a;
$b = 2;
var_dump( $a ); // int(2)
$a = 6;
var_dump( $b ); // int(6)
● Основной запрос хранится в $wp_the_query.● Его копия хранится в $wp_query.
wp-blog-header.php
// Загружаем окружение WordPress
require './wp-load.php';
// Определяем, какие файлы шаблонов подключить
require WPINC . '/template-loader.php';
wp-blog-header.php
// Загружаем окружение WordPress
require './wp-load.php';
// Всё происходит здесь
wp();
// Определяем, какие файлы шаблонов подключить
require WPINC . '/template-loader.php';
Что делает вызов wp()?
function wp( $query_vars = '' ) {
global $wp;
$wp->main( $query_vars );
}
Что это было?!
При загрузке
$wp = new WP();
Есть функция wp() и класс WP.
class WP {
…
function main() {
$this->init();
$this->parse_request();
$this->send_headers();
$this->query_posts();
$this->handle_404();
$this->register_globals();
...
class WP {
…
function main() {
$this->init();
$this->parse_request();
$this->send_headers();
$this->query_posts();
$this->handle_404();
$this->register_globals();
...
WP::parse_request()● Разбирает URL с помощью WP_Rewrite● Задаёт переменные запроса для WP_Query
WP::query_posts() {
global $wp_the_query;
$wp_the_query->query( $this->query_vars );
}
SELECT SQL_CALC_FOUND_ROWS
wp_posts.*
FROM wp_posts
WHERE 1=1
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10
wp-blog-header.php
// Загружаем окружение WordPress
require './wp-load.php';
// Определяем, какие записи нужны, затем запрашиваем их
wp();
// Загружаем тему оформления
require WPINC . '/template-loader.php';
Когда загружается тема оформления,мы уже получили записи.
Тогда зачем делать так?
query_posts( 'author=-5' );
get_header();
while ( have_posts() ) :
the_post();
endwhile;
get_footer();
Получается в два раза больше запросов!
● Первый, который WordPress сделал по умолчанию.● Второй, который мы будем использовать.
* В общем случае WP_Query выполняетне один запрос, а четыре.
1. Получаем записи:SELECTSQL_CALC_FOUND_ROWS …FROM wp_posts LIMIT 0, 10
2. Сколько найдено записей?SELECT FOUND_ROWS()
3. Получаем метаданные для этих записей.
4. Получаем элементы таксономий для этих записей.
(Эти запросы можно выборочно отключить...)
$my_query = new WP_Query( array(
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
) );
Запрос неиспользуемых данныхснижает производительность.
Другие проблемы query_posts()
Не работает постраничная навигация
Параметры навигации WordPress вычисляетдля основного запроса, а не дополнительного.
query_posts( array(
'author' => -5,
'posts_per_page' => 25,
) );
Этот код проблематичен.
Переопределяются глобальные переменные
Это может нарушить работу виджетов, комментариев и т.д.
query_posts() — это плохо
Согласимся?
Действие pre_get_posts
class WP_Query {
…
function get_posts() {
$this->parse_query();
// Ура!
do_action_ref_array( 'pre_get_posts', array( &$this ) );
…
function alter_my_home_query( $query ) {
if ( $query->is_home() )
$query->set( 'author', '-5' );
}
add_action( 'pre_get_posts', 'alter_my_home_query' );
Здесь начинаются сложности.
'pre_get_posts' выполняетсядля каждого запроса
● get_posts()● new WP_Query● Виджет последних записей, установленный клиентом без
вашего ведома.● И т.д.
Как изменить только основной запрос?
Триумфальное возвращение $wp_the_query
Метод WP_Query::is_main_query()
class WP_Query {
…
function is_main_query() {
global $wp_the_query;
return $wp_the_query === $this;
}
…
Только главный запрос!
function alter_my_home_query( $query ) {
if ( $query->is_home() && $query->is_main_query() )
$query->set( 'author', '-5' );
}
add_action( 'pre_get_posts', 'alter_my_home_query' );
function alter_my_home_query( $query ) {
if ( is_admin() || ! $query->is_main_query() )
return;
if ( $query->is_home() )
$query->set( 'author', '-5' );
}
add_action( 'pre_get_posts', 'alter_my_home_query' );
Как работает WP_Query::is_main_query()?
● $wp_the_query никогда не меняется и всегда содержит основной запрос.
● В $wp_query хранится ссылка на $wp_the_query, если только не используется query_posts().
● Не путать с функцией is_main_query().
query_posts( 'author=-5' );
while ( have_posts() ) :
the_post();
endwhile;
wp_reset_query();
query_posts( 'author=-5' );
while ( have_posts() ) :
the_post();
endwhile;
wp_reset_query();
function query_posts( $query ) {
// Убираем ссылку на $wp_the_query
unset( $wp_query );
$wp_query =& new WP_Query( $query );
return $wp_query;
}
query_posts( 'author=-5' );
while ( have_posts() ) :
the_post();
endwhile;
wp_reset_query();
function wp_reset_query() {
// Восстанавливаем ссылку на $wp_the_query
unset( $wp_query );
$wp_query =& $wp_the_query;
// Восстанавливаем глобальные переменные
wp_reset_postdata();
}
● Вызываете the_post()?wp_reset_query() восстановит $wp_query и глобальные переменные.
● Вызываете $my_query->the_post()?wp_reset_postdata() восстановит глобальные переменные.
Как быть с шаблонами страниц?
/* Template: My Template */
query_posts( $query_string . '&author=-5&posts_per_page=25' );
get_header();
while ( have_posts() ) :
the_post();
endwhile;
function alter_my_template( $query ) {
if ( ! $query->is_main_query() )
return;
if ( ! is_page_template( 'my-template.php' ) )
return;
$query->set( 'author', '-5' );
$query->set( 'posts_per_page', 25 );
}
add_action( 'pre_get_posts', 'alter_my_template' );
Выводы
● У каждого объекта WP_Query есть методы, соответствующие глобальным условным тегам.
● Глобальные условные теги используют $wp_query — основной или текущий запрос.
● $wp_query — это основной запрос, если не используется query_posts(). Восстанавливайте его с помощью wp_reset_query().
В заключение
● 'pre_get_posts' — мощный и гибкий инструмент (при правильном использовании).
● Всегда проверяйте с помощью $query->is_main_query(), что меняете именно основной запрос.