WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru Рекомендуемые продукты со скидкой от Template Monster

Замена $wpdb->update, где в значении $where можно указать массив

Указанное значение для $where поля превратиться в IN ( значения массива через запятую ) при запросе.

В WordPress в методе $wpdb->update в значении поля в параметре $where нельзя указать массив, чтобы он превратился в IN часть запроса. И это неудобно!

Покажу на примере, о чем идет речь.

Допустим, у нас есть массив с ID постов: [ 1, 5, 9 ] и нам нужно всем этим постам обновить поле post_status, установить туда статус draft.

Обычно в WordPress эта задача решается через $wpdb->query и написание отдельного запроса:

$post_ids = [ 1, 5, 9 ];
$wpdb->query( 
	"UPDATE $wpdb->posts SET post_status = 'draft' 
	WHERE ID IN (". implode(',', array_map('intval',$post_ids) ) .")"
);

Как можно видеть из примера, запрос не особо читаемый и в нем легко можно допустить ошибку. И это при том, что тут нужно обновить всего одно поле, и в WHERE части также указывается всего одно поле. Если добавить еще полей, то запрос станет еще менее читаемый.

Было бы гораздо удобнее, если бы можно было сделать так:

$post_ids = [ 1, 5, 9 ];
$wpdb->update( $wpdb->posts, [ 'post_status'=>'draft' ], [ 'ID'=>$post_ids ] );

Предлагаю небольшую функцию wpdb_update()

Функция полностью заменяет $wpdb->update(). В ней можно указывать массив в качестве значений поля в параметре $where.

Функцию я упростил: убрал от туда параметры форматов. Все передаваемые данные интерпретируются как строки. Числа автоматически превратятся в числа во время SQL запроса. С таким подход я багов пока не встречал, поэтому не вижу тут проблемы.

/**
 * Update a row in the table
 *
 * Extends basic $wpdb->update to allow pass array in value of $where field array. Passed array become `IN ()` sql statement.
 *
 *     $wpdb->update( 'table', [ 'column' => 'foo', 'field' => 1337 ], [ 'ID' => [1,3,5] ] )
 *
 * @param string       $table        Table name
 * @param array        $data         Data to update (in column => value pairs).
 *                                   Both $data columns and $data values should be "raw" (neither should be SQL escaped).
 *                                   Sending a null value will cause the column to be set to NULL.
 * @param array        $where        A named array of WHERE clauses (column => value).
 *                                   value can be an array, it becomes `IN ()` sql statement in this case.
 *                                   Multiple clauses will be joined with ANDs.
 *                                   Both $where columns and $where values should be "raw".
 *                                   Sending a null value will create an IS NULL comparison.
 *
 * @return int|bool Number of rows affected/selected for all other queries. Boolean false on error.

 * @see wpdb::update() https://wp-kama.ru/filecode/wp-includes/wp-db.php#L2214-2255
 *
 * @author Kama
 * 
 * @ver 1.0
 */
function wpdb_update( $table, $data, $where ){
	global $wpdb;

	if ( ! is_array( $data ) || ! is_array( $where ) )
		return false;

	$SET = $WHERE = [];

	// SET
	foreach ( $data as $field => $value ) {
		$field = sanitize_key( $field );

		if ( is_null( $value ) ) {
			$SET[] = "`$field` = NULL";
			continue;
		}

		$SET[] = $wpdb->prepare( "`$field` = %s", $value );
	}

	// WHERE
	foreach ( $where as $field => $value ) {
		$field = sanitize_key( $field );

		if ( is_null( $value ) ) {
			$WHERE[] = "`$field` IS NULL";
			continue;
		}

		if( is_array($value) ){
			foreach( $value as & $val ){
				$val = $wpdb->prepare( "%s", $val );
			}
			unset( $val );

			$WHERE[] = "`$field` IN (". implode(',', $value) .")";
		}
		else
			$WHERE[] = $wpdb->prepare( "`$field` = %s", $value );
	}

	$sql = "UPDATE `$table` SET ". implode( ', ', $SET ) ." WHERE ". implode( ' AND ', $WHERE );

	return $wpdb->query( $sql );
}

Теперь задачу описанную выше можно решить таким кодом:

$post_ids = [ 1, 5, 9 ];
wpdb_update( $wpdb->posts, [ 'post_status'=>'draft' ], [ 'ID'=>$post_ids ] );

Так гораздо удобнее и понятнее.

9 комментов
  • @ mihdan415 www.kobzarev.com

    А почему отдельной функцией? Можно, например, на хуке init расширить сам $wpdb.

    Ответить6 мес назад #
    • Kama7610

      Как? Там вроде бы переподключаться надо будет, не круто тоже.

      2
      Ответить6 мес назад #
      • @ mihdan415 www.kobzarev.com

        Простым способом, согласен, не сделать, можно только костылём расширить методы.

        Ответить6 мес назад #
  • @ Дмитрий

    Приветствую! Спасибо за полезнейший блог, всегда когда гуглю функцию в ВП, ваши записи в топе.

    Подскажите пжлста синтаксис. У меня в поле fc_start = '2019-06-21 21:00:00'
    мне надо эту дата время разнести в черновиках правильно: fc_start = '2019-06-21' fc_start_time = '21:00'

    Посмотрел функцию wpdb, правильный ли это ход?:

    $wpdb->update( 'wp_postmeta',
    	array( 'fc_start' => SUBSTR('fc_start', 0, 10), 'fc_start_time' => SUBSTR('fc_start_time', 11, 5) ),
    	( 'post_status' => 'draft' )
    );
    
    1
    Ответить3 мес назад #
    • Kama7610

      У таблицы wp_postmeta нет полей fc_start, fc_start_time и post_status так писать нельзя...

      Как я понимаю тебе надо драфтам создать такие метаполя, сделать это можно:
      1) Получить все драфты и пройти по ним циклом обновляя нужные метаполя.
      2) Составить UPDATE + JOIN запрос.

      1
      Ответить3 мес назад #
      • @ Дмитрий

        Спасибо за ответ.
        есть, у меня плагин calendarize it + WP automatic plugin, этот автоматик с фб парсит события, но вот с такими полями, поэтому и надо разнести. Ах да, совсем забыл, статус это же в посте а не в пост мета, ну это не проблема, я при парсинге в пост мета могу произвольно поле добавлять, например "DatetimeUpdated= false" и дальше:
        ( 'DatetimeUpdated' => 'false' )

        И тут все таки остается вопрос, правильно ли я эту часть делаю - fc_start_time' => SUBSTR('fc_start_time', 11, 5)?

        Ну и еще в апдейт вставлю ( 'DatetimeUpdated' => 'true' ), чтобы помечать обработанные поля

        Ответить3 мес назад #
        • Kama7610

          И тут все таки остается вопрос, правильно ли я эту часть делаю - fc_start_time' => SUBSTR('fc_start_time', 11, 5)?

          Нет SUBSTR() это SQL функция, а ты работаешь с PHP. Тебе там надо передавать строки... Если бы так можно было как ты пишешь, то ВП был бы одной огромной дырой для хакеров smile

          Ответить3 мес назад #
Здравствуйте, !     Войти . Зарегистрироваться