add_filter( ‘the_posts’, …) VS. add_action( ‘pre_get_posts’, …)
Glossary overview

add_filter( ‘the_posts’, …) VS. add_action( ‘pre_get_posts’, …)

🔧 add_action( 'pre_get_posts', ... )

📍 Когда используется:

Этот хук вызывается до того, как WordPress выполнит SQL-запрос в базу данных.

📌 Что ты можешь сделать:

Ты можешь изменить параметры запроса, например:

  • Тип постов (post_type)
  • Количество (posts_per_page)
  • Сортировку (orderby, order)
  • Фильтрацию по таксономиям (tax_query)
  • Исключить или включить посты и многое другое

🧠 В твоём коде:

function modify_query_order_for_providers( $query ) {
    if ( ! is_admin() && $query->is_main_query() ) {
        $request_path = trim( $_SERVER['REQUEST_URI'], '/' );
        if ( $request_path === 'providers' || strpos( $request_path, 'providers/' ) === 0 ) {
            $query->set( 'post_type', 'projects' );
            $query->set( 'orderby', 'title' );
            $query->set( 'order', 'ASC' );
        }
    }
}
add_action( 'pre_get_posts', 'modify_query_order_for_providers' );

🧹 add_filter( 'the_posts', ... )

📍 Когда используется:

Этот хук вызывается после того, как WordPress уже выполнил SQL-запрос и получил массив постов.

📌 Что ты можешь сделать:

Ты не можешь изменить параметры запроса, но можешь:

  • Изменить порядок записей (кастомная сортировка)
  • Убрать/добавить посты вручную
  • Группировать их или вставить искусственные посты
  • Например, отфильтровать по кастомным меткам, отношениям, сложной логике и т. д.

🧠 В твоём коде:

function custom_modify_and_sort_providers( $posts, $query ) {
    if ( ! is_admin() && $query->is_main_query() ) {
        $request_path = trim( $_SERVER['REQUEST_URI'], '/' );
        if ( $request_path === 'providers' || strpos( $request_path, 'providers/' ) === 0 ) {
            $ncris = [];
            $non_ncris = [];

            foreach ( $posts as $post ) {
                if ( has_term( 'yes', 'ncris-provider', $post->ID ) ) {
                    $ncris[] = $post;
                } else {
                    $non_ncris[] = $post;
                }
            }

            usort( $ncris, fn($a, $b) => strcmp($a->post_title, $b->post_title) );
            usort( $non_ncris, fn($a, $b) => strcmp($a->post_title, $b->post_title) );

            $posts = array_merge( $ncris, $non_ncris );
        }
    }

    return $posts;
}
add_filter( 'the_posts', 'custom_modify_and_sort_providers', 10, 2 );

Когда использовать что?

ЦельИспользовать pre_get_postsИспользовать the_posts
Изменить SQL-запрос (тип, сортировка, фильтры)✅ Да❌ Нет
Влиять на то, какие посты будут запрошены из базы✅ Да❌ Нет
Переупорядочить полученные посты вручную❌ Нет✅ Да
Разделить посты по условию (например, NCRIS yes / no)🚫 Трудно (в SQL)✅ Легко
Добавить искусственные посты или удалить некоторые❌ Нет✅ Да

Вывод для твоей задачи:

Тебе стоит использовать оба хука вместе:

  1. pre_get_posts — чтобы задать нужный post_type (projects) и отсортировать по title ASC.
  2. the_posts — чтобы разделить полученные посты на группы по наличию NCRIS и реорганизовать порядок вручную.

Если хочешь оставить всё в одной функции, как ты сделал — то можно просто использовать the_posts и переупорядочивать только, не трогая SQL.

function custom_modify_and_sort_providers( $posts, $query ) {
    // Применяем функционал только для основного запроса на фронтенде.
    if ( ! is_admin() && $query->is_main_query() ) {
        // Определяем текущий URL (без ведущих и завершающих слешей).
        $request_path = trim( $_SERVER['REQUEST_URI'], '/' );

        // Если мы находимся на странице провайдеров
        if ( $request_path === 'providers' || strpos( $request_path, 'providers/' ) === 0 ) {
            $ncris_funded     = array();
            $non_ncris_funded = array();

            // Разбиваем записи на две группы.
            foreach ( $posts as $post ) {
                // Функция has_term проверяет, связан ли пост с термином 'yes' в таксономии 'ncris-provider'.
                if ( has_term( 'yes', 'ncris-provider', $post->ID ) ) {
                    $ncris_funded[] = $post;
                } else {
                    $non_ncris_funded[] = $post;
                }
            }

            // Сортируем каждую группу по заголовку.
            usort( $ncris_funded, function( $a, $b ) {
                return strcmp( $a->post_title, $b->post_title );
            } );
            usort( $non_ncris_funded, function( $a, $b ) {
                return strcmp( $a->post_title, $b->post_title );
            } );

            // Объединяем так, чтобы в начале шли NCRIS-funded провайдеры.
            $posts = array_merge( $ncris_funded, $non_ncris_funded );
        }
    }

    return $posts;
}
add_filter( 'the_posts', 'custom_modify_and_sort_providers', 10, 2 );