[wp-trac] [WordPress Trac] #56764: Support IN and NOT IN data in wpdb::prepare()
WordPress Trac
noreply at wordpress.org
Sat Oct 8 19:26:22 UTC 2022
#56764: Support IN and NOT IN data in wpdb::prepare()
-----------------------------+-----------------------------
Reporter: sc0ttkclark | Owner: (none)
Type: feature request | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Database | Version:
Severity: normal | Keywords:
Focuses: |
-----------------------------+-----------------------------
== The problem
Often times I find myself having to work with dynamic conditionals in a
very roundabout way. The amount of logic and complexity that preparing `IN
()` and `NOT IN ()` statements requires can put people off from using
`wpdb::prepare()` and I believe direct support in `prepare()` would
encourage more developers to prepare their SQL statements properly.
Many devs will choose to not use prepare() for those statements that have
`IN ()` and they will choose to call things like `esc_sql()` and their
`$wpdb->____()` counterparts. While this makes quick work for writing a
query, it is not providing a method like `prepare()` all of the
information to write a clean SQL query that has values properly prepared.
Here's an example of what someone might do now:
{{{#!php
<?php
/**
* @var string[] $list_of_strings
*/
$list_of_strings_escaped = array_map( 'esc_sql', $list_of_strings );
$list_of_strings_sql = implode( "', '", $list_of_strings_escaped );
$final_sql = "
SELECT *
FROM `{$wpdb->prefix}whatever_table`
WHERE `whatever_column` IN ( {$list_of_strings_sql} )
";
}}}
== Using prepare for `IN ()` in core right now
You can technically use `prepare()` right now with `IN ()` clauses with
extra logic and smart use of `array_fill()` or manually putting `IN ( %s,
%s, %s )` into the SQL statement yourself.
Here's an example of that:
{{{#!php
<?php
/**
* @var string[] $list_of_strings
* @var int $int1
* @var int $int2
* @var int $int3
*/
$values_to_prepare = $list_of_strings;
// Fill an array with the %s placeholder for each value.
$placeholders = array_fill( 0, count( $list_of_strings ), '%s' );
// Build the string as comma-separated between each placeholder.
$placeholder_sql = implode( ', ', $placeholders );
$values_to_prepare[] = $int1;
$values_to_prepare[] = $int2;
$values_to_prepare[] = $int3;
$sql = "
SELECT *
FROM `{$wpdb->prefix}whatever_table`
WHERE
`whatever_column` IN ( {$placeholder_sql} )
OR `whatever_integer_column IN ( %d, %d, %d )
";
$final_sql = $wpdb->prepare( $sql, $values_to_prepare );
}}}
== Introducing `%L` placeholders
With the introduction of `%i` in #52506 it seems that we may finally be
ready to close up the remaining gaps for `wpdb::prepare()`.
For my example on what I think we could use here, I'm using the `%L`
placeholder because lowercase would be ambiguous and the `%L` placeholder
doesn't appear to conflict with normal `sprintf()` syntax.
The `%L` usage would default to a comma-separated list of `%s` prepared
values but you could have a `%Ld` variation that would use a comma-
separated list of `%d` prepared values too.
{{{#!php
<?php
/**
* @var string[] $list_of_strings
* @var int[] $list_of_integers
*/
$sql = "
SELECT *
FROM `{$wpdb->prefix}whatever_table`
WHERE
`whatever_column` IN ( %L )
OR `whatever_integer_column` IN ( %Ld )
";
$final_sql = $wpdb->prepare( $sql, [ $list_of_strings, $list_of_integers ]
);
}}}
In this way, it sees that first placeholder `%L` and uses the
corresponding value from the first item in the array of prepare values
passed. It would expand the prepared values automatically.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/56764>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list