Elementor Widget WooCommerce

User
create elelmentor wiget to display new arival 5 product based on attached layout wordpess woocommerce.
Assistant
To create an **Elementor widget** that displays **5 latest arrival WooCommerce products** using a layout similar to your provided image, follow these steps:

### ✅ **Step-by-Step: Create "Latest Arrival" Elementor Widget for 5 Products**

### 1. **Register Custom Elementor Widget**

Create a new PHP file inside your custom plugin or child theme (e.g., `widgets/class-latest-arrivals-widget.php`):

<?php
use ElementorWidget_Base;
use ElementorControls_Manager;

if (!defined('ABSPATH')) exit;

class Latest_Arrivals_Widget extends ElementorWidget_Base {

    public function get_name() {
        return 'latest_arrivals';
    }

    public function get_title() {
        return __('Latest Arrivals', 'plugin-name');
    }

    public function get_icon() {
        return 'eicon-products';
    }

    public function get_categories() {
        return ['general'];
    }

    public function render() {
        $args = [
            'post_type' => 'product',
            'posts_per_page' => 5,
            'orderby' => 'date',
            'order' => 'DESC'
        ];

        $query = new WP_Query($args);
        if ($query->have_posts()) {
            echo '<div class="latest-arrivals">';
            while ($query->have_posts()) : $query->the_post();
                global $product;
                ?>
                <div class="arrival-item">
                    <div class="arrival-image">
                        <img src="<?php echo get_the_post_thumbnail_url(); ?>" alt="<?php the_title(); ?>">
                        <span class="badge">LATEST ARRIVAL</span>
                    </div>
                    <div class="arrival-content">
                        <h3><?php the_title(); ?></h3>
                        <p class="desc"><?php echo wc_trim_string(get_the_excerpt(), 20); ?></p>
                        <div class="price"><?php echo $product->get_price_html(); ?></div>
                        <a href="?add-to-cart=<?php echo esc_attr($product->get_id()); ?>" class="button">Add to Cart</a>
                        <a href="<?php echo get_permalink(); ?>" class="similar-button">Shop for Similar Products</a>
                    </div>
                </div>
                <?php
            endwhile;
            echo '</div>';
            wp_reset_postdata();
        }
    }
}

### 2. **Enqueue Widget CSS**

In your plugin or child theme:

function enqueue_latest_arrival_styles() {
    wp_enqueue_style('latest-arrival-style', get_stylesheet_directory_uri() . '/css/latest-arrivals.css');
}
add_action('wp_enqueue_scripts', 'enqueue_latest_arrival_styles');

### 3. **CSS Layout (`/css/latest-arrivals.css`)**

.latest-arrivals {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 30px;
}
.arrival-item {
    border: 1px solid #ccc;
    padding: 20px;
    position: relative;
    background: #fff;
}
.arrival-image {
    position: relative;
}
.arrival-image img {
    max-width: 100%;
    height: auto;
}
.badge {
    position: absolute;
    top: 10px;
    right: 10px;
    background: red;
    color: #fff;
    font-size: 12px;
    padding: 3px 8px;
}
.arrival-content h3 {
    font-weight: 700;
}
.desc {
    font-size: 14px;
    color: #555;
}
.price {
    font-size: 20px;
    font-weight: bold;
    margin: 10px 0;
}
.button {
    display: inline-block;
    background: red;
    color: white;
    padding: 10px 15px;
    margin-right: 10px;
    text-decoration: none;
    border-radius: 5px;
}
.similar-button {
    background: black;
    color: white;
    padding: 10px 15px;
    text-decoration: none;
    border-radius: 5px;
}

### 4. **Register Widget in Elementor**

In your Elementor custom plugin `plugin.php`:

add_action('elementor/widgets/widgets_registered', function($widgets_manager) {
    require_once(__DIR__ . '/widgets/class-latest-arrivals-widget.php');
    $widgets_manager->register(new Latest_Arrivals_Widget());
});

### 🔁 Optional Enhancements

– Add star ratings with `wc_get_rating_html($product->get_average_rating())`.
– Add custom fallback for products without a description.
– Use Swiper or Carousel if you want horizontal scroll.

Would you like me to generate this as a complete plugin zip for direct install?

User
when clicking add to cart then also show same meesage for adding to cart successfully and undo like default woo add to cart
Assistant
To show the **default WooCommerce "added to cart" message with undo** (like:

> “**Bosch Easy Sander 12** has been added to your cart. [Undo]”

) when clicking the **“Add to Cart”** button in your custom Elementor widget, you should use WooCommerce’s built-in `wc_add_to_cart_message()` mechanism **and** make sure the **button uses the standard WooCommerce structure**.

### ✅ **Solution Overview**

You need to:
1. Use a correct WooCommerce Add to Cart URL.
2. Let WooCommerce handle the redirection/message.
3. (Optional) Use AJAX add to cart if your theme supports it.

### ✅ Modify the “Add to Cart” Button in Widget Render Function

Replace this:

<a href="?add-to-cart=<?php echo esc_attr($product->get_id()); ?>" class="button">Add to Cart</a>

With this:

<a href="<?php echo esc_url( $product->add_to_cart_url() ); ?>" 
   data-quantity="1" 
   class="button add_to_cart_button ajax_add_to_cart" 
   data-product_id="<?php echo esc_attr( $product->get_id() ); ?>" 
   data-product_sku="<?php echo esc_attr( $product->get_sku() ); ?>" 
   aria-label="<?php echo esc_attr( $product->add_to_cart_description() ); ?>" 
   rel="nofollow">
   <?php echo esc_html( $product->add_to_cart_text() ); ?>
</a>

✅ **Explanation**:
– `add_to_cart_url()` generates the correct link with nonce.
– `ajax_add_to_cart` class triggers WooCommerce’s AJAX handler (if your theme supports it).
– WooCommerce will then automatically show the **“Product added”** message with **Undo**.

### 🧩 Ensure AJAX Add to Cart is Enabled

Make sure your theme supports WooCommerce AJAX add to cart. If you use Storefront or any standard Woo theme, it already does.

Otherwise, enqueue WooCommerce's `add-to-cart.js` manually:

function enable_ajax_add_to_cart_support() {
    wp_enqueue_script('wc-add-to-cart');
}
add_action('wp_enqueue_scripts', 'enable_ajax_add_to_cart_support');

### 🧪 Testing

After implementing:
1. Click “Add to Cart” → ✅ Product should be added with success message.
2. ✅ Message includes “Undo” link that removes product from cart.

Would you like me to provide a full updated version of the widget PHP file including this improvement?

User
also show rating
Assistant
Perfect — here's the **updated code** for your Elementor widget that:

1. ✅ Displays the 5 latest WooCommerce products
2. ✅ Shows **"Add to Cart"** button with correct AJAX + message + Undo
3. ✅ Displays **product rating stars** below the price

### ✅ **Final Widget Render Code** (replace your `render()` method with this)

public function render() {
    $args = [
        'post_type' => 'product',
        'posts_per_page' => 5,
        'orderby' => 'date',
        'order' => 'DESC'
    ];

    $query = new WP_Query($args);
    if ($query->have_posts()) {
        echo '<div class="latest-arrivals">';
        while ($query->have_posts()) : $query->the_post();
            global $product;
            ?>
            <div class="arrival-item">
                <div class="arrival-image">
                    <img src="<?php echo get_the_post_thumbnail_url(); ?>" alt="<?php the_title(); ?>">
                    <span class="badge">LATEST ARRIVAL</span>
                </div>
                <div class="arrival-content">
                    <h3><?php the_title(); ?></h3>
                    <p class="desc"><?php echo wc_trim_string(get_the_excerpt(), 20); ?></p>
                    <div class="price"><?php echo $product->get_price_html(); ?></div>

                    <!-- ⭐ RATING -->
                    <div class="rating">
                        <?php echo wc_get_rating_html($product->get_average_rating()); ?>
                    </div>

                    <!-- 🛒 Add to Cart Button with AJAX -->
                    <a href="<?php echo esc_url( $product->add_to_cart_url() ); ?>"
                       data-quantity="1"
                       class="button add_to_cart_button ajax_add_to_cart"
                       data-product_id="<?php echo esc_attr( $product->get_id() ); ?>"
                       data-product_sku="<?php echo esc_attr( $product->get_sku() ); ?>"
                       aria-label="<?php echo esc_attr( $product->add_to_cart_description() ); ?>"
                       rel="nofollow">
                        <?php echo esc_html( $product->add_to_cart_text() ); ?>
                    </a>

                    <a href="<?php echo get_permalink(); ?>" class="similar-button">Shop for Similar Products</a>
                </div>
            </div>
            <?php
        endwhile;
        echo '</div>';
        wp_reset_postdata();
    }
}

### 🧩 Add Rating Styles (CSS)

In your `latest-arrivals.css`:

.rating {
    margin: 10px 0;
}
.star-rating {
    color: #f5a623;
    font-size: 16px;
}

### ✅ Result:

– Looks like your original layout
– Shows star rating if available
– “Add to Cart” triggers WooCommerce’s native AJAX add-to-cart with message & Undo
– Fully dynamic

Let me know if you want this packed into a ready-to-install plugin ZIP or converted into an Elementor Pro dynamic loop instead.

User
deisgn it same design pattern and 1 product in one row
Assistant
Great — here’s the **updated Elementor widget design**:

– ✅ **One product per row**, matching your screenshot layout
– ✅ “Latest Arrival” badge
– ✅ Product image left, content right
– ✅ Title, short description, price, rating stars
– ✅ "Add to Cart" button (Woo default AJAX + message)
– ✅ "Shop for Similar Products" button

### ✅ Final Widget `render()` Code (Single Product per Row, Styled)

Replace your widget's `render()` method with:

public function render() {
    $args = [
        'post_type' => 'product',
        'posts_per_page' => 5,
        'orderby' => 'date',
        'order' => 'DESC'
    ];

    $query = new WP_Query($args);
    if ($query->have_posts()) {
        echo '<div class="latest-arrivals-wrapper">';
        while ($query->have_posts()) : $query->the_post();
            global $product;
            ?>
            <div class="latest-arrival-row">
                <div class="arrival-image">
                    <img src="<?php echo get_the_post_thumbnail_url(); ?>" alt="<?php the_title(); ?>">
                    <div class="arrival-badge">LATEST ARRIVAL</div>
                </div>

                <div class="arrival-content">
                    <h3 class="arrival-title"><?php the_title(); ?></h3>
                    <p class="arrival-subtitle"><?php echo $product->get_short_description(); ?></p>
                    <div class="arrival-price"><?php echo $product->get_price_html(); ?></div>
                    <div class="arrival-rating"><?php echo wc_get_rating_html($product->get_average_rating()); ?></div>

                    <div class="arrival-actions">
                        <a href="<?php echo esc_url( $product->add_to_cart_url() ); ?>"
                           data-quantity="1"
                           class="arrival-cart-btn button add_to_cart_button ajax_add_to_cart"
                           data-product_id="<?php echo esc_attr( $product->get_id() ); ?>"
                           data-product_sku="<?php echo esc_attr( $product->get_sku() ); ?>"
                           aria-label="<?php echo esc_attr( $product->add_to_cart_description() ); ?>"
                           rel="nofollow">
                           <?php echo esc_html( $product->add_to_cart_text() ); ?>
                        </a>

                        <a href="<?php echo get_permalink(); ?>" class="arrival-similar-btn">SHOP FOR SIMILAR PRODUCTS</a>
                    </div>
                </div>
            </div>
            <?php
        endwhile;
        echo '</div>';
        wp_reset_postdata();
    }
}

### 🎨 CSS for 1-Row Layout (in `/css/latest-arrivals.css`)

.latest-arrivals-wrapper {
    display: flex;
    flex-direction: column;
    gap: 40px;
}

.latest-arrival-row {
    display: flex;
    flex-wrap: wrap;
    border: 1px solid #ccc;
    padding: 25px;
    align-items: center;
    background: #fff;
    position: relative;
}

.arrival-image {
    flex: 0 0 250px;
    margin-right: 30px;
    position: relative;
}

.arrival-image img {
    width: 100%;
    height: auto;
    border-radius: 6px;
}

.arrival-badge {
    position: absolute;
    top: 10px;
    right: 10px;
    background: red;
    color: #fff;
    font-weight: bold;
    font-size: 12px;
    padding: 5px 12px;
    border-radius: 3px;
}

.arrival-content {
    flex: 1;
}

.arrival-title {
    font-weight: 700;
    font-size: 18px;
    margin-bottom: 5px;
}

.arrival-subtitle {
    font-size: 14px;
    color: #555;
    margin-bottom: 10px;
}

.arrival-price {
    font-size: 22px;
    font-weight: bold;
    margin-bottom: 10px;
    color: #000;
}

.arrival-rating {
    margin-bottom: 15px;
}

.arrival-actions {
    display: flex;
    gap: 15px;
    flex-wrap: wrap;
}

.arrival-cart-btn {
    background: #ff0000;
    color: #fff;
    padding: 10px 20px;
    font-weight: bold;
    border: none;
    border-radius: 4px;
    text-transform: uppercase;
    transition: background 0.3s;
}

.arrival-cart-btn:hover {
    background: #cc0000;
}

.arrival-similar-btn {
    background: #000;
    color: #fff;
    padding: 10px 20px;
    font-weight: bold;
    text-transform: uppercase;
    text-decoration: none;
    border-radius: 4px;
    transition: background 0.3s;
}

.arrival-similar-btn:hover {
    background: #222;
}

### ✅ Enqueue CSS in Your Plugin or Theme

function enqueue_latest_arrival_styles() {
    wp_enqueue_style('latest-arrival-style', get_stylesheet_directory_uri() . '/css/latest-arrivals.css');
}
add_action('wp_enqueue_scripts', 'enqueue_latest_arrival_styles');

Let me know if you'd like:
– 🎁 A ZIP file ready to install as a plugin
– 🧩 Conversion to an Elementor Loop Template (Pro)
– 🛍️ Add "Sale" tag or out-of-stock label

Happy to help polish this even further!

User
title ,price and add to cart in left (stacked)
image
then
right side
description,star and shop for similar product button
Assistant
Got it! You want the layout like this for **each product row**:

### ✅ **Product Row Layout (One per row):**

**Left Side (stacked vertically):**
– Product **Title**
– **Price**
– **Add to Cart** button

**Middle:**
– Product **Image**

**Right Side (stacked vertically):**
– **Description**
– **Star Rating**
– **"Shop for Similar Products"** button

### ✅ Updated `render()` Code

Replace your widget's `render()` function with this:

public function render() {
    $args = [
        'post_type' => 'product',
        'posts_per_page' => 5,
        'orderby' => 'date',
        'order' => 'DESC'
    ];

    $query = new WP_Query($args);
    if ($query->have_posts()) {
        echo '<div class="latest-arrivals-wrapper">';
        while ($query->have_posts()) : $query->the_post();
            global $product;
            ?>
            <div class="latest-arrival-row">
                <!-- LEFT: Title, Price, Add to Cart -->
                <div class="arrival-left">
                    <h3 class="arrival-title"><?php the_title(); ?></h3>
                    <div class="arrival-price"><?php echo $product->get_price_html(); ?></div>
                    <a href="<?php echo esc_url($product->add_to_cart_url()); ?>"
                       data-quantity="1"
                       class="arrival-cart-btn button add_to_cart_button ajax_add_to_cart"
                       data-product_id="<?php echo esc_attr($product->get_id()); ?>"
                       data-product_sku="<?php echo esc_attr($product->get_sku()); ?>"
                       aria-label="<?php echo esc_attr($product->add_to_cart_description()); ?>"
                       rel="nofollow">
                        <?php echo esc_html($product->add_to_cart_text()); ?>
                    </a>
                </div>

                <!-- MIDDLE: Product Image -->
                <div class="arrival-image">
                    <img src="<?php echo get_the_post_thumbnail_url(); ?>" alt="<?php the_title(); ?>">
                    <div class="arrival-badge">LATEST ARRIVAL</div>
                </div>

                <!-- RIGHT: Description, Rating, Similar Btn -->
                <div class="arrival-right">
                    <p class="arrival-subtitle"><?php echo $product->get_short_description(); ?></p>
                    <div class="arrival-rating"><?php echo wc_get_rating_html($product->get_average_rating()); ?></div>
                    <a href="<?php echo get_permalink(); ?>" class="arrival-similar-btn">SHOP FOR SIMILAR PRODUCTS</a>
                </div>
            </div>
            <?php
        endwhile;
        echo '</div>';
        wp_reset_postdata();
    }
}

### 🎨 Updated CSS (`latest-arrivals.css`)

.latest-arrivals-wrapper {
    display: flex;
    flex-direction: column;
    gap: 40px;
}

.latest-arrival-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 30px;
    padding: 25px;
    border: 1px solid #ccc;
    background: #fff;
    flex-wrap: wrap;
    position: relative;
}

/* LEFT COLUMN */
.arrival-left {
    flex: 0 0 200px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.arrival-title {
    font-size: 18px;
    font-weight: 700;
    margin: 0;
}
.arrival-price {
    font-size: 20px;
    font-weight: bold;
    color: #000;
}
.arrival-cart-btn {
    background: #ff0000;
    color: white;
    padding: 10px 20px;
    font-weight: bold;
    border-radius: 5px;
    text-transform: uppercase;
    text-align: center;
}

/* IMAGE COLUMN */
.arrival-image {
    flex: 0 0 200px;
    position: relative;
}
.arrival-image img {
    width: 100%;
    height: auto;
    border-radius: 6px;
}
.arrival-badge {
    position: absolute;
    top: 10px;
    right: 10px;
    background: red;
    color: #fff;
    font-weight: bold;
    font-size: 12px;
    padding: 5px 10px;
    border-radius: 3px;
}

/* RIGHT COLUMN */
.arrival-right {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.arrival-subtitle {
    font-size: 14px;
    color: #444;
}
.arrival-rating .star-rating {
    color: #f5a623;
}
.arrival-similar-btn {
    background: #000;
    color: #fff;
    padding: 10px 20px;
    font-weight: bold;
    text-transform: uppercase;
    border-radius: 4px;
    text-decoration: none;
    width: fit-content;
}

### ✅ Result

Each row will now display:

| Title  |
| Price  |         [Product Image]         | Description         |
| Add to Cart                             | ★★★★★               |
                                         | Shop for Similar Btn |

Would you like this bundled into a downloadable WordPress plugin zip file?

User
not working diplay excerpt
Assistant
You're right — “ often returns empty because WooCommerce doesn't always populate that value directly.

✅ Instead, use this fallback approach to **always show a trimmed excerpt** from the post content or excerpt field:

### ✅ Recommended Code for Description (Handles All Cases)

Replace:

<?php echo $product->get_short_description(); ?>

With:

<?php
$desc = get_the_excerpt();
if (empty($desc)) {
    $desc = wp_trim_words(strip_tags(get_the_content()), 20, '...');
}
echo esc_html($desc);
?>

### ✅ What It Does:
– Tries `get_the_excerpt()` first.
– If it's empty, it trims the post content to 20 words.
– Escapes it safely for output.

Now your updated **description block** in `arrival-right` will reliably show a short summary for each product.

Let me know if you want to:
– ✅ Add support for multilingual (e.g. WPML)
– ✅ Limit by characters instead of words
– ✅ Add a “Read More” link after the excerpt

Ready for any of those!

User
also justfiy sections to space between
Assistant
Perfect — to make your 3 sections (**Left: Title/Price/Cart**, **Middle: Image**, **Right: Description/Rating/Button**) align **evenly spaced with `space-between`**, we’ll tweak the `latest-arrival-row` style.

### ✅ Final CSS Update to Justify Sections

Update your `.latest-arrival-row` block to this:

.latest-arrival-row {
    display: flex;
    justify-content: space-between; /* this is the key */
    align-items: flex-start;
    gap: 30px;
    padding: 25px;
    border: 1px solid #ccc;
    background: #fff;
    flex-wrap: wrap;
    position: relative;
}

### 🔧 Additional Tweaks for Consistency

To ensure each section has the right sizing:

.arrival-left {
    flex: 0 0 220px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.arrival-image {
    flex: 0 0 200px;
    text-align: center;
}

.arrival-right {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 10px;
}

### ✅ Result

Your product row layout will now be neatly spaced out:

|  Title + Price + Cart  |  Product Image  |  Description + Rating + Button  |

All three sections will distribute across the full row with even space between them.

Let me know if you'd like:
– Items to align **vertically centered**
– Mobile responsive behavior (stacked layout)
– A plugin zip with all this built-in

Happy to bundle it!

User
get_short_description not working
get_description is working
Assistant
Thanks for confirming — you're absolutely right.

### ✅ Correct Usage in WooCommerce:

– `$product->get_short_description()` → may return nothing unless explicitly set in **Product > Short Description**
– `$product->get_description()` → returns the **main long description** (from the editor content)

### 🔄 Recommended Logic for Your Widget:

To ensure a fallback and still get a trimmed, presentable description:

<?php
$desc = $product->get_short_description();
if (empty($desc)) {
    $desc = wp_trim_words(strip_tags($product->get_description()), 20, '...');
}
echo esc_html($desc);
?>

### ✅ Why This Is Best:
– If Short Description is set → it shows that.
– Else → it trims the main product description (long content).
– Keeps output clean and safe.

Let me know if you want:
– A character limit instead of words
– HTML formatting allowed (like “ or “)
– Mobile styling adjustments

Happy to include!

Leave a Comment

Your email address will not be published. Required fields are marked *