2

i want to display subcategories in the root category, i found this way that works for me.

app/design/frontend/[Vendor]/[theme]/Magento_Catalog/templates/category/products.phtml

<?php $category = $block->getCurrentCategory(); ?>
<?php $subcategories = $category->getChildrenCategories(); ?>
<?php $_helper = $this->helper('Magento\Catalog\Helper\Output'); ?>
<?php $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); ?>
<?php if(count($subcategories) > 0): ?>
    <div class="products wrapper grid products-grid">
        <ol class="products list items product-items">
            <?php foreach($category->getChildrenCategories() as $subcategory): ?>
                <?php $subcategory = $objectManager->create('Magento\Catalog\Model\Category')->load($subcategory->getId()); ?>
                <li class="item product product-item">
                    <div class="product-item-info">
                        <?php if ($_imgUrl = $subcategory->getImageUrl()): ?>
                            <a href="<?= $subcategory->getUrl() ?>" class="product photo product-item-photo">
                                <span class="product-image-container">
                                    <?php $_imgHtml = '<img src="' . $_imgUrl . '" />'; ?>
                                    <?php echo $_imgHtml = $_helper->categoryAttribute($subcategory, $_imgHtml, 'image'); ?>
                                </span>
                            </a>
                        <?php endif; ?>
                        <div class="product details product-item-details">
                            <strong class="product name product-item-name">
                                <a class="product-item-link" href="<?= $subcategory->getUrl() ?>"><?= $subcategory->getName() ?></a>
                            </strong>
                        </div>
                    </div>
                </li>
            <?php endforeach; ?>
        </ol>
    </div>
<?php else: ?>
    <?php if (!$block->isContentMode() || $block->isMixedMode()): ?>
        <?= $block->getProductListHtml() ?>
    <?php endif; ?>
<?php endif; ?>

But this method alarms me with the fact that the objectManager is called to get the data, it is just as bad that this logic is used in the template.

Help implement this method through a block or View Model approach.

Still hope for help

1

ViewModel example

Create module

app/code/Vendor/Module/etc/module.xm

app/code/Vendor/Module/registration.php

Add to

app/design/frontend/Theme/theme/Magento_Catalog/layout/catalog_category_view.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>
        <referenceBlock name="category.products">
            <arguments>
                <argument name="view_model" xsi:type="object">Vendor\Module\ViewModel\SubCats</argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Create Class SubCats in your module

<?php
declare(strict_types=1);

namespace Vendor\Module\ViewModel;

use \Magento\Catalog\Model\CategoryRepository;

class SubCats extends \Magento\Framework\View\Element\Template implements \Magento\Framework\View\Element\Block\ArgumentInterface
{
    public $storeManager;

    public $viewAssetRepo;

    public  $coreRegistry;

    public $categoryFactory;

    public $catalogHelperOutput;

    public function __construct(
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\View\Asset\Repository $viewAssetRepo,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\Catalog\Model\CategoryFactory $categoryFactory,
        \Magento\Catalog\Helper\Output $catalogHelperOutput,
        \Magento\Framework\View\Element\Template\Context $context,
        array $data = []
    ) {
        $this->storeManager        = $storeManager;
        $this->viewAssetRepo       = $viewAssetRepo;
        $this->coreRegistry        = $coreRegistry;
        $this->categoryFactory     = $categoryFactory;
        $this->catalogHelperOutput = $catalogHelperOutput;
        parent::__construct($context, $data);
    }

    public function getCategories()
    {

        $category = $this->getCurrentCategory();
        if(!$category) return;

        $categoryId = $category->getId();

        $sortAttribute = $this->getSortAttribute();
        $model = $this->categoryFactory->create();
        $categories = $model->getCollection()
            ->addAttributeToSelect(['name', 'url_key', 'url_path', 'image','description'])
            // ->addAttributeToFilter('include_in_menu', 1)
            ->addAttributeToFilter('parent_id', $categoryId)
            ->addAttributeToSort($sortAttribute)
            ->addIsActiveFilter();

        return $categories;
    }

    public function getDescription($category)
    {
        $description = $category->getDescription();
        if ($description) {
            $categoryDescription = $this->catalogHelperOutput
                ->categoryAttribute($category, $description, 'description');
        } else {
            $categoryDescription = '';
        }
        return trim($categoryDescription);
    }

    public function getImage($category)
    {
        $placeholderImageUrl = $this->viewAssetRepo->getUrl(
            'Magento_Catalog::images/product/placeholder/small_image.jpg'
        );
        $image = $category->getImage();
        if ($image != null) {
            $url = $this->getImageUrl($image);
        } else {
            $url = $placeholderImageUrl;
        }
        return $url;
    }

    public function getImageUrl($image)
    {
        $url = false;
        if ($image) {
            if (substr($image, 0, 1) === '/') {
                $url = $this->storeManager->getStore()->getBaseUrl(
                        \Magento\Framework\UrlInterface::URL_TYPE_WEB
                    ) . ltrim($image, '/');
            } else {
                $url = $this->storeManager->getStore()->getBaseUrl(
                        \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
                    ) . 'catalog/category/' . $image;
            }
        }
        return $url;
    }

    public function getCurrentCategory()
    {
        return $this->coreRegistry->registry('current_category');
    }
}

Your template in theme

app/design/frontend/Theme/theme/Magento_Catalog/templates/category/products.phtml

<?php
/** @var Vendor\Module\ViewModel\SubCats $viewmodel */
$viewmodel = $block->getViewModel();
?>

<?php if ($viewmodel->getCategories() && count($viewmodel->getCategories()) > 0): ?>
        <div class="products wrapper grid products-grid">
            <ol class="products list items product-items">
                <?php foreach($viewmodel->getCategories() as $subcategory): ?>
                    <li class="item product product-item">
                        <div class="product-item-info">
                            <?php if ($_imgUrl = $viewmodel->getImage($subcategory)): ?>
                                <a href="<?= $subcategory->getUrl() ?>" class="product photo product-item-photo">
                                <span class="product-image-container">
                                    <?php $_imgHtml = '<img src="' . $_imgUrl . '" />'; ?>
                                    <?php echo $_imgHtml = $viewmodel->catalogHelperOutput->categoryAttribute($subcategory, $_imgHtml, 'image'); ?>
                                </span>
                                </a>
                            <?php endif; ?>
                            <div class="product details product-item-details">
                                <strong class="product name product-item-name">
                                    <a class="product-item-link" href="<?= $subcategory->getUrl() ?>"><?= $subcategory->getName() ?></a>
                                </strong>
                            </div>
                        </div>
                    </li>
                <?php endforeach; ?>
            </ol>
        </div>
<?php else: ?>
    <?php if (!$block->isContentMode() || $block->isMixedMode()): ?>
        <?= $block->getProductListHtml() ?>
    <?php endif; ?>
<?php endif; ?>
|improve this answer|||||
0

Try this code ( you can alter as per your need ):

in layout file use this block class ( Magento\LayeredNavigation\Block\Navigation\Category ):

<block class="Magento\LayeredNavigation\Block\Navigation\Category" name="category.left.navigation" template="Magento_Catalog::category/navigation_subcategory.phtml" />

in template file :

<div class="category-sldebar">
<?php

if($this->getLayer()->getCurrentCategory()):
    $subcategories=$this->getLayer()->getCurrentCategory()->getCategories($this->getLayer()->getCurrentCategory()->getId());
    /* count and check the category have any sub category */
    if($subcategories->count()>0){
        ?>
<h2>Categories</h2>
<ul class="list-category">
<?php
        foreach($subcategories as $subcategory){
            echo '<li class="parent-category"><a href="'.$this->getBaseUrl().$subcategory->getRequestPath().'"><strong class="title">' . $subcategory->getName() . '</strong></a>';
            if($subcategory->getChildrenCount() > 0)
            {
                $subcategories2=$this->getLayer()->getCurrentCategory()->getCategories($subcategory->getId());
                echo "<ul class='sub-menu'>";
                foreach ($subcategories2 as $subsub) {
                    if ($subsub->getIsActive()) {
                        echo '<li><a href="'.$this->getBaseUrl().$subsub->getRequestPath().'">' . $subsub->getName() . '</a></li>';
                    }
                }
                echo "</ul>";
            }
            echo '</li>';
        }

    }else{
        // category does not have any sub category
    }
else:
    // category does not have any sub category
echo "</ul>";
endif;
?>
</div>
|improve this answer|||||
  • But, it doesn’t take logic out of the template into the block – ann karsksya Dec 9 '19 at 8:47
0

Instead of overwriting or extending the block, you can use a view model instead.

https://www.yireo.com/blog/2017-08-12-viewmodels-in-magento-2


The following is just some rough code that should point you in the right direction.

You create an XML file {{theme_dir}}/Magento_Catalog/view/frontend/layout/catalog_category_view.xml to inject the view model from

<referenceBlock name="category.products">
    <arguments>
        <argument name="view_model" xsi:type="object">Vendor\Module\ViewModel\SubCats</argument>
     </arguments>
</referenceBlock>

Now you can create your view model under app/code/Vendor/Module/ViewModel but with the code from your template abstracted into it.

<?php

namespace Vendor\Module\ViewModel;

use \Magento\Framework\View\Element\Block\ArgumentInterface;
use \Magento\Catalog\Model\CategoryRepository;

class SubCats implements ArgumentInterface
{
    protected $_catRepo;    

    public function __construct (CategoryRepository $catRepo)
    {
        $this->_catRepo = $catRepo;
    }

    public function getCatById($id)
    {
        return $this->_catRepo->get($id);
    }

}

Then in your template file, you can now access these methods like so.

<?php 
$viewmodel = $block->getViewModel();
$category = $block->getCurrentCategory();
$subcategories = $category->getChildrenCategories();

if(count($subcategories) > 0) {
  foreach($subcategories as $cat) {
    $cat = $viewmodel->getCatById($cat->getId());
    ... Do some more stuff and that
  }
}

?>


<?php if (!$block->isContentMode() || $block->isMixedMode()) :?>
    <?= $block->getProductListHtml() ?>
<?php endif; ?>
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.