Strapi v4 relations population - strapi

Hi I’m migration from strapi v3 to v4. I’m stuck for quite sometime regarding population of relations in v4.
BEHAVIOUR IN V3
Previously in v3 if I queried a service, it populated uptill the 2nd level by default if I’m not wrong, and in the second level if returned foreign keys from the further nested tables/relations respectively.
Example case I have following relations:
activity → cities → province , [images]
Querying activity like this in code:
const activity = await strapi.services.activity.findOne({ id });
would return activity → cities → { provinceforeignkey , images }
e.g. sample response
{
id: activity_id,
cities: [
id,
province: id_of_province,
images: [
// IMAGES DETAILED RESPONSE
]
]
}
BEHAVIOUR IN V4
I’m not able to get the desired response as above either by direct querying:
const activity = await strapi
.service("api::activity.activity")
.findOne(id,populate: ["cities"]);
Or by using entityService:
await strapi.entityService.findOne(
"api::activity.activity",
id,
populate: ["cities"]
);
I know we can use populate field to populate the desired relation, but on second level e.g. province case inside cities I need the id of province not the entire object population.
This is really important to us as we’ve tonnes of custom apis and custom code already implemented in v3 as per the following approach, otherwise we would have to go and change each case specifically in frontend and backend.
Can anyone guide me on this?

the entityService and db.query accept fields parameter that is undocumented but may work? However, what i would recommend is doing your own normalize function like:
let activities = await strapi.db.query('api::activity.activity')
.findOne({where: { id }, populate: { cities: { populate: ['state'] } } );
return normalizeManyActivities(activities)
and
const normalizeActivity = (activity) =>
({...activity,
cities: activity.cities.map(city =>
({...city, state: city.state.id })
})
);
const normalizeManyActivities = (activities) =>
activities.map(activity => normalizeActivity(activity));
and the second approach is to use middleware's witch you can take reference from here:
https://www.youtube.com/watch?v=YIbhKm1o0fE

Related

Strapi : populate from a relation

In the example
on the site
Strapi Docs
To populate specific relations, one or several levels deep, use the LHS bracket notation for fields names in combination with the populate parameter. The qs library (opens new window)is helpful to build complex URLs:
const qs = require('qs');
const query = qs.stringify({
populate: {
author: {
populate: ['company'],
}
}
}, {
encodeValuesOnly: true,
});
await request(`/api/articles?${query}`);
// GET /api/articles?populate[author][populate][0]=company
How to fill relationships, two three or more several levels deep?
In my example, I tried
/api/regions?populate[cities][populate][0]=holding?spopulate[companies][populate][0]=holdings
The answer to this question was posted here. Make sure you're using Strapi v4 and simply install the plugin to the Strapi server.

Strapi change one collection and apply change to other one

Lets say i create an entry where i can select two collections like this
now , how to detec when someone changed first collection and then apply some filter to data in second collection ?
not sure if you're still looking for the answer but your question ranked high on my search so here's an answer for future visitors.
Say you add a post entry for your Posts collection. In the ./api/post/models/post.js you can create a hook like this
'use strict';
module.exports = {
lifecycles: {
async afterCreate(result, data) {
const id = result.streakID;
const streak = strapi.services.streaks.findOne({ id });
strapi.services.streaks.update({ id }, { counter: streak.counter++ });
},
},
};
My source
Best regards
EDIT: the hook runs on Post creation and accesses the Streak model by way of strapi.services.streaks. To be clear.

Strapi sum values for a certain field

i'm trying to build an api for expenses notes with strapi.
I have trips and for each trip multiple expenses. (relation one to many).
I would like, when I go to /trips it will give me also the sum of all expenses for that trip. (each expense will have the proper amount)
I would need the sumField only in response and not on the model of the api (so i cannot modify it).
I Am using postrgres.
Definetelly i have to modify the controller find and find one but I don't know how make the sum.
Also if it is possible to have it querable from graphql.
Any help?
Exactly you will have to customize your API to add a new route and controller (to keep the current default API stuff) that will return what you need.
First you will have to create a route and a controller in the API you want. Trip in you case.
There is a doc here for an API named hello - replace hello by post. https://strapi.io/documentation/3.0.0-beta.x/guides/controllers.html#custom-controllers
And then in your controller, you will have something like this:
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async tripsWithSum (ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.services.trip.search(ctx.query);
} else {
entities = await strapi.services.trip.find(ctx.query);
}
entities = entities.map(entity => sanitizeEntity(entity, { model: Trip }));
entities = entities.map(entry => {
entry = Object.assign(entry, {
sumField: entry.expenses.length
});
});
return entities;
}
};
Thank you I will try. what if expenses is a relation field? Trips will have many expenses.

redux-form initialize FieldArray

I'm trying to create a redux form which integrates many aspects of redux-form.
To give a better introduction to the project I would like to share the main goals with you.
At the moment i've a dynamic form which can be extended with participants. Each participant has a superior. On user input (Async.Creatable React select) I request (call to API) all users based on the text. To sum up, an Autosuggestion. When receiving the users I make a second call to get the corresponding superior. Afterwards I would like to autofill the fields for the participant as well as for the superior.
Everything works quite well for the first participant + superior. Unlike the first participant the second superior is not getting autofilled.
Is it required to call the initialize action (redux-form) manual, when mutating the initalValues Property?
The function getFormData access the current state. These state is as expected and contains the following data.
These state is created with the LOAD action of redux-form
initial
:
attendees
:
Array(2) <-- Autofilled fields [{name: FirstTest, firstName: FirstTest}, {name: SecondTest, firstName: SecondTest}]
const mapStateToProps = (state) => {
const attendeesFromFormData = getFormData(state, REGISTRATION_FORM, [{}]).attendees
const attendees = attendeesFromFormData ? attendeesFromFormData : [{}]
return {
initialValues: {
attendees: attendees
}
}
}
export default connect(mapStateToProps, {searchADUser, autoFillAttendee, getADUser})(reduxForm({
form: REGISTRATION_FORM,
enableReinitialize: true
})(RegistrationForm))
Okay after a bit research and testing I find a simple solution for my problem.
Each time you add a participant you have to initialize manually the redux-form.

Fetching multiple levels of objects with kendo ui datasource?

I'm very new to developing mobile applications with telerik appbuilder. There are some things I have a hard time to understand with fetching data from Everlive.
Lets consider a simple example. Lets say I have Blog Posts and Comments that belong to those Posts. And both Posts and Comments are made by Users.
In one view I want to present the Post with corresponding Comments and I also need the Username of the User who posted the Comment (Comment table only contains userId).
Both the Post and the Comments are easy to fetch since I have the id of the Post. But how do I fetch the corresponding user for each Comment?
The FriendsApp example does something very similar but it uses this line to get the user:
var user = $.grep(app.Users.users(), function (e) {
return e.Id === userId;
})[0];
This fetches all users and filters them client side? I guess this is okay if you have like 10 users. But what if you have a million users?
I am guessing that the FriendsApp uses this way of resolving the relations just for keeping the simplicity of the sample. Everlive offers a far more meaningful toolset for resolving relation fields called Expand. You can explore the REST API here:
http://docs.telerik.com/platform/backend-services/development/rest-api/relations/simple-expanding
or the JS SDK function here:
http://docs.telerik.com/platform/backend-services/development/javascript-sdk/relations/simple-expanding.
As the Friends app uses the Kendo UI data source component you can send an Expand header with the request. The following configuration of the data source will return the DisplayName of the user in each Activity/Comments entity:
var expandObject = {
"CreatedBy": {
"ReturnAs": "User",
"SingleField": "DisplayName"
}
};
var dataSource = new kendo.data.DataSource({
type: "everlive",
transport: {
typeName: 'Activities', // replace this with Comments
read: {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-Everlive-Expand", JSON.stringify(expandObject))
},
}
},
schema: {
model: {
id: Everlive.idField
}
}
});
dataSource.fetch(function (data) {
console.log(data.items);
});
Same could be applied for resolving the comments for each Blog post. Given the Friends data schema you will need to use the External Relation resolver of the Everlive API. Note that it is available only in a GetById scenario, e.g. when retrieving an Activity by Id, you can resolve the Comments that point to this activity, which is generally very handy in master-detail views.

Resources