Próbuję dodać drzewo kategorii do niestandardowego rozszerzenia, drzewa kategorii, które znajduje się na jednej z kart strony edycji produktu
Próbuję dodać drzewo kategorii do niestandardowego rozszerzenia, drzewa kategorii, które znajduje się na jednej z kart strony edycji produktu
Odpowiedzi:
Przygotuj się, to będzie długie. Tutaj idzie.
Potrzebne będą następujące pliki:
app/code/local/[Namespace]/[Module]/Block/Adminhtml/[Entity]/Edit/Tab/Categories.php
- zakładka wyświetlająca kategorie.
<?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
- szablon potrzebny do renderowania kategorii
<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; ?>
W pliku formularza, do którego dodajesz zakładki jednostki niestandardowej, dodaj również:
$this->addTab('categories', array(
'label' => Mage::helper('[module]')->__('Associated categories'),
'url' => $this->getUrl('*/*/categories', array('_current' => true)),
'class' => 'ajax'
));
W kontrolerze administracyjnym jednostki niestandardowej te 2 działania, które obsłużą żądania dotyczące kategorii:
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'))
);
}
i upewnij się, że w tym samym kontrolerze istnieje ta metoda:
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];
}
W pliku układu administratora modułu dodaj ten uchwyt do akcji kategorii:
<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>
Teraz przejdźmy do zapisywania twoich danych.
W tym celu potrzebne będą następujące elementy w jednym ze skryptów instalacyjnych / aktualizacyjnych modułu. Spowoduje to utworzenie tabeli, w której będą przechowywane połączone wartości
$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);
Zadeklaruj swój stół. Dodaj to config.xml
wewnątrz <[module]_resource><entities>
tagu
<[entity]_category>
<table>[module]_[entity]_category</table>
</[entity]_category>
Będziesz potrzebował modelu do łączenia z kategoriami:
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;
}
}
i model zasobów 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;
}
}
oraz model zasobów kolekcji: 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;
}
}
Teraz w saveAction kontrolera administratora dodaj to prawo przed wywołaniem $[entity]->save()
$categories = $this->getRequest()->getPost('category_ids', -1);
if ($categories != -1) {
$categories = explode(',', $categories);
$categories = array_unique($categories);
$[entity]->setCategoriesData($categories);
}
W swoim modelu encji dodaj to na górze swojej klasy: protected $_categoryInstance = null;
i te metody w dowolnym miejscu:
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;
}
O to chodzi. Mam nadzieję, że niczego nie przegapiłem. Kod może wymagać pewnych zmian, ponieważ nie wiem, jak dokładnie zbudowany jest twój moduł, ale główne pomysły są tam. Po pewnym debugowaniu powinieneś go uruchomić.
Uwaga: powyższy kod został wygenerowany przy użyciu Ultimate Module Creator v1.9 .
Przynajmniej dla Magento 1.9 musisz mieć pewność, że extJs jest załadowany.
Użyj jednej z następujących metod, aby aktywować użycie extJS w wewnętrznej bazie danych:
W kontrolerze użyj tego:
$this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
W swoim układzie xml użyj tego:
<reference name="head">
<action method="setCanLoadExtJs">
<value>1</value>
</action>
</reference>