Liferay 更新ModelListener中web内容的类别

Liferay 更新ModelListener中web内容的类别,liferay,liferay-6,Liferay,Liferay 6,我正在根据用户使用的结构链接一个类别。示例:我的结构是“A”,那么使用此结构创建的任何内容的类别都应为“A” 我已经在JournalArticlemodel上创建了一个扩展BaseModelListener的侦听器。我覆盖了onBeforeUpdate(),它看起来像: @Override public void onBeforeUpdate(JournalArticle aModel) throws ModelListenerException { try { long

我正在根据用户使用的结构链接一个类别。示例:我的结构是“A”,那么使用此结构创建的任何内容的类别都应为“A”

我已经在
JournalArticle
model上创建了一个扩展
BaseModelListener
的侦听器。我覆盖了onBeforeUpdate(),它看起来像:

@Override
public void onBeforeUpdate(JournalArticle aModel) throws ModelListenerException {
    try {
        long[] assetCategoryIds = refactorCategories(anArticle); // this reads the structure key and updates the list of category to the correct one.
        String[] assetTagNames = getTags(anArticle); 
        long[] assetEntryLinkIds = getEntryLinkIds(anArticle);
        JournalArticleLocalServiceUtil.updateAsset(anArticle.getUserId(), anArticle, assetCategoryIds, assetTagNames, assetEntryLinkIds);
    } catch (PortalException |SystemException ex) {
        throw new ModelListenerException("Cannot update the categories for articleId" + anArticle.getArticleId(), ex);
    }
}
其中,我已使用一组业务规则更正了
类别ID

以下是两种可能的情况:

  • 没有指定类别:

    • 在这种情况下,上面的
      onBeforeUpdate()
      就像一个符咒,并且分配了正确的类别
  • 如果用户在调用之前分配了一个类别(可能是错误的类别):

    • 在这种情况下,将调用
      onBeforeUpdate()
      实现,并且我能够看到更新的资产(使用调试进行验证),但是当完成整个过程时,web内容不会反映正确的类别
  • 我的理解是: 当调用
    updateAsset
    时,它接受更改的类别。但当Hibernate保存内容时,它会保存用户选择的类别。这对我来说似乎有点奇怪

    因此,我希望检查是否有任何可能的解决方案或API,我可以使用

    另外,我尝试了在AfterCreate(),
    在AfterUpdate()
    在BeforeCreate()
    ,但运气不好

    我相信我可以钩住
    JournalArticleService
    ,但如果可能的话,我希望使用listener来实现这一点

    根据Tobias建议更新代码:

    @Override
    public void onBeforeUpdate(JournalArticle aModel) throws ModelListenerException {
        ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext();
         try {
            long[] assetCategoryIds = refactorCategories(anArticle); // this reads the structure key and updates the list of category to the correct one.
            if (serviceContext != null) {
                serviceContext.setAssetCategoryIds(assetCategoryIds);
            }
        } catch (PortalException |SystemException ex) {
            throw new ModelListenerException("Cannot update the categories for articleId" + anArticle.getArticleId(), ex);
        }
    }
    

    请参阅日志ArticleLocalServiceImpl.addArticle()resp<代码>更新条目():

    第一行
    journalArticlePersistence.update(article)
    将调用侦听器,而
    updateaset
    将保存用户提供的类别。在给你的听众打电话之后

    您可以获取当前服务上下文,并从侦听器内部对其进行更改:

    ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext();
    if (serviceContext != null) {
      serviceContext.setAssetCategoryIds(assetCategoryIds);
    }
    

    我建议创建一个钩子插件并使用服务包装来扩展
    JournalArticleLocalService
    。我发现服务钩子更适合这个任务。根据Liferay认证计划,通过服务钩子执行此操作也是首选方法

    一般而言:

    • 我使用模型监听器在文章持久化之前更新文章属性(例如自定义url标题)
    • 我使用服务钩子更新相关的资产条目(例如类别和标签)
    扩展服务示例:

    public class MyJournalArticleLocalService extends JournalArticleLocalServiceWrapper {
    
        @Override
        public JournalArticle addArticle(..., ServiceContext serviceContext) throws PortalException, SystemException {
    
            // set the right category in the service context, as the service context 
            // that the original service will work with can be modified here
            serviceContext.setAssetCategoryIds(refactorCategories(...));
    
            // delegate to the original implementation
            return super.addArticle(..., serviceContext);
        }
    
        @Override
        public JournalArticle updateArticle(..., , ServiceContext serviceContext) throws PortalException, SystemException {
    
            // set the right category in the service context, as the service context 
            // that the original service will work with can be modified here
            serviceContext.setAssetCategoryIds(refactorCategories(...));
    
            // delegate to the original implementation
            return super.updateArticle(..., serviceContext);
        }
    }
    
    使用服务钩子,您可以在Liferay完成文章持久化和创建/更新相关资产条目之前或之后更新类别

    您可以在
    liferay hook.xml
    descriptor中注册服务挂钩:

    <?xml version="1.0"?>
    <!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">
    
    <hook>
        <service>
            <service-type>com.liferay.portlet.journal.service.JournalArticleLocalService</service-type>
            <service-impl>com.test.MyJournalArticleLocalService</service-impl>
        </service>
    </hook>
    
    
    com.liferay.portlet.journal.service.JournalArticleLocalService
    com.test.MyJournalArticleLocalService
    
    谢谢你的指点。我也在同样的轨道上,但我坚持了一点:根据我的理解,当我们添加/更新一篇文章时,触发了一个事件,它调用了与我相同的上述重写方法:ServiceContext ServiceContext=ServiceContextThreadLocal.getServiceContext();并使用正确的CategoryID更新了serviceContext引用(使用更新的代码更新帖子),但是当我调试时,我没有看到传递这个更新的对象。我想我犯了一些错误,但还不能追踪它。你能想到任何明显的问题吗?再次感谢!你指的是哪个更新对象<代码>服务上下文?能否检查侦听器中的serviceContext是否与
    updateArticle
    中的相同(实际上==)?有趣的是,侦听器中的serviceContext对象引用与updateArticle()/addArticle()对象引用不同。我还以为是一样的。不确定哪里出了问题,因为我希望当我们试图获取serviceContext时,侦听器中应该有与调用侦听器的方法相同的对象,就像从ServiceContextThreadLocal获取上下文一样。你怎么认为?再次感谢!查看代码时,我希望操作中保存的服务上下文与
    addArticle()
    中提供的服务上下文相同。但显然它是克隆的。如果仍在调试,则可以检查
    serviceContext
    的来源。但也可能是你无法获得那个实例,所以你最终需要一个钩子。要么像Tomas那样包装
    JournalArticleLocalService
    ,要么包装
    AssetentTylLocalService
    。在服务挂钩中修改服务上下文而不是在之后添加更新不是更好吗?否则,您将在很短的时间内(从业务角度)出现无效的类别设置,如果在工作流中使用这些类别(在这么短的时间内触发),或者在第二次更新中出现问题,这可能会导致问题。这是一个非常好的观点。谢谢我使用原始答案中的序列添加新的独特标签(只是添加,没有替换),这不是问题。
    <?xml version="1.0"?>
    <!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">
    
    <hook>
        <service>
            <service-type>com.liferay.portlet.journal.service.JournalArticleLocalService</service-type>
            <service-impl>com.test.MyJournalArticleLocalService</service-impl>
        </service>
    </hook>