ofbiz学习——分类

这篇文章介绍主页中左边的分类树是如何实现的。


1. 首先打开主页,https://localhost:8443/ecommerce/control/main  

2.找到 上面分类树界面对应的html源码。可以看到开始是以  component://ecommerce/widget/CatalogScreens.xml#productCategories 开始的。


3.打开component://ecommerce/widget/CatalogScreens.xml#productCategories 

    <screen name="productCategories">
        <section>
            <actions>
                <set field="tabButtonItem" value="LookupProductCategories"/>
                
                <script location="component://ecommerce/groovyScripts/catalog/ProductCategories.groovy"/>
            </actions>
            <widgets>
                <platform-specific><html><html-template location="component://ecommerce/template/catalog/ProductCategories.ftl"/></html></platform-specific>
            </widgets>
        </section>
    </screen>


打开component://ecommerce/groovyScripts/catalog/ProductCategories.groovy

/*
 * This script is also referenced by the ecommerce's screens and
 * should not contain order component's specific code.
 */
import org.apache.ofbiz.entity.util.EntityUtil

import org.apache.ofbiz.base.util.*
import org.apache.ofbiz.product.catalog.*
import org.apache.ofbiz.product.category.*
import org.apache.ofbiz.entity.*

List fillTree(rootCat ,CatLvl, parentCategoryId) {
    if(rootCat) {
        rootCat.sort{ it.productCategoryId }
        def listTree = []
        for(root in rootCat) {
            preCatChilds = from("ProductCategoryRollup").where("parentProductCategoryId", root.productCategoryId).queryList()
            catChilds = EntityUtil.getRelated("CurrentProductCategory",null,preCatChilds,false)
            def childList = []
            
            // CatLvl uses for identify the Category level for display different css class
            if(catChilds) {
                if(CatLvl==2)
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId.replaceAll("/", "")+'/'+root.productCategoryId)
                    // replaceAll and '/' uses for fix bug in the breadcrum for href of category
                else if(CatLvl==1)
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId.replaceAll("/", "")+root.productCategoryId)
                else
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId+'/'+root.productCategoryId)
            }
            
            productsInCat  = from("ProductCategoryAndMember").where("productCategoryId", root.productCategoryId).queryList()
            
            // Display the category if this category containing products or contain the category that's containing products
            if(productsInCat || childList) {
                def rootMap = [:]
                category = from("ProductCategory").where("productCategoryId", root.productCategoryId).queryOne()
                categoryContentWrapper = new CategoryContentWrapper(category, request)
                context.title = categoryContentWrapper.get("CATEGORY_NAME", "html")
                categoryDescription = categoryContentWrapper.get("DESCRIPTION", "html")
                
                if(categoryContentWrapper.get("CATEGORY_NAME", "html").toString())
                    rootMap["categoryName"] = categoryContentWrapper.get("CATEGORY_NAME", "html")
                else
                    rootMap["categoryName"] = root.categoryName
                
                if(categoryContentWrapper.get("DESCRIPTION", "html").toString())
                    rootMap["categoryDescription"] = categoryContentWrapper.get("DESCRIPTION", "html")
                else
                    rootMap["categoryDescription"] = root.description
                
                rootMap["productCategoryId"] = root.productCategoryId
                rootMap["parentCategoryId"] = parentCategoryId
                rootMap["child"] = childList

                listTree.add(rootMap)
            }
        }
        return listTree
    }
}

CategoryWorker.getRelatedCategories(request, "topLevelList", CatalogWorker.getCatalogTopCategoryId(request, CatalogWorker.getCurrentCatalogId(request)), true)
curCategoryId = parameters.category_id ?: parameters.CATEGORY_ID ?: ""
request.setAttribute("curCategoryId", curCategoryId)
CategoryWorker.setTrail(request, curCategoryId)

categoryList = request.getAttribute("topLevelList")
if (categoryList) {
    catContentWrappers = [:]
    CategoryWorker.getCategoryContentWrappers(catContentWrappers, categoryList, request)
    context.catContentWrappers = catContentWrappers
    completedTree = fillTree(categoryList, 1, "")
    context.completedTree = completedTree
}


打开component://ecommerce/template/catalog/ProductCategories.ftl
<script language="javascript" type="text/javascript"
        src="<@ofbizContentUrl>/images/jquery/plugins/jsTree/jquery.jstree.js</@ofbizContentUrl>"></script>
<script type="text/javascript"
        src="<@ofbizContentUrl>/images/jquery/ui/js/jquery.cookie-1.4.0.js</@ofbizContentUrl>"></script>

<script type="text/javascript">
  <#-- some labels are not unescaped in the JSON object so we have to do this manuely -->
  function unescapeHtmlText(text) {
    return jQuery('<div />').html(text).text()
  }

  jQuery(window).load(createTree());

  <#-- creating the JSON Data -->
  var rawdata = [
  <#if (requestAttributes.topLevelList)??>
    <#assign topLevelList = requestAttributes.topLevelList>
  </#if>
  <#if (topLevelList?has_content)>
    <@fillTree rootCat=completedTree/>
  </#if>
  <#macro fillTree rootCat>
    <#if (rootCat?has_content)>
      <#list rootCat?sort_by("productCategoryId") as root>
      {
        "data": {
          "title": unescapeHtmlText(
              "<#if root.categoryName??>${root.categoryName?js_string}<#elseif root.categoryDescription??>${root.categoryDescription?js_string}<#else>${root.productCategoryId?js_string}</#if>"),
          "attr": {
            "href": "javascript: void(0);",
            "onClick": "callDocument('${root.productCategoryId}', '${root.parentCategoryId}')",
            "class": "${root.cssClass!}"
          }
        },
        "attr": {"id": "${root.productCategoryId}"}
        <#if root.child?has_content>
          , "children": [
          <@fillTree rootCat=root.child/>]
        </#if>
        <#if root_has_next>
        },
        <#else>
        }
        </#if>
      </#list>
    </#if>
  </#macro>
  ]
  ;

  <#-------------------------------------------------------------------------------------define Requests-->
  var editDocumentTreeUrl = '<@ofbizUrl>/views/EditDocumentTree</@ofbizUrl>';
  var listDocument = '<@ofbizUrl>/views/ListDocument</@ofbizUrl>';
  var editDocumentUrl = '<@ofbizUrl>/views/EditDocument</@ofbizUrl>';
  var deleteDocumentUrl = '<@ofbizUrl>removeDocumentFromTree</@ofbizUrl>';

  <#-------------------------------------------------------------------------------------create Tree-->
  function createTree() {
    jQuery(function () {
      jQuery("#tree").jstree({
        "themes": {
          "theme": "classic",
          "icons": false
        },
        "cookies": {
          "cookie_options": {path: '/'}
        },
        "plugins": ["themes", "json_data", "cookies"],
        "json_data": {
          "data": rawdata
        }
      });
    });
  }

  <#-------------------------------------------------------------------------------------callDocument function-->
  function callDocument(id, parentCategoryStr) {
    var checkUrl = '<@ofbizUrl>productCategoryList</@ofbizUrl>';
    if (checkUrl.search("http"))
      var ajaxUrl = '<@ofbizUrl>productCategoryList</@ofbizUrl>';
    else
      var ajaxUrl = '<@ofbizUrl>productCategoryListSecure</@ofbizUrl>';

    //jQuerry Ajax Request
    jQuery.ajax({
      url: ajaxUrl,
      type: 'POST',
      data: {"category_id": id, "parentCategoryStr": parentCategoryStr},
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#div3').html(msg);
      }
    });
  }
  <#-------------------------------------------------------------------------------------callCreateDocumentTree function-->
  function callCreateDocumentTree(contentId) {
    jQuery.ajax({
      url: editDocumentTreeUrl,
      type: 'POST',
      data: {
        contentId: contentId,
        contentAssocTypeId: 'TREE_CHILD'
      },
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#Document').html(msg);
      }
    });
  }
  <#-------------------------------------------------------------------------------------callCreateSection function-->
  function callCreateDocument(contentId) {
    jQuery.ajax({
      url: editDocumentUrl,
      type: 'POST',
      data: {contentId: contentId},
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#Document').html(msg);
      }
    });
  }
  <#-------------------------------------------------------------------------------------callEditSection function-->
  function callEditDocument(contentIdTo) {
    jQuery.ajax({
      url: editDocumentUrl,
      type: 'POST',
      data: {contentIdTo: contentIdTo},
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#Document').html(msg);
      }
    });

  }
  <#-------------------------------------------------------------------------------------callDeleteItem function-->
  function callDeleteDocument(contentId, contentIdTo, contentAssocTypeId, fromDate) {
    jQuery.ajax({
      url: deleteDocumentUrl,
      type: 'POST',
      data: {
        contentId: contentId,
        contentIdTo: contentIdTo,
        contentAssocTypeId: contentAssocTypeId,
        fromDate: fromDate
      },
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        location.reload();
      }
    });
  }
  <#-------------------------------------------------------------------------------------callRename function-->
  function callRenameDocumentTree(contentId) {
    jQuery.ajax({
      url: editDocumentTreeUrl,
      type: 'POST',
      data: {
        contentId: contentId,
        contentAssocTypeId: 'TREE_CHILD',
        rename: 'Y'
      },
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#Document').html(msg);
      }
    });
  }
  <#------------------------------------------------------pagination function -->
  function nextPrevDocumentList(url) {
    url = '<@ofbizUrl>'+ url+'</@ofbizUrl>';
    jQuery.ajax({
      url: url,
      type: 'POST',
      error: function (msg) {
        alert("An error occurred loading content! : " + msg);
      },
      success: function (msg) {
        jQuery('#Document').html(msg);
      }
    });
  }

</script>


<div id="quickadd" class="screenlet">
  <div class="screenlet-title-bar">
    <ul>
      <li class="h3">${uiLabelMap.ProductCategories}</li>
    </ul>
  </div>
  <div class="screenlet-body" id="tree">
  </div>
</div>

数据填充部分主要看<#-- creating the JSON Data -->那一块。看rawdata是如何组织的。

分析ProductCategories.groovy代码:

1.  CatalogWorker.getCurrentCatalogId(request)

这个应该是获取当前目录id。默认是DemoCatalog

2. CategoryWorker.getRelatedCategories(request, "topLevelList", CatalogWorker.getCatalogTopCategoryId(request, CatalogWorker.getCurrentCatalogId(request)), true)

查看源码CategoryWorker.getRelatedCategories

public static String getCatalogTopCategoryId(ServletRequest request, String prodCatalogId) {
        if (UtilValidate.isEmpty(prodCatalogId)) return null;

        List<GenericValue> prodCatalogCategories = getProdCatalogCategories(request, prodCatalogId, "PCCT_BROWSE_ROOT");

        if (UtilValidate.isNotEmpty(prodCatalogCategories)) {
            GenericValue prodCatalogCategory = EntityUtil.getFirst(prodCatalogCategories);

            return prodCatalogCategory.getString("productCategoryId");
        } else {
            return null;
        }
    }

可以知道实际执行的sql语句是:

SELECT * FROM Prod_Catalog_Category t WHERE t.prod_catalog_id='DemoCatalog' AND t.prod_catalog_category_type_id='PCCT_BROWSE_ROOT'

结果如下:


查询结果会放入request的topLevelList属性里。即categoryList就是上面的查询结果。这个例子的查询结果只有一条记录。

3.接下来重点看fillTree方法。

3.1 for(root in rootCat) {  //由于categoryList结果只有一条记录,所以for只会循环一次。

3.2 preCatChilds = from("ProductCategoryRollup").where("parentProductCategoryId", root.productCategoryId).queryList() 等价于

SELECT * FROM Product_Category_Rollup WHERE parent_Product_Category_Id='CATALOG1'
root.productCategoryId的取值就是上面 categoryList结果的product_Category_Id字段的值。

结果显示:


3.3 catChilds = EntityUtil.getRelated("CurrentProductCategory",null,preCatChilds,false)

这行代码是根据preCatChilds关联对应的分类记录。等价于下面sql的查询结果:

SELECT t2.* FROM Product_Category_Rollup t1 
INNER JOIN Product_Category t2 ON t1.Product_Category_id = t2.Product_Category_id
WHERE t1.parent_Product_Category_Id='CATALOG1'
结果显示:


3.4 递归调用获取分类的子分类。

// CatLvl uses for identify the Category level for display different css class
            if(catChilds) {
                if(CatLvl==2)
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId.replaceAll("/", "")+'/'+root.productCategoryId)
                    // replaceAll and '/' uses for fix bug in the breadcrum for href of category
                else if(CatLvl==1)
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId.replaceAll("/", "")+root.productCategoryId)
                else
                    childList = fillTree(catChilds,CatLvl+1, parentCategoryId+'/'+root.productCategoryId)
            }

第一次执行时CatLvl是等于1. 

SELECT t2.* FROM Product_Category_Rollup t1 
INNER JOIN Product_Category t2 ON t1.Product_Category_id = t2.Product_Category_id
WHERE t1.parent_Product_Category_Id=?
上面的参数?表示catChilds列表每条记录的产品分类id即Product_Category_id,取值有:100,200,dropShip,FA-100,FOOD-001,GC-100,PC-100,SERV-001  八条记录循环遍历。


3.5 构造rootMap。

rootMap的主要属性有:categoryName,categoryDescription,productCategoryId,parentCategoryId,child,cssClass

研究了很久,发现下面三条记录的category_name和description字段都为null,而分类树中确显示Configurables Foods,Gift Cards,Configurables PCs。不知道这些内容是哪里来的,找了好久也没找到,暂时先不管了。以后有时间再研究。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值