简体   繁体   中英

Filter properties from ES6 class

I have a Dto that I want to enable the service layer to filter: The method selectFields takes an array of field names that should be returned, the other properties will be removed.
What is a short way to enumerate the properties on the class so I can loop through them and set the filtered ones to null?
In the BaseDto I take care of cleaning falsy values (well I need the same function here too as a matter of fact).

class UserServiceDto extends BaseDto {
  constructor(userDto) {
    super();
    this.fbUserId = userDto.fbUserId;
    this.fbFirstName = userDto.fbFirstName;
    this.fbLastName = userDto.fbLastName;
    this.gender = userDto.gender;
    this.birthdate = userDto.birthdate;
    this.aboutMe = userDto.aboutMe;
    this.deviceToken = userDto.deviceToken;
    this.refreshToken = userDto.refreshToken;
    this.updatedAt = userDto.updatedAt;
    this.createdAt = userDto.createdAt;
  }

  selectFields(fields) {
    // --> what's your take? 
  }

  toJson() {
    return super.toJson();
  }
}

Edit:
The service layer receives a dto from repository layer including all database fields. The ServiceLayerDto aims at filtering out fields that are not required by the web api (or should not be exposed as a security measure eg PK field, isDeleted, etc). So the result would I'm looking at the end of a service method for would look something like:

return new UserServiceDto(userDto)
  .selectFields('fbUserId', 'fbFirstName', 'fbLastName', 'birthdate', 'aboutMe', 'updatedAt', 'createdAt')
  .toJson();

The return value would be a plain json object that the web layer (controller) sends back to the http client.

If you are ok with spread operator, you may try following approach:

class UserServiceDto {
  constructor() {
    this.a = 1;
    this.b = 2;
    this.c = 3;
  }
  selectFields(...fields) {
    const result = {};
    fields.forEach(key => result[key] = this[key]);
    return result;
  }
}

new UserServiceDto().selectFields('a', 'c'); // {a: 1, c: 3}

Looking to super.toJson() call, I think that it would not work due to the result of my selectFields() call would not be an instance of UserServiceDto class. There are some possible ways from this point I see:

  • instantiate new UserServiceDto object inside selectFields() body, remove all fields that not listed in the ...fields array (javascript delete is okey) and return it;
  • play with UserServiceDto constructor params to save positive logic on selectFields() , and pass to constructor only that props that need to be set up; in this case instantiating a temporary object will not require properties removing;
  • change the signature of toJson method, or better add a new signature, which would allow to pass fields array and then put current selectFields logic inside toJson method (and remove selectFields method at all): new UserServiceDto().toJson('a', 'c') ...

Purely for info, I ultimately changed my app architecture.

The repository returns a Dto to the service layer (dto being mapped directly from the sql queries).
The service builds a static View based on the Dto and returns it to the web layer (represented by a plain json object).

In my directory structure, I have:

- service
-- views
--- index.js
--- UserInfo.js

The view is a simple filter. Eg UserInfoView:

exports.build = ({ fbUserId, fbFirstName, fbLastName, gender, birthdate, aboutMe, updatedAt, createdAt }) => {
  return {
    fbUserId,
    fbFirstName,
    fbLastName,
    gender,
    birthdate,
    aboutMe,
    updatedAt,
    createdAt,
  };
};

Using the view, eg UserInfoView in the service looks like this:

const Views = require('../service/views');

exports.findActiveByUserId = async (pUserId) => {
  const userDto = await UserRepository.findActiveByUserId(pUserId);
  if (!userDto) {
    throw new ServiceError(Err.USER_NOT_FOUND, Err.USER_NOT_FOUND_MSG);
  }
  return Views.UserInfo.build(userDto.toJson());
};

I think that this is much more descriptive compared to my initial take on the problem. Also, it keeps the data objects plain (no additional methods required).
It is unfortunate that I can't require receiving an type (View) in the web layer, I might be able to solve that problem with Typescript later on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM