The Wayback Machine - https://web.archive.org/web/20201121125404/http://github.com/bookshelf/bookshelf/issues/2038
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override visibility options of nested relations #2038

Open
romanmatolych opened this issue Dec 6, 2019 · 0 comments
Open

Override visibility options of nested relations #2038

romanmatolych opened this issue Dec 6, 2019 · 0 comments

Comments

@romanmatolych
Copy link

@romanmatolych romanmatolych commented Dec 6, 2019

Issue Description

Let's say that I have 2 Bookshelf models: Profiles and Users. Here are the simplified versions of them:

Bookshelf.model('User', Bookshelf.Model.extend({
  tableName: 'users',
  hidden: ['password', 'salt'],
  hasTimestamps: true,
  ...
  profile: function () {
    return this.hasOne('Profile', 'id', 'id');
  },
  ...
}));
Bookshelf.model('Profile', Bookshelf.Model.extend({
  tableName: 'profiles',
  hidden: ['name', 'email'],
  hasTimestamps: true,
  ...
  user() {
    return this.belongsTo('User', 'id', 'id');
  },
  ...
}));

What I am trying to do in some controller is:

return new User()
  .fetchPage({
    pageSize: PageLimit,
    page: PageNumber,
    withRelated: ['profile']
  })
  .then(result => {
    return res.status(200).send({
      data: result.toJSON({
        hidden: ['name'],
      }),
      pagination: result.pagination
    }));
  })
  .catch(next);

So I want to return a list of users with their profiles, but I also want to show the email values, for instance. That's why I override the hidden list in the toJSON call. After that, all other properties show up in the result, including hidden ones from the Users model that I still want to hide. My email column, that I have to write even without dot notation (i.e., "table.column") btw., is not there, though.

I was able to trace how this argument is passed down with:

serialize(...args) {
    console.log(args);
    return Bookshelf.Model.prototype.serialize.apply(this, args);
},

And it's just the array ['name'], that I gave to the toJSON call for all nested models.

I thought, that I could gather all hidden members, then exclude the needed columns and utilize this new list. So I came up with this function:

function getAllFieldsToHide(model, hidden = []) {
  if (model instanceof Bookshelf.Collection && model.length > 0) {
    return getAllFieldsToHide(model.at(0), hidden);
  } else if (model instanceof Bookshelf.Model) {
    hidden = hidden.concat(model.hidden === null ? [] : model.hidden);
    Object.values(model.relations).forEach(relation => {
      hidden = getAllFieldsToHide(relation, hidden);
    });
    return hidden;
  } else {
    return hidden;
  }
}

And then I do:

const hiddenFieldsToInclude = ['email'];
const newHiddenList = getAllFieldsToHide(result)
  // exclude column names that we need to show
  .filter(field => hiddenFieldsToInclude.indexOf(field) === -1);

It retrieves an array of attributes to remove from the output for the given Bookshelf model and its nested relations. But, name conflict may occur here, if models have columns with the same names. The function works, but maybe there is some more sophisticated way to achieve this? Or it can be an issue request.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.