I'm using the DevBridge jQuery Autocomplete plugin with the ajax option (using serviceURL instead NOT lookup). I have a php file that queries the db (WordPress FYI) and gets all the results just fine. So when users type into my autocomplete field, it's showing ALL the results with their query highlighted. Apparently my server-side script is supposed to handle the searching and return ONLY the filtered results based on the user's on-the-fly input. I'm just not sure how to go about doing that.
I've found a ton of posts online stating this is how it's supposed to work but can't find any working examples of a serviceURL file that returns filtered results based on what the user is typing.
Here's the code I have so far...
My jQuery...
$('#product_sku_autocomplete').autocomplete({
serviceUrl: '/blah/blah/ajax-product-sku.php',
minChars: 1,
onSelect: function (suggestion) {
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
the contents of my ajax-product-sku.php file. This gets all the various meta values for a custom field (sku) across all 'products'
if ( ! defined('ABSPATH') ) {
require_once( '../../../../wp-load.php' );
}
global $wpdb;
$term = $_GET['query'];
$query = $wpdb->get_col( $wpdb->prepare( "
SELECT pm.meta_value FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_status = %s
AND p.post_type = %s
", 'sku', 'publish', 'products' ) );
$reply = array();
$reply['query'] = $term;
$reply['suggestions'] = array();
foreach ($query as $sku) {
$reply['suggestions'][] = array(
"value" => $sku,
"data" => $sku
);
}
echo json_encode($reply);
The results if I access ajax-product-sku.php directly...
{"query":null,"suggestions":[{"value":"52N242","data":"52N242"},{"value":"52F230","data":"52F230"},{"value":"52F235","data":"52F235"}]}
So I'm getting my full list of results (properly formatted as far as I can tell) and the autocomplete field retrieves those BUT when you type into the autocomplete field it's showing ALL the results not just the ones that match what the user has typed in.
I suspect that I need to add the $term var somewhere within my db $query statement but not sure how. Any help is greatly appreciated.
I think I've got it. Pretty simple solution but my sql chops are lacking obviously. Need to add a LIKE condition to the sql statement. Here's the revised ajax-product-sku.php file contents.
If anyone has a more efficient way to handle this from a coding or performance standpoint, I'd welcome the knowledge...
ajax-product-sku.php
if ( ! defined('ABSPATH') ) {
require_once( '../../../../wp-load.php' );
}
global $wpdb;
$term = $_GET['query']; // this is the var that autocomplete sends as users type.
//$term = '1';
$query = $wpdb->get_col( $wpdb->prepare( "
SELECT pm.meta_value FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_status = %s
AND p.post_type = %s
AND pm.meta_value LIKE '%{$term}%'
", 'sku', 'publish', 'products' ) );
$reply = array();
$reply['query'] = $term;
$reply['suggestions'] = array();
foreach ($query as $sku) {
$reply['suggestions'][] = array(
"value" => $sku,
"data" => $sku
);
}
echo json_encode($reply);
Related
I have the following loop for getting Woocommerce coupons on a page within the my account section of a customers dashboard.
Currently we have 10k+ coupons and just by performing this loop, it's a huge drain on resources and not very efficient causing time outs. Are there any obvious ways in which I can improve the efficiency of it?
Is there a way I can limit the loop to the only search for emails in the "Allowed emails" field (as each coupon is tied to an email address)?
<?php $smart_coupons = get_posts( array(
'posts_per_page' => -1,
'orderby' => 'name',
'order' => 'desc',
'post_type' => 'shop_coupon',
'post_status' => 'publish'
) );
if ( $smart_coupons ) {
foreach( $smart_coupons as $smart_coupon) {
$strcode = strtolower($smart_coupon->post_title);
$full_coupon = new WC_Coupon( $strcode ); ?>
<?php if($full_coupon->discount_type == "smart_coupon"){
$emails = $full_coupon->get_email_restrictions();
if (in_array($current_email, $emails)) {
if($full_coupon->usage_count < $full_coupon->usage_limit){ ?>
coupon content
<?php }
}
}
}
}
As email restrictions are in an array (so an indexed array in the database) it is not possible to get that from a meta query in your WP_Query for many technical reasons.
Now instead you can do a custom very light and effective SQL query to get the "smart" coupons that belong to an email, using the WPDB Class.
I have embedded this SQL query in the function below (where the $discount_type argument is already set buy default to "smart_coupon"):
function get_coupons_from_email( $current_email, $discount_type = 'smart_coupon' ) {
global $wpdb;
return $wpdb->get_col( $wpdb->prepare("
SELECT p.post_name
FROM {$wpdb->prefix}posts p
INNER JOIN {$wpdb->prefix}postmeta pm
ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}postmeta pm2
ON p.ID = pm2.post_id
WHERE p.post_type = 'shop_coupon'
AND p.post_status = 'publish'
AND pm.meta_key = 'discount_type'
AND pm.meta_value = '%s'
AND pm2.meta_key = 'customer_email'
AND pm2.meta_value LIKE '%s'
ORDER BY p.post_name DESC
", $discount_type, '%'.$current_email.'%' ) );
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Now you can use it in your code as follows:
// Get smart coupons from email
$smart_coupon_codes = get_coupons_from_email( $current_email );
if ( count($smart_coupon_codes) > 0 ) {
// Loop through smart coupons code
foreach ( $smart_coupon_codes as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code ); // Get the WC_Coupon Object
if( $coupon->get_usage_count() < $coupon->get_usage_limit() ){
?>
<p>coupon content</p>
<?php
}
}
}
It should work smoothly now.
I'm trying to get the ranking the number of meta_key at specific custom post type.
But I'm not great at SQL so therefor not great with using $wpdb.
For example, if I write something like this,
<?php
$tarms = array( ‘sweet, sour’ );
echo get_count_ranking( $tarms );
?>
Then I would like to display rankings from comment("custom post of reply") of "custom post of fluits" with "term of sweet" and "term of sour" in order of "meta_key of count".
Here is my code:
function get_count_ranking( $tarms ){
global $wpdb;
$counts = $wpdb->get_results( $wpdb->prepare( "
SELECT p.post_author AS user_id, sum(m.meta_value) AS SumUser
FROM $wpdb->posts AS p, $wpdb->postmeta AS m
WHERE p.ID = m.post_ID
AND p.ID IN (
SELECT tr.object_id
FROM $wpdb->term_relationships AS tr, $wpdb->posts AS p, $wpdb->term_taxonomy AS tt
WHERE p.post_type = 'reply'
AND tt.term_id = %s
AND p.id = tr.object_id
AND tr.term_taxonomy_id = tt.term_taxonomy_id
)
AND p.post_status = 'publish'
AND m.meta_key = 'count'
GROUP BY p.post_author
ORDER BY m.meta_value DESC LIMIT 10
", $tarms ) );
$result = '';
foreach ( $counts as $count ) {
$result .= '<li><img>'.get_avatar($count->user_id, 30).'<span></span></li>';
}
return $result;
}
I am sorry that my English is so bad.
So I attach that image for your reference.
Thanks.
enter image description here
--
Updated code:
function get_count_ranking( $tarms ){
$customPostArg = array(
'posts_per_page' => 5,
'post_type' => 'fluits',
'tax_query' => array(
array(
'taxonomy' => 'taste-tag',
'field' => 'slug',
'terms' => $tarms
)
)
);
$array_with_post_ids = get_posts($customPostArg);
$argsp = array(
'post__in' => $array_with_post_ids
);
$commentsp = get_comments( $argsp );
$needed_data_array = array();
foreach ($comments as $key => $comment) {
$ranking = get_comment_meta($comment->ID, 'count', $return_single_value = true);
$author_id = $comment->user_id;
// make sure we have an author id
if($author_id) {
$needed_data_array[$author_id][] = $ranking;
}
}
}
$tarms = array( ‘sweet, sour’ );
echo get_count_ranking( $tarms );
I would not use $wpdb for this.
Also, it looks like you save rankings to a POST meta field, I would use a comment meta field.
Please NOTE, i'm not going to help you write everything, but here are
some pointers how I would accomplish this.
Register the custom post types with comment capability.
Extend the add comment to include a ranking setter. Save this ranking data in a comment_meta field. Now the comment and ranking meta are linked. When you remove the comment, the ranking meta data is also removed from the DB.
Now post comments and rankings are saved.
Collecting data
WP has a get_comments() function, this function accepts many arguments. Sadly, I miss an argument to get comments from posts with a certain taxonomy. So we have to collect all the posts first:
Collect all the posts with 'sweet' and/or 'sour' taxonomy, use get_posts().
Build an array with post id's.
Use get_comments() to get all comments connected to the posts.
Example:
$args = array(
'post__in' => $array_with_post_ids,
);
$comments = get_comments( $args );
Now you have all the comments you need for creating overviews, I would loop (iterate) through them and build an array with author_names and their rankings.
Example:
$needed_data_array = array();
foreach ($comments as $key => $comment) {
$ranking = get_comment_meta($comment->ID, 'ranking_meta_key', $return_single_value = true);
$author_id = $comment->user_id;
// make sure we have an author id
if($author_id) {
$needed_data_array[$author_id][] = $ranking;
}
}
//
// Now the $needed_data_array holds all authors
// and their post rankings, you can count them
// to get ranking totals for each comment-author.
//
I am using custom field on custom post types.
To display value of single custom field I use this code:
$gym_title = get_post_meta($post->ID, 'cast', true);
echo $gym_title;
Is there a way to display all custom fields with key ="gym" ?
(I have tried to use foreach and to write custom database search but no luck for now.)
Here's an example function which queries the database for all posts and returns all custom fields with a specific key:
function get_all_custom_fields( $key ) {
global $wpdb;
$r = $wpdb->get_col( $wpdb->prepare( "
SELECT pm.meta_value FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '%s'
", $key) );
return $r;
}
Usage:
$gym_titles = get_all_custom_fields('gym');
In "category.php" file I need to order my posts in this strange way:
First all the posts where author is different than "admin" (in alphabetical order by title)
Than all the post by "admin" (in the same alphabetical order)
Here is the standard code I use to do my query:
<?php global
$wp_query;
query_posts(
array_merge(
array('orderby' => 'title', 'order' => 'ASC'),
$wp_query->query
)
);
?>
Any idea about how to accomplish it without nesting two queries?
Thanks in advance!
EDIT: Following is some code that was tried, as suggested by Sepster in a previous version of his answer. But at the moment this code starts showing all the posts from 'admin' (instead of the others) until the posts with author different than 'admin' come. At that point it breaks the results and jump to the next page of results.
<?php
global $wp_query;
query_posts(
array_merge(
array('orderby' => 'title', 'order' => 'ASC'),
$wp_query->query
)
);
$adminPosts = false;
for ($i=1; $i<=2; $i++) {
while ( $wp_query->have_posts() ) {
$wp_query->the_post();
$author = get_the_author();
if ($author == 'admin' && $adminPosts == false) break;
if ($author != 'admin' && $adminPosts == true) break;
// ALL MY STUFF
} // end while
rewind_posts();
$adminPosts=true;
} // end FOR
?>
Update:
I've finally come up with a somewhat workable solution for this, but it's fair to say this really is an excercise in academics; Yes, it's do-able without executing a second loop, but really, it's pretty convoluted.
The solution is, in summary
Develop a custom SQL statement that will return the rows in the order required.
This is because in SQL, the only way to get the results in the order you need is by doing a UNION of your two subsets. To my knowledge there's no way of doing that using the "normal" WP query operations.
Execute this query, and loop over its results rather than a standard "the loop".
This is because we're getting back a recordset, rather than a WP_Query object.
Set the "Blog pages show at most X posts" setting to 1.
(on /wp-admin/options-reading.php)
A common complaint is pagination breaking when using custom queries (incidentally, the query_posts() method you're using is susceptible to this issue).
There are numerous turorials on how to do this properly, eg:
https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
https://codex.wordpress.org/Pagination#Troubleshooting_Broken_Pagination
The first of those recommends the implementation of "Offset and Manual Pagination".
The closest I've found to an implementation of this in conjunction with a custom SQL statement is this https://wordpress.stackexchange.com/a/28717. I've borrowed heavily from this answer (and so I recommend you go over and give it an up-vote!).
However, this technique (among other more "standard" custom query approaches) suffers from a known behaviour where WP produces a 404 on the final page (if I understand correctly, because WP is still using its own query and associated max-page=posts-per-page/posts calculations to map between the page number in the URL and the delivered content).
Refer http://wordpress.org/support/topic/explanation-and-workaround-for-error-404-on-category-pagination?replies=14 for details about this issue, and a proposed solution (which unfortunately won't work for our custom SQL approach).
A known "work-around" for this issue is to reduce the number of posts-per-page to 1, as per eg http://wordpress.org/support/topic/custom-post-type-pagination-404-on-last-page
So, assuming you're happy with a global setting of 1 posts-per-page (remember you'd need to override this manually in your custom queries), here's the code:
functions.php:
...
function get_users_posts_last($userDisplayName = 'Admin', $categoryName = '') {
global $wpdb, $paged, $max_num_pages;
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$post_per_page = 5;
$offset = ($paged - 1)*$post_per_page;
$sql = "
SELECT SQL_CALC_FOUND_ROWS q.* FROM
(
(
SELECT
p.*
FROM
{$wpdb->posts} p
INNER JOIN {$wpdb->users} u ON p.post_author = u.ID
LEFT JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
WHERE
tt.taxonomy = 'category'
AND p.post_status = 'publish'
AND p.post_type = 'post'
AND u.display_name != '{$userDisplayName}'
" . ( $categoryName != '' ? "AND t.name = '{$categoryName}'" : "" ) . "
ORDER BY
p.post_title ASC
)
UNION
(
SELECT
p.*
FROM
{$wpdb->posts} p
INNER JOIN {$wpdb->users} u ON p.post_author = u.ID
LEFT JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
WHERE
tt.taxonomy = 'category'
AND p.post_status = 'publish'
AND p.post_type = 'post'
AND u.display_name = '{$userDisplayName}'
" . ( $categoryName != '' ? "AND t.name = '{$categoryName}'" : "" ) . "
ORDER BY
p.post_title ASC
)
) q
LIMIT {$offset}, {$post_per_page};
";
$sql_result = $wpdb->get_results( $sql, OBJECT);
$sql_posts_total = $wpdb->get_var( "SELECT FOUND_ROWS();" );
$max_num_pages = ceil($sql_posts_total / $post_per_page);
return $sql_result;
}
...
category.php:
...
$postList = get_users_posts_last('admin'); // Note you can also pass a category name if necessary
if($postList) {
global $post;
foreach( $postList as $key=>$post ) {
setup_postdata($post);
// Render the post here
?>
<header class='entry-header'><h1 class='entry-title'><?php the_title(); ?></h1></header>
<div class='entry-content'><?php the_content(); ?></div>
<?php
}
// Render pagination here
?>
<div class="navigation">
<div class="previous panel"><?php previous_posts_link('« Previous page',$max_num_pages) ?></div>
<div class="next panel"><?php next_posts_link('Next page »',$max_num_pages) ?></div>
</div>
<?php
}
...
Or, just set up two separate queries ;-)
I created a custom taxonomy named 'technologies' but cannot query multiple terms like I can with categories or tags.
These querys DO work:
query_posts('tag=goldfish,airplanes');
query_posts('technologies=php');
However, neither of the following work correctly:
query_posts('technologies=php,sql');
query_posts('technologies=php&technologies=sql');
My objective: Show all posts with a technology of 'php' and all posts with a technology of 'sql'
Any ideas? Is this even possible? Thanks!
Apparently query_posts cannot help in this specific situation. (Hopefully it will be added in future versions of Wordpress!) The solution is to use a custom select query like the following:
SELECT *
FROM $wpdb->posts
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
LEFT JOIN $wpdb->terms ON($wpdb->term_taxonomy.term_id = $wpdb->terms.term_id)
WHERE $wpdb->posts.post_type = 'post'
AND $wpdb->posts.post_status = 'publish'
AND $wpdb->term_taxonomy.taxonomy = 'technologies'
AND $wpdb->terms.slug = 'php' OR $wpdb->terms.slug = 'css'
ORDER BY $wpdb->posts.post_date DESC
More information can be found at the Wordpress Codex:
http://codex.wordpress.org/Displaying_Posts_Using_a_Custom_Select_Query
This is a bit of a delayed reply, but it's first on Google at the moment for "wordpress related posts by multiple terms" so thought I'd contribute my findings.
Since this question was posted Wordpress has been changed to allow for this type of query. This will give you a list of posts related by any of the custom taxonomy terms assigned to an object:
$post_cats = wp_get_object_terms(get_the_ID(), 'video_category', array('fields' => 'ids'));
$args=array(
"tax_query" => array(
array(
"taxonomy" => "video_category",
"field" => "id",
"terms" => $post_cats
)
),
'post__not_in' => array(get_the_ID()),
'post_type' => 'video',
'posts_per_page' => 8,
'caller_get_posts' => 1
);
$related_by_cats = new WP_Query($args);
This is my first contribution to SO, I hope it's up to standards.
You can use this plugin:
http://scribu.net/wordpress/query-multiple-taxonomies/
Does this work? query_posts('tag=bread+baking+recipe')
From: http://codex.wordpress.org/Template_Tags/query_posts
OK, so here is my crack at this. It's a little hacky, but it works. The big downside is that any other query variables need to be re-added, as when multiple terms are invoked, the fail strips out all of the query vars.
Also, I did not test this against querying across multiple taxonomies. This only works within a specific taxonomy. Use at your own risk.
function multi_tax_terms($where) {
global $wp_query;
if ( strpos($wp_query->query_vars['term'], ',') !== false && strpos($where, "AND 0") !== false ) {
// it's failing because taxonomies can't handle multiple terms
//first, get the terms
$term_arr = explode(",", $wp_query->query_vars['term']);
foreach($term_arr as $term_item) {
$terms[] = get_terms($wp_query->query_vars['taxonomy'], array('slug' => $term_item));
}
//next, get the id of posts with that term in that tax
foreach ( $terms as $term ) {
$term_ids[] = $term[0]->term_id;
}
$post_ids = get_objects_in_term($term_ids, $wp_query->query_vars['taxonomy']);
if ( !is_wp_error($post_ids) && count($post_ids) ) {
// build the new query
$new_where = " AND wp_posts.ID IN (" . implode(', ', $post_ids) . ") ";
// re-add any other query vars via concatenation on the $new_where string below here
// now, sub out the bad where with the good
$where = str_replace("AND 0", $new_where, $where);
} else {
// give up
}
}
return $where;
}
add_filter("posts_where", "multi_tax_terms");
It's somehow silly that after implementing custom taxonomies in WP there are no built-in functions to use them at will, and the documentation is close to non-existent. I was looking for a solution, this query solves it (and made my day). Thanks.
Still, sadly I'm too dumb (OOP blind) to make it into a function so I don't repeat it all over.
I get: **Fatal error**: Call to a member function get_results() on a non-object
I guess I don't know how to call $wpdb from within a function.
it should be like this:
global $wp_query;
query_posts(
array_merge(
array('taxonomy' => 'technologies', 'term' => array('sql', 'php')),
$wp_query->query
)
);
that works for custom post_types, at least.
Hey, I also faced the same problem once. If you don't have many multiple values, then you can do it in the following way, rather than writing a raw SQL query:
$loop = new WP_Query(array('technologies' => 'php','technologies' => 'sql'));
Then you can loop through the wp_query object created here.
Though this is a very old post and am sure you have already solved the problem. :)
//equivalent to get_posts
function brand_get_posts($args=array()){
global $wpdb;
$sql = "SELECT p.* ";
$sql.= " FROM $wpdb->posts p";
$sql.= " LEFT JOIN $wpdb->term_relationships term_r ON(p.ID = term_r.object_id)";
$sql.= " LEFT JOIN $wpdb->term_taxonomy term_t ON(term_r.term_taxonomy_id = term_t.term_taxonomy_id)";
$sql.= " LEFT JOIN $wpdb->terms terms ON(term_t.term_id = terms.term_id)";
$sql.= " WHERE 1=1 ";
if(!empty($args['post_type'])){
$sql.= " AND p.post_type = '".$args['post_type']."'";
}
$sql.= " AND p.post_status = 'publish'";
if(!empty($args['taxonomy'])){
$sql.= " AND term_t.taxonomy = '".$args['taxonomy']."'";
}
if(!empty($args['terms'])&&is_array($args['terms'])){
$sql.= " AND terms.slug IN ('".implode(",",$args['terms'])."')";
}
$sql.= " ORDER BY p.post_date DESC";
if(!empty($args['posts_per_page'])){
$sql.=" LIMIT ".$args['posts_per_page'];
}
if(!empty($args['offset'])){
$sql.=" OFFSET ".$args['offset'];
}
//echo '<h1>'.$sql.'</h1>';
return $wpdb->get_results($sql);
}