This is concept of show 12 bestsellers for last 3 month
You can change period and limits for your own
app/code/Acme/StackExchange/Block/Bestsellers.php
<?php
namespace Acme\StackExchange\Block;
use Magento\Catalog\Block\Product\AbstractProduct;
use Magento\Catalog\Block\Product\Context;
use Magento\Catalog\Model\Product\Visibility as ProductVisibility;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Locale\ResolverInterface;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Sales\Model\ResourceModel\Report\Bestsellers\CollectionFactory as BestsellersCollectionFactory;
/**
* @method int getLimit()
* @method $this setLimit(int $value)
*/
class Bestsellers extends AbstractProduct implements IdentityInterface
{
/**
* @var BestsellersCollectionFactory
*/
protected $reportCollectionFactory;
/**
* @var ProductCollectionFactory
*/
protected $productCollectionFactory;
/**
* @var Collection
*/
protected $collection;
/**
* @var TimezoneInterface
*/
protected $timezone;
/**
* @var string
*/
protected $locale;
/**
* @var ProductVisibility
*/
protected $productVisibility;
public function __construct(
Context $context,
BestsellersCollectionFactory $reportCollectionFactory,
ProductCollectionFactory $productCollectionFactory,
ProductVisibility $productVisibility,
TimezoneInterface $timezone,
ResolverInterface $localeResolver,
array $data = []
) {
$this->reportCollectionFactory = $reportCollectionFactory;
$this->productCollectionFactory = $productCollectionFactory;
$this->timezone = $timezone;
$this->locale = $localeResolver->getLocale();
$this->productVisibility = $productVisibility;
if (!isset($data['limit'])) {
$data['limit'] = 12;
}
parent::__construct($context, $data);
}
/**
* @return Collection
* @throws \Exception
*/
public function getProductCollection(): Collection
{
if ($this->collection === null) {
$from = $this->timezone->date(
new \DateTime('3 month ago', new \DateTimeZone('UTC')),
$this->locale
);
$reportCollection = $this->reportCollectionFactory->create()
->setDateRange($from)
// additional buffer of 10 products
->setPageSize($this->getLimit() + 10)
->load(false, true);
$productIds = [];
foreach ($reportCollection as $item) {
$productIds[] = $item->getData('product_id');
}
$this->collection = $this->productCollectionFactory->create();
$this->collection->addFieldToFilter('entity_id', ['in' => $productIds]);
if (count($productIds) > 0) {
$this->collection->getSelect()
->order(new \Zend_Db_Expr('FIELD(e.entity_id,' . implode(',', $productIds) . ')'));
}
$this->collection->setVisibility($this->productVisibility->getVisibleInCatalogIds());
$this->_addProductAttributesAndPrices($this->collection)
->addStoreFilter()
->setPageSize($this->getLimit());
$this->collection->load(false, true);
}
return $this->collection;
}
/**
* @inheritDoc
*/
public function getIdentities(): array
{
$identities = [];
foreach ($this->getProductCollection() as $product) {
$identities[] = $product->getIdentities();
}
return $identities;
}
}
app/code/Acme/StackExchange/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="Acme\StackExchange\Block\Bestsellers" name="product-bestsellers"
template="Acme_StackExchange::bestsellers.phtml">
<arguments>
<argument name="view_model" xsi:type="object">Magento\Catalog\ViewModel\Product\Listing\PreparePostData</argument>
</arguments>
</block>
</referenceContainer>
</body>
</page>
app/code/Acme/StackExchange/view/frontend/templates/bestsellers.phtml
<?php
/**
* @var \Acme\StackExchange\Block\Bestsellers $block
* @var \Magento\Framework\View\TemplateEngine\Php $this
* @var \Magento\Framework\Escaper $escaper
* @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
*/
use Magento\Catalog\Block\Product\ReviewRendererInterface;
use Magento\Catalog\ViewModel\Product\Listing\PreparePostData;
use Magento\Framework\App\ActionInterface;
$type = 'bestsellers';
$class = 'widget' . ' ' . $type;
$image = 'new_products_content_widget_grid';
$title = __('Bestsellers');
$items = $block->getProductCollection();
$showAddTo = true;
$showCart = true;
$templateType = ReviewRendererInterface::SHORT_VIEW;
$canItemsAddToCart = false;
?>
<?php if ($items->getSize()): ?>
<div class="block <?= $escaper->escapeHtmlAttr($class) ?>">
<div class="block-title title">
<strong id="block-<?= $escaper->escapeHtmlAttr($class) ?>-heading" role="heading"
aria-level="2"><?= $block->escapeHtml($title) ?></strong>
</div>
<div class="block-content content" aria-labelledby="block-<?= $escaper->escapeHtmlAttr($class) ?>-heading">
<div class="products wrapper grid products-grid products-<?= $escaper->escapeHtmlAttr($type) ?>">
<ol class="products list items product-items">
<?php foreach ($items as $_item): ?>
<li class="item product product-item">
<div class="product-item-info">
<?= /* @noEscape */
'<!-- ' . $image . '-->' ?>
<a href="<?= $escaper->escapeUrl($block->getProductUrl($_item)) ?>"
class="product photo product-item-photo">
<?= $block->getImage($_item, $image)->toHtml() ?>
</a>
<div class="product details product-item-details">
<strong class="product name product-item-name"><a
class="product-item-link"
title="<?= $escaper->escapeHtmlAttr($_item->getName()) ?>"
href="<?= $escaper->escapeUrl($block->getProductUrl($_item)) ?>">
<?= $block->escapeHtml($_item->getName()) ?></a>
</strong>
<?= /* @noEscape */
$block->getProductPrice($_item) ?>
<?php if ($templateType): ?>
<?= $block->getReviewsSummaryHtml($_item, $templateType) ?>
<?php endif; ?>
<?php if ($showAddTo || $showCart): ?>
<?php // phpcs:disable ?>
<div class="product actions product-item-actions">
<?php if ($showCart): ?>
<div class="actions-primary">
<?php if ($_item->isSaleable()): ?>
<?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item)): ?>
<button
class="action tocart primary"
data-mage-init='{"redirectUrl": {"url": "<?= $escaper->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}'
type="button"
title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>">
<span><?= $block->escapeHtml(__('Add to Cart')) ?></span>
</button>
<?php else : ?>
<?php
/** @var $viewModel PreparePostData */
$viewModel = $block->getViewModel();
$postArray = $viewModel->getPostData(
$escaper->escapeUrl($block->getAddToCartUrl($_item)),
['product' => $_item->getEntityId()]
);
$value = $postArray['data'][ActionInterface::PARAM_NAME_URL_ENCODED];
?>
<form data-role="tocart-form"
data-product-sku="<?= $escaper->escapeHtmlAttr($_item->getSku()) ?>"
action="<?= $escaper->escapeUrl($block->getAddToCartUrl($_item)) ?>"
method="post">
<input type="hidden" name="product"
value="<?= /* @noEscape */
(int)$_item->getEntityId() ?>">
<input type="hidden"
name="<?= /* @noEscape */
ActionInterface::PARAM_NAME_URL_ENCODED ?>"
value="<?= /* @noEscape */
$value ?>">
<?= $block->getBlockHtml('formkey') ?>
<button type="submit"
title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>"
class="action tocart primary">
<span><?= $block->escapeHtml(__('Add to Cart')) ?></span>
</button>
</form>
<?php endif; ?>
<?php else: ?>
<?php if ($_item->isAvailable()): ?>
<div class="stock available">
<span><?= $block->escapeHtml(__('In stock')) ?></span>
</div>
<?php else: ?>
<div class="stock unavailable">
<span><?= $block->escapeHtml(__('Out of stock')) ?></span>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($showAddTo): ?>
<div class="secondary-addto-links actions-secondary"
data-role="add-to-links">
<?php if ($addToBlock = $block->getChildBlock('addto')): ?>
<?= $addToBlock->setProduct($_item)->getChildHtml() ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php // phpcs:enable ?>
<?php endif; ?>
</div>
</div>
</li>
<?php endforeach ?>
</ol>
</div>
</div>
</div>
<?php if (!$block->isRedirectToCartEnabled() && $_item): ?>
<script type="text/x-magento-init">
{
"[data-role=tocart-form], .form.map.checkout": {
"catalogAddToCart": {
"product_sku": "<?= $block->escapeJs($_item->getSku()) ?>"
}
}
}
</script>
<?php endif; ?>
<?php endif; ?>