You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建Magento 2自定义模块展示客户专属畅销品列表?

Got it, let's walk through building a custom Magento 2 module that shows a customer's personal bestsellers—sorted by how many times they've ordered each product. This is a great way to add personalization to your store, and I'll break it down into actionable, tested steps:

1. Create the Custom Module Skeleton

First up, let's set up the basic module structure. We'll name it Vendor_PersonalBestsellers (swap Vendor with your actual vendor name).

Create these core files:

app/code/Vendor/PersonalBestsellers/registration.php

<?php
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Vendor_PersonalBestsellers', __DIR__);

app/code/Vendor/PersonalBestsellers/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_PersonalBestsellers" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Customer"/>
            <module name="Magento_Sales"/>
        </sequence>
    </module>
</config>
2. Build a Helper to Fetch & Calculate Personal Bestsellers

We'll use a Helper class to handle the data logic: fetching the logged-in customer's orders, counting product occurrences, and sorting the list.

Create app/code/Vendor/PersonalBestsellers/Helper/Data.php:

<?php
namespace Vendor\PersonalBestsellers\Helper;

use Magento\Customer\Model\Session;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;

class Data
{
    protected $customerSession;
    protected $orderRepository;
    protected $searchCriteriaBuilder;
    protected $productRepository;

    public function __construct(
        Session $customerSession,
        OrderRepositoryInterface $orderRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        ProductRepositoryInterface $productRepository
    ) {
        $this->customerSession = $customerSession;
        $this->orderRepository = $orderRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->productRepository = $productRepository;
    }

    public function getPersonalBestsellers($limit = 5)
    {
        // Only proceed if customer is logged in
        if (!$this->customerSession->isLoggedIn()) {
            return [];
        }

        $customerId = $this->customerSession->getCustomerId();
        $productCounts = [];

        // Fetch all customer orders (uncomment the status filter to only count completed orders)
        $searchCriteria = $this->searchCriteriaBuilder
            ->addFilter('customer_id', $customerId)
            // ->addFilter('status', 'complete') 
            ->create();

        $orders = $this->orderRepository->getList($searchCriteria)->getItems();

        // Count product occurrences across all orders
        foreach ($orders as $order) {
            foreach ($order->getItems() as $item) {
                $productId = $item->getProductId();
                if (!isset($productCounts[$productId])) {
                    $productCounts[$productId] = 0;
                }
                $productCounts[$productId] += $item->getQtyOrdered();
            }
        }

        // Sort products by order count (descending)
        arsort($productCounts);

        // Fetch full product data and limit results
        $bestsellers = [];
        $count = 0;
        foreach ($productCounts as $productId => $qty) {
            if ($count >= $limit) break;
            try {
                $product = $this->productRepository->getById($productId);
                $bestsellers[] = [
                    'product' => $product,
                    'order_count' => $qty
                ];
                $count++;
            } catch (NoSuchEntityException $e) {
                // Skip if product no longer exists in the catalog
                continue;
            }
        }

        return $bestsellers;
    }
}
3. Create a Block to Expose Data to the Template

Next, we'll make a Block class that bridges our Helper and frontend template, so the template can easily access the sorted product list.

Create app/code/Vendor/PersonalBestsellers/Block/PersonalBestsellers.php:

<?php
namespace Vendor\PersonalBestsellers\Block;

use Magento\Framework\View\Element\Template;
use Vendor\PersonalBestsellers\Helper\Data as BestsellerHelper;

class PersonalBestsellers extends Template
{
    protected $bestsellerHelper;

    public function __construct(
        Template\Context $context,
        BestsellerHelper $bestsellerHelper,
        array $data = []
    ) {
        $this->bestsellerHelper = $bestsellerHelper;
        parent::__construct($context, $data);
    }

    public function getPersonalBestsellers($limit = 5)
    {
        return $this->bestsellerHelper->getPersonalBestsellers($limit);
    }

    public function getProductUrl($product)
    {
        return $this->getUrl('catalog/product/view', ['id' => $product->getId()]);
    }
}
4. Build the Frontend Template

Now let's create a template to display the list. It's kept simple here, but you can tweak the HTML/CSS to match your store's theme.

Create app/code/Vendor/PersonalBestsellers/view/frontend/templates/personal_bestsellers.phtml:

<?php
/** @var \Vendor\PersonalBestsellers\Block\PersonalBestsellers $block */
$bestsellers = $block->getPersonalBestsellers(5);
?>

<?php if (!empty($bestsellers)): ?>
<div class="personal-bestsellers">
    <h2><?= __('Your Top Ordered Products') ?></h2>
    <ul class="bestsellers-list">
        <?php foreach ($bestsellers as $item): ?>
        <?php $product = $item['product']; ?>
        <li class="bestseller-item">
            <a href="<?= $block->getProductUrl($product) ?>" title="<?= $product->getName() ?>">
                <img src="<?= $block->getImage($product, 'product_small_image')->getUrl() ?>" 
                     alt="<?= $product->getName() ?>" 
                     class="bestseller-image">
            </a>
            <div class="bestseller-info">
                <h3><a href="<?= $block->getProductUrl($product) ?>"><?= $product->getName() ?></a></h3>
                <p class="order-count"><?= __('Ordered %1 times', $item['order_count']) ?></p>
                <?php if ($product->getFinalPrice()): ?>
                <p class="price"><?= $block->getProductPrice($product) ?></p>
                <?php endif; ?>
            </div>
        </li>
        <?php endforeach; ?>
    </ul>
</div>
<?php else: ?>
<div class="personal-bestsellers">
    <p><?= __('You haven\'t placed any orders yet. Start shopping to see your top products!') ?></p>
</div>
<?php endif; ?>
5. Add the Block to a Frontend Page

Let's add this block to the customer's account dashboard so they see it right when they log in.

Create app/code/Vendor/PersonalBestsellers/view/frontend/layout/customer_account_index.xml:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Vendor\PersonalBestsellers\Block\PersonalBestsellers" 
                   name="personal.bestsellers" 
                   template="Vendor_PersonalBestsellers::personal_bestsellers.phtml"
                   before="-"/>
        </referenceContainer>
    </body>
</page>

Want to add this to the homepage? Use cms_index_index.xml instead and adjust the reference container as needed.

6. Test the Module

Now let's get the module up and running:

  • Run these commands in your Magento root directory:
    bin/magento module:enable Vendor_PersonalBestsellers
    bin/magento setup:upgrade
    bin/magento setup:di:compile
    bin/magento cache:clean
    
  • Log in as a customer with existing orders (or create a test customer and place a few orders for different products)
  • Head to the customer account dashboard—you'll see the "Your Top Ordered Products" list sorted by how many times each product was ordered.
Bonus Tips
  • Filter by Product Visibility: Add if ($product->isVisibleInCatalog()) in the Helper to hide products that are no longer available in the catalog.
  • Adjust Product Limit: Change the $limit parameter in getPersonalBestsellers() to show more or fewer items.
  • Custom Styling: Add CSS to your store's theme to style the .personal-bestsellers class and make it fit your design.

内容的提问来源于stack exchange,提问作者Shay Zalman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:18:51