strapi-plugin-parent-child-relationships
v1.0.3
Published
Strapi plugin to create and manage parent-child relationships between content types using custom fields
Downloads
179
Maintainers
Readme
Strapi Plugin: Parent-Child Relationships
✨ Features
- Custom Fields (Introduces two new custom fields: Parent-Child Relation and Dynamic Relation Root)
- Hierarchical Relationships (Enables creation and management of parent-child structures between content types)
- Contextual Filtering (Automatically filters and displays relevant child content based on parent selection)
- Flexible Configuration (Supports various settings for fine-tuning relationship behaviors)
- Advanced Filtering Options (Includes additional filtering capabilities like
current table column filter
) - Common Relational Table Support (Allows filtering based on shared relational fields across collections)
- Custom Query Parameters (Passes dynamic or static parameters to child relation services)
- Dynamic Relational Field Display (Uses Dynamic Relation Root to conditionally show/hide relational fields based on user selection)
🖐 Requirements
- Node.js:
>=14.19.1
- NPM:
>=6.x.x
Supported Strapi versions:
- Strapi
>=4.4.0 <5.0.0
⏳ Installation
To install the plugin, run the following command in your Strapi project directory:
With npm
:
npm install strapi-plugin-parent-child-relationships
With yarn
:
yarn add strapi-plugin-parent-child-relationships
🖐 Usage
After installation you will find two custom fields at the custom fields section of the content-type builder with the following names:
- Parent-Child Relation
- Dynamic Relation Root
📝 Overview
The Parent-Child Relationships plugin for Strapi allows you to create and manage hierarchical relationships between content types using custom fields. Enhance your content organization and establish clear hierarchical connections within your Strapi application. This functionality is ideal for structuring related data, such as a Brand -> Model -> Version hierarchy. It ensures contextual filtering and improves content organization and efficiency.
Default Strapi Relationships
In Strapi by default, relationships between content types are managed using standard relation fields. These allow you to link different content types but lack advanced features for managing hierarchical relationships such as parent-child structures
Parent-Child Relationships with the Plugin
The Parent-Child Relationships plugin introduces custom fields that extend default relationships, enabling you to filter and display data in a parent-child manner. For example, selecting a brand filters models associated with that brand, and selecting a model filters the available versions.
🔧 Configuration - Setting Up a Parent-Child Relationship
In this section we will use the following custom field:
Parent-Child Relation
Where you have the ability to configure some BASIC and ADVANCED SETTINGS. For now, it's not necessary to understand the purpose of every field in the settings I'm about to list. I’ll explain how to configure them step-by-step using examples.
BASIC SETTINGS
Name
(This is the only default Strapi basic settings field)Relation name
ADVANCED SETTINGS
parent
current table column
child table column
common relational table
current table column filter
db_name: param_name - one per row
static_value: param_name - one per row
Example: Car Collection
In this section, we'll demonstrate the configuration of a parent-child relationship using only the following options available in the Parent-Child Relation custom field settings:
BASIC OPTIONS:
- Name
- Relation name
ADVANCED OPTIONS:
- parent
- current table column
- child table column
In this example, we will configure a parent-child relationship using the Parent-Child Relationships plugin by creating a Car
collection type. This collection will include the following relational fields:
- brand (Relationship with the
Brand
collection) - e.g., Audi, BMW - model (Relationship with the
Model
collection) - e.g., A1, A3, X5, X6
When a specific brand is selected—let's say Audi—our objective is to dynamically filter and display only the associated models for that brand (e.g., A1, A3) while excluding unrelated models.
Achieving Contextual Filtering Using the Parent-Child Schema
To accomplish this, we need to establish a parent-child hierarchy where selecting a brand automatically filters the available model options. This setup requires that the model results are dynamically filtered based on the identifier of the selected parent brand.
For our example, we will use the code
field in the Brand
collection and the brand_code
field in the Model
collection to create this relationship. By linking these two fields, we can ensure that when a brand is chosen, only its corresponding models are displayed.
Collections Schema
Brand
| name | code | | ---- | ---- | | Audi | AUD | | BMW | BMW |
Model
| name | brand_code | model_code | | ---- | ---------- | ---------- | | A1 | AUD | AUD_A1 | | A3 | AUD | AUD_A3 | | X5 | BMW | BMW_X5 | | X6 | BMW | BMW_X6 |
In this scheme, each brand has a code
that can be used as a unique identifier to group the associated models. The brand_code
field in the Model
collection is used to filter the displayed models based on the selected brand.
For instance, models belonging to Audi have a brand_code
of "AUD"
, while models belonging to BMW use "BMW"
.
Leveraging the Parent-Child Relation Custom Field in Strapi
The Parent-Child Relation custom field is crucial for defining the hierarchical relationships between collections in Strapi. In our example, we will focus on the following fields:
code
in the Brand collectionbrand_code
in the Model collection
While these fields do not share identical names, it is essential that their values match to establish the desired parent-child filtering effect.
For instance, when the Audi brand is selected, which has a code
of "AUD"
, the custom field will automatically filter and display only those models that have a corresponding brand_code
of "AUD"
.
Step-by-Step Configuration in Strapi
Step 1: Create the Car Collection
Begin by creating the Car
collection type in Strapi. Ensure that it includes the necessary relational fields (brand
and model
) that will require filtering.
Step 2: Add Parent-Child Relation Custom Fields
Next, add a Parent-Child Relation custom field for each of the relational fields involved in the filtering operation.
- Brand Field: Set the Relation name as
brand
Note: The Name chosen for this field is
c_brand
. This is just my convention to indicate thatc_brand
is a custom field related to the Brand collection. You can enter any name in this field; it has no restrictions. In contrast, the Relation name field must match the name of the related relational field to which it is linked. If you try to enter a relation name that does not exist in the collection (in our case,Car
), this will not allow you to proceed when you click on the finish button, the window will remain open until a valid relation name is provided.
- Model Field: Set the Relation name as
model
.
Final Overview of Car Collection Type Fields
Here is a visual overview of the fields configured for the Car
collection type:
Advanced Configuration of Filtering Logic
Brand Field Configuration: The
c_brand
field will be configured without a parent.Inside the current table column settings option, insert
code
(the field related to thecode
in the brand collection).Inside the child table column settings option, insert
brand_code
(the field related to thebrand_code
in the child relation model collection).
This configuration ensures that the models displayed have a matching brand_code
corresponding to the selected brand's code
.
Model Field Configuration: For the model field (
c_model
), specify that its parent isc_brand
.- Inside the parent settings option, choose
c_brand
as the parent field. This ensures that the filtering logic will be based on the brand selected in thec_brand
field.
- Inside the parent settings option, choose
This configuration establishes a dependent relationship where the c_model
field (child) is filtered according to the selected c_brand
field (parent), effectively creating a parent-child relationship between the two.
You have now successfully set up a hierarchical relationship between brand
and model
in your Car
collection type by specifying parent-child filtering rules.
Customizing the Content Manager View
In the Content Manager, you will now see both the default Strapi fields and the custom fields whit the same name. This is expected behavior.
To resolve this and display only the custom fields, use the Configure The View feature to hide the default fields.
Final Output
Now, you can start creating entries with the correctly filtered brand and model relationships.
## Next Step: Extending Parent-Child Relationships to Versions
Now that we’ve learned how to create parent-child relationships between brands and models, we can extend this concept further by introducing versions.
Objective: Filter Versions Based on Model Selection
The goal is to dynamically filter and display only the versions associated with a selected model. This setup requires establishing a parent-child relationship between the selected model and its corresponding versions.
Collections Schema
Brand
| name | code | | ---- | ---- | | Audi | AUD | | BMW | BMW |
Model
| name | brand_code | model_code | | ---- | ---------- | ---------- | | A1 | AUD | AUD_A1 | | A3 | AUD | AUD_A3 | | X5 | BMW | BMW_X5 | | X6 | BMW | BMW_X6 |
Version
| name | version_code | | ------------------ | ------------ | | Sport line edition | AUD_A1 | | Business edition | AUD_A1 | | S line | AUD_A3 | | xDrive sport | BMW_X5 | | xDrive city | BMW_X5 | | xDrive competition | BMW_X6 | | xDrive sport | BMW_X6 | | xDrive 4X4 | BMW_X6 |
How It Works
- Each model has a
model_code
that can be used as a unique identifier for grouping associated versions. - The
version_code
field in the Version collection is used to filter the versions based on the selected model.
For example, versions belonging to the A1 model will have a version_code
of "AUD_A1"
, and versions for the X5 model will use "BMW_X5"
.
Setting Up Version Filtering in the Content Type Builder
Let's begin by adding a relational field for version in the Car
collection type and creating the custom field for the newly added relational field.
Next, we will configure the custom field for version and specify that its parent is the custom field c_model.
Finally, we need to configure the c_model field, defining the fields involved in filtering the versions based on the selected model.
Content Manager View with Version Filtering
In the Content Manager, you will now see three default relational fields provided by Strapi:
- brand
- model
- version
And three custom fields you have created:
- c_brand
- c_model
- c_version
In the video below, you can see the difference between using the original fields (the first three in a row, right below the name), which display all possible results without any filtering, and the custom fields displayed in separate rows.
Notice how the default fields show no filtering, while the custom fields provide contextual filtering based on your selections. You cannot select a model without first choosing a brand, and similarly, you cannot select a version without first selecting a model.
In Configure The View, you can hide the default fields and display only the custom fields to streamline the interface.
This entire process is demonstrated in the video.
Advanced Field Options: Explanation of Additional Fields
In this section, we'll explain some advanced options available in the Parent-Child Relationship settings, such as:
- common relational table
- current table column filter
- db_name: param_name - one per row
- static_value: param_name - one per row
current table column filter
The current table column filter
field allows you to add an extra layer of filtering to the results returned by a relation. Let’s look at an example.
Suppose we have an additional field called segment in our Versions
collection:
Versions
| name | version_code | segment | | ------------------ | ------------ | ------- | | Sport line edition | AUD_A1 | sport | | Business edition | AUD_A1 | city | | S line | AUD_A3 | sport | | xDrive sport | BMW_X5 | sport | | xDrive city | BMW_X5 | city | | xDrive competition | BMW_X6 | sport | | xDrive sport | BMW_X6 | sport | | xDrive 4X4 | BMW_X6 | suv |
With the current table column filter
, you can apply a JSON filter to limit the results further.
For instance, if we only want to display sports versions of cars, we can apply the following filter:
{ "segment": "sport" }
By adding this filter, only the sports versions will be displayed in the results.
For example, if we select the brand BMW and the model X5, without the filter we would see:
- xDrive sport
- xDrive city
With the filter applied, we will only see:
- xDrive sport
It's possible to create more complex filters.
For example, let's say we want to introduce an additional condition, where we only want versions whose names contain xdrive (case-insensitive).
We can modify the previously applied filter like this:
{ "segment": "sport", "name": { "$containsi": "xdrive" } }
Now, the results will only return versions where the segment
is equal to "sport" and the name
contains the word "xdrive" (case-insensitive).
common relational table
To understand how the common relational table filter works, let's walk through an example.
Suppose we've already created several entries in the Car
collection. Now, we want to associate the Car
collection as a relational field in another collection called Lead
with the following relational fields:
- car (Relationship with the
Car
collection) - options (Relationship with the
Option
collection)
Collections Schema
Fields marked with * are relational fields.
- brand*
- model*
- version*
Cars
| name | brand* | model* | version* | | ------------------ | ------- | ------- | ------------------ | | BMW X5 SPORT | BMW | X5 | xDrive sport | | BMW X6 COMPETITION | BMW | X6 | xDrive competition |
Cars
have a relationship with brand, model, and version.
Options
| name | version* | | ---------------- | ------------------ | | co-pilot pack | S line | | color pure white | xDrive competition | | full lead | xDrive competition |
Options
have a relationship with version*.
As you can see, both Cars
and Options
share a common relational field, version*.
Filtering Options Based on a Common Relation
During the creation of a Lead
, after selecting a specific car (for instance, BMW X6 COMPETITION), we want to filter the Options
displayed based on the common relational field they share—version.
This means only the following options should be displayed:
- color pure white
- full lead
Both the BMW X6 COMPETITION and these two options share the same version, xDrive competition.
How to Configure
We follow a similar approach as when we configured the parent-child relationship between brand and model. This time, the configuration is between car and options, specifying the common relational table they share.
Below are the configuration details with corresponding images:
Overview of the
Lead
fields.Configuration of
c_car
.Configuration of
c_options
.
Lead - Content Manager View
db_name: param_name - one per row
OR DYNAMIC FILTER PARAMS
Let’s take a closer look at the Car
collection and its relational field model
, which includes the following fields and values:
Models
| name | brand_code | model_code | | ---- | ---------- | ---------- | | A1 | AUD | AUD_A1 | | A3 | AUD | AUD_A3 | | X5 | BMW | BMW_X5 | | X6 | BMW | BMW_X6 |
In the advanced settings of c_model
, you can define db_name: param_name - one per row
like this:
- model_code: different_model_code_name
This configuration enables passing a dynamic filters
object to the child collection’s service, formatted as:
filters: { different_model_code_name: { '$eq': [selected_model_code_value] } }
When the model X6 is selected, and we click on the versions select, the following dynamic filter will be populated and sent to the version service:
filters: { different_model_code_name: { '$eq': 'BMW_X6' } }
static_value: param_name - one per row
OR STATIC FILTER PARAMS
In the advanced settings of c_model
, if we define static_value: param_name - one per row
, for example:
- static_value: generic_param_name
This configuration allows you to set static filter parameters that are sent directly to the child collection’s service. When we click on the versions select, the following static filter is populated and sent to the version service:
filters: { generic_param_name: { '$eq': 'static_value' } }
This is useful when you need to apply a fixed value to your query, independent of dynamic user selection.
🔧 Configuration - Setting Up a Dynamic Relation Root
Where you have the ability to configure only some BASIC SETTINGS
BASIC SETTINGS
Name
(This is the only default Strapi basic settings field)Relations: one per row
The Dynamic Relation Root custom field is a conditional custom field that allows defining a set of relational fields that can dynamically change based on a simple select input.
To understand it better, let's look at this example where we build the Course Page
collection with the following relevant fields:
- free_course (Relation with
Free_course
) - paid_course (Relation with
Paid_course
)
Our goal is to hide both fields in the Content Manager and provide the ability to choose from a dropdown which field to display/use.
Step-by-Step Configuration
The configuration is quite simple this time. You need to define in an enumeration field which relations can be selected from a dropdown.
As shown in the image, we have named the custom field c_course_type.
Now, we need to add the custom field we’ve discussed before, Parent-Child Relation, and in the basic settings, for the relation name
, we enter c_course_type
.
In the advanced settings, specify that the parent
is always c_course_type
.
That’s it! The configuration is now complete. In the Content Manager, you will have a dropdown that dynamically changes the Parent-Child Relation custom field we named c_course
.
From this point forward, you can proceed with the same logic used for the Parent-Child Relation field.
In this example, the filtering logic is based on a shared course
relational table between the selected course type (either free_course
or paid_course
) and the course_options
that must share the same relational table as the selected course.
Even though I didn’t explicitly mention it earlier, all the collections free_course, paid_course, and course_options have a relation to the Course
collection.
Below is the schema with some values that these collections have in the database:
Collections Schema
Free_course
| name | course* | other_fields | | ------------------------- | ------------------------- | ------------ | | Building Website with PHP | Building Website with PHP | ... | | Mastering Node.js | Mastering Node.js | ... | | PHP and MySQL | PHP and MySQL | ... |
Paid_course
| name | course* | other_fields | | ------------------------------- | ------------------------------- | ------------ | | Advanced Python Data Structures | Advanced Python Data Structures | ... | | PHP Automation Scripts | PHP Automation Scripts | ... | | Python for Automation | Python for Automation | ... |
- course* indicates that this is a relational field within the schema (Relation with
Course
). - Note that the names within the records match the associated course names. This is just a coincidence.
Course Option
| name | course* | other_fields | | --------------------------------------- | ------------------------------- | ------------ | | Opt - 1 Advanced Python Data Structures | Advanced Python Data Structures | ... | | Opt - 2 Advanced Python Data Structures | Advanced Python Data Structures | ... | | Opt - 3 Advanced Python Data Structures | Advanced Python Data Structures | ... | | ... | ... | ... | | Opt - 1 Building Website with PHP | Building Website with PHP | ... | | Opt - 2 Building Website with PHP | Building Website with PHP | ... | | Opt - 3 Building Website with PHP | Building Website with PHP | ... | | ... | ... | ... |
Here’s the video of how this looks in the Content Manager:
🤝 Contributing
Feel free to fork and make a pull request of this plugin. All the input is welcome!
⭐️ Show your support
Give a star if this project helped you.
🔗 Links
GitHub Repository with Examples
You can clone the repository and explore all the examples described in this guide, as well as additional configurations and use cases.
🌎 Community support
- You can contact me on the Strapi Discord channel.