在 ModelListener 中更新 Web 内容的类别

Update categories of a web content in ModelListener

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

类别

我在 JournalArticle 模型上创建了一个扩展 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);
    }
}

我使用一组业务规则更正了 categoryIds

以下是两种可能的情况:

  1. 没有分配类别:

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

    • 在这种情况下,onBeforeUpdate() 实施被调用并且我能够看到更新的资产(使用调试验证),但是当完成整个过程时,网络内容没有反映正确的类别。

我的理解: 当调用 updateAsset 时,它采用更改后的类别。但是,当 Hibernate 保存内容时,它会保存用户选择的类别。这对我来说似乎有点奇怪。

所以我想看看是否有任何可能的解决方案或API我可以使用。

此外,我尝试了 onAfterCreate()onAfterUpdate()onBeforeCreate(),但运气不好。

我相信我可以挂钩 JournalArticleService 但我希望尽可能使用侦听器来做到这一点。

根据 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);
    }
}

参见 JournalArticleLocalServiceImpl.addArticle() updateArticle():

  journalArticlePersistence.update(article);

  ...

  updateAsset(userId, article, serviceContext.getAssetCategoryIds(),
    serviceContext.getAssetTagNames(),
    serviceContext.getAssetLinkEntryIds());

第一行 journalArticlePersistence.update(article) 将调用您的侦听器,而 updateAsset 将保存用户提供的类别。在调用您的听众之后。

您可以获取当前服务上下文并从您的侦听器内部更改它:

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描述符中注册服务挂钩:

<?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>