Quite frankly, I am surprised Strapi released v4 without this functionality for deeply nested dynamic zones utilizing ?populate=*, as it renders the Content-Type Builder GUI natively impotent of the utility of nested dynamic zones.
For anyone wishing to implement this improvement, the “strapi develop” script will need to restarted to see the effect of the quoted “populate.js” helper.
For anyone who needs to modify multiple controllers to utilize the “populate.js” helper, the addition of a template literal helps marginally speed up the process:
Uh oh. This gives us a real stomach ache.
This should be out-of-the-box much easier, especially if this is the recommended release to use.
If you are wanting a single item, most of the time you just want the all the content. This should just be standard. We don’t want to start hacking controllers for every component/collection.
For the time being, we are going the graphql route. Have to hard type attributes, but it seems more straight forward.
Seems this is not developed enough to use out of the box. Too bad!
Correction: adding the code from @nextrapi works (easier than typing out the graphql attributes), but still seems crazy that we will have to add this hack to our controllers for collections.…, and then update it when we update attributes, dynamic zones, etc.
Can we please have a better solution here? Seems like this could break in an update down the road. Hence the stomach ache we feel about this.
For anyone wishing to populate all attributes (à la Strapi v3) with ?populate=*, here’s a merge of the solutions provided by @thylo, @JayTee, and myself.
Hey,
Here is a very small improvement of the last @sg-code suggestion, that allows to populate medias in repeatable relational components. I think it’s a rather specific need, maybe too specific for a global helper.
For those still struggling with this, we use this simple customization to the controller find() function for an entity’s dynamic zones and content like below (this entity is a Page we created, so it is the controller for this ./src/api/page/controllers/page.js). (You will need to augment the populate fields with the names of your own repeatable components or media attributes, and add to it as you add new sub-components to the entity… bit annoying but it has worked so far for our needs…)
async find(ctx) {
const { query } = ctx;
const entity = await strapi.entityService.findMany("api::page.page", { // page.page is our entity here
...query, // attach any incoming queries already in the request
populate: {
Image: true, // get data for media item field called "Image"
Content: {// a dynamic zone with different components,
// and those components might have some repeatable sub-component too
// We only seem to need to add the sub-components...
populate: {
Image: true,// media field called "Image"
Buttons: {// repeatable sub-component called "Buttons" used in a dynamic zone component
populate: {
page: true,// the Button component has a relation to a page item, so populate that...
},
},
Icons: true,// another repeatable sub-component used in a dynamic zone component
},
},
},
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
Hello everyone, I am sharing with you my solution for nested relationships. My entity “home-setting” has two attributes of type “media” (“image” and “logo”) and looks like:
“home-setting”: {
“interval”,
“slides”: {
“image”: { … },
“logo”: {…}
}
}.
In my home-setting “services”, when writing only this:
Here’s a modification of @urbandale’s solution, which includes the code for overwriting the findOne controller, and maintains default functionality for API requests which do not include populate=*.
This seems to be the best working solution for v4.1.7.
'use strict'
/**
* page controller
*
* info: https://forum.strapi.io/t/strapi-v4-populate-media-and-dynamiczones-from-components/12670/26
*/
const { createCoreController } = require('@strapi/strapi').factories
// declare the uid for the controller
const uid = 'api::page.page'
// see @urbandale's post for context: https://forum.strapi.io/t/strapi-v4-populate-media-and-dynamiczones-from-components/12670/26
const components = {
Image: true, // get data for media item field called "Image"
Content: {
// a dynamic zone with different components,
// and those components might have some repeatable sub-component too
// We only seem to need to add the sub-components...
populate: {
Image: true, // media field called "Image"
Buttons: {
// repeatable sub-component called "Buttons" used in a dynamic zone component
populate: {
page: true, // the Button component has a relation to a page item, so populate that...
},
},
Icons: true, // another repeatable sub-component used in a dynamic zone component
},
},
}
module.exports = createCoreController(uid, () => {
return {
async find(ctx) {
// overwrite default populate=* functionality
if (ctx.query.populate === '*') {
const entity = await strapi.entityService.findMany(uid, {
...ctx.query,
populate: components,
})
const sanitizedEntity = await this.sanitizeOutput(entity, ctx)
return this.transformResponse(sanitizedEntity)
}
// maintain default functionality for all other request
return super.find(ctx)
},
async findOne(ctx) {
const { id } = ctx.request.params
if (ctx.query.populate === '*') {
const entity = await strapi.entityService.findOne(uid, id, {
...ctx.query,
populate: components,
})
const sanitizedEntity = await this.sanitizeOutput(entity, ctx)
return this.transformResponse(sanitizedEntity)
}
return super.findOne(ctx)
},
}
})
As we already know we can populate by field like “/articles?populate=tags,author”. But, if we have a media filed in author table (lets say an ‘avatar’ image) , it will not be returned in response.
To fix this, we can just access it like this, “/articles?populate=tags,author.avatar” where avatar is the media field we want to populate in our nested author object.
I do it for selective populate use cases. Been bummed about it for couple of days and found out this works by trial and error (smashes_head)