사용자 정의 확장에 카테고리 트리 추가


답변:


27

자신을 준비하십시오, 이것은 긴 것이 될 것입니다. 간다
다음 파일이 필요합니다.

app/code/local/[Namespace]/[Module]/Block/Adminhtml/[Entity]/Edit/Tab/Categories.php -카테고리를 렌더링 할 탭

<?php
class [Namespace]_[Module]_Block_Adminhtml_[Entity]_Edit_Tab_Categories
    extends Mage_Adminhtml_Block_Catalog_Category_Tree {
    protected $_categoryIds = null;
    protected $_selectedNodes = null;
    public function __construct() {
        parent::__construct();
        $this->setTemplate('[namespace]_module/[entity]/edit/tab/categories.phtml');
        $this->_withProductCount = false;
    }
    public function get[Entity](){
        return Mage::registry('current_[entity]'); //use other registration key if you have one
    }

    public function getCategoryIds(){
        if (is_null($this->_categoryIds)){
            $categories = $this->get[Entity]()->getSelectedCategories();
                $ids = array();
                foreach ($categories as $category){
                    $ids[] = $category->getId();
                }
                $this->_categoryIds = $ids;
        }
        return $this->_categoryIds;
    }
    public function getIdsString(){
        return implode(',', $this->getCategoryIds());
    }
    public function getRootNode(){
        $root = $this->getRoot();
        if ($root && in_array($root->getId(), $this->getCategoryIds())) {
            $root->setChecked(true);
        }
        return $root;
    }

    public function getRoot($parentNodeCategory = null, $recursionLevel = 3){
        if (!is_null($parentNodeCategory) && $parentNodeCategory->getId()) {
            return $this->getNode($parentNodeCategory, $recursionLevel);
        }
        $root = Mage::registry('category_root');
        if (is_null($root)) {
            $rootId = Mage_Catalog_Model_Category::TREE_ROOT_ID;
            $ids = $this->getSelectedCategoryPathIds($rootId);
            $tree = Mage::getResourceSingleton('catalog/category_tree')
                ->loadByIds($ids, false, false);
            if ($this->getCategory()) {
                $tree->loadEnsuredNodes($this->getCategory(), $tree->getNodeById($rootId));
            }
            $tree->addCollectionData($this->getCategoryCollection());
            $root = $tree->getNodeById($rootId);
            Mage::register('category_root', $root);
        }
        return $root;
    }
    protected function _getNodeJson($node, $level = 1){
        $item = parent::_getNodeJson($node, $level);
        if ($this->_isParentSelectedCategory($node)) {
            $item['expanded'] = true;
        }
        if (in_array($node->getId(), $this->getCategoryIds())) {
            $item['checked'] = true;
        }
        return $item;
    }
    protected function _isParentSelectedCategory($node){
        $result = false;
        // Contains string with all category IDs of children (not exactly direct) of the node
        $allChildren = $node->getAllChildren();
        if ($allChildren) {
            $selectedCategoryIds = $this->getCategoryIds();
            $allChildrenArr = explode(',', $allChildren);
            for ($i = 0, $cnt = count($selectedCategoryIds); $i < $cnt; $i++) {
                $isSelf = $node->getId() == $selectedCategoryIds[$i];
                if (!$isSelf && in_array($selectedCategoryIds[$i], $allChildrenArr)) {
                    $result = true;
                    break;
                }
            }
        }
        return $result;
    }
    protected function _getSelectedNodes(){
        if ($this->_selectedNodes === null) {
            $this->_selectedNodes = array();
            $root = $this->getRoot();
            foreach ($this->getCategoryIds() as $categoryId) {
                if ($root) {
                    $this->_selectedNodes[] = $root->getTree()->getNodeById($categoryId);
                }
            }
        }
        return $this->_selectedNodes;
    }

    public function getCategoryChildrenJson($categoryId){
        $category = Mage::getModel('catalog/category')->load($categoryId);
        $node = $this->getRoot($category, 1)->getTree()->getNodeById($categoryId);
        if (!$node || !$node->hasChildren()) {
            return '[]';
        }
        $children = array();
        foreach ($node->getChildren() as $child) {
            $children[] = $this->_getNodeJson($child);
        }
        return Mage::helper('core')->jsonEncode($children);
    }
    public function getLoadTreeUrl($expanded = null){
        return $this->getUrl('*/*/categoriesJson', array('_current' => true));
    }
    public function getSelectedCategoryPathIds($rootId = false){
        $ids = array();
        $categoryIds = $this->getCategoryIds();
        if (empty($categoryIds)) {
            return array();
        }
        $collection = Mage::getResourceModel('catalog/category_collection');
        if ($rootId) {
            $collection->addFieldToFilter('parent_id', $rootId);
        }
        else {
            $collection->addFieldToFilter('entity_id', array('in'=>$categoryIds));
        }

        foreach ($collection as $item) {
            if ($rootId && !in_array($rootId, $item->getPathIds())) {
                continue;
            }
            foreach ($item->getPathIds() as $id) {
                if (!in_array($id, $ids)) {
                    $ids[] = $id;
                }
            }
        }
        return $ids;
    }
}

app/design/adminhtml/default/default/[namespace]_[module]/[entity]/tab/edit/categories.phtml -카테고리를 렌더링하는 데 필요한 템플릿

<div class="entry-edit">
    <div class="entry-edit-head">
        <h4 class="icon-head head-edit-form fieldset-legend">
            <?php echo Mage::helper('[module]')->__('Categories') ?>
        </h4>
    </div>
    <fieldset id="grop_fields">
        <input type="hidden" name="category_ids" id="[entity]_categories" value="<?php echo $this->getIdsString() ?>">
        <div id="[entity]-categories" class="tree"></div>
    </fieldset>
</div>
<?php if($this->getRootNode() && $this->getRootNode()->hasChildren()): ?>
<script type="text/javascript">
    Ext.EventManager.onDocumentReady(function() {
        var categoryLoader = new Ext.tree.TreeLoader({
           dataUrl: '<?php echo $this->getLoadTreeUrl()?>'
        });
        categoryLoader.createNode = function(config) {
            config.uiProvider = Ext.tree.CheckboxNodeUI;
            var node;
            if (config.children && !config.children.length) {
                delete(config.children);
                node = new Ext.tree.AsyncTreeNode(config);
            }
            else {
                node = new Ext.tree.TreeNode(config);
            }
            return node;
        };
        categoryLoader.on("beforeload", function(treeLoader, node) {
            treeLoader.baseParams.category = node.attributes.id;
        });

        categoryLoader.on("load", function(treeLoader, node, config) {
            varienWindowOnload();
        });
        var tree = new Ext.tree.TreePanel('[entity]-categories', {
            animate:true,
            loader: categoryLoader,
            enableDD:false,
            containerScroll: true,
            rootUIProvider: Ext.tree.CheckboxNodeUI,
            selModel: new Ext.tree.CheckNodeMultiSelectionModel(),
            rootVisible: '<?php echo $this->getRootNode()->getIsVisible() ?>'
        });
        tree.on('check', function(node) {
            if(node.attributes.checked) {
                categoryAdd(node.id);
            } else {
                categoryRemove(node.id);
            }
            varienElementMethods.setHasChanges(node.getUI().checkbox);
        }, tree);
        var root = new Ext.tree.TreeNode({
            text: '<?php echo $this->jsQuoteEscape($this->getRootNode()->getName()) ?>',
            draggable:false,
            checked:'<?php echo $this->getRootNode()->getChecked() ?>',
            id:'<?php echo $this->getRootNode()->getId() ?>',
            disabled: <?php echo ($this->getRootNode()->getDisabled() ? 'true' : 'false') ?>,
            uiProvider: Ext.tree.CheckboxNodeUI
        });
        tree.setRootNode(root);
        bildCategoryTree(root, <?php echo $this->getTreeJson() ?>);
        tree.addListener('click', categoryClick.createDelegate(this));
        tree.render();
        root.expand();
    });
    function bildCategoryTree(parent, config){
        if (!config) {
            return null;
        }
        if (parent && config && config.length){
            for (var i = 0; i < config.length; i++){
                config[i].uiProvider = Ext.tree.CheckboxNodeUI;
                var node;
                var _node = Object.clone(config[i]);
                if (_node.children && !_node.children.length) {
                    delete(_node.children);
                    node = new Ext.tree.AsyncTreeNode(_node);

                }
                else {
                    node = new Ext.tree.TreeNode(config[i]);
                }
                parent.appendChild(node);
                node.loader = node.getOwnerTree().loader;
                if(config[i].children){
                    bildCategoryTree(node, config[i].children);
                }
            }
        }
    }
    function categoryClick(node, e){
        if (node.disabled) {
            return;
        }
        node.getUI().check(!node.getUI().checked());
        varienElementMethods.setHasChanges(Event.element(e), e);
    };
    function categoryAdd(id) {
        var ids = $('[entity]_categories').value.split(',');
        ids.push(id);
        $('[entity]_categories').value = ids.join(',');
    }
    function categoryRemove(id) {
        var ids = $('[entity]_categories').value.split(',');
        while (-1 != ids.indexOf(id)) {
            ids.splice(ids.indexOf(id), 1);
        }
        $('[entity]_categories').value = ids.join(',');
    }
</script>
<?php endif; ?>

사용자 지정 엔터티의 탭을 추가하는 양식 파일에서 다음을 추가하십시오.

    $this->addTab('categories', array(
        'label' => Mage::helper('[module]')->__('Associated categories'),
        'url'   => $this->getUrl('*/*/categories', array('_current' => true)),
        'class'    => 'ajax'
    ));

사용자 지정 엔터티의 관리자 컨트롤러에서 범주에 대한 요청을 처리하는이 두 가지 작업 :

public function categoriesAction(){
    $this->_init[Entity]();
    $this->loadLayout();
    $this->renderLayout();
}
public function categoriesJsonAction(){
    $this->_init[Entity]();
    $this->getResponse()->setBody(
        $this->getLayout()->createBlock('[module]/adminhtml_[entity]_edit_tab_categories')
            ->getCategoryChildrenJson($this->getRequest()->getParam('category'))
    );
}

동일한 컨트롤러 에이 방법이 있는지 확인하십시오.

protected function _init[Entity](){
    $[entity]Id  = (int) $this->getRequest()->getParam('id');
    $[enity]    = Mage::getModel('[module]/[entity]');

    if ($[entity]Id) {
        $[entity]->load($[entity]Id);
    }
    Mage::register('current_[entity]', $[entity]);
    return $[entity];
}

모듈의 관리자 레이아웃 파일에서 카테고리 조치에이 핸들을 추가하십시오.

<adminhtml_[module]_[entity]_categories>
    <block type="core/text_list" name="root" output="toHtml">
        <block type="[module]/adminhtml_[entity]_edit_tab_categories" name="[entity].edit.tab.categories"/>
    </block>
</adminhtml_[module]_[entity]_categories>

이제 데이터 저장을 진행하겠습니다.
이를 위해 모듈의 설치 / 업그레이드 스크립트 중 하나에 다음이 필요합니다. 연결된 값이 저장 될 테이블이 생성됩니다

$table = $this->getConnection()
    ->newTable($this->getTable('[module]/[entity]_category'))
    ->addColumn('rel_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
        'unsigned'  => true,
        'identity'  => true,
        'nullable'  => false,
        'primary'   => true,
        ), 'Relation ID')
    ->addColumn('[entity]_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
        'unsigned'  => true,
        'nullable'  => false,
        'default'   => '0',
    ), '[Entity] ID')
    ->addColumn('category_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
        'unsigned'  => true,
        'nullable'  => false,
        'default'   => '0',
    ), 'Category ID')
    ->addColumn('position', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
        'nullable'  => false,
        'default'   => '0',
    ), 'Position')
    ->addIndex($this->getIdxName('[module]/[entity]_category', array('category_id')), array('category_id'))
    ->addForeignKey($this->getFkName('[module]/[entity]_category', '[entity]_id', '[module]/[entity]', 'entity_id'), '[entity]_id', $this->getTable('[module]/[entity]'), 'entity_id', Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
    ->addForeignKey($this->getFkName('[module]/[entity]_category', 'category_id', 'catalog/category', 'entity_id'),    'category_id', $this->getTable('catalog/category'), 'entity_id', Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
    ->addIndex(
    $this->getIdxName(
        '[module]/[entity]_category',
        array('[entity]_id', 'category_id'),
        Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE
    ),
    array('[entity]_id', 'category_id'),
    array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE))
    ->setComment('[Entity] to Category Linkage Table');
$this->getConnection()->createTable($table);

테이블을 선언하십시오. 태그 config.xml안에 이것을 추가하십시오<[module]_resource><entities>

<[entity]_category>
    <table>[module]_[entity]_category</table>
</[entity]_category>

카테고리에 연결하려면 모델이 필요합니다.
app/code/local/[Namespace]/[Module]/Model/[Entity]/Category.php

<?php

class [Namespace]_[Module]_Model_[Entity]_Category
    extends Mage_Core_Model_Abstract {
    protected function _construct(){
        $this->_init('[module]/[entity]_category');
    }
    public function save[Entity]Relation($[entity]){
        $data = $[entity]->getCategoriesData();
        if (!is_null($data)) {
            $this->_getResource()->save[Entity]Relation($[entity], $data);
        }
        return $this;
    }
    public function getCategoryCollection($[entity]){
        $collection = Mage::getResourceModel('[module]/[entity]_category_collection')
            ->add[Entity]Filter($[entity]);
        return $collection;
    }
}

그리고 자원 모델 app/code/local/[Namespace]/[Module]/Model/Resource/[Entity]/Category.php:

<?php

class [Namespace]_[Module]_Model_Resource_[Entity]_Category
    extends Mage_Core_Model_Resource_Db_Abstract {

    protected function  _construct(){
        $this->_init('[module]/[entity]_category', 'rel_id');
    }
    public function save[Entity]Relation($[entity], $data){
        if (!is_array($data)) {
            $data = array();
        }
        $deleteCondition = $this->_getWriteAdapter()->quoteInto('[entity]_id=?', $[entity]->getId());
        $this->_getWriteAdapter()->delete($this->getMainTable(), $deleteCondition);

        foreach ($data as $categoryId) {
            if (!empty($categoryId)){
                $this->_getWriteAdapter()->insert($this->getMainTable(), array(
                    '[entity]_id'      => $[entity]->getId(),
                    'category_id'     => $categoryId,
                    'position'      => 1
                ));
            }
        }
        return $this;
    }
}

및 콜렉션 자원 모델 : app/code/local/[Namespace]/[Module]/Model/Resource/[Entity]/Category/Collection.php

<?php
class [Namespace]_[Module]_Model_Resource_[Entity]_Category_Collection
    extends Mage_Catalog_Model_Resource_Category_Collection{
    protected $_joinedFields = false;
    public function joinFields(){
        if (!$this->_joinedFields){
            $this->getSelect()->join(
                array('related' => $this->getTable('[module]/[entity]_category')),
                'related.category_id = main_table.entity_id',
                array('position')
            );
            $this->_joinedFields = true;
        }
        return $this;
    }
    public function add[Entity]Filter($[entity]){
        if ($[entity] instanceof [Namespace]_[Module]_Model_[Entity]){
            $[entity] = $[entity]->getId();
        }
        if (!$this->_joinedFields){
            $this->joinFields();
        }
        $this->getSelect()->where('related.[entity]_id = ?', $[entity]);
        return $this;
    }
}

이제 관리자 컨트롤러의 saveAction에서 전화하기 전에 이것을 추가하십시오. $[entity]->save()

$categories = $this->getRequest()->getPost('category_ids', -1);
if ($categories != -1) {
    $categories = explode(',', $categories);
    $categories = array_unique($categories);
    $[entity]->setCategoriesData($categories);
}

엔티티 모델에서 클래스 맨 위에 이것을 추가하십시오 protected $_categoryInstance = null;.

protected function _afterSave() {
    $this->getCategoryInstance()->save[Entity]Relation($this);
    return parent::_afterSave();
}
public function getCategoryInstance(){
    if (!$this->_categoryInstance) {
        $this->_categoryInstance = Mage::getSingleton('[module]/[entity]_category');
    }
    return $this->_categoryInstance;
}
public function getSelectedCategories(){
    if (!$this->hasSelectedCategories()) {
        $categories = array();
        foreach ($this->getSelectedCategoriesCollection() as $category) {
            $categories[] = $category;
        }
        $this->setSelectedCategories($categories);
    }
    return $this->getData('selected_categories');
}
public function getSelectedCategoriesCollection(){
    $collection = $this->getCategoryInstance()->getCategoryCollection($this);
    return $collection;
}

그게 다야. 나는 아무것도 놓치지 않기를 바랍니다. 모듈이 정확히 어떻게 작성되었는지 모르지만 주요 아이디어가 있기 때문에 코드에 약간의 변경이 필요할 수 있습니다. 약간의 디버깅으로 작동하도록해야합니다.


참고 : 위의 코드는 Ultimate Module Creator v1.9를 사용하여 생성 되었습니다 .


멋진 게시물! 후속 조치를 위해서는 약간의 기술이 필요하지만, 이런 종류의 답변을 찾고 있다면 충분해야합니다. 어딘가에 오타가 있습니다 (죄송합니다) 감사합니다 Marius
Giuseppe

Magento 카테고리 트리 API를 사용하여 모듈의 데이터를 가져 와서보기에 통과하지 않은 이유는 무엇입니까? 아주 쉽게 연주 할 수 있습니다.
Sourabh Modi 2016 년

So 카테고리를 제품 추가 / 편집 양식에서 제품과 연관시키는 데 사용되는 것과 동일한 방식으로 카테고리 연결을 다시 작성했습니다. 일관성을 유지하는 것에 대해 생각했습니다.
Marius

데이터를 category_ids라는 모델 필드에 저장했습니다.
주세페

1
@Marius 나는 당신의 요점을 얻었지만 문제는 당신이 Magento의 API를 호출하면 측면에서 코드보다 훨씬 적을 것이고 내부에서 Magento는 제품 추가 / 편집 양식에서 카테고리를 연결하기 위해 수행 한 것처럼 API가 호출 될 때 내부적으로 동일하게 수행됩니다 . 따라서 제품 추가 / 편집 양식과 동일한 필수 기능을 내부적으로 사용하는 함수 (API 호출은 함수 호출)를 호출하여 동일한 기능을 사용할 수 있으므로이 코드를 사용자가 다시 작성하지 않아도됩니다.
Sourabh Modi 2016 년

2

적어도 Magento 1.9의 경우 extJ가로드되어 있는지 확인해야합니다.
다음 방법 중 하나를 사용하여 백엔드에서 extJS 사용을 활성화하십시오.

  1. 컨트롤러에서 이것을 사용하십시오 :

    $this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
  2. 레이아웃 xml에서 이것을 사용하십시오 :

    <reference name="head">
        <action method="setCanLoadExtJs">
            <value>1</value>
        </action>
    </reference>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.