feat(product): return parents tree (#6944)
*What* include option `include_ancestors_tree` to list products including the parent tree.
This commit is contained in:
committed by
GitHub
parent
8ecfa4b6f5
commit
09a2220569
6
.changeset/forty-lamps-admire.md
Normal file
6
.changeset/forty-lamps-admire.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/product": patch
|
||||
"@medusajs/types": patch
|
||||
---
|
||||
|
||||
Add parents to product categories
|
||||
@@ -65,3 +65,127 @@ export const productCategoriesRankData = [
|
||||
rank: 2,
|
||||
},
|
||||
]
|
||||
|
||||
export const eletronicsCategoriesData = eval(`[
|
||||
{
|
||||
id: "electronics",
|
||||
name: "Electronics",
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
id: "computers",
|
||||
name: "Computers & Accessories",
|
||||
parent_category_id: "electronics",
|
||||
},
|
||||
{
|
||||
id: "desktops",
|
||||
name: "Desktops",
|
||||
parent_category_id: "computers",
|
||||
},
|
||||
{
|
||||
id: "gaming-desktops",
|
||||
name: "Gaming Desktops",
|
||||
parent_category_id: "desktops",
|
||||
},
|
||||
{
|
||||
id: "office-desktops",
|
||||
name: "Office Desktops",
|
||||
parent_category_id: "desktops",
|
||||
},
|
||||
{
|
||||
id: "laptops",
|
||||
name: "Laptops",
|
||||
parent_category_id: "computers",
|
||||
},
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
name: "Gaming Laptops",
|
||||
parent_category_id: "laptops",
|
||||
},
|
||||
{
|
||||
id: "budget-gaming",
|
||||
name: "Budget Gaming Laptops",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
{
|
||||
id: "high-performance",
|
||||
name: "High Performance Gaming Laptops",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
{
|
||||
id: "vr-ready",
|
||||
name: "VR-Ready High Performance Gaming Laptops",
|
||||
parent_category_id: "high-performance",
|
||||
},
|
||||
{
|
||||
id: "4k-gaming",
|
||||
name: "4K Gaming Laptops",
|
||||
parent_category_id: "high-performance",
|
||||
},
|
||||
{
|
||||
id: "ultrabooks",
|
||||
name: "Ultrabooks",
|
||||
parent_category_id: "laptops",
|
||||
},
|
||||
{
|
||||
id: "thin-light",
|
||||
name: "Thin & Light Ultrabooks",
|
||||
parent_category_id: "ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "convertible-ultrabooks",
|
||||
name: "Convertible Ultrabooks",
|
||||
parent_category_id: "ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "touchscreen-ultrabooks",
|
||||
name: "Touchscreen Ultrabooks",
|
||||
parent_category_id: "convertible-ultrabooks",
|
||||
},
|
||||
{
|
||||
id: "detachable-ultrabooks",
|
||||
name: "Detachable Ultrabooks",
|
||||
parent_category_id: "convertible-ultrabooks",
|
||||
},
|
||||
|
||||
{
|
||||
id: "mobile",
|
||||
name: "Mobile Phones & Accessories",
|
||||
parent_category_id: "electronics",
|
||||
},
|
||||
{
|
||||
id: "smartphones",
|
||||
name: "Smartphones",
|
||||
parent_category_id: "mobile",
|
||||
},
|
||||
{
|
||||
id: "android-phones",
|
||||
name: "Android Phones",
|
||||
parent_category_id: "smartphones",
|
||||
},
|
||||
{
|
||||
id: "flagship-phones",
|
||||
name: "Flagship Smartphones",
|
||||
parent_category_id: "android-phones",
|
||||
},
|
||||
{
|
||||
id: "budget-phones",
|
||||
name: "Budget Smartphones",
|
||||
parent_category_id: "android-phones",
|
||||
},
|
||||
{
|
||||
id: "iphones",
|
||||
name: "iPhones",
|
||||
parent_category_id: "smartphones",
|
||||
},
|
||||
{
|
||||
id: "pro-phones",
|
||||
name: "Pro Models",
|
||||
parent_category_id: "iphones",
|
||||
},
|
||||
{
|
||||
id: "mini-phones",
|
||||
name: "Mini Models",
|
||||
parent_category_id: "iphones",
|
||||
},
|
||||
]`)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { ProductCategoryService } from "@services"
|
||||
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||
import {
|
||||
eletronicsCategoriesData,
|
||||
productCategoriesData,
|
||||
productCategoriesRankData,
|
||||
} from "../../../__fixtures__/product-category/data"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -138,14 +139,12 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
@@ -153,7 +152,6 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-b-1",
|
||||
@@ -161,7 +159,6 @@ moduleIntegrationTestRunner({
|
||||
mpath:
|
||||
"category-0.category-1.category-1-b.category-1-b-1.",
|
||||
parent_category_id: "category-1-b",
|
||||
parent_category: "category-1-b",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
@@ -173,6 +170,365 @@ moduleIntegrationTestRunner({
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of descendants when include_descendants_tree is true for multiple results", async () => {
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
parent_category_id: "category-1",
|
||||
include_descendants_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
category_children: [],
|
||||
},
|
||||
{
|
||||
id: "category-1-b",
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
category_children: [
|
||||
{
|
||||
id: "category-1-b-1",
|
||||
handle: "category-1-b-1",
|
||||
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
||||
parent_category_id: "category-1-b",
|
||||
category_children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of parents when include_ancestors_tree is true", async () => {
|
||||
await createProductCategories(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
eletronicsCategoriesData
|
||||
)
|
||||
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
id: "4k-gaming",
|
||||
include_ancestors_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "4k-gaming",
|
||||
handle: "4k-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
||||
parent_category_id: "high-performance",
|
||||
parent_category: {
|
||||
id: "high-performance",
|
||||
parent_category_id: "gaming-laptops",
|
||||
handle: "high-performance-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||
parent_category: {
|
||||
id: "gaming-laptops",
|
||||
handle: "gaming-laptops",
|
||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||
parent_category_id: "laptops",
|
||||
parent_category: {
|
||||
id: "laptops",
|
||||
parent_category_id: "computers",
|
||||
handle: "laptops",
|
||||
mpath: "electronics.computers.laptops.",
|
||||
parent_category: {
|
||||
id: "computers",
|
||||
handle: "computers-&-accessories",
|
||||
mpath: "electronics.computers.",
|
||||
parent_category_id: "electronics",
|
||||
parent_category: {
|
||||
id: "electronics",
|
||||
parent_category_id: null,
|
||||
handle: "electronics",
|
||||
mpath: "electronics.",
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of descendants when include_descendants_tree is true", async () => {
|
||||
await createProductCategories(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
eletronicsCategoriesData
|
||||
)
|
||||
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
include_descendants_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
handle: "gaming-laptops",
|
||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||
parent_category_id: "laptops",
|
||||
category_children: [
|
||||
{
|
||||
id: "budget-gaming",
|
||||
handle: "budget-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
||||
parent_category_id: "gaming-laptops",
|
||||
category_children: [],
|
||||
},
|
||||
{
|
||||
id: "high-performance",
|
||||
handle: "high-performance-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||
parent_category_id: "gaming-laptops",
|
||||
category_children: [
|
||||
{
|
||||
id: "4k-gaming",
|
||||
handle: "4k-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
||||
parent_category_id: "high-performance",
|
||||
category_children: [],
|
||||
},
|
||||
{
|
||||
id: "vr-ready",
|
||||
handle: "vr-ready-high-performance-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.vr-ready.",
|
||||
parent_category_id: "high-performance",
|
||||
category_children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true", async () => {
|
||||
await createProductCategories(
|
||||
MikroOrmWrapper.forkManager(),
|
||||
eletronicsCategoriesData
|
||||
)
|
||||
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
include_descendants_tree: true,
|
||||
include_ancestors_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "gaming-laptops",
|
||||
handle: "gaming-laptops",
|
||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||
parent_category_id: "laptops",
|
||||
parent_category: {
|
||||
id: "laptops",
|
||||
handle: "laptops",
|
||||
mpath: "electronics.computers.laptops.",
|
||||
parent_category_id: "computers",
|
||||
parent_category: {
|
||||
id: "computers",
|
||||
handle: "computers-&-accessories",
|
||||
mpath: "electronics.computers.",
|
||||
parent_category_id: "electronics",
|
||||
parent_category: {
|
||||
id: "electronics",
|
||||
handle: "electronics",
|
||||
mpath: "electronics.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
category_children: [
|
||||
{
|
||||
id: "budget-gaming",
|
||||
handle: "budget-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
{
|
||||
id: "high-performance",
|
||||
handle: "high-performance-gaming-laptops",
|
||||
mpath:
|
||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||
parent_category_id: "gaming-laptops",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of parents when include_ancestors_tree is true for multiple results", async () => {
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
parent_category_id: "category-1",
|
||||
include_ancestors_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: {
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: {
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "category-1-b",
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: {
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: {
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true for multiple results", async () => {
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
parent_category_id: "category-1",
|
||||
include_descendants_tree: true,
|
||||
include_ancestors_tree: true,
|
||||
},
|
||||
{
|
||||
select: ["id", "handle"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedObject = JSON.parse(
|
||||
JSON.stringify(productCategoryResults)
|
||||
)
|
||||
|
||||
expect(serializedObject).toEqual([
|
||||
{
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: {
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: {
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
category_children: [],
|
||||
},
|
||||
{
|
||||
id: "category-1-b",
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: {
|
||||
id: "category-1",
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: {
|
||||
id: "category-0",
|
||||
handle: "category-0",
|
||||
mpath: "category-0.",
|
||||
parent_category_id: null,
|
||||
parent_category: null,
|
||||
},
|
||||
},
|
||||
category_children: [
|
||||
{
|
||||
id: "category-1-b-1",
|
||||
handle: "category-1-b-1",
|
||||
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
||||
parent_category_id: "category-1-b",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("scopes children when include_descendants_tree is true", async () => {
|
||||
const productCategoryResults = await service.list(
|
||||
{
|
||||
@@ -202,14 +558,12 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
@@ -456,14 +810,12 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
@@ -471,7 +823,6 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1-b",
|
||||
mpath: "category-0.category-1.category-1-b.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-b-1",
|
||||
@@ -479,7 +830,6 @@ moduleIntegrationTestRunner({
|
||||
mpath:
|
||||
"category-0.category-1.category-1-b.category-1-b-1.",
|
||||
parent_category_id: "category-1-b",
|
||||
parent_category: "category-1-b",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
@@ -522,14 +872,12 @@ moduleIntegrationTestRunner({
|
||||
handle: "category-1",
|
||||
mpath: "category-0.category-1.",
|
||||
parent_category_id: "category-0",
|
||||
parent_category: "category-0",
|
||||
category_children: [
|
||||
expect.objectContaining({
|
||||
id: "category-1-a",
|
||||
handle: "category-1-a",
|
||||
mpath: "category-0.category-1.category-1-a.",
|
||||
parent_category_id: "category-1",
|
||||
parent_category: "category-1",
|
||||
category_children: [],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
ProductCategoryTransformOptions,
|
||||
ProductTypes,
|
||||
} from "@medusajs/types"
|
||||
import { DALUtils, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import {
|
||||
LoadStrategy,
|
||||
FilterQuery as MikroFilterQuery,
|
||||
FindOptions as MikroOptions,
|
||||
LoadStrategy,
|
||||
} from "@mikro-orm/core"
|
||||
import { ProductCategory } from "@models"
|
||||
import { Context, DAL, ProductCategoryTransformOptions } from "@medusajs/types"
|
||||
import groupBy from "lodash/groupBy"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { DALUtils, isDefined, MedusaError } from "@medusajs/utils"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { ProductCategory } from "@models"
|
||||
|
||||
export type ReorderConditions = {
|
||||
targetCategoryId: string
|
||||
@@ -34,13 +37,13 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
const { includeDescendantsTree } = transformOptions
|
||||
const { includeDescendantsTree, includeParentsTree } = transformOptions
|
||||
findOptions_.options ??= {}
|
||||
const fields = (findOptions_.options.fields ??= [])
|
||||
|
||||
// Ref: Building descendants
|
||||
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
||||
if (includeDescendantsTree) {
|
||||
if (includeDescendantsTree || includeParentsTree) {
|
||||
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
||||
fields.indexOf("parent_category_id") === -1 &&
|
||||
fields.push("parent_category_id")
|
||||
@@ -56,65 +59,118 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
||||
findOptions_.options as MikroOptions<ProductCategory>
|
||||
)
|
||||
|
||||
if (!includeDescendantsTree) {
|
||||
if (!includeDescendantsTree && !includeParentsTree) {
|
||||
return productCategories
|
||||
}
|
||||
|
||||
return this.buildProductCategoriesWithDescendants(
|
||||
return this.buildProductCategoriesWithTree(
|
||||
{
|
||||
descendants: includeDescendantsTree,
|
||||
parents: includeParentsTree,
|
||||
},
|
||||
productCategories,
|
||||
findOptions_
|
||||
)
|
||||
}
|
||||
|
||||
async buildProductCategoriesWithDescendants(
|
||||
async buildProductCategoriesWithTree(
|
||||
include: {
|
||||
descendants?: boolean
|
||||
parents?: boolean
|
||||
},
|
||||
productCategories: ProductCategory[],
|
||||
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<ProductCategory[]> {
|
||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
for (let productCategory of productCategories) {
|
||||
const whereOptions = {
|
||||
...findOptions.where,
|
||||
mpath: {
|
||||
$like: `${productCategory.mpath}%`,
|
||||
},
|
||||
const hasPopulateParentCategory = (
|
||||
findOptions.options?.populate ?? ([] as any)
|
||||
).find((pop) => pop.field === "parent_category")
|
||||
|
||||
include.parents = include.parents || hasPopulateParentCategory
|
||||
|
||||
const mpaths: any[] = []
|
||||
const parentMpaths = new Set()
|
||||
for (const cat of productCategories) {
|
||||
if (include.descendants) {
|
||||
mpaths.push({ mpath: { $like: `${cat.mpath}%` } })
|
||||
}
|
||||
|
||||
if ("parent_category_id" in whereOptions) {
|
||||
delete whereOptions.parent_category_id
|
||||
}
|
||||
|
||||
if ("id" in whereOptions) {
|
||||
delete whereOptions.id
|
||||
}
|
||||
|
||||
const descendantsForCategory = await manager.find(
|
||||
ProductCategory,
|
||||
whereOptions as MikroFilterQuery<ProductCategory>,
|
||||
findOptions.options as MikroOptions<ProductCategory>
|
||||
)
|
||||
|
||||
const descendantsByParentId = groupBy(
|
||||
descendantsForCategory,
|
||||
(pc) => pc.parent_category_id
|
||||
)
|
||||
|
||||
const addChildrenToCategory = (category, children) => {
|
||||
category.category_children = (children || []).map((categoryChild) => {
|
||||
const moreChildren = descendantsByParentId[categoryChild.id] || []
|
||||
|
||||
return addChildrenToCategory(categoryChild, moreChildren)
|
||||
if (include.parents) {
|
||||
let parent = ""
|
||||
cat.mpath?.split(".").forEach((mpath) => {
|
||||
if (mpath === "") {
|
||||
return
|
||||
}
|
||||
parentMpaths.add(parent + mpath + ".")
|
||||
parent += mpath + "."
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mpaths.push({ mpath: Array.from(parentMpaths) })
|
||||
|
||||
const whereOptions = {
|
||||
...findOptions.where,
|
||||
$or: mpaths,
|
||||
}
|
||||
|
||||
if ("parent_category_id" in whereOptions) {
|
||||
delete whereOptions.parent_category_id
|
||||
}
|
||||
|
||||
if ("id" in whereOptions) {
|
||||
delete whereOptions.id
|
||||
}
|
||||
|
||||
let allCategories = await manager.find(
|
||||
ProductCategory,
|
||||
whereOptions as MikroFilterQuery<ProductCategory>,
|
||||
findOptions.options as MikroOptions<ProductCategory>
|
||||
)
|
||||
|
||||
allCategories = JSON.parse(JSON.stringify(allCategories))
|
||||
|
||||
const categoriesById = new Map(allCategories.map((cat) => [cat.id, cat]))
|
||||
|
||||
allCategories.forEach((cat: any) => {
|
||||
if (cat.parent_category_id) {
|
||||
cat.parent_category = categoriesById.get(cat.parent_category_id)
|
||||
}
|
||||
})
|
||||
|
||||
const populateChildren = (category, level = 0) => {
|
||||
const categories = allCategories.filter(
|
||||
(child) => child.parent_category_id === category.id
|
||||
)
|
||||
|
||||
if (include.descendants) {
|
||||
category.category_children = categories.map((child) => {
|
||||
return populateChildren(categoriesById.get(child.id), level + 1)
|
||||
})
|
||||
}
|
||||
|
||||
if (level === 0) {
|
||||
return category
|
||||
}
|
||||
|
||||
const children = descendantsByParentId[productCategory.id] || []
|
||||
productCategory = addChildrenToCategory(productCategory, children)
|
||||
if (include.parents) {
|
||||
delete category.category_children
|
||||
}
|
||||
if (include.descendants) {
|
||||
delete category.parent_category
|
||||
}
|
||||
|
||||
return category
|
||||
}
|
||||
|
||||
return productCategories
|
||||
const populatedProductCategories = productCategories.map((cat) => {
|
||||
const fullCategory = categoriesById.get(cat.id)
|
||||
return populateChildren(fullCategory)
|
||||
})
|
||||
|
||||
return populatedProductCategories
|
||||
}
|
||||
|
||||
async findAndCount(
|
||||
@@ -125,7 +181,7 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
const { includeDescendantsTree } = transformOptions
|
||||
const { includeDescendantsTree, includeParentsTree } = transformOptions
|
||||
findOptions_.options ??= {}
|
||||
const fields = (findOptions_.options.fields ??= [])
|
||||
|
||||
@@ -146,13 +202,20 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
||||
findOptions_.where as MikroFilterQuery<ProductCategory>,
|
||||
findOptions_.options as MikroOptions<ProductCategory>
|
||||
)
|
||||
|
||||
if (!includeDescendantsTree) {
|
||||
return [productCategories, count]
|
||||
}
|
||||
|
||||
if (!includeDescendantsTree && !includeParentsTree) {
|
||||
return [productCategories, count]
|
||||
}
|
||||
|
||||
return [
|
||||
await this.buildProductCategoriesWithDescendants(
|
||||
await this.buildProductCategoriesWithTree(
|
||||
{
|
||||
descendants: includeDescendantsTree,
|
||||
parents: includeParentsTree,
|
||||
},
|
||||
productCategories,
|
||||
findOptions_
|
||||
),
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ProductCategory } from "@models"
|
||||
import { ProductCategoryRepository } from "@repositories"
|
||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
isDefined,
|
||||
} from "@medusajs/utils"
|
||||
import { ProductCategory } from "@models"
|
||||
import { ProductCategoryRepository } from "@repositories"
|
||||
|
||||
type InjectedDependencies = {
|
||||
productCategoryRepository: DAL.TreeRepositoryService
|
||||
@@ -71,8 +71,10 @@ export default class ProductCategoryService<
|
||||
): Promise<TEntity[]> {
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||
includeParentsTree: filters?.include_ancestors_tree || false,
|
||||
}
|
||||
delete filters.include_descendants_tree
|
||||
delete filters.include_ancestors_tree
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
filters,
|
||||
@@ -95,8 +97,10 @@ export default class ProductCategoryService<
|
||||
): Promise<[TEntity[], number]> {
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||
includeParentsTree: filters?.include_ancestors_tree || false,
|
||||
}
|
||||
delete filters.include_descendants_tree
|
||||
delete filters.include_ancestors_tree
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
filters,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { RepositoryTransformOptions } from '../common'
|
||||
import { RepositoryTransformOptions } from "../common"
|
||||
|
||||
export interface ProductCategoryTransformOptions extends RepositoryTransformOptions {
|
||||
export interface ProductCategoryTransformOptions
|
||||
extends RepositoryTransformOptions {
|
||||
includeDescendantsTree?: boolean
|
||||
includeParentsTree?: boolean
|
||||
}
|
||||
|
||||
@@ -915,6 +915,10 @@ export interface FilterableProductCategoryProps
|
||||
* Whether to include children of retrieved product categories.
|
||||
*/
|
||||
include_descendants_tree?: boolean
|
||||
/**
|
||||
* Whether to include parents of retrieved product categories.
|
||||
*/
|
||||
include_ancestors_tree?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user