WordPress как на ладони

$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.1
 */
function wpdb_update( $table, $data, $where ){
	global $wpdb;

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

	$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 комментариев
    Войти