如何在禁用ObjectManager实例的合规前提下获取分类名称、ID及详情
Great question! Ditching direct ObjectManager calls is exactly how you should build Magento 2 code—it keeps things testable, maintainable, and aligned with the framework's core principles. Let’s break down the proper ways to fetch category data (name, ID, and more) without touching ObjectManager::getInstance().
Magento 2 is built around dependency injection, so injecting the right category-related interface or class into your constructor is the cleanest method. We’ll use interfaces where possible (following the dependency inversion principle) to make your code more flexible.
Scenario 1: Fetch a Single Category (e.g., in a Block or Controller)
Use Magento\Catalog\Api\CategoryRepositoryInterface to fetch a category by ID, or get the current category on a category page.
Example in a Block:
<?php namespace Your\CustomModule\Block; use Magento\Framework\View\Element\Template; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Registry; class CategoryDetails extends Template { protected $categoryRepository; protected $coreRegistry; // Inject dependencies via constructor public function __construct( Template\Context $context, CategoryRepositoryInterface $categoryRepository, Registry $coreRegistry, array $data = [] ) { $this->categoryRepository = $categoryRepository; $this->coreRegistry = $coreRegistry; parent::__construct($context, $data); } // Get category data by ID public function getCategoryById(int $categoryId): ?array { try { $category = $this->categoryRepository->get($categoryId); return [ 'id' => $category->getId(), 'name' => $category->getName(), 'url_key' => $category->getUrlKey(), 'description' => $category->getDescription(), 'is_active' => $category->isActive(), 'parent_id' => $category->getParentId() ]; } catch (NoSuchEntityException $e) { // Handle missing category (log it, return null, etc.) return null; } } // Get the current category (if on a category listing page) public function getCurrentCategory(): ?array { $currentCategory = $this->coreRegistry->registry('current_category'); if (!$currentCategory) { return null; } return $this->getCategoryById($currentCategory->getId()); } }
Example in a Controller:
<?php namespace Your\CustomModule\Controller\Category; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Controller\Result\JsonFactory; class GetData extends Action { protected $categoryRepository; protected $resultJsonFactory; public function __construct( Context $context, CategoryRepositoryInterface $categoryRepository, JsonFactory $resultJsonFactory ) { $this->categoryRepository = $categoryRepository; $this->resultJsonFactory = $resultJsonFactory; parent::__construct($context); } public function execute() { $categoryId = $this->getRequest()->getParam('category_id'); $result = $this->resultJsonFactory->create(); if (!$categoryId) { return $result->setData(['error' => 'Category ID parameter is missing']); } try { $category = $this->categoryRepository->get($categoryId); return $result->setData([ 'id' => $category->getId(), 'name' => $category->getName(), 'level' => $category->getLevel(), 'url_path' => $category->getUrlPath() ]); } catch (NoSuchEntityException $e) { return $result->setData(['error' => 'Category not found']); } } }
Scenario 2: Fetch Multiple Categories (Category Collection)
If you need a list of categories (e.g., all active top-level categories), use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory to build a collection.
Example in a Model/Helper:
<?php namespace Your\CustomModule\Model; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; class CategoryList { protected $categoryCollectionFactory; public function __construct(CollectionFactory $categoryCollectionFactory) { $this->categoryCollectionFactory = $categoryCollectionFactory; } // Get all active top-level categories public function getActiveTopLevelCategories(): array { $collection = $this->categoryCollectionFactory->create() ->addAttributeToSelect(['name', 'id', 'url_key']) ->addAttributeToFilter('is_active', 1) ->addAttributeToFilter('level', 2) // Top-level categories are level 2 (root is level 1) ->setOrder('position', 'ASC'); $categoryList = []; foreach ($collection as $category) { $categoryList[] = [ 'id' => $category->getId(), 'name' => $category->getName(), 'url_key' => $category->getUrlKey() ]; } return $categoryList; } }
ObjectManager::getInstance() Is Critical Just to reinforce why this rule exists (since you mentioned it’s widely accepted):
- Testability: Direct ObjectManager calls make unit testing impossible—you can’t mock dependencies to isolate your code.
- Maintainability: It bypasses Magento’s DI configuration, so other developers can’t easily track where dependencies are used or modify them via di.xml.
- Compatibility: Magento’s DI container handles proxies, caching, and class instantiation rules. Skipping it can lead to unexpected behavior (e.g., duplicate instances, missing lazy loading).
内容的提问来源于stack exchange,提问作者John

