wordpress custom query is too slow - php

I am trying to create a custom filter with the custom table in woocommerce. there is approx 24 000 products that makes the custom filter very slow. How to make it faster?
here is my code:
function custom_wpquery( $query ){
if(isset($_GET['min-price'])){
// the main query
global $wp_the_query;
global $wpdb;
if ( 'product' === $query->get( 'post_type' ) ) {
$rr = g($_GET['min-price'],$_GET['max-price']);
foreach($rr as $rrr){
$fabricc = $wpdb->get_results("SELECT * FROM CWF_posts WHERE post_title = '".$rrr."' AND post_type = 'product'");
foreach($fabricc as $fabriccc){
$cID[] = $fabriccc->ID;
}
}
//print_r();exit;
// foreach($cID as $cIDD){
// $cat= wp_get_post_terms( $dd->ID, 'product_cat' );
// }
//$dd = get_page_by_title( $rrr, OBJECT, 'product' );
// $include[]=$dd->ID;
//
// $c[] = $cat[0]->slug;
//$c2[] = $cat[1]->slug;
$query->set('post__in', $cID);
}
}
}
add_filter( 'pre_get_posts', 'custom_wpquery' );
Thanks

You can create a multiple-index on fields that you are querying... Since your one field is constant I advise that convert your SQL to
SELECT * FROM CWF_posts WHERE post_type = 'product' AND post_title = '".$rrr."'
and create an index on post_type and post_title with this command on MySQL
CREATE INDEX type_title_index on CWF_posts (post_type, post_title)
also you can check
Understanding multiple column indexes in MySQL query
https://dev.mysql.com/doc/refman/8.0/en/create-index.html

Related

Wordpress DB SQL SELECT query with variable returning empty array

I am using wordpress to develop a site. I am creating a CPT by taking data direct from the database. The idea is that I will create a CPT from the WP DB. If the CPT is empty it will retrieve all data to populate and create the CPT instances. However, if the CPT already has instances it will check the meta_id of the CPT against the DB and see what's missing. I was then hoping it would pass them missing DB data into the CPT therefore not updating the whole CPT each time.
The 1st function check_for_cpt_external_authors() gets the CPT meta ids
The 2nd function query_external_author_table() accepts the above ids. If empty it gets all the data to populate the CPT else compare meta id with DB and see whats missing.
The third function insert_into_auto_cpt() takes the result of 2nd function and populates the CPT.
Everything works as expected apart from the else section of the 2nd function. The following query does not appear to pick missing meta ids - $results = $wpdb->get_results( 'SELECT * FROM `wp_postmeta` WHERE `meta_key` LIKE "authors_%_authors_name" AND `meta_id` NOT IN ( $meta_ids )');
Any advice would be much appreciated. Also I assume this hook should run on init so it is ran every time the page loads or is refreshed.
/*
* Action to run all functions below
*/
add_action( 'admin_init', 'insert_into_auto_cpt', 11 );
/*
* Get all External Authors in one array by title(name)
*/
function check_for_cpt_external_authors() {
$cpt_meta_id_arr = array();
$args = array(
'post_type' => 'external-authors',
'posts_per_page' => -1,
);
$loop = new WP_Query($args);
while( $loop->have_posts() ) {
$loop->the_post();
$cpt_meta_id_arr[] = get_post_meta( get_the_ID(), 'meta_id', true );
}
return $cpt_meta_id_arr;
}
/*
* Get external authors from DB
*
* If CPT external authors empty then get from DB, else if CPT then check against DB for any not in CPT.
*/
function query_external_author_table($available_in_cpt_array) {
global $wpdb;
if ( NULL === $available_in_cpt_array || 0 === $available_in_cpt_array || '0' === $available_in_cpt_array
|| empty( $available_in_cpt_array ) ) {
$results = $wpdb->get_results('SELECT * FROM `'. $wpdb->prefix .'postmeta` WHERE `meta_key` LIKE "authors_%_authors_name" GROUP BY `meta_value`');
return $results;
} else {
$meta_ids = implode( ",", $available_in_cpt_array );
print_r($meta_ids);
$results = $wpdb->get_results( 'SELECT * FROM `wp_postmeta` WHERE `meta_key` LIKE "authors_%_authors_name" AND `meta_id` NOT IN ( $meta_ids )');
echo '---------------------------------------------- Reached ELSE Statement -----------------------------------------------------';
return $results;
}
}
/*
* Enter external authors into CPT
*/
function insert_into_auto_cpt() {
$available_in_cpt_array = check_for_cpt_external_authors();
$database_results = query_external_author_table($available_in_cpt_array);
if ( NULL === $database_results || 0 === $database_results || '0' === $database_results || empty( $database_results ) ) {
return;
}
foreach ( $database_results as $result ) {
$external_author = array(
'post_title' => $result->meta_value,
'post_type' => 'external-authors',
'post_status' => 'publish',
'meta_input' => array(
'meta_id' => $result->meta_id,
),
);
wp_insert_post( $external_author );
}
}

WooCommerce Custom Product Column Sortable Backed not working

I have a custom postmeta field which stores the post/product_id in a serialized way. Example _related_ids => a:4:{i:0;i:2462;i:1;i:2466;i:2;i:2469;i:3;i:2472;}
I'm showing the product_id count of _related_ids in WooCommerce Product Listing screen (Backed), which is working fine. Now I want to make that column sortable. So I write a function related_product_col_sort which is hooked into manage_edit-product_sortable_columns. Column Shorting is not working. (not working means it is not ordering the product_id count properly).
Here is my full code
//_related_ids => a:4:{i:0;i:2462;i:1;i:2466;i:2;i:2469;i:3;i:2472;}
//manage_product_posts_custom_column
add_filter('manage_edit-product_columns', 'related_product_col');
function related_product_col($columns) {
$new_columns = (is_array($columns)) ? $columns : array();
$new_columns['RELATED'] = 'Related Product';
return $new_columns;
}
add_action('manage_product_posts_custom_column', 'related_product_col_data', 2);
function related_product_col_data($column) {
global $post;
$related_product_ids = get_post_meta($post->ID, '_related_ids', true);
if ($column == 'RELATED') {
if (isset($related_product_ids) && !empty($related_product_ids)) {
echo count($related_product_ids);
} else {
echo "--";
}
}
}
add_filter("manage_edit-product_sortable_columns", 'related_product_col_sort');
function related_product_col_sort($columns) {
$custom = array(
'RELATED' => '_related_ids'
);
return wp_parse_args($custom, $columns);
}
Can anyone help me out with the correct logic/code with related_product_col_sort function.
Thanks.
Long Answer short - Basically, if you need to sort on a meta_value you can't store it serialized. Check https://wordpress.stackexchange.com/questions/87265/order-by-meta-value-serialized-array.
I think the best possible solution for you is to store the count of related products in a new meta_key and use that to sort the columns.
Below are the steps to sort your data if the column consists normal data instead of a serialized array.
There are actually 2 steps to make a custom column sortable
Register the column as sortable
Implement the sort logic
To register a column as sortable you use the manage_{YOUR_SCREEN_ID}_sortable_columns filter and add your column
You have already registered your column using the related_product_col_sort function, to implement the sort functionality you have couple ways depending on the type of data.
If the data is numeric or simple alphabets you can use the pre_get_posts action and set the meta_key and orderby values
add_action( 'pre_get_posts', 'manage_wp_posts_my_custom_pre_get_posts', 1 );
function manage_wp_posts_my_custom_pre_get_posts( $query ) {
/**
* We only want our code to run in the main WP query
* AND if an orderby query variable is designated.
*/
if ( $query->is_main_query() && ( $orderby = $query->get( 'orderby' ) ) ) {
switch( $orderby ) {
// If we're ordering by 'film_rating'
case 'RELATED':
// set our query's meta_key, which is used for custom fields
$query->set( 'meta_key', 'RELATED' );
/**
* Tell the query to order by our custom field/meta_key's
* This will only work is the meta_value for the RELATED key is either a number or a simple string
*/
$query->set( 'orderby', 'meta_value' );
break;
}
}
}
If your sorting value is more complicated like a date
add_filter( 'posts_clauses', 'manage_wp_posts_my_custom_posts_clauses', 1, 2 );
function manage_wp_posts_my_custom_posts_clauses( $pieces, $query ) {
global $wpdb;
/**
* We only want our code to run in the main WP query
* AND if an orderby query variable is designated.
*/
if ( $query->is_main_query() && ( $orderby = $query->get( 'orderby' ) ) ) {
// Get the order query variable - ASC or DESC
$order = strtoupper( $query->get( 'order' ) );
// Make sure the order setting qualifies. If not, set default as ASC
if ( ! in_array( $order, array( 'ASC', 'DESC' ) ) )
$order = 'ASC';
switch( $orderby ) {
// If we're ordering by release_date
case 'RELATED':
/**
* We have to join the postmeta table to
* include our release date in the query.
*/
$pieces[ 'join' ] .= " LEFT JOIN $wpdb->postmeta wp_rd ON wp_rd.post_id = {$wpdb->posts}.ID AND wp_rd.meta_key = '_related_ids'";
// This will only work if the meta_value is a date.
$pieces[ 'orderby' ] = "STR_TO_DATE( wp_rd.meta_value,'%m/%d/%Y' ) $order, " . $pieces[ 'orderby' ];
break;
}
}
return $pieces;
}

Fastest approach: Count all products (including variations) in WooCommerce

I'm wondering if there exist any faster approaches to count all products in WooCommerce (and their child-variations), than this following code-example:
function total_product_count() {
$product_count = 0;
$args = array(
'post_type' => 'product',
'posts_per_page' => -1
);
/* Initialize WP_Query, and retrieve all product-pages */
$loop = new WP_Query($args);
/* Check if array contains any posts */
if ($loop->have_posts()):
while ($loop->have_posts()):
$loop->the_post();
global $product;
/* Count the children - add count to product_count */
product_count += count($product->get_children());
endwhile;
endif;
return $product_count;
}
On my local XAMPP web-server the function executes in 3 seconds (having 253 products), which is way too long time. The function returns 1269.
$product->get_children() is not always correct in this situation. get_children() return child products as well as grouped products if exist.
I hope the better and fastest way is to use wpdb:: MySQL query method. Try the following code
echo 'Number of main products => '.main_product_count();
echo 'Number of variations => '. variation_product_count();
function main_product_count() {
global $wpdb;
$count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE `post_type` LIKE 'product'");
return $count;
}
function variation_product_count() {
global $wpdb;
$count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE `post_type` LIKE 'product_variation'");
return $count;
}
as your naming
$loop =new WP_Query($args);
try this code
$loop->post_count;
while ($loop->have_posts()){
global $product;
/* Count the children - add count to product_count */
$loop->post_count += count($product->get_children());
}
WP_Query has a property which gives you the number of posts found matching the current query parameters:
function total_product_count() {
$args = array( 'post_type' => 'product', 'posts_per_page' => -1 );
$products = new WP_Query( $args );
return $products->found_posts;
}

PHP IF OR statement validation error

Im using the below code in order to add all Wordpress posts (excluding the 'sliders' category) to a category called 'Frontpage' ID = 28
function add_category_automatically1($post_ID) {
global $wpdb;
$postsWeWants = $wpdb->get_results("SELECT ID, post_author FROM $wpdb->posts where ID = $post_ID");
foreach ($postsWeWants as $postsWeWant) {
if (!in_category('sliders')) {
$cat = array(28, );
wp_set_object_terms($post_ID, $cat, 'category', true);
}
}
I want to add the exception of an additional category called 'business-information' but I can't get the OR operator to validate properly.
I was looking at using something like below
function add_category_automatically1($post_ID) {
global $wpdb;
$postsWeWants = $wpdb->get_results("SELECT ID, post_author FROM $wpdb->posts where ID = $post_ID");
foreach ($postsWeWants as $postsWeWant) {
if (!in_category('sliders')) OR (!in_category('business-information')) {
$cat = array(28, );
wp_set_object_terms($post_ID, $cat, 'category', true);
}
}
This:
if (!in_category('sliders')) OR (!in_category('business-information'))
// ^ -- ^ -- and here
is wrong. Because you close ) and open ( too early.
Proper code is:
if (!in_category('sliders') OR !in_category('business-information'))
And by the way such logic is invalid. If item is in 'business-information' then !in_category('sliders') true. I suppose you need to check for not existing in both cats:
if (!in_category('sliders') AND !in_category('business-information'))
You are using it wrong instead if (!in_category('sliders')) OR (!in_category('business-information')) {
write it like this (one more thing which is mentioned by #FirstOne you need to use AND instead OR to apply both conditions not one of them)
if( !in_category('sliders') AND !in_category('business-information') ) {
...
}
so that both !in_category checks will be in same if( ... ) scops

How can I sort posts by featured image(no image posts at the bottom) in wordpress using PHP?

I am working with the Wordpress Query Object using the WP Types/Views Toolset.
http://wp-types.com
We built out the parametric search which allows the user to search through posts using various taxonomies. It works fine, it display search results as needed. BUT..
Most of the posts don't have featured images, we want to display posts with featured images at the top, and the featured imageless posts would go below.
I have a good head start on this as far as logic goes, just need a bit of help.
The code below allows me manipulate the query before rendering it to the user.
add_filter( 'wpv_filter_query_post_process', 'prefix_rearrange_by_thumbnail', 10, 3 );
function prefix_rearrange_by_thumbnail( $query, $view_settings, $view_id ) {
// sort posts by thumbnail
return $query;
}
How can I sort through the query->posts and rearrange them so the ones that do have featured images show up before those without.
All of the WP_Post objects are found in an array under $query->posts. You can sort the array using usort() based on whatever criteria you want. Keep in mind that this will only sort each page of results as that is what is returned.
add_filter( 'wpv_filter_query_post_process', 'prefix_rearrange_by_thumbnail' );
function prefix_rearrange_by_thumbnail( $query ) {
$posts = $query->posts;
usort( $posts, 'sort_by_thumbnail' );
return $query;
}
// function used to sort the posts array
function sort_by_thumbnail( $a, $b ){
// the the featured image id so we can sort by it
$a_thumb = get_post_meta( $a->ID, '_thumbnail_id', true );
$b_thumb = get_post_meta( $b->ID, '_thumbnail_id', true );
if ( empty( $a_thumb ) && ! empty( $b_thumb ) ) return 1;
if ( ! empty( $a_thumb ) && empty( $b_thumb ) ) return -1;
return 0;
}
To sort all the pages of the results you need to modify the arguments that are passed to WP_Query using the wpv_filter_query filter. There is a meta_key called "_thumbnail_id" set for each post/page that has a featured image, but the issue is that this meta_key is not set at all for posts/pages without a featured image, so if you use it the results will be sorted by the ID of the featured images, but if it is missing they will be omitted. One option is to set another flag based on that meta_key to use in your sort. A one time cleaned would be needed then you could use a hook on save_post
One time SQL (adjust for your db prefix):
INSERT INTO wp_postmeta( post_id, meta_key, meta_value )
SELECT p.ID, '_has_featured_image', IF ( meta_value IS NULL, 0, 1 ) AS _has_featured_image
FROM wp_posts p
LEFT JOIN wp_postmeta m ON p.ID = m.post_id AND meta_key = '_thumbnail_id'
WHERE p.post_status = 'publish'
AND p.post_type IN ('post','page')
Hook on save_post to keep "_has_featured_image" clean going forward:
add_action( 'save_post', 'set_has_featured_image' );
function set_has_featured_image( $post_id ){
$post = get_post( $post_id );
switch ( $post->post_type ){
case 'post': case 'page':
$thumbnail_id = get_post_meta( $post_id, '_thumbnail_id', true );
update_post_meta( $post_id, '_has_featured_image', ! empty( $thumbnail_id ) );
break;
}
}
Now you can filter on the "_has_featured_image" meta_key:
add_filter( 'wpv_filter_query', 'prefix_rearrange_by_thumbnail' );
function prefix_rearrange_by_thumbnail( $args ){
$args['orderby'] = 'meta_value';
$args['meta_key'] = '_has_featured_image';
return $args;
}
I managed to do it in an easier way...
add_filter( 'wpv_filter_query_post_process', 'prefix_rearrange_by_thumbnail', 10, 3 );
function prefix_rearrange_by_thumbnail( $query, $view_settings, $view_id ) {
if ( !empty( $query->posts ) ) {
$sorted_posts_top = array();
$sorted_posts_bottom = array();
$all_posts = $query->posts;
foreach ($all_posts as $post) {
if ( has_post_thumbnail($post->ID) ) {
$sorted_posts_top[] = $post;
}
else {
$sorted_posts_bottom[] = $post;
}
$query->posts = array_merge((array)$sorted_posts_top, (array)$sorted_posts_bottom);
}
}
return $query;
}
However, your answer is still VERY helpful. Thank you SO MUCH for sharing!!!!!!!!!!!!!!!!

Categories