Я пытаюсь добавить дерево категорий в пользовательское расширение, дерево категорий, которое находится на одной из вкладок страницы редактирования продукта.
Я пытаюсь добавить дерево категорий в пользовательское расширение, дерево категорий, которое находится на одной из вкладок страницы редактирования продукта.
Ответы:
Приготовься, это будет долго. Вот оно.
Вам понадобятся следующие файлы:
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'
));
В контроллере администратора вашей пользовательской сущности эти 2 действия, которые будут обрабатывать запросы по категориям:
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 .
По крайней мере, для Magento 1.9 вы должны быть уверены, что extJ загружен.
Используйте один из следующих методов, чтобы активировать использование extJS в бэкэнде:
В вашем контроллере используйте это:
$this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
В вашем макете XML используйте это:
<reference name="head">
<action method="setCanLoadExtJs">
<value>1</value>
</action>
</reference>