Фильтр posts_clauses или как «внедриться» в WP_Query

Недавно поступила просьба создать интеграцию с моим плагином Market Exporter и плагином WooCommerce Warehouses, чтобы можно было менять склад из которого идет выгрузка. Забегая вперед скажу, что я был не в восторге от предоставленной мне версии плагина, потому что она не работала с php 7.1 и не дружила с последней версией WooCommerce. Но я все же попытался помочь решить поставленную задачу.

Для тех кто не знает — Market Exporter экспортирует товарные предложения из WooCommerce в файл .yml, пригодный для импорта в Яндекс Маркет. Выгрузка происходит по товарам, которые есть в наличии и у которых задана цена. Плагин WooCommerce Warehouses, в свою очередь, позволяет задать товару несколько складов. Например, есть склад поставщика, где 250 товаров в наличии и склад магазина, где товара в наличии на данный момент нет. Так вот при настройках по умолчанию плагин Market Exporter выгрузил бы все товарные предложения, ведь они есть в наличии… на складе поставщика… Но это плохо, надо это решать.

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

Фильтр posts_clauses позволяет получить доступ к запросу WP_Query до его исполнения, когда сам запрос уже сформирован, но еще не исполнен. Если погуглить информацию о posts_clauses, то можно будет узнать, что есть более конкретные фильтры для изменения WHERE, JOIN и т.д. Но для примера мы воспользуемся именно posts_clauses, как универсальным (не оптимальным!) фильтром.

Создаем фильтр:

add_filter( 'posts_clauses', 'filter_by_warehouse_id', 10, 2 );

или если у нас класс:

add_filter( 'posts_clauses', array( $this, 'filter_by_warehouse_id' ), 10, 2 );

И создаем метод filter_by_warehouse_id:

public function filter_by_warehouse_id( $clauses, $query_object ) {
	global $wpdb;

	$warehouse_id = 1;

	$join = &$clauses['join'];
	
	if ( ' INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )  INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )' === $join ) {
		$join .= " JOIN {$wpdb->prefix}inventory_manager_product_warehouse AS warehouse ON {$wpdb->prefix}posts.ID = warehouse.product_id";	
		
		$where = &$clauses['where'];
		$where .= " AND ( warehouse.warehouse_id = {$warehouse_id} AND warehouse.stock > 0 )";
	}

	return $clauses;
}

Пробежимся по строчкам.

Строчка 3

global $wpdb;

Нам нужен будет глобальный объект $wpdb, чтобы получить префикс для базы таблицы базы данных,

Строчка 5

$warehouse_id = 1;

Задаем ID склада, откуда будет идти выгрузка. Здесь мы его задаем вручную, но можно его забирать и из базы данных, и из select… да откуда угодно.

Строчка 7

$join = &$clauses['join'];

Ссылаемся на JOIN-часть WP_Query.

Строчка 9

if ( ' INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )  INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id )' === $join ) {

Не делайте так, но ради примера — сверяем запрос с тем, который нам нужен. И если он совпадает, то добавляем наш JOIN (строчка 10).

Строчки 12-13 мы добавляем условие WHERE.

$where = &$clauses['where'];
$where .= " AND ( warehouse.warehouse_id = {$warehouse_id} AND warehouse.stock > 0 )";

Итого. У нас есть запрос, который будет делать дополнительный запрос к таблице wp_inventory_manager_product_warehouse и проверять наличие товара на заданном складе. Мы не нарушили работу плагина, не меняли его функционал, а лишь дополнили… Дальнейшие действия — повесить данный фильтр на опцию плагина или сделать некий интеграционный файл, который можно было бы включить в плагин/тему.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *