简体   繁体   中英

Get unpurchased items paginated as Custom My account page content in Woocommerce

first I want to say this is my very first question on Stack overflow but, I have found many helpful solutions over the years so I want to thank everyone who participates here.

Now, onto my first question...

In my woocommerce setup, every product is a digital download and customers are limited to buying each only once. All downloads expire after 14 days for security reasons.

We have some customers who have been with us for up to 5 years so, when going through products we decided to offer a page in their account that shows what they havent purchased yet. Just makes it easier for them.

Now, my question is two fold. First for the code below, which shows the page in their account dashboard, I can not get paginav to work so if you can see what could be wrong there, please let me know.

But the bigger question is, how can I turn the query into a sort function? Meaning, if the customer chooses they can sort by "Not yet purchased"

Or, if anyone knows an easier way to accomplish this, please let me know.

Here is the code I am currently using to display a page that shows them products they have "Not yet purchased"

// *NOT PURCHASED
add_filter ( 'woocommerce_account_menu_items',     'custom_purchased_products_link', 40 );
add_action( 'init', 'custom_add_products_endpoint' );
add_action( 'woocommerce_account_purchased-products_endpoint',     'custom_populate_products_page' );

// here we hook the My Account menu links and add our custom one
function custom_purchased_products_link( $menu_links ){
    // we use array_slice() because we want our link to be on the 3rd position
    return array_slice( $menu_links, 0, 2, true )
    + array( 'purchased-products' => 'Not Purchased Yet' )
    + array_slice( $menu_links, 2, NULL, true );
}

// here we register our rewrite rule
function custom_add_products_endpoint() {
    add_rewrite_endpoint( 'purchased-products', EP_PAGES );
}

// here we populate the new page with the content
function custom_populate_products_page() {
    global $wpdb;

    // this SQL query allows to get all the products purchased by the current     user
    // in this example we sort products by date but you can reorder them another     way
    $purchased_products_ids = $wpdb->get_col( $wpdb->prepare(
        "
        SELECT      itemmeta.meta_value
        FROM        " . $wpdb->prefix . "woocommerce_order_itemmeta itemmeta
        INNER JOIN  " . $wpdb->prefix . "woocommerce_order_items items
                    ON itemmeta.order_item_id = items.order_item_id
        INNER JOIN  $wpdb->posts orders
                    ON orders.ID = items.order_id
        INNER JOIN  $wpdb->postmeta ordermeta
                    ON orders.ID = ordermeta.post_id
        WHERE       itemmeta.meta_key = '_product_id'
                    AND ordermeta.meta_key = '_customer_user'
                    AND ordermeta.meta_value = %s
        ORDER BY    orders.post_date DESC
        ",
        get_current_user_id()
    ) );

    // some orders may contain the same product, but we do not need it twice
    $purchased_products_ids = array_unique( $purchased_products_ids );

    // if the customer purchased something
    if( !empty( $purchased_products_ids ) ) :

        // it is time for a regular WP_Query
        $purchased_products = new WP_Query( array(
            'post_type' => 'product',
            'paginate' => true,
            'posts_per_page' => 50,
            'paginate' => true,
            'post_status' => 'publish',
            'post__not_in' => $purchased_products_ids,
        ) );

        echo '<div class="woocommerce columns-3">';
        echo '<h3>Some Products you have not yet purchased (Max display 39)    
        </h3>';

        woocommerce_product_loop_start();

        while ( $purchased_products->have_posts() ) : $purchased_products- 
        >the_post();

            wc_get_template_part( 'content', 'product' );

        endwhile;
        woocommerce_product_loop_end();

        woocommerce_reset_loop();
        wp_reset_postdata();

    else:
        echo 'Nothing purchased yet.';
    endif;
}

add_action( 'woocommerce_after_shop_loop_item', 
'user_logged_in_product_already_bought', 30 );

You can simplify and optimize the code making a unique SQL query to get the product IDs not purchased by the current customer. Then you can use Woocommerce [products] shortcode to get a nice paginated page of this not purchased product.

So the code will be much more lighter and efficient, with pagination (on Woocommerce 3.2+) :

// Add a new custom menu item to tabbed "My account" menu
add_filter ( 'woocommerce_account_menu_items', 'add_account_new_menu_item', 30, 1 );
function add_account_new_menu_item( $items ){
    // Define the new item position in the tabbed menu
    $position = 3;

    // Define the new item to include in the tabbed menu
    $item = array( 'unpurchased-products' => 'Not purchased' );

    return array_slice( $items, 0, ($position - 1), true ) + $item + array_slice( $items, ($position - 1), NULL, true );
}

// Enable the new custom menu item tabbed "My account" menu
add_action( 'init', 'add_account_new_menu_item_endpoint' );
function add_account_new_menu_item_endpoint() {
    add_rewrite_endpoint( 'unpurchased-products', EP_PAGES );

    // ==> Always reflush rewrite rules the first time:
    // ==> Go to the Permalinks options page and re-save the permalinks
}

// Add the corresponding content for this new custom menu item
add_action( 'woocommerce_account_unpurchased-products_endpoint', 'custom_populate_products_page' );
function custom_populate_products_page() {
    global $wpdb;

    //Unique SQL query to get tne non purchased product Ids
    $not_purchased_ids = $wpdb->get_col( "SELECT DISTINCT ID
        FROM {$wpdb->prefix}posts
        WHERE post_type = 'product'
        AND post_status = 'publish'
        AND ID NOT IN (SELECT DISTINCT woim.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta AS woim
        JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON woim.order_item_id = woi.order_item_id
        JOIN {$wpdb->prefix}posts AS p ON woi.order_id = p.ID
        JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
        WHERE woim.meta_key = '_product_id' AND pm.meta_key = '_customer_user'
        AND pm.meta_value != '{get_current_user_id()}')
    " );

    $included_ids = implode(',', $not_purchased_ids);

    echo do_shortcode("[products limit='9' columns='3' paginate='true' orderby='' ids='$included_ids']");
}

// Rename the default option from sorting select field in My account only
add_filter ( 'woocommerce_catalog_orderby', 'custom_catalog_orderby', 30, 1 );
function custom_catalog_orderby( $options ){
    if( is_account_page() )
        $options['menu_order'] = __( 'Not purchased yet', 'woocommerce' );

    return $options;
}

Code goes in function.php file of your active child theme (or active theme). Tested and works.

Enable the new My account tabbed menu item

Once this code is saved, you will need to flush Wordpress rewrite rules:
Go in WordPress settings in Permalinks options page and re-save the permalinks.


Changing the number of products by page and the number of columns:

In the shortcode limit is number of products by page and columns the number of columns.

  [products limit='9' columns='3' paginate='true' orderby='' ids='$included_ids'] 

在此处输入图片说明


Useful links:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM