ServiceBuilder - Developer

Asset Publisher

null ServiceBuilder

 ایجاد لایه Service Builder

اگر path یا مسیر /vitamins را در نظر بگیریم، سختی کار به این دلیل است که می‌خواهیم از مدل لایفری تبعیت کنیم و بنابراین پیاده‌سازی ما باید از Search و Permissionها و Sorting و Filtering  و paging نتایج پشتیبانی کند.

بقیه قسمتهای این بخش شامل اضافه کردن امکانات فوق با استفاده از سازوکارهای پشتیبانی شده از Liferay است. اگر نیازی به پشتیبانی بحث فیلتر کردن لیست ، جستجو یا مرتب سازی ندارید یا اگر می‌خواهید از یک یا چند مورد پشتیبانی کنید اما از تکنیک های Liferay استفاده نمی کنید ، احتمالا ادامه مطالب این بخش برای شما مفید نخواهد بود.

سه لینک زیر در مورد تکنیکهای لایفری برای پشتیبانی صفحه‌بندی لیست حاوی دیتا (سایز صفحات) و همچنین serach-sort-filtering آنها ، اطلاعاتی در اختیار شما قرار می‌دهد.

https://portal.liferay.dev/docs/7-2/frameworks/-/knowledge_base/f/pagination

https://portal.liferay.dev/docs/7-2/frameworks/-/knowledge_base/f/filter-sort-and-search

https://portal.liferay.dev/docs/7-2/frameworks/-/knowledge_base/f/restrict-properties

 

پیاده سازی کلاس VitaminResourceImpl

این کلاس در مسیر زیر قرار دارد :

headless-vitamins-impl/src/main/java/com/dnebinger/headless/vitamins/internal/resource/v1_0/VitaminResourceImpl.java

ابتدا رفرنسهای زیر را به انتهای کلاس اضافه کنید:

@Reference
private Portal _portal;

@Reference
private UserLocalService _userLocalService;

@Reference
private PersistedVitaminService _persistedVitaminService;

private static final Logger _log = LoggerFactory.getLogger(VitaminResourceImpl.class);

متد deleteVitamin

@Override
public void deleteVitamin(@NotNull String vitaminId) throws Exception {
   try {
      // super easy case, just pass through to the service layer.
      _persistedVitaminService.deletePersistedVitamin(vitaminId);
   } catch (Exception e) {
      _log.error("Error deleting vitamin: " + e.getMessage(), e);
      throw e;
   }
}
برای مدیریت داده‌ها فقط از سرویسهای remote استفاده کنید نه local. به بیان ساده‌تر از کلاسهایی که در نام آنها کلمه local دارد استفاده نکنید چرا که در این صورت کنترلهای مربوط به permission انجام نمی‌شود و احتمال hack بیشتر می‌شود. اما وقتی از کلاسهای بدون کلمه local  استفاده می‌کنید، ابتدا کنترلهای permission انجام می‌شود و سپس کلاسهای local صدا زده می‌شود.

 

متد  toVitamin

داده‌های فراخوانی شده از بانک اطلاعاتی باید به دیتای مناسب برای componentهای headless تبدیل شود :

protected Vitamin _toVitamin(PersistedVitamin pv) throws Exception {
   return new Vitamin() {{
      creator = CreatorUtil.toCreator(_portal, _userLocalService.getUser(pv.getUserId()));
      articleId = pv.getArticleId();
      group = pv.getGroupName();
      description = pv.getDescription();
      id = pv.getSurrogateId();
      name = pv.getName();
      type = _toVitaminType(pv.getType());
      attributes = ListUtil.toArray(pv.getAttributes(), VALUE_ACCESSOR);
      chemicalNames = ListUtil.toArray(pv.getChemicalNames(), VALUE_ACCESSOR);
      properties = ListUtil.toArray(pv.getProperties(), VALUE_ACCESSOR);
      risks = ListUtil.toArray(pv.getRisks(), VALUE_ACCESSOR);
      symptoms = ListUtil.toArray(pv.getSymptoms(), VALUE_ACCESSOR);
   }};
}



protected Vitamin.Type _toVitaminType(int typeCode) {
   if (typeCode == PersistedVitaminType.VITAMIN) {
      return Vitamin.Type.VITAMIN;
   }
   if (typeCode == PersistedVitaminType.MINERAL) {
      return Vitamin.Type.MINERAL;
   }

   return Vitamin.Type.OTHER;
}



protected static final Accessor<VitaminDetail, String> VALUE_ACCESSOR = new Accessor<VitaminDetail, String>() {
   @Override
   public String get(VitaminDetail vd) {
      return vd.getValue();
   }
   @Override
   public Class<String> getAttributeClass() {
      return String.class;
   }
   @Override
   public Class<VitaminDetail> getTypeClass() {
      return VitaminDetail.class;
   }
};

البته ما از روش

 

 

متد getVitami

برای بازیابی یک رکورد

@Override
public Vitamin getVitamin(@NotNull String vitaminId) throws Exception {
  // fetch the entity class...
  PersistedVitamin pv = _persistedVitaminService.getPersistedVitamin(vitaminId);

  return _toVitamin(pv);
}

متدهای postVitamin و patchVitamin  و putVitamin

postVitamin برای ایجاد رکورد جدید
patchVitamin و putVitamin برای بازیابی و تغییر رکورد موجود

@Override
public Vitamin postVitamin(Vitamin v) throws Exception {
  PersistedVitamin pv = _persistedVitaminService.addPersistedVitamin(
      v.getId(), v.getName(), v.getGroup(), v.getDescription(), _toTypeCode(v.getType()), v.getArticleId(), v.getChemicalNames(),
      v.getProperties(), v.getAttributes(), v.getSymptoms(), v.getRisks(), _getServiceContext());

  return _toVitamin(pv);
}

@Override
public Vitamin patchVitamin(@NotNull String vitaminId, Vitamin v) throws Exception {
  PersistedVitamin pv = _persistedVitaminService.patchPersistedVitamin(vitaminId,
      v.getId(), v.getName(), v.getGroup(), v.getDescription(), _toTypeCode(v.getType()), v.getArticleId(), v.getChemicalNames(),
      v.getProperties(), v.getAttributes(), v.getSymptoms(), v.getRisks(), _getServiceContext());

  return _toVitamin(pv);
}

@Override
public Vitamin putVitamin(@NotNull String vitaminId, Vitamin v) throws Exception {
  PersistedVitamin pv = _persistedVitaminService.updatePersistedVitamin(vitaminId,
      v.getId(), v.getName(), v.getGroup(), v.getDescription(), _toTypeCode(v.getType()), v.getArticleId(), v.getChemicalNames(),
      v.getProperties(), v.getAttributes(), v.getSymptoms(), v.getRisks(), _getServiceContext());

  return _toVitamin(pv);
}

در لایه Service من به ServiceContext نیاز دارم و لایفری از طریق com.liferay.headless.common.spi.service.context.ServiceContextUtil آن را در اختیار من قرار می‌دهد. فقط لازم است موارد اضافی مانند شناسه شرکت و شناسه کاربر فعلی را در آن اضافه کنم. بنابراین همه اینها را در متد _getServiceContext () قرار دادم:

 

EntityModel

EntityModel جاییست که شما با تعریف آن می‌توانید مقوله‌های Search و Sort و filtring داده‌ها را که قبلاً صحبت شد، قابلیت اجرا ببخشید. این کلاس درپکیج با مسیر زیر قرار دارد :

headless-vitamins-impl/src/main/java/com/dnebinger/headless/vitamins/internal/odata/entity/v1_0/VitaminEntityModel.java

و محتوای آن به صورت زیر است :


public class VitaminEntityModel implements EntityModel {
  public VitaminEntityModel() {
    _entityFieldsMap = Stream.of(
        // chemicalNames is a string array of the chemical names of the vitamins/minerals
        new CollectionEntityField(
            new StringEntityField(
                "chemicalNames", locale -> Field.getSortableFieldName("chemicalNames"))),
        
        // we'll support filtering based upon user creator id.
        new IntegerEntityField("creatorId", locale -> Field.USER_ID),
        
        // sorting/filtering on name is okay too
        new StringEntityField(
            "name", locale -> Field.getSortableFieldName(Field.NAME)),
        
        // as is sorting/filtering on the vitamin group
        new StringEntityField(
            "group", locale -> Field.getSortableFieldName("vitaminGroup")),
        
        // and the type (vitamin, mineral, other).
        new StringEntityField(
            "type", locale -> Field.getSortableFieldName("vType"))
    ).collect(
        Collectors.toMap(EntityField::getName, Function.identity())
    );
  }

  @Override
  public Map<String, EntityField> getEntityFieldsMap() {
    return _entityFieldsMap;
  }

  private final Map<String, EntityField> _entityFieldsMap;
}


من فیلدهای chemicalNames, Field.USER_ID, Field.NAME, vitaminGroup and vType  را برای ایندکس در عمل Search استفاده کردم و فیلد creatorId نیز برای فیلتر داده ها استفاده می‌شود. من برای استفاده از سایر فیلدهای کامپوننت Vitamin احساس نیاز نکردم.
اکنون  باید کلاس <Component> ResourceImpl را نیز تغییر دهیم تا متوجه شود که می‌تواند یک EntityModel را ارائه دهد. یعنی کلاس VitaminResourceImpl  باید متد getEntityModel  از  interface  در مسیر  com.liferay.portal.vulcan.resource.EntityModelResource را پیاده‌سازی کند تا یک شی از EntityModel را برگرداند. بنابراین بخش بالای کلاس را به صورت زیر تغییر می‌دهیم.

public class VitaminResourceImpl extends BaseVitaminResourceImpl
    implements EntityModelResource {

  private VitaminEntityModel _vitaminEntityModel = new VitaminEntityModel();

  @Override
  public EntityModel getEntityModel(MultivaluedMap multivaluedMap) throws Exception {
    return _vitaminEntityModel;
  }

پیاده‌سازی متد getVitaminsPage

public Page<Vitamin> getVitaminsPage(String search, Filter filter, Pagination pagination, Sort[] sorts) throws Exception {
  return SearchUtil.search(
    booleanQuery -> {
      // does nothing, we just need the UnsafeConsumer<BooleanQuery, Exception> method
    },
    filter, PersistedVitamin.class, search, pagination,
    queryConfig -> queryConfig.setSelectedFieldNames(
      Field.ENTRY_CLASS_PK),
    searchContext -> searchContext.setCompanyId(contextCompany.getCompanyId()),
    document -> _toVitamin(
      _persistedVitaminService.getPersistedVitamin(
        GetterUtil.getLong(document.get(Field.ENTRY_CLASS_PK)))),
    sorts);
}

مرحله نهایی

با توجه به تغییر در متدهای کلاس VitaminResourceImpl باید دوباره دستور buildREST را اجرا کنیم.

فهرست منابع :

ردیفمنابعنوع محتوا
۱creating headless api part 4متن