Commit ec1b86e9 by halweg

feat: joshine category 提示徽章模块

parent 7dbd6356
......@@ -9,7 +9,6 @@ static
errors
media
opt
code
/app/etc/env.php
generated
var
......
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Joshine\Category\Block\Html;
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\Data\Tree\Node\Collection;
use Magento\Theme\Block\Html\Topmenu as JoshineMenu;
/**
* Html page top menu block
*
* @api
* @since 100.0.2
*/
class Topmenu extends JoshineMenu
{
/**
* Remove children from collection when the parent is not active
*
* @param Collection $children
* @param int $childLevel
* @return void
*/
private function removeChildrenWithoutActiveParent(Collection $children, int $childLevel): void
{
/** @var Node $child */
foreach ($children as $child) {
if ($childLevel === 0 && $child->getData('is_parent_active') === false) {
$children->delete($child);
}
}
}
/**
* Retrieve child level based on parent level
*
* @param int $parentLevel
*
* @return int
*/
private function getChildLevel($parentLevel): int
{
return $parentLevel === null ? 0 : $parentLevel + 1;
}
/**
* Check if new column should be added.
*
* @param array $colBrakes
* @param int $counter
* @return bool
*/
private function shouldAddNewColumn(array $colBrakes, int $counter): bool
{
return count($colBrakes) && $colBrakes[$counter]['colbrake'];
}
protected function _getHtml(
Node $menuTree,
$childrenWrapClass,
$limit,
array $colBrakes = []
) {
$html = '';
$children = $menuTree->getChildren();
$childLevel = $this->getChildLevel($menuTree->getLevel());
$this->removeChildrenWithoutActiveParent($children, $childLevel);
$counter = 1;
$childrenCount = $children->count();
$parentPositionClass = $menuTree->getPositionClass();
$itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';
/** @var Node $child */
foreach ($children as $child) {
$child->setLevel($childLevel);
$child->setIsFirst($counter === 1);
$child->setIsLast($counter === $childrenCount);
$child->setPositionClass($itemPositionClassPrefix . $counter);
$outermostClassCode = '';
$outermostClass = $menuTree->getOutermostClass();
if ($childLevel === 0 && $outermostClass) {
$outermostClassCode = ' class="' . $outermostClass . '" ';
$this->setCurrentClass($child, $outermostClass);
}
if ($this->shouldAddNewColumn($colBrakes, $counter)) {
$html .= '</ul></li><li class="column"><ul>';
}
$html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
$html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml(
$child->getName()
) . '</span>'.$this->_addBadge($child).'</a>' . $this->_addSubMenu(
$child,
$childLevel,
$childrenWrapClass,
$limit
) . '</li>';
$counter++;
}
if (is_array($colBrakes) && !empty($colBrakes) && $limit) {
$html = '<li class="column"><ul>' . $html . '</ul></li>';
}
return $html;
}
private function _addBadge($child )
{
$badge = $child->getBadge();
if (!$badge || !json_decode($badge)) {
return "";
}
$json = json_decode($badge, true);
$class = $json['class'];
$text = $json['text'];
return "<span class='{$class}'>".$this->escapeHtml($text) .'</span>';
}
}
<?php
namespace Joshine\Category\Model\Category\Attribute\Source;
use function Joshine\Model\Category\Attribute\Source\count;
class Badge extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
{
protected $_optionsData;
/**
* Constructor
*
* @param array $options
*/
public function __construct(array $options = [])
{
$this->_optionsData = $options;
}
/**
* getAllOptions
*
* @return array
*/
public function getAllOptions()
{
if (!$this->_optionsData) {
$this->_optionsData = [
['value' => '', 'label' => '----'],
['value' => json_encode(['text'=>'HOT!', 'class'=>'ui-menu-icon-3 ui-icon-3']), 'label' => 'Hot'],
['value' => json_encode(['text'=>'NEW!', 'class'=>'ui-menu-icon-1 ui-icon-1']), 'label' => 'New'],
['value' => json_encode(['text'=>'SALE!','class'=>'ui-menu-icon-2 ui-icon-2']), 'label' => 'Sale'],
];
}
return $this->_optionsData;
}
}
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Joshine\Category\Plugin\Block;
use Magento\Catalog\Plugin\Block\Topmenu as Menu;
use Magento\Framework\Data\Collection;
use Magento\Framework\Data\Tree\Node;
/**
* Plugin for top menu block
*/
class Topmenu extends Menu
{
/**
* Catalog category
*
* @var \Magento\Catalog\Helper\Category
*/
protected $catalogCategory;
/**
* @var \Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory
*/
private $collectionFactory;
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
private $storeManager;
/**
* @var \Magento\Catalog\Model\Layer\Resolver
*/
private $layerResolver;
/**
* Initialize dependencies.
*
* @param \Magento\Catalog\Helper\Category $catalogCategory
* @param \Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory $categoryCollectionFactory
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Catalog\Model\Layer\Resolver $layerResolver
*/
public function __construct(
\Magento\Catalog\Helper\Category $catalogCategory,
\Magento\Catalog\Model\ResourceModel\Category\StateDependentCollectionFactory $categoryCollectionFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Catalog\Model\Layer\Resolver $layerResolver
) {
parent::__construct($catalogCategory, $categoryCollectionFactory, $storeManager, $layerResolver);
$this->collectionFactory = $categoryCollectionFactory;
$this->storeManager = $storeManager;
$this->layerResolver = $layerResolver;
}
/**
* Build category tree for menu block.
*
* @param \Magento\Theme\Block\Html\Topmenu $subject
* @param string $outermostClass
* @param string $childrenWrapClass
* @param int $limit
* @return void
* @SuppressWarnings("PMD.UnusedFormalParameter")
*/
public function beforeGetHtml(
\Magento\Theme\Block\Html\Topmenu $subject,
$outermostClass = '',
$childrenWrapClass = '',
$limit = 0
) {
$rootId = $this->storeManager->getStore()->getRootCategoryId();
$storeId = $this->storeManager->getStore()->getId();
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
$collection = $this->getCategoryTree($storeId, $rootId);
$currentCategory = $this->getCurrentCategory();
$mapping = [$rootId => $subject->getMenu()]; // use nodes stack to avoid recursion
foreach ($collection as $category) {
$categoryParentId = $category->getParentId();
if (!isset($mapping[$categoryParentId])) {
$parentIds = $category->getParentIds();
foreach ($parentIds as $parentId) {
if (isset($mapping[$parentId])) {
$categoryParentId = $parentId;
}
}
}
/** @var Node $parentCategoryNode */
$parentCategoryNode = $mapping[$categoryParentId];
$categoryNode = new Node(
$this->getCategoryAsArray(
$category,
$currentCategory,
$category->getParentId() == $categoryParentId
),
'id',
$parentCategoryNode->getTree(),
$parentCategoryNode
);
$parentCategoryNode->addChild($categoryNode);
$mapping[$category->getId()] = $categoryNode; //add node in stack
}
}
/**
* Get current Category from catalog layer
*
* @return \Magento\Catalog\Model\Category
*/
private function getCurrentCategory()
{
$catalogLayer = $this->layerResolver->get();
if (!$catalogLayer) {
return null;
}
return $catalogLayer->getCurrentCategory();
}
/**
* Convert category to array
*
* @param \Magento\Catalog\Model\Category $category
* @param \Magento\Catalog\Model\Category $currentCategory
* @param bool $isParentActive
* @return array
*/
private function getCategoryAsArray($category, $currentCategory, $isParentActive)
{
$categoryId = $category->getId();
return [
'name' => $category->getName(),
'id' => 'category-node-' . $categoryId,
'url' => $this->catalogCategory->getCategoryUrl($category),
'badge' => $category->getData('badge'),
'has_active' => in_array((string)$categoryId, explode('/', (string)$currentCategory->getPath()), true),
'is_active' => $categoryId == $currentCategory->getId(),
'is_category' => true,
'is_parent_active' => $isParentActive
];
}
/**
* Get Category Tree
*
* @param int $storeId
* @param int $rootId
* @return \Magento\Catalog\Model\ResourceModel\Category\Collection
* @throws \Magento\Framework\Exception\LocalizedException
*/
protected function getCategoryTree($storeId, $rootId)
{
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
$collection = $this->collectionFactory->create();
$collection->setStoreId($storeId);
$collection->addAttributeToSelect('name');
$collection->addAttributeToSelect('badge');
$collection->addFieldToFilter('path', ['like' => '1/' . $rootId . '/%']); //load only from store root
$collection->addAttributeToFilter('include_in_menu', 1);
$collection->addIsActiveFilter();
$collection->addNavigationMaxDepthFilter();
$collection->addUrlRewriteToResult();
$collection->addOrder('level', Collection::SORT_ORDER_ASC);
$collection->addOrder('position', Collection::SORT_ORDER_ASC);
$collection->addOrder('parent_id', Collection::SORT_ORDER_ASC);
$collection->addOrder('entity_id', Collection::SORT_ORDER_ASC);
return $collection;
}
}
<?php
namespace Joshine\Category\Setup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class InstallData implements InstallDataInterface
{
private $eavSetupFactory;
public function __construct(EavSetupFactory $eavSetupFactory)
{
$this->eavSetupFactory = $eavSetupFactory;
}
public function install(
ModuleDataSetupInterface $setup,
ModuleContextInterface $context
)
{
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$eavSetup->addAttribute(
\Magento\Catalog\Model\Category::ENTITY,
'badge',
[
'type' => 'varchar',
'label' => 'badge',
'input' => 'select',
'sort_order' => 100,
'source' => '\Joshine\Category\Model\Category\Attribute\Source\Badge',
'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
'visible_on_front' => true,
'required' => false,
'user_defined' => false,
'default' => null,
'group' => '',
'backend' => ''
]
);
}
}
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Plugin\Block\Topmenu" type="Joshine\Category\Plugin\Block\Topmenu" />
<preference for="Magento\Theme\Block\Html\Topmenu" type="Joshine\Category\Block\Html\Topmenu" />
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Joshine_Category" setup_version="1.0.0"/>
</config>
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Joshine_Category',
__DIR__
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment