File "sql-builder.php"
Full Path: /home/vantageo/public_html/cache/cache/cache/.wp-cli/wp-content/plugins/pw-bulk-edit/includes/sql-builder.php
File size: 22.13 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/*
Copyright (C) Pimwick, LLC
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'PWBE_SQL_Builder' ) ) :
final class PWBE_SQL_Builder {
public function get_products( $post ) {
global $wpdb, $pw_bulk_edit;
$pw_bulk_edit->increase_memory_limit();
$wpdb->show_errors();
$common_fields = "
post.ID AS post_id,
post.post_content AS post_content,
post.post_excerpt AS post_excerpt,
post.post_status AS post_status,
post.menu_order AS menu_order,
parent.ID AS parent_post_id,
parent.post_title AS post_title,
meta__sku.meta_value AS _sku,
meta__regular_price.meta_value AS _regular_price,
meta__sale_price.meta_value AS _sale_price
";
$common_joins = "
LEFT JOIN
{$wpdb->postmeta} AS meta__sku ON (meta__sku.post_id = post.ID AND meta__sku.meta_key = '_sku')
LEFT JOIN
{$wpdb->postmeta} AS meta__regular_price ON (meta__regular_price.post_id = post.ID AND meta__regular_price.meta_key = '_regular_price')
LEFT JOIN
{$wpdb->postmeta} AS meta__sale_price ON (meta__sale_price.post_id = post.ID AND meta__sale_price.meta_key = '_sale_price')
";
if ( !empty( $post['order_by'] ) ) {
$column = PWBE_Columns::get_by_field( $post['order_by'] );
if ( !empty( $column ) ) {
if ( $column['field'] == '_visibility' ) {
$common_fields .= ",
visibility_exclude_from_catalog.product_id AS `visibility_exclude_from_catalog_product_id`,
visibility_exclude_from_search.product_id AS `visibility_exclude_from_search_product_id`
";
$common_joins = $this->maybe_add_catalog_visibility_joins( $common_joins );
} else if ( $column['table'] == 'meta' && !in_array( $column['field'], array( '_sku', '_regular_price', '_sale_price' ) ) ) {
if ( $column['field'] == '_featured' && PW_Bulk_Edit::wc_min_version( '3.0' ) ) {
$common_fields .= "
, CASE WHEN featured_products.product_id IS NOT NULL THEN 'yes' ELSE 'no' END AS `is_featured_product`
";
$common_joins .= "
LEFT JOIN (
SELECT
r.object_id AS product_id
FROM
{$wpdb->term_taxonomy} AS tax
JOIN
{$wpdb->terms} AS t ON (t.term_id = tax.term_id AND t.name = 'featured')
JOIN
{$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id)
WHERE
tax.taxonomy = 'product_visibility'
) AS featured_products ON (featured_products.product_id = post.ID)
";
} else {
$common_fields .= '
, `meta_' . $column['field'] . '`.meta_value AS `' . $column['field'] . '`
';
$common_joins .= '
LEFT JOIN
' . $wpdb->postmeta . ' AS `meta_' . $column['field'] . '` ON (`meta_' . $column['field'] . '`.post_id = post.ID AND `meta_' . $column['field'] . '`.meta_key = \'' . $column['field'] . '\')
';
}
}
}
}
$common_where = $this->build_common_sql( '-0', $post['main_group_type'], $post, $common_fields, $common_joins );
$common_fields = apply_filters( 'pwbe_common_fields', $common_fields );
$common_joins = apply_filters( 'pwbe_common_joins', $common_joins );
$common_where = apply_filters( 'pwbe_common_where', $common_where );
@set_time_limit( 0 );
$wpdb->query("SET SQL_BIG_SELECTS=1");
$wpdb->query("DROP TABLE IF EXISTS pwbe_variations");
$wpdb->query("CREATE TEMPORARY TABLE pwbe_variations (post_id BIGINT(20) UNSIGNED, parent_post_id BIGINT(20) UNSIGNED, PRIMARY KEY (post_id) )");
$variations_sql = "
INSERT INTO pwbe_variations
SELECT
post.ID AS post_id,
MAX(parent.ID) AS parent_post_id
FROM
{$wpdb->posts} AS post
JOIN
{$wpdb->posts} AS parent ON (parent.ID = post.post_parent)
$common_joins
WHERE
post.post_type = 'product_variation'
AND ($common_where)
";
if ( PWBE_PREFILTER_VARIATIONS ) {
$variations_sql .= "
AND (
SELECT
terms.slug
FROM
{$wpdb->term_relationships} AS term_relationships
JOIN
{$wpdb->term_taxonomy} AS term_taxonomy ON (term_taxonomy.term_taxonomy_id = term_relationships.term_taxonomy_id)
JOIN
{$wpdb->terms} AS terms ON (terms.term_id = term_taxonomy.term_id)
WHERE
term_relationships.object_id = post.post_parent
AND term_taxonomy.taxonomy = 'product_type'
LIMIT 1
) IN ( 'variable', 'variable-subscription' )
";
}
$variations_sql .= "
GROUP BY
post.ID
";
$wpdb->query( $variations_sql );
$wpdb->query("DROP TABLE IF EXISTS pwbe_products");
$wpdb->query("CREATE TEMPORARY TABLE pwbe_products (post_id BIGINT(20) UNSIGNED, PRIMARY KEY (post_id) )");
$wpdb->query("
INSERT INTO pwbe_products
SELECT
DISTINCT
post.ID AS post_id
FROM
{$wpdb->posts} AS post
JOIN
{$wpdb->posts} AS parent ON (parent.ID = post.ID)
$common_joins
WHERE
post.post_type = 'product'
AND (
($common_where)
OR post.ID IN (SELECT parent_post_id FROM pwbe_variations)
)
");
$products = PWBE_DB::query( "
SELECT
DISTINCT
(
SELECT
terms.slug
FROM
{$wpdb->term_relationships} AS term_relationships
JOIN
{$wpdb->term_taxonomy} AS term_taxonomy ON (term_taxonomy.term_taxonomy_id = term_relationships.term_taxonomy_id)
JOIN
{$wpdb->terms} AS terms ON (terms.term_id = term_taxonomy.term_id)
WHERE
term_relationships.object_id = post.ID
AND term_taxonomy.taxonomy = 'product_type'
LIMIT 1
) AS product_type,
$common_fields
FROM
pwbe_products
JOIN
{$wpdb->posts} AS post ON (post.ID = pwbe_products.post_id)
JOIN
{$wpdb->posts} AS parent ON (parent.ID = post.ID)
$common_joins
WHERE
post.post_type = 'product'
UNION ALL
SELECT
DISTINCT
'variation' AS product_type,
$common_fields
FROM
pwbe_variations
JOIN
{$wpdb->posts} AS post ON (post.ID = pwbe_variations.post_id)
JOIN
{$wpdb->posts} AS parent ON (parent.ID = post.post_parent)
$common_joins
WHERE
post.post_type = 'product_variation'
AND (
SELECT
terms.slug
FROM
{$wpdb->term_relationships} AS term_relationships
JOIN
{$wpdb->term_taxonomy} AS term_taxonomy ON (term_taxonomy.term_taxonomy_id = term_relationships.term_taxonomy_id)
JOIN
{$wpdb->terms} AS terms ON (terms.term_id = term_taxonomy.term_id)
WHERE
term_relationships.object_id = post.post_parent
AND term_taxonomy.taxonomy = 'product_type'
LIMIT 1
) = 'variable'
ORDER BY
" . $this->build_order_by( $post ) . "
" );
if ( $products !== false ) {
return $products;
} else {
return 'MySQL Error: ' . PWBE_DB::error();
}
}
public function build_common_sql( $suffix, $group_type, $fields, &$sql_fields, &$sql_joins ) {
global $wpdb;
$sql_where = "(";
// Inside each group, loop through the nested statements.
for ($row_index = 0; $row_index < count( $fields['row'] ); $row_index++ ) {
$field_name = '';
$filter_type = '';
$field_value = '';
$field_value2 = '';
if ( isset ( $fields[$row_index . 'filter_name' . $suffix] ) ) {
$field_name = $fields[$row_index . 'filter_name' . $suffix];
}
if ( isset ( $fields[$row_index . 'filter_type' . $suffix] ) ) {
$filter_type = $fields[$row_index . 'filter_type' . $suffix];
}
// Value is either filter_value or filter_select.
if ( isset( $fields[$row_index . 'filter_value' . $suffix] ) ) {
$field_value = $fields[$row_index . 'filter_value' . $suffix];
// Value2 is optional
if ( isset( $fields[$row_index . 'filter_value2' . $suffix] ) ) {
$field_value2 = $fields[$row_index . 'filter_value2' . $suffix];
}
} else if ( isset( $fields[$row_index . 'filter_select' . $suffix] ) ) {
$field_value = $fields[$row_index . 'filter_select' . $suffix];
}
switch ( $field_name ) {
case 'pwbe_and':
case 'pwbe_or':
$row_sql = $this->build_common_sql( "$suffix-$row_index", $field_name, $fields, $sql_fields, $sql_joins );
break;
case 'categories':
$row_sql = $this->taxonomy_search( 'product_cat', $filter_type, $field_value );
break;
case 'tags':
$row_sql = $this->taxonomy_search( 'product_tag', $filter_type, $field_value );
break;
case 'post_content':
$row_sql = $this->string_search( 'parent.post_content', $filter_type, $field_value );
break;
case 'post_title':
$row_sql = $this->string_search( 'parent.post_title', $filter_type, $field_value );
break;
case 'regular_price':
$row_sql = $this->numeric_search( 'meta__regular_price.meta_value', $filter_type, $field_value, $field_value2 );
break;
case 'sale_price':
$row_sql = $this->numeric_search( 'meta__sale_price.meta_value', $filter_type, $field_value, $field_value2 );
break;
case 'post_excerpt':
$row_sql = $this->string_search( 'parent.post_excerpt', $filter_type, $field_value );
break;
case 'sku':
$row_sql = $this->string_search( 'meta__sku.meta_value', $filter_type, $field_value );
break;
default:
if ( $filter_type == 'is checked' || $filter_type == 'is not checked' ) {
$row_sql = $this->boolean_search( "`{$field_name}`.meta_value", $filter_type, $field_value );
} else {
if ( PW_Bulk_Edit::starts_with( 'pa_', $field_name ) ) {
$row_sql = $this->attributes_search( $field_name, $filter_type, $field_value );
} else {
if ( $group_type == 'pwbe_and' ) {
$row_sql = ' 1 = 1 ';
} else if ( $group_type == 'pwbe_or' ) {
$row_sql = ' 1 != 1 ';
}
}
}
break;
}
$sql_where .= apply_filters( 'pwbe_where_clause', $row_sql, $field_name, $filter_type, $field_value, $field_value2, $group_type );
if ( $group_type == 'pwbe_and' ) {
$sql_where .= " AND ";
} else if ( $group_type == 'pwbe_or' ) {
$sql_where .= " OR ";
}
}
// Yank the trailing AND/OR.
$sql_where = substr($sql_where, 0, -5);
$sql_where .= ") ";
return $sql_where;
}
public function string_search( $field_name, $filter_type, $value ) {
global $wpdb;
switch( $filter_type ) {
case 'is':
return $wpdb->prepare("$field_name = %s", $value);
break;
case 'is not':
return $wpdb->prepare("$field_name != %s", $value);
break;
case 'contains':
return $wpdb->prepare("$field_name LIKE %s", '%' . str_replace( '_', '\_', $value ) . '%');
break;
case 'does not contain':
return $wpdb->prepare("$field_name NOT LIKE %s", '%' . str_replace( '_', '\_', $value ) . '%');
break;
case 'begins with':
return $wpdb->prepare("$field_name LIKE %s", str_replace( '_', '\_', $value ) . '%');
break;
case 'ends with':
return $wpdb->prepare("$field_name LIKE %s", '%' . str_replace( '_', '\_', $value ) );
break;
}
}
public function numeric_search( $field_name, $filter_type, $value, $value2 ) {
global $wpdb;
//$field_sql = "$field_name IS NOT NULL AND $field_name != '' AND CAST($field_name AS DECIMAL(12, 2))";
$field_sql = "$field_name IS NOT NULL AND $field_name != '' AND $field_name";
switch( $filter_type ) {
case 'is':
return $wpdb->prepare("$field_sql = %f", $value);
break;
case 'is not':
return $wpdb->prepare("$field_sql != %f", $value);
break;
case 'is greater than':
return $wpdb->prepare("$field_sql > %f", $value);
break;
case 'is less than':
return $wpdb->prepare("$field_sql < %f", $value);
break;
case 'is in the range':
return $wpdb->prepare("($field_sql >= %f AND CAST($field_name AS DECIMAL(12, 2)) <= %f)", $value, $value2);
break;
}
}
public function taxonomy_search( $taxonomy, $filter_type, $values ) {
global $wpdb;
$placeholders = implode( ', ', array_fill( 0, count( $values ), '%s' ) );
if ( !empty( $values ) ) {
array_unshift( $values, $taxonomy );
}
// Allow parent taxonomy matching to be disabled in case it hurts performance on specific sites.
$maybe_search_parents = '';
if ( PWBE_SEARCH_PARENT_CATEGORIES ) {
$maybe_search_parents = 'OR t.term_id = tax.parent';
}
switch( $filter_type ) {
case 'is any of':
case 'is none of':
$negator = ( $filter_type == 'is none of' ) ? 'NOT' : '';
return $wpdb->prepare("$negator EXISTS (SELECT 1 FROM {$wpdb->term_taxonomy} AS tax JOIN {$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id) JOIN {$wpdb->terms} AS t ON (t.term_id = tax.term_id $maybe_search_parents) WHERE tax.taxonomy = %s AND r.object_id = parent.ID AND t.slug IN ($placeholders))", $values);
break;
case 'is all of':
array_push( $values, count($values) - 1 );
return $wpdb->prepare("(SELECT COUNT(*) FROM {$wpdb->term_taxonomy} AS tax JOIN {$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id) JOIN {$wpdb->terms} AS t ON (t.term_id = tax.term_id $maybe_search_parents) WHERE tax.taxonomy = %s AND r.object_id = parent.ID AND t.slug IN ($placeholders)) = %d", $values );
break;
case 'is empty':
case 'is not empty':
$negator = ( $filter_type == 'is empty' ) ? 'NOT' : '';
return $wpdb->prepare("$negator EXISTS (SELECT 1 FROM {$wpdb->term_taxonomy} AS tax JOIN {$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id) JOIN {$wpdb->terms} AS t ON (t.term_id = tax.term_id $maybe_search_parents) WHERE tax.taxonomy = %s AND r.object_id = parent.ID)", $taxonomy);
break;
}
}
public function boolean_search( $field_name, $filter_type, $value ) {
global $wpdb;
switch( $filter_type ) {
case 'is checked':
return "LOWER(TRIM($field_name)) IN ('yes', 'true')";
break;
default:
return "COALESCE(NULLIF(LOWER(TRIM($field_name)), ''), 'no') IN ('no', 'false')";
break;
}
}
public function attributes_search( $field_name, $filter_type, $values ) {
global $wpdb;
$slugs = implode( ', ', array_fill( 0, count( $values ), '%s' ) );
switch( $filter_type ) {
case 'is any of':
case 'is none of':
$negator = ( $filter_type == 'is none of' ) ? 'NOT' : '';
$simple_attribute = $wpdb->prepare("
post.post_type != 'product_variation' AND (
$negator EXISTS (
SELECT 1
FROM {$wpdb->term_relationships} AS r
JOIN {$wpdb->term_taxonomy} AS tax ON (tax.term_taxonomy_id = r.term_taxonomy_id)
JOIN {$wpdb->terms} AS t ON (t.term_id = tax.term_id)
WHERE r.object_id = post.ID AND t.slug IN ($slugs)
)
)
",
$values);
$variable_attribute = $this->variation_attributes_search( $field_name, $filter_type, $values );
return "($simple_attribute OR $variable_attribute)";
break;
case 'is all of':
$wpdb_values = $values;
$wpdb_values[] = $field_name;
$wpdb_values[] = count( $values );
$simple_attribute = $wpdb->prepare("
post.post_type != 'product_variation' AND (
SELECT COUNT(*)
FROM {$wpdb->term_relationships} AS r
JOIN {$wpdb->term_taxonomy} AS tax ON (tax.term_taxonomy_id = r.term_taxonomy_id)
JOIN {$wpdb->terms} AS t ON (t.term_id = tax.term_id)
WHERE r.object_id = post.ID AND t.slug IN ($slugs) AND tax.taxonomy = %s
) = %d
",
$wpdb_values );
$variable_attribute = $this->variation_attributes_search( $field_name, $filter_type, $values );
return "($simple_attribute OR $variable_attribute)";
break;
}
}
public function variation_attributes_search( $field_name, $filter_type, $values ) {
global $wpdb;
$slugs = implode( ', ', array_fill( 0, count( $values ), '%s' ) );
switch( $filter_type ) {
case 'is any of':
case 'is none of':
$negator = ( $filter_type == 'is none of' ) ? 'NOT' : '';
array_unshift( $values, 'attribute_' . $field_name );
return $wpdb->prepare("
post.post_type = 'product_variation' AND (
$negator EXISTS (
SELECT 1
FROM {$wpdb->postmeta} AS m
WHERE
m.post_id = post.ID
AND m.meta_key = %s
AND m.meta_value IN ($slugs)
)
)
",
$values);
break;
case 'is all of':
array_push( $values, count($values) );
array_unshift( $values, 'attribute_' . $field_name );
return $wpdb->prepare("
post.post_type = 'product_variation' AND (
SELECT COUNT(*)
FROM {$wpdb->postmeta} AS m
WHERE
m.post_id = post.ID
AND m.meta_key = %s
AND m.meta_value IN ($slugs)
) = %d
",
$values);
break;
case 'is empty':
case 'is not empty':
$negator = ( $filter_type == 'is empty' ) ? 'NOT' : '';
return $wpdb->prepare("
post.post_type = 'product_variation' AND $negator EXISTS (
SELECT 1
FROM {$wpdb->postmeta} AS m
WHERE
m.post_id = post.ID
AND m.meta_key = %s
)
",
$field_name);
break;
}
}
public function build_order_by( $post ) {
$order_by = 'post_title';
$direction = 'ASC';
if ( !empty( $post['order_by_desc'] ) ) {
$direction = 'DESC';
}
if ( !empty( $post['order_by'] ) ) {
$column = PWBE_Columns::get_by_field( $post['order_by'] );
if ( !empty( $column ) ) {
if ( $column['type'] == 'currency' ) {
$order_by = "LENGTH(`$column[field]`) $direction, `$column[field]` $direction";
} else {
if ( $column['field'] == '_featured' && PW_Bulk_Edit::wc_min_version( '3.0' ) ) {
$order_by = "is_featured_product $direction";
} else if ( $column['field'] == '_visibility' ) {
$order_by = "
CASE
WHEN (visibility_exclude_from_catalog_product_id IS NULL AND visibility_exclude_from_search_product_id IS NULL) THEN 1
WHEN (visibility_exclude_from_catalog_product_id IS NOT NULL AND visibility_exclude_from_search_product_id IS NOT NULL) THEN 2
WHEN (visibility_exclude_from_catalog_product_id IS NOT NULL AND visibility_exclude_from_search_product_id IS NULL) THEN 3
ELSE 4
END $direction
";
} else if ( $column['type'] == 'number' ) {
$order_by = "CAST( `$column[field]` AS DECIMAL(10,6) ) $direction";
} else {
$order_by = "`$column[field]` $direction";
}
}
}
}
$order_by .= ", COALESCE(NULLIF(parent_post_id, 0), post_id), CASE WHEN product_type = 'variation' THEN menu_order ELSE -1000 END, post_id";
return $order_by;
}
public function maybe_add_catalog_visibility_joins( $sql_joins ) {
global $wpdb;
if ( strpos( $sql_joins, 'visibility_exclude_from_catalog' ) === false ) {
$sql_joins .= "
LEFT JOIN (
SELECT
r.object_id AS product_id
FROM
{$wpdb->term_taxonomy} AS tax
JOIN
{$wpdb->terms} AS t ON (t.term_id = tax.term_id AND t.name = 'exclude-from-catalog')
JOIN
{$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id)
WHERE
tax.taxonomy = 'product_visibility'
) AS visibility_exclude_from_catalog ON (visibility_exclude_from_catalog.product_id = post.ID)
LEFT JOIN (
SELECT
r.object_id AS product_id
FROM
{$wpdb->term_taxonomy} AS tax
JOIN
{$wpdb->terms} AS t ON (t.term_id = tax.term_id AND t.name = 'exclude-from-search')
JOIN
{$wpdb->term_relationships} AS r ON (r.term_taxonomy_id = tax.term_taxonomy_id)
WHERE
tax.taxonomy = 'product_visibility'
) AS visibility_exclude_from_search ON (visibility_exclude_from_search.product_id = post.ID)
";
}
return $sql_joins;
}
}
endif;
?>