Hello!
I have a problem with model in relationship
I have Cities and Buildings in Cities
Relationship settings:
City belongs to many Buildings
City has many Buildings
In cities API i see
cities API
{
id: 1,
city_name: "Chicago",
created_at: "2021-01-06T10:11:06.000Z",
updated_at: "2021-01-09T15:50:23.000Z",
building: {
id: 1,
name: "SomeBuilding in City",
created_at: "2021-01-06T10:10:45.000Z",
updated_at: "2021-01-09T15:50:23.000Z",
city: 1
}
In Building API i see
Building API
{
id: 1,
name: “SomeBuilding in City”,
created_at: “2021-01-06T10:10:45.000Z”,
updated_at: “2021-01-09T15:50:23.000Z”,
citys: [
{
id: 1,
name: “Chicago”,
created_at: “2021-01-06T10:11:06.000Z”,
updated_at: “2021-01-09T15:50:23.000Z”,
storage: 1
}
}
Why in cities api in the building model city is only ID of city
But in Buildings api city is model of city?
And how to make api of cities looks like example
example cities API
example
{
id: 1,
city_name: “Chicago”,
created_at: “2021-01-06T10:11:06.000Z”,
updated_at: “2021-01-09T15:50:23.000Z”,
building: {
id: 1,
name: “SomeBuilding in City”,
created_at: “2021-01-06T10:10:45.000Z”,
updated_at: “2021-01-09T15:50:23.000Z”,
citys: [
{
id: 1,
name: “Chicago”,
created_at: “2021-01-06T10:11:06.000Z”,
updated_at: “2021-01-09T15:50:23.000Z”,
storage: 1
}
}
Related issue: Strapi different models of the object with relationships · Issue #9081 · strapi/strapi · GitHub
Is your project code public so I can take a look at the model/relationship structure?
185.4.75.155:1337/users/
users role is model
185.4.75.155:1337/offices/
users role is ID
One object user have two diferent models
Same problem
185.4.75.155:1337/cities
buildings city is ID
185.4.75.155:1337/buildings
buildings city is model
Demo server
http://185.4.75.155:1337/admin/
user mail@bk.ru
password Test123456
How to make sure the role / city is always either ID or model
It will be best if there is a model everywhere, not an ID
Ah you are referring to the model population, by default we only populate one level deep. If you need additional population you will need to customize the default controller.
Are you familiar with doing so?
No, unfortunately I’m not familiar, I’m generally not good at nodeJS
For overriding the controllers you can take a look at this document: https://strapi.io/documentation/developer-docs/latest/concepts/controllers.html#concept
But the specific line I’m referring to is this one in the find controller:
entities = await strapi.services.restaurant.find(ctx.query);
Looking at the service documentation here: https://strapi.io/documentation/developer-docs/latest/concepts/services.html#core-services
There is an undefined key that you can customize for the population level.
In your case you can use the following line:
entities = await strapi.services.office.find(ctx.query, [ 'users', 'users.office' ]);
This should give you the expected output such as:
[
{
"id": 1,
"office_name": "Chicago Office",
"published_at": "2021-01-16T14:15:14.000Z",
"created_at": "2021-01-16T14:11:30.000Z",
"updated_at": "2021-01-16T14:15:14.000Z",
"users": [
{
"id": 1,
"username": "someuser",
"email": "dsds@maill.ru",
"provider": "local",
"confirmed": false,
"blocked": false,
"role": 1,
"created_at": "2021-01-16T14:11:10.000Z",
"updated_at": "2021-01-16T14:11:32.000Z",
"office": {
"id": 1,
"office_name": "Chicago Office",
"published_at": "2021-01-16T14:15:14.000Z",
"created_at": "2021-01-16T14:11:30.000Z",
"updated_at": "2021-01-16T14:15:14.000Z"
}
}
]
}
]
CtFelix
January 21, 2021, 2:22pm
10
Thank you, It’s helped for me.
I decided to make ID everywhere, not models. But I can’t figure out how to make the user role in the form of an ID, not a model.
In extensions / users-permissions / created the controllers folder
Inside it User.js
And the code is as below:
example
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
/**
* Retrieve records.
*
* @return {Array}
*/
async find(ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.plugins['users-permissions'].services.user.search(ctx.query);
} else {
entities = await strapi.plugins['users-permissions'].services.user.find(ctx.query, [ 'role', 'role.user' ]);
}
return entities.map(entity => sanitizeEntity(entity, { model: strapi.models.user }));
},
};
i was tried do this like, but it not works
Depends on which endpoint you mean. If it’s the /users/me
one this is the one you need to override:
/**
* Promise to fetch authenticated user.
* @return {Promise}
*/
fetchAuthenticatedUser(id) {
return strapi.query('user', 'users-permissions').findOne({ id }, ['role']);
},
Specifically the array containing 'role'
CtFelix
January 21, 2021, 5:38pm
12
endpoint is users
185.4.75.155:1337/users/
Role of users in this endpoint = model, I need ID
CtFelix
January 24, 2021, 12:41pm
13
Is there any way to do this?
The easiest way to track down what you need to modify is looking at it from this perspective:
You know the route, in your case FIND /users
thus we need to look at where that route comes from. We know in this case it’s the users-permissions plugin, and routes are always stored in the config/routes.json
Thus:
{
"method": "GET",
"path": "/users",
"handler": "User.find",
"config": {
"policies": [],
"prefix": "",
"description": "Retrieve all user documents",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "find"
}
}
},
Specifically what we are looking for is the handler
or in this case: "handler": "User.find",
. The Handler is a Koa term which for Strapi translates to a controller, the pathing you see there means we are now looking inside of the users-permission’s controller folder. The handler tells us it’s in the User.js
file under the find
function.
/**
* Retrieve user records.
* @return {Object|Array}
*/
async find(ctx, next, { populate } = {}) {
let users;
if (_.has(ctx.query, '_q')) {
// use core strapi query to search for users
users = await strapi.query('user', 'users-permissions').search(ctx.query, populate);
} else {
users = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query, populate);
}
ctx.body = users.map(sanitizeUser);
},
Just like in the previous example I gave you there is a populate variable that could change based on the request:
users = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query, populate);
And the specific line here: { populate } = {}
states if it isn’t defined, just leave it as an empty object or AKA the default population of 1 level deep.
Much like the previous example I gave you for the /users/me, this service comes from the exact same place just a few lines below:
/**
* Promise to fetch all users.
* @return {Promise}
*/
fetchAll(params, populate) {
return strapi.query('user', 'users-permissions').find(params, populate);
},
The same logic applies, if you pass an empty array, nothing will be populated or you include the fields you want populated.