Im extremely new to WordPress and WooCommerce code. I've been provided with some working code that resides in the plugins/woocommerce/templates/archive-product.php
The function is pretty simple, it simply fetches for an array of data from a remote site and makes use of the JSON returned to find the matching SKUs and inject them as items in the product list.
Works quite nicely, however, as I'm new to Woo & WP, I'm hoping someone might be able to show me how I can transform this code into the proper way of it being defined as a plugin?
I'm hoping its just a case of wrapping some additional code around the function, but I'm unsure as to where to start
any tips greatly appreciated
if ( wc_get_loop_prop( 'total' ) ) {
$actual_link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$cat = explode('/', $actual_link);
if ($cat[4] == 'my-product-list') {
global $current_user;
get_currentuserinfo();
$data = array( 'email' => $current_user->user_email);
$response = wp_remote_post( 'https://www.shop.com/remote-data/', array( 'data' => $data ) );
$curl = 'https://www.shop.com/remote-data/';
$response = wp_remote_get( $curl );
$rows = wp_remote_retrieve_body( $response ) ;
$decode = json_decode(stripslashes($rows), true);
global $wpdb;
$product_id = array();
$i = 0;
foreach ($decode as $single_data) {
foreach ($single_data['items'] as $data) {
$result = $wpdb->get_results ( "SELECT post_id FROM wp_postmeta WHERE meta_key = '_sku' AND meta_value = '".$data."'" );
$product_id[$i] = $result[0]->post_id;
$i++;
}
}
$product_id = array_filter(array_unique($product_id));
$args = array(
'post_type' => 'product',
'post__in' => $product_id
);
//The Query
$the_query = new WP_Query( $args );
while ( $the_query->have_posts() ) {
$the_query->the_post();
do_action( 'woocommerce_shop_loop' );
wc_get_template_part( 'content', 'product' );
}
You could try to hook it into woocommerce_before_main_content
Like so:
function fetch_shop_data() {
// your code
}
add_action('woocommerce_before_main_content', 'fetch_shop_data');
Related
Is it possible to query multiple CPTs and then order in a preset way?
For example, I have 2 CPTs & 1 being the WP default 'post' and would like the loop to return 6 results, ordering as below.
CPT-1
CPT-2
POST
CPT-1
CPT-2
POST
Is this possible without splitting the loop?
I did have a quick search, but could only find one article relating to this, the solutions from which don't appear to work any more...
Wordpress order posts by post type
Here is a shortened version of Sephsekla's code:
$my_post_types = array( 'CPT-1', 'CPT-2', 'post', 'CPT-1', 'CPT-2', 'post' );
$posts_shown = array();
$args = array(
'post_type' => array( 'CPT-1', 'CPT-2', 'post' ),
'post_status' => 'publish',
'posts_per_page' => -1
);
$my_query = new WP_Query( $args );
foreach ( $my_post_types as $post_type ):
while ( $my_query->have_posts() ): $my_query->the_post();
if ( $post_type == get_post_type() && ! in_array( get_the_ID(), $posts_shown ) ) {
echo '<pre>' . get_post_type() .': '. get_the_title() . '</pre>';
$posts_shown[] = get_the_id();
break;
}
endwhile;
$my_query->rewind_posts();
endforeach;
wp_reset_postdata();
This is something I've done before a while ago, and while it is doable with just one query it's a bit of a long-winded setup.
The gist of it is that you can use one query, then loop through the query until you find the first post you're after. Then exit the loop and use WP_Query->rewind_posts() to put the query back to the start.
Then you can run a second loop with a different condition. And then a third.
For the fourth, fifth and sixth loops you also need to check that you're not repeating the first set.
See below for the code in all its glory.
<?php
$my_query = new WP_Query(
array(
'post_status' => 'publish',
)
);
$post_1 = $post_2 = $post_3 = $post_4 = $post_5 = $post_6 = 0;
if ( $my_query->have_posts() ) {
/*First loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the first post
*/
if ( 'CPT-1' == get_post_type() && $post_1 == 0 ) {
do_something_with_the_post();
$post_1 = get_the_id();
break;
}
}
$my_query->rewind_posts();
/*Second loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the second post
*/
if ( 'CPT-2' == get_post_type() && $post_2 == 0 ) {
do_something_with_the_post();
$post_2 = get_the_id();
break;
}
}
$my_query->rewind_posts();
/*Third loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the third post
*/
if ( 'post' == get_post_type() && $post_3 == 0 ) {
do_something_with_the_post();
$post_3 = get_the_id();
break;
}
}
$my_query->rewind_posts();
/**
* Then we repeat this process but also check we don't use the same post twice
*/
/*Fourth loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the fourth post
*/
if ( 'CPT-1' == get_post_type() && $post_4 == 0 && get_the_id() !== $post_1 ) {
do_something_with_the_post();
$post_1 = get_the_id();
break;
}
}
$my_query->rewind_posts();
/*Fifth loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the fifth post
*/
if ( 'CPT-2' == get_post_type() && $post_5 == 0 && get_the_id() !== $post_2 ) {
do_something_with_the_post();
$post_5 = get_the_id();
break;
}
}
$my_query->rewind_posts();
/*Sixth loop through posts*/
while ( $my_query->have_posts() ) {
$my_query->the_post();
/**
* Find the sixth post
*/
if ( 'post' == get_post_type() && $post_6 == 0 && get_the_id() !== $post_3 ) {
do_something_with_the_post();
$post_6 = get_the_id();
break;
}
}
/**
* And we're finished
*/
}
I'm trying to accomplish something similar and although Vayu's code is super elegant, it doesn't accomplish what I need - specifically I need to be able to include a different template part for each post type (they have slightly different content) as well as add an $arg for the WP post (that is not a CPT) to target a specific category
What I'm trying to accomplish is this (just in logic format with $args above):
$my_post_types = array( 'auctions', 'liquidations', 'inventory', 'post' );
// set args for ALL posts
$args = array(
'post_type' => $my_post_types,
'orderby' => 'date',
'order' => 'DESC',
'post_status' => 'publish',
);
Now we loop through posts (while loop)
IF 'post_type == auctions'
add number_posts to $args from ACF field for this post type
get_template_part( 'template-parts/card', 'auction' );
IF 'post_type == liquidations'
add number_posts to $args from ACF field for this post type
get_template_part( 'template-parts/card', 'liquidations' );
IF 'post_type == inventory'
add number_posts to $args from ACF field for this post type
get_template_part( 'template-parts/card', 'inventory' );
IF 'post_type == post'
add 'cat => 304' to $args
add number_posts to $args from ACF field for this post type
and get_template_part( 'template-parts/card', 'studies' );
The goal is to show all of the CPTs and Posts, in the qty set for each type in the admin, sorted by date, on one page. I need to update/add to the $args for each of the post type loops....
So i make custom form-tag in contact form 7! It is a drop down with list of my courses and now I want to make it required because that is the main thing in whole form.
So can someone give me a tip how to do that?
When I do the [myCustomField* course-name class:custom-field]
It does not working with *
So if someone can help it will be great!
I have been working on this myself this afternoon and I do not think Mahmoud has added everything that is needed to get the validation working well and the messages showing up.
using what I have learnt from the posts on contact form 7 here:
https://contactform7.com/2015/01/10/adding-a-custom-form-tag
https://contactform7.com/2015/02/27/using-values-from-a-form-tag/
and looking at this file in the plugin: contact-form-7/modules/select.php which helped a lot.
I think this will work better and needs to be added to your functions.php file in your child-theme.
add_action( 'wpcf7_init', 'custom_add_form_tag_myCustomField' );
function custom_add_form_tag_myCustomField() {
wpcf7_add_form_tag( array( 'myCustomField', 'myCustomField*' ),
'custom_myCustomField_form_tag_handler', true );
}
function custom_myCustomField_form_tag_handler( $tag ) {
$tag = new WPCF7_FormTag( $tag );
if ( empty( $tag->name ) ) {
return '';
}
$validation_error = wpcf7_get_validation_error( $tag->name );
$class = wpcf7_form_controls_class( $tag->type );
if ( $validation_error ) {
$class .= ' wpcf7-not-valid';
}
$atts = array();
$atts['class'] = $tag->get_class_option( $class );
$atts['id'] = $tag->get_id_option();
if ( $tag->is_required() ) {
$atts['aria-required'] = 'true';
}
$atts['aria-invalid'] = $validation_error ? 'true' : 'false';
$atts['name'] = $tag->name;
$atts = wpcf7_format_atts( $atts );
$myCustomField = '';
$query = new WP_Query(array(
'post_type' => 'CUSTOM POST TYPE HERE',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
));
while ($query->have_posts()) {
$query->the_post();
$post_title = get_the_title();
$myCustomField .= sprintf( '<option value="%1$s">%1$s</option>',
esc_html( $post_title ) );
}
wp_reset_query();
$myCustomField = sprintf(
'<span class="wpcf7-form-control-wrap %1$s"><select %2$s>%3$s</select>%4$s</span>',
sanitize_html_class( $tag->name ),
$atts,
$myCustomField,
$validation_error
);
return $myCustomField;
}
That is how we create the custom tag. The important differences here are the addition of the $validation_error variables as wells the aria-required and aria-invalid data. It is also important to include the $validation_error in the final output so that we can see the validation messages being created.
Then to finish it off we need to add some validation via filters.
There is no documentation on this yet, but I used the functions from the select.php and altered them to what I needed.
/* Validation filter */
add_filter( 'wpcf7_validate_myCustomField', 'wpcf7_myCustomField_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_myCustomField*', 'wpcf7_myCustomField_validation_filter', 10, 2 );
function wpcf7_myCustomField_validation_filter( $result, $tag ) {
$tag = new WPCF7_FormTag( $tag );
$name = $tag->name;
if ( isset( $_POST[$name] ) && is_array( $_POST[$name] ) ) {
foreach ( $_POST[$name] as $key => $value ) {
if ( '' === $value ) {
unset( $_POST[$name][$key] );
}
}
}
$empty = ! isset( $_POST[$name] ) || empty( $_POST[$name] ) && '0' !== $_POST[$name];
if ( $tag->is_required() && $empty ) {
$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
}
return $result;
}
This code should also go in your functions.php file just under the code for the custom CF7 tag.
Here the filter's first string $tag should match with the class that is being generated in the custom CF7 tag so if your custom tag->type = 'myCustomField' then the $tag of the filter must include the name, like so wpcf7_validate_myCustomField as well as the required version of it, wpcf7_validate_myCustomField*.
I hope that helps anyone else looking for this.
If you want even more of the options available from the backend of Contact Form 7 check the select.php file as it lays it out quite nicely on how to get each option and include it.
You can use [select*] to output a required drop-down menu.
[select* course-name include_blank "English" "Math"]
Check https://contactform7.com/checkboxes-radio-buttons-and-menus/
EDIT:
So you have your own shortcode [myCustomField]. To make two versions of your shortcode as [myCustomField] and [myCustomField*] you have to pass both shortcodes to your function as the following:
add_action( 'wpcf7_init', 'wpcf7_add_form_tag_mycustomfield' );
function wpcf7_add_form_tag_mycustomfield() {
wpcf7_add_form_tag( array( 'myCustomField', 'myCustomField*'),
'wpcf7_mycustomfield_form_tag_handler', array( 'name-attr' => true ) );
}
function wpcf7_mycustomfield_form_tag_handler( $tag ) {
$tag = new WPCF7_FormTag( $tag );
if ( empty( $tag->name ) ) {
return '';
}
$atts = array();
$class = wpcf7_form_controls_class( $tag->type );
$atts['class'] = $tag->get_class_option( $class );
$atts['id'] = $tag->get_id_option();
$atts['name'] = $tag->name;
$atts = wpcf7_format_atts( $atts );
$html = sprintf( '<your-tag %s></your-tag>', $atts );
return $html;
}
Then, you can use it:
[myCustomField course-name class:custom-field]
or
[myCustomField* course-name class:custom-field]
References:
https://contactform7.com/2015/01/10/adding-a-custom-form-tag
https://contactform7.com/2015/02/27/using-values-from-a-form-tag/
I've got a function added in functions.php to improve the search functionality in my directory section: http://www.highwaysindustry.com/?post_type=directory_listings&s=king
However, with the following enabled, it overrides the search for the rest of the website also: http://www.highwaysindustry.com
This particular function I would prefer it to be targeted for the directory search only.
Function in functions.php
function __search_by_title_only( $search, $wp_query )
{
global $wpdb;
if ( empty( $search ) )
return $search; // skip processing - no search term in query
$q = $wp_query->query_vars;
$n = ! empty( $q['exact'] ) ? '' : '%';
$search =
$searchand = '';
foreach ( (array) $q['search_terms'] as $term ) {
$term = esc_sql( like_escape( $term ) );
$search .= "{$searchand}($wpdb->posts.post_title REGEXP '[[:<:]]{$term}[[:>:]]')";
$searchand = ' AND ';
}
if ( ! empty( $search ) ) {
$search = " AND ({$search}) ";
if ( ! is_user_logged_in() )
$search .= " AND ($wpdb->posts.post_password = '') ";
}
return $search;
}
add_filter( 'posts_search', '__search_by_title_only', 1000, 2 );
Snippet of my directory-search.php which the form on /Directory uses:
<?php
$args = array(
'post_type'=> 'directory_listings',
's' => $s,
'paged' => $paged,
'meta_query' => array(
array(
'key' => 'membership_type',
'orderby' => 'meta_value',
'order' => "DESC"
,
),
),
);
?>
<div class="row">
<div class="small-12 columns">
<div class="directory-banner">
<img src="<?php echo site_url(); ?>/wp-content/themes/highways/images/directory-banner.jpg" alt="" title="" />
<h1><?php echo $s; ?></h1>
</div>
</div>
</div>
**I currently have the function defined above commented out, when enabled it's a lot more specific, for example, searching "King" would only provide back 2 results. Right now the generic search provides too many unspecific results.
The other 2 answers are also worth investigating. However I'll stick to your solution.
To search only directory_listings post type, tweak the function as shown below
function __search_by_title_only( $search, $wp_query )
{
global $wpdb;
if ( empty( $search ) )
return $search; // skip processing - no search term in query
$q = $wp_query->query_vars;
// post_type query var has other posts types as well, return default search
if( ! 'directory_listings' == $q['post_type'] )
return $search
// We are here, it means post_type var contains only directory_listings
// continue with the rest of the code
........
Answering your 2nd query
There is a company called blakedale, if you search just blake you won't get the result. Any method to increase the method of the returned result(s).
The regexp being used in the code below searches for full words hence "blake" doesn't return any results
$search .= "{$searchand}($wpdb->posts.post_title REGEXP '[[:<:]]{$term}[[:>:]]')";
You need to either change the regexp or use SQL LIKE
$search .= "{$searchand}($wpdb->posts.post_title LIKE '%{$term}%')";
P.S: I haven't tested the line of code above, so it may need polishing.
did you try using pre_get_posts ?
function search_filter($query) {
if ( !is_admin() && $query->is_main_query() ) {
if ($query->is_search && $_GET['post_type'] == 'directory_listings') {
// add custom meta / tax queries
}
}
}
add_action('pre_get_posts','search_filter');
You can try this .
$query_args = array( 'post_type' => 'directory_listings', 'tax_query' => array() );
if ( !empty( $search_terms ) ) {
$query_args['s'] = $search_terms;
}
if ( !empty( $search_terms ) ) {
$query_args['tax_query'][] = array(
array(
'taxonomy' => 'directory_listings',
'field' => 'slug',
'terms' => $news_category
)
);
}
$my_posts = new WP_Query( $query_args );
if ( $my_posts->have_posts() ) { // etc
}
I hadn't realised that WordPress had added a native function for it to the Codex, but for some odd reason the page is blank. Does that mean the functionality is still coming or the page was added by mistake?
http://codex.wordpress.org/Function_Reference/get_permalink_by_slug
It's blank because it doesn't exist. If you change that function name to any random name you like you'll still see a blank page (unless of course the name you change it to is a real function).
function get_permalink_by_slug( $slug, $post_type = '' ) {
// Initialize the permalink value
$permalink = null;
// Build the arguments for WP_Query
$args = array(
'name' => $slug,
'max_num_posts' => 1
);
// If the optional argument is set, add it to the arguments array
if( '' != $post_type ) {
$args = array_merge( $args, array( 'post_type' => $post_type ) );
} // end if
// Run the query (and reset it)
$query = new WP_Query( $args );
if( $query->have_posts() ) {
$query->the_post();
$permalink = get_permalink( get_the_ID() );
} // end if
wp_reset_postdata();
return $permalink;
}
I have a Custom Post Type, 'ioni_codex'
I am using built-in Wordpress category as taxonomy
I want to list all categories used by 'ioni_codex'.
I assume that this code will do the trick:
$myargs = array (
'type' => 'ioni_codex'
);
$categories = get_categories( $myargs );
However instead I see the list of all categories not the categories assigned to by 'ioni_codex'.
What Am I doing wrong?
get_categories() does not accept post_type as an argument, use taxonomy instead and refer to the taxonomy by the name you gave it when registering it. Here is a link to the codex which can explain it in more detail - http://codex.wordpress.org/Function_Reference/get_categories.
Instead of type you have to set post_type , by default get_categories try to hide empty categories , if you want display it all add hide_empty property set to false
get_categories(array(
'post_type' => 'ioni_codex',
'hide_empty' => false,
) );
I have an answer at a sister project:
Bainternet♦ has re-written get_terms() function to provide for the post_type
Please refer to his solution here or just copy and past from below:
/* get terms limited to post type
# $taxonomies - (string|array) (required) The taxonomies to retrieve terms from.
# $args - (string|array) all Possible Arguments of get_terms http://codex.wordpress.org/Function_Reference/get_terms
# $post_type - (string|array) of post types to limit the terms to
# $fields - (string) What to return (default all) accepts ID,name,all,get_terms.
if you want to use get_terms arguments then $fields must be set to 'get_terms'
*/
function get_terms_by_post_type($taxonomies,$args,$post_type,$fields = 'all'){
$args = array(
'post_type' => (array)$post_type,
'posts_per_page' => -1
);
$the_query = new WP_Query( $args );
$terms = array();
while ($the_query->have_posts()){
$the_query->the_post();
$curent_terms = wp_get_object_terms( $post->ID, $taxonomy);
foreach ($curent_terms as $t){
//avoid duplicates
if (!in_array($t,$terms)){
$terms[] = $c;
}
}
}
wp_reset_query();
//return array of term objects
if ($fields == "all")
return $terms;
//return array of term ID's
if ($fields == "ID"){
foreach ($terms as $t){
$re[] = $t->term_id;
}
return $re;
}
//return array of term names
if ($fields == "name"){
foreach ($terms as $t){
$re[] = $t->name;
}
return $re;
}
// get terms with get_terms arguments
if ($fields == "get_terms"){
$terms2 = get_terms( $taxonomies, $args );
foreach ($terms as $t){
if (in_array($t,$terms2)){
$re[] = $t;
}
}
return $re;
}
}
While quick reading #ioni's answer; it looks like it would give you a proper result it will most likely be pretty slow as it goes through every post and then looks for all terms that belong to each post individually. This will easily result in thousands of SQL queries.
My recommendation is to use a single query to get the IDs of all terms that belong to a post type. Then use those ids to filter get_terms().
MySQL query:
SELECT
wp_term_taxonomy.term_id
FROM
wp_term_relationships
LEFT JOIN wp_posts ON wp_term_relationships.object_id = wp_posts.ID
LEFT JOIN wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
WHERE
wp_term_taxonomy.taxonomy = 'category'
AND wp_posts.post_type = 'ioni_codex'
GROUP BY
term_id
PHP function:
<?php
function ioni_get_terms_by_post_type( $taxonomy = 'category', $post_type = 'post', $args = array() ) {
global $wpdb;
$sql = $wpdb->prepare(
"
SELECT
{$wpdb->prefix}term_taxonomy.term_id
FROM
{$wpdb->prefix}term_relationships
LEFT JOIN {$wpdb->prefix}posts ON {$wpdb->prefix}term_relationships.object_id = {$wpdb->prefix}posts.ID
LEFT JOIN {$wpdb->prefix}term_taxonomy ON {$wpdb->prefix}term_relationships.term_taxonomy_id = {$wpdb->prefix}term_taxonomy.term_taxonomy_id
WHERE
{$wpdb->prefix}term_taxonomy.taxonomy = '%s'
AND {$wpdb->prefix}posts.post_type = '%s'
GROUP BY
term_id
",
$taxonomy,
$post_type,
);
$term_ids = $wpdb->get_col( $sql );
if ( empty( $term_ids ) ) {
return array();
}
// custom code to allow for exclude to work
if ( ! empty( $args['exclude'] ) ) {
// allow exclude to be either a string or array
$exclude = is_string( $args['exclude'] ) ? (array) $args['exclude'] : $args['exclude'];
// filter $term_ids with array from $args['exclude']
$term_ids = array_filter( $term_ids, function( $term_id ) use ( $exclude ) {
return ! in_array( $term_id, $exclude );
} );
}
$args = wp_parse_args(
$args,
array(
'taxonomy' => $taxonomy,
'include' => $term_ids,
),
);
return get_terms( $args );
}
Usage:
<?php
$categories = ioni_get_terms_by_post_type( 'category', 'ioni_codex' );