Is there a way to create custom product URL based on the product attributes, I have a product sunglasses which has a couple of attributes tied to it: metal, blue and round, so the current URL is:
website.com/glasses/sunglasses/abram-widana-629/
What I am trying to get is a URL which has those attributes included:
website.com/glasses/sunglasses/abram-widana-meta-blue-round-629/
I would really appreciate if someone would even just point me to the right direction on how to tackle this.
There are two ways to do this, either Manually or Programmatically.
Manually adjusting permalinks:
In your example, you are simply adjusting the product URL to include attributes. This can be achieved manually by editing the permalink on the product itself.
Once the product has been added/saved, you will see the permalink showing directly under the title field like this:
Simply click the Edit button next to it and change it from abram-widana-629 to abram-widana-meta-blue-round-629
Programmatically adding attributes to permalinks:
If you want to try to achieve this permanently for all products you will have to work through the "save_post" filter/hook to add all the attributes to the permalink. The only downfall from this is that you will no longer be able to adjust your individual permalinks for your products as they will simply revert back once you click save.
Below is a code example of how to achieve that:
add_action( 'save_post', 'add_custom_attributes_to_permalink', 10, 3 );
function add_custom_attributes_to_permalink( $post_id, $post, $update ) {
//make sure we are only working with Products
if ($post->post_type != 'product' || $post->post_status == 'auto-draft') {
return;
}
//get the product
$_product = wc_get_product($post_id);
//get the "clean" permalink based on the post title
$clean_permalink = sanitize_title( $post->post_title, $post_id );
//next we get all of the attribute slugs, and separate them with a "-"
$attribute_slugs = array(); //we will be added all the attribute slugs to this array
foreach ($_product->get_attributes(); as $attribute_slug => $attribute_value) {
$attribute_slugs[] = $attribute_value;
}
$attribute_suffix = implode('-', $attribute_slugs);
//then add the attributes to the clean permalink
$full_permalink = $clean_permalink.$attribute_suffix;
// unhook the save post action to avoid a broken loop
remove_action( 'save_post', 'add_custom_attributes_to_permalink', 10, 3 );
// update the post_name (which becomes the permalink)
wp_update_post( array(
'ID' => $post_id,
'post_name' => $full_permalink
));
// re-hook the save_post action
add_action( 'save_post', 'add_custom_attributes_to_permalink', 10, 3 );
}
I am facing very strange issue. I am working on woocomerce based wordpress site, in which I have imported about more than 1 lack product form XML programatically, My problem is product meta data named Related Product saved in database, Displayed in back-end, But not displayed on single product page on front-end. But when I manually update post from back-end then after it displayed. It is not possible for me to manually update more than 1 lack post. So I decided to create function that will update post.
add_action( 'wp_loaded', 'my_update_posts' );
if ( $_REQUEST['test'] == 1 ) {
function my_update_posts() {
$args = array(
'post_type' => 'product',
'numberposts' => -1
);
$myposts = get_posts($args);
//echo "<pre>"; print_r($myposts); die;
foreach ( $myposts as $mypost ) {
if ( $mypost->ID == 17 ) {
//$mypost->title = "test";
wp_update_post( $mypost );
}
}
}
}
So I created this function and tested but it will update/save post data like content and title, and not updating/saving meta data.
I want create function that can save post programatically, including meta data.
The best way to save post data is using the update_post_meta function.
I have no idea how Woocommerce saves the related products. Keep in mind that it might make more then one meta field, maybe even create meta in the other related post.
Hope this helps, your question is a bit vague.
I have a custom post type named houses. Inside my custom post type I have several custom fields which I created using ACF.
What I need to do is to change the permalink when I create a new post.
I would like to use the code and title fields to customize the permalink:
//code + post title
4563312-house-example-1
I'm developing a plugin which controls everything.
Is there a way to intermediate the creation of a post to update its permalink?
Thanks.
After some research, I found an answer related to wp_insert_post_data.
Using wp_insert_post_data, I couldn't get the custom field value, and to achieve that, I had to use another action, save_post instead.
function rci_custom_permalink($post_id) {
$post = get_post($post_id);
if($post->post_type !== 'houses') return;
$code = get_field('code', $post_id);
$post_name = sanitize_title($post->post_title);
$permalink = $code . '-' . $post_name;
// remove the action to not enter in a loop
remove_action('save_post', 'rci_custom_permalink');
// perform the update
wp_update_post(array('ID' => $post_id, 'post_name' => $permalink));
// add the action again
add_action('save_post', 'rci_custom_permalink');
}
add_action('save_post', 'rci_custom_permalink');
PS: Since all these fields are required, I didn't need to check if they are empty or not.
For reference about save_post action:
Plugin API/Action Reference/save post
I'm trying to populate an Advanced Custom Fields' radio field pulling out the data from a custom post type located in the main blog of a multisite install.
For a better understanding I made this simple flow graphic.
So I created a function in order to pull out the data from Main Blog and show as radio items on child site.
The function looks like this and I used this as reference
function getctas($field) {
$field['choices'] = array();
switch_to_blog(1);
$args = array(
'post_type' => 'location_icons',
'posts_per_page' => '-1',
);
$ctas = new WP_Query( $args );
while ( $ctas->have_posts()) {
$ctas->the_post();
$choices = get_field('icon',false);
$choices = explode("\n", $choices);
foreach( $choices as $choice ):
$field['choices'][ $choice ] = '<img src="'.$choice.'"/>';
endforeach;
}
restore_current_blog();
return $field;
}
add_filter('acf/load_field/name=call_to_action_icon', 'getctas');
I get the options listed correctly (options are images), I successfully pulled out the field icon from main blog and put as radio label and value.
The issue I'm having is that once the post is saved when I query it on the child's page template I get the correct images but the title of the post on blog 1 repeated. The ideal would be to have:
Image
Child Blog Post's Title
Child Blog Post's Desc
And what I'm, instead getting is:
Correct Image
CTP Title that contains the image on blog 1
No description
Correct Image
Same title as previous one
No description
And so on.
If any of you need more clarifications to help me solve this I'd be pleased to explain further.
Please declare global $switched; before switching to the main blog , it seems wp is not switching it back to the current blog if it does not work after declaring the global variable, Please try this
get current blog id before switching to main blog $current_site =get_current_blog_id();
once you are done. switch it back using
switch_to_blog( $current_site );
$GLOBALS['_wp_switched_stack'] = array();
$GLOBALS['switched'] = FALSE;
Im trying to disable "shop" page in Woocommerce. Basically im creating a shop theme to sell prints and image downloads for a photographer.
Because i need to create private galleries i created a custom post type where i use the woocommerce category shortcode to show products and then i password protect the post type.
This is a workaround for password protecting the woocommerce categories (if someone knows a better one please explain).
The problem is that is someone goes to /shop they will all products, including the "protected ones". So i need to disable the shop page and i need to do it programmatically on my theme functions. Any thoughts?
To disable the shop page, copy over the archive-product.php file from the /wp-content/plugins/woocommerce/templates/archive-product.php and put in /wp-content/themes/{Your Theme}/woocommerce/archive-product.php
Open up the file and overwrite everything in file with the following code below:
<?php
global $wp_query;
$wp_query->set_404();
status_header(404);
get_template_part('404');
Save the file, and now your Shop page is gone and replaced with a 404 Page!
Add this to functions:
function woocommerce_disable_shop_page() {
global $post;
if (is_shop()):
global $wp_query;
$wp_query->set_404();
status_header(404);
endif;
}
add_action( 'wp', 'woocommerce_disable_shop_page' );
Docs: WooCommerce Conditional Functions Documentation
WooCommerce has a filter for the array that it uses to create the Product post type: woocommerce_register_post_type_product.
Rather changing the archive template to force it to redirect, you can completely remove the post type’s archive, but changing the has_archive attribute on the post type on creation.
add_filter('woocommerce_register_post_type_product', function($post_type) {
$post_type['has_archive'] = false;
return $post_type;
});
You should then remove the shop page in the CMS by going to WooCommerce » Settings » Product » Display, and clicking the “x” on the “Shop Page” option.
You might need to flush the permalink cache, which you can do just by clicking the “Update” button in Settings » Permalinks.
*Edit -
Apparently the page setting I suggested below no longer works. If WooCommerce doesn't have a plugin setting to change it, I personally would use a wordpress redirect plugin like Redirection. This way you can automatically redirect them from the undesired shop page to whatever page displays your products. It avoids a 404 issue and keeps everything in tact. It also avoids editing template files which adds complications to non-developers.
Old Answer:
Have you tried Woo settings?
Admin area, left main menu, Woocommerce > Settings
Click the pages tab.
Under Pages setup is "Shop Base Page", on the dropdown, there's a small "x" to right right. Click that to get rid of the page.
If there are links elsewhere that need to be fixed let me know and I'll find the hooks/filters to remedy it.
template_redirect is the last hook before page render so in my use case I ask if the page being viewed is the "shop" page and if it is I redirect to (in my case) a pricing page.
function my__template_redirect(){
if(is_shop()){
wp_redirect(site_url() . '/pricing/', '302');
}
}
add_action('template_redirect', 'my__template_redirect');
The last suggestion didn't work for me with WP 4.6.1 and WooCommerce 2.6.4. Hiding products in the Publish tab works for me.
http://paperhedge.com/hide-products-from-displaying-in-shop-page-woocommerce/
To disable the default shop page and leave the /shop/ slug free for custom pages use this:
function remove_woocommerce_default_shop( $args, $post_type ) {
if (class_exists('WooCommerce')) {
if ( $post_type == "product" ) {
$args['has_archive'] = true;
}
return $args;
}
}
add_filter('register_post_type_args', 'remove_woocommerce_default_shop', 20, 2);
You need to hook a couple (or maybe more) things:
/* hide category from shop pages */
add_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
function custom_pre_get_posts_query( $q ) {
if ( ! $q->is_main_query() ) return;
if ( ! $q->is_post_type_archive() ) return;
if ( ! is_admin() ) {
$q->set( 'tax_query', array(array(
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => array( YOUR-CATEGORY-SLUG, YOUR-CATEGORY-SLUG-2 ), // Don't display products in the knives category on the shop page
'operator' => 'NOT IN'
)));
}
remove_action( 'pre_get_posts', 'custom_pre_get_posts_query' );
}
Change, obviusly YOUR-CATEGORY-SLUG, YOUR-CATEGORY-SLUG-2 for your cat or cats to hide from shop pages.
Then, you need to hide them from menues too, right?:
/* hide category from menues */
add_filter( 'get_terms', 'get_subcategory_terms', 10, 3 );
function get_subcategory_terms( $terms, $taxonomies, $args ) {
$new_terms = array();
// if category and on the shop page (change it with is_woocommerce() if want to hide from all woo pages)
if ( in_array( 'product_cat', $taxonomies ) && ! is_admin() && is_shop() ) {
foreach ( $terms as $key => $term ) {
if ( ! in_array( $term->slug, array( YOUR-CATEGORY-SLUG, YOUR-CATEGORY-SLUG-2 ) ) ) {
$new_terms[] = $term;
}
}
$terms = $new_terms;
}
return $terms;
}
But i don´t think this solve 100% the thing, since then, what about search results?
Try this
Create new page named "Shop"
Go to "woocommerce" > "Settings" > "Product" > "Display tab"
Select shop page named "Shop" then click save changes
Back to "Pages" then delete "Shop" page (keep the page on trash,
don't delete permanently)
This method works fine for me