I have created something similar using following code.
app/code/Anshu/Bestsellerwidget/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Anshu_Bestsellerwidget',
__DIR__
);
app/code/Anshu/Bestsellerwidget/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="Anshu_Bestsellerwidget" setup_version="1.0.0">
</module>
</config>
app/code/Anshu/Bestsellerwidget/etc/widget.xml
<?xml version="1.0" encoding="UTF-8"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
<widget id="anshu_bestsellerwidget" class="Anshu\Bestsellerwidget\Block\Product\Widget\BestsellerWidget" ttl="86400">
<label translate="true">Bestseller Products List</label>
<description translate="true">List of Best Selling Products</description>
<parameters>
<parameter name="productcount" xsi:type="text" visible="true" sort_order="1" >
<label translate="true">Product Count</label>
</parameter>
<parameter name="imagewidth" xsi:type="text" visible="true" sort_order="2" >
<label translate="true">Image Width</label>
</parameter>
<parameter name="imageheight" xsi:type="text" visible="true" sort_order="3" >
<label translate="true">Image Height</label>
</parameter>
<parameter name="cache_lifetime" xsi:type="text" visible="true">
<label translate="true">Cache Lifetime (Seconds)</label>
<description translate="true">86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache.</description>
</parameter>
</parameters>
</widget>
</widgets>
app/code/Anshu/Bestsellerwidget/Block/Product/Widget/BestsellerWidget.php
<?php
namespace Anshu\Bestsellerwidget\Block\Product\Widget;
/**
* Best Seller products widget
*/
class BestsellerWidget extends \Magento\Framework\View\Element\Template implements \Magento\Widget\Block\BlockInterface
{
protected $_template = 'bestsellerproduct.phtml';
/**
* Default value for products count that will be shown
*/
const DEFAULT_PRODUCTS_COUNT = 10;
const DEFAULT_IMAGE_WIDTH = 150;
const DEFAULT_IMAGE_HEIGHT = 150;
/**
* Products count
*
* @var int
*/
protected $_productsCount;
/**
* @var \Magento\Framework\App\Http\Context
*/
protected $httpContext;
protected $_resourceFactory;
/**
* Catalog product visibility
*
* @var \Magento\Catalog\Model\Product\Visibility
*/
protected $_catalogProductVisibility;
/**
* Product collection factory
*
* @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
*/
protected $_productCollectionFactory;
/**
* Image helper
*
* @var Magento\Catalog\Helper\Image
*/
protected $_imageHelper;
/**
* @var \Magento\Checkout\Helper\Cart
*/
protected $_cartHelper;
/**
* @param Context $context
* @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
* @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility
* @param \Magento\Framework\App\Http\Context $httpContext
* @param array $data
*/
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
\Magento\Reports\Model\ResourceModel\Report\Collection\Factory $resourceFactory,
\Magento\Reports\Model\Grouped\CollectionFactory $collectionFactory,
\Magento\Reports\Helper\Data $reportsData,
array $data = []
) {
$this->_resourceFactory = $resourceFactory;
$this->_collectionFactory = $collectionFactory;
$this->_reportsData = $reportsData;
$this->_imageHelper = $context->getImageHelper();
$this->_cartHelper = $context->getCartHelper();
parent::__construct($context, $data);
}
/**
* Image helper Object
*/
public function imageHelperObj(){
return $this->_imageHelper;
}
/**
* get featured product collection
*/
public function getBestsellerProduct(){
$limit = $this->getProductLimit();
$resourceCollection = $this->_resourceFactory->create('Magento\Sales\Model\ResourceModel\Report\Bestsellers\Collection');
$resourceCollection->setPageSize($limit);
return $resourceCollection;
}
/**
* Get the configured limit of products
* @return int
*/
public function getProductLimit() {
if($this->getData('productcount')==''){
return DEFAULT_PRODUCTS_COUNT;
}
return $this->getData('productcount');
}
/**
* Get the widht of product image
* @return int
*/
public function getProductimagewidth() {
if($this->getData('imagewidth')==''){
return DEFAULT_IMAGE_WIDTH;
}
return $this->getData('imagewidth');
}
/**
* Get the height of product image
* @return int
*/
public function getProductimageheight() {
if($this->getData('imageheight')==''){
return DEFAULT_IMAGE_HEIGHT;
}
return $this->getData('imageheight');
}
/**
* Get the add to cart url
* @return string
*/
public function getAddToCartUrl($product, $additional = [])
{
return $this->_cartHelper->getAddUrl($product, $additional);
}
/**
* Return HTML block with price
*
* @param \Magento\Catalog\Model\Product $product
* @param string $priceType
* @param string $renderZone
* @param array $arguments
* @return string
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function getProductPriceHtml(
\Magento\Catalog\Model\Product $product,
$priceType = null,
$renderZone = \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST,
array $arguments = []
) {
if (!isset($arguments['zone'])) {
$arguments['zone'] = $renderZone;
}
$arguments['zone'] = isset($arguments['zone'])
? $arguments['zone']
: $renderZone;
$arguments['price_id'] = isset($arguments['price_id'])
? $arguments['price_id']
: 'old-price-' . $product->getId() . '-' . $priceType;
$arguments['include_container'] = isset($arguments['include_container'])
? $arguments['include_container']
: true;
$arguments['display_minimal_price'] = isset($arguments['display_minimal_price'])
? $arguments['display_minimal_price']
: true;
/** @var \Magento\Framework\Pricing\Render $priceRender */
$priceRender = $this->getLayout()->getBlock('product.price.render.default');
$price = '';
if ($priceRender) {
$price = $priceRender->render(
\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE,
$product,
$arguments
);
}
return $price;
}
}
app/code/Anshu/Bestsellerwidget/view/frontend/templates/bestsellerproduct.phtml
<?php
if ($exist = ($this->getBestsellerProduct() && $this->getBestsellerProduct()->getPageSize())) {
$fet_prodcollection = $this->getBestsellerProduct();
$productcount = $this->getProductLimit();
$imagewidth = $this->getProductimagewidth();
$imageheight = $this->getProductimageheight();
$mode = 'grid';
$title = __('Bestseller Products');
$type = 'widget-bestseller-grid';
$image = 'bestseller_products_content_widget_grid';
}
?>
<?php if ($exist) { ?>
<div class="block widget block-bestseller-products <?php /* @escapeNotVerified */ echo $mode; ?>">
<div class="block-title">
<strong role="heading" aria-level="2"><?php /* @escapeNotVerified */ echo $title; ?></strong>
</div>
<div class="block-content">
<?php /* @escapeNotVerified */ echo '<!-- ' . $image . '-->' ?>
<div class="products-<?php /* @escapeNotVerified */ echo $mode; ?> <?php /* @escapeNotVerified */ echo $mode; ?>">
<ol class="product-items <?php /* @escapeNotVerified */ echo $type; ?>">
<?php $iterator = 1; ?>
<?php foreach ($fet_prodcollection as $item):
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$_product = $objectManager->get('Magento\Catalog\Model\Product')->load($item->getProductId());
?>
<?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="product-item">' : '</li><li class="product-item">' ?>
<div class="product-item-info">
<?php
$image_url = $block->imageHelperObj()->init($_product, 'product_page_image_small')
->setImageFile($_product->getFile())
->resize($imagewidth,$imageheight)
->getUrl();
?>
<a href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>" class="product-item-photo">
<img src="<?php echo $image_url;?>" alt="<?php echo $this->escapeHtml($_product->getName()) ?>" />
</a>
<div class="product-item-details">
<strong class="product-item-name">
<a title="<?php echo $block->escapeHtml($_product->getName()) ?>"
href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>"
class="product-item-link">
<?php echo $block->escapeHtml($_product->getName()) ?>
</a>
</strong>
<?php
echo $this->getProductPriceHtml($_product, $type);
?>
<div class="product-item-actions">
<div class="actions-primary">
<?php if ($_product->isSaleable()): ?>
<?php if ($_product->getTypeInstance()->hasRequiredOptions($_product)): ?>
<button class="action tocart primary"
data-mage-init='{"redirectUrl":{"url":"<?php /* @escapeNotVerified */ echo $block->getAddToCartUrl($_product) ?>"}}'
type="button" title="<?php /* @escapeNotVerified */ echo __('Add to Cart') ?>">
<span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>
</button>
<?php else: ?>
<?php
$postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper');
$postData = $postDataHelper->getPostData($block->getAddToCartUrl($_product), ['product' => $_product->getId()]);
?>
<button class="action tocart primary"
data-post='<?php /* @escapeNotVerified */ echo $postData; ?>'
type="button" title="<?php /* @escapeNotVerified */ echo __('Add to Cart') ?>">
<span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>
</button>
<?php endif; ?>
<?php else: ?>
<?php if ($_product->getIsSalable()): ?>
<div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div>
<?php else: ?>
<div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php echo($iterator == count($fet_prodcollection)+1) ? '</li>' : '' ?>
<?php endforeach ?>
</ol>
</div>
<?php } ?>
I have taken reference from https://github.com/emizentech/magento2-best-seller
I hope this will be helpful.