feat: convert MikroORM entities to DML entities (#10043)
* feat: convert MikroORM entities to DML entities * feat: wip on repository changes * continue repositories and types rework * fix order repository usage * continue to update product category repository * Add foreign key as part of the inferred DML type * ../../core/types/src/dml/index.ts * ../../core/types/src/dml/index.ts * fix: relationships mapping * handle nullable foreign keys types * handle nullable foreign keys types * handle nullable foreign keys types * continue to update product category repository * fix all product category repositories issues * fix product category service types * fix product module service types * fix product module service types * fix repository template type * refactor: use a singleton DMLToMikroORM factory instance Since the MikroORM MetadataStorage is global, we will also have to turn DML to MikroORM entities conversion use a global bucket as well * refactor: update product module to use DML in tests * wip: tests * WIP product linkable fixes * continue type fixing and start test fixing * test: fix more tests * fix repository * fix pivot table computaion + fix mikro orm repository * fix many to many management and configuration * fix many to many management and configuration * fix many to many management and configuration * update product tag relation configuration * Introduce experimental dml hooks to fix some issues with categories * more fixes * fix product tests * add missing id prefixes * fix product category handle management * test: fix more failing tests * test: make it all green * test: fix breaking tests * fix: build issues * fix: build issues * fix: more breaking tests * refactor: fix issues after merge * refactor: fix issues after merge * refactor: surpress types error * test: fix DML failing tests * improve many to many inference + tests * Wip fix columns from product entity * remove product model before create hook and manage handle validation and transformation at the service level * test: fix breaking unit tests * fix: product module service to not update handle on product update * fix define link and joiner config * test: fix joiner config test * test: fix joiner config test * fix joiner config primary keys * Fix joiner config builder * Fix joiner config builder * test: remove only modifier from test * refactor: remove hooks usage from product collection * refactor: remove hooks usage from product-option * refactor: remove hooks usage for computing category handle * refactor: remove hooks usage from productCategory model * refactor: remove hooks from DML * refactor: remove cruft * cleanup * re add foerign key indexes * chore: remove unused types * refactor: cleanup * migration and models configuration adjustments * cleanup * fix random ordering * fix * test: fix product-category tests * test: update breaking DML tests * test: array assertion to not care about ordering * fix: temporarily apply id ordering for products * fix ordering * fix ordering remove logs --------- Co-authored-by: adrien2p <adrien.deperetti@gmail.com> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
co-authored by
adrien2p
Oli Juhl
parent
d6fa912b22
commit
9f204817b0
@@ -1,140 +1,45 @@
|
||||
import {
|
||||
DALUtils,
|
||||
Searchable,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
kebabCase,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
EventArgs,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import Product from "./product"
|
||||
|
||||
const categoryHandleIndexName = "IDX_category_handle_unique"
|
||||
const categoryHandleIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: categoryHandleIndexName,
|
||||
tableName: "product_category",
|
||||
columns: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const categoryMpathIndexName = "IDX_product_category_path"
|
||||
const categoryMpathIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: categoryMpathIndexName,
|
||||
tableName: "product_category",
|
||||
columns: ["mpath"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
categoryMpathIndexStatement.MikroORMIndex()
|
||||
categoryHandleIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_category" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductCategory {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: false })
|
||||
name?: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", default: "", nullable: false })
|
||||
description?: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: false })
|
||||
handle?: string
|
||||
|
||||
@Property({ columnType: "text", nullable: false })
|
||||
mpath?: string
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
is_active?: boolean
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
is_internal?: boolean
|
||||
|
||||
@Property({
|
||||
columnType: "integer",
|
||||
nullable: false,
|
||||
default: 0,
|
||||
const ProductCategory = model
|
||||
.define("ProductCategory", {
|
||||
id: model.id({ prefix: "pcat" }).primaryKey(),
|
||||
name: model.text().searchable(),
|
||||
description: model.text().searchable().default(""),
|
||||
handle: model.text().searchable(),
|
||||
mpath: model.text(),
|
||||
is_active: model.boolean().default(false),
|
||||
is_internal: model.boolean().default(false),
|
||||
rank: model.number().default(0),
|
||||
metadata: model.json().nullable(),
|
||||
parent_category: model
|
||||
.belongsTo(() => ProductCategory, {
|
||||
mappedBy: "category_children",
|
||||
})
|
||||
.nullable(),
|
||||
category_children: model.hasMany(() => ProductCategory, {
|
||||
mappedBy: "parent_category",
|
||||
}),
|
||||
products: model.manyToMany(() => Product, {
|
||||
mappedBy: "categories",
|
||||
}),
|
||||
})
|
||||
rank: number
|
||||
|
||||
@ManyToOne(() => ProductCategory, {
|
||||
columnType: "text",
|
||||
fieldName: "parent_category_id",
|
||||
nullable: true,
|
||||
mapToPk: true,
|
||||
onDelete: "cascade",
|
||||
.cascades({
|
||||
delete: ["category_children"],
|
||||
})
|
||||
parent_category_id?: string | null
|
||||
|
||||
@ManyToOne(() => ProductCategory, { nullable: true, persist: false })
|
||||
parent_category?: ProductCategory
|
||||
|
||||
@OneToMany({
|
||||
entity: () => ProductCategory,
|
||||
mappedBy: (productCategory) => productCategory.parent_category,
|
||||
})
|
||||
category_children = new Collection<ProductCategory>(this)
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at?: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at?: Date
|
||||
|
||||
@Index({ name: "IDX_product_category_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata: Record<string, unknown> | null = null
|
||||
|
||||
@ManyToMany(() => Product, (product) => product.categories)
|
||||
products = new Collection<Product>(this)
|
||||
|
||||
@OnInit()
|
||||
async onInit() {
|
||||
this.id = generateEntityId(this.id, "pcat")
|
||||
this.parent_category_id ??= this.parent_category?.id ?? null
|
||||
}
|
||||
|
||||
@BeforeCreate()
|
||||
async onCreate(args: EventArgs<ProductCategory>) {
|
||||
this.id = generateEntityId(this.id, "pcat")
|
||||
this.parent_category_id ??= this.parent_category?.id ?? null
|
||||
|
||||
if (!this.handle && this.name) {
|
||||
this.handle = kebabCase(this.name)
|
||||
}
|
||||
|
||||
this.mpath = `${this.mpath ? this.mpath + "." : ""}${this.id}`
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_product_category_path",
|
||||
on: ["mpath"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_category_handle_unique",
|
||||
on: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductCategory
|
||||
|
||||
@@ -1,81 +1,23 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
OneToMany,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import {
|
||||
createPsqlIndexStatementHelper,
|
||||
DALUtils,
|
||||
generateEntityId,
|
||||
kebabCase,
|
||||
Searchable,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import Product from "./product"
|
||||
|
||||
const collectionHandleIndexName = "IDX_collection_handle_unique"
|
||||
const collectionHandleIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: collectionHandleIndexName,
|
||||
tableName: "product_collection",
|
||||
columns: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
collectionHandleIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_collection" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductCollection {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
title: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
handle?: string
|
||||
|
||||
@OneToMany(() => Product, (product) => product.collection)
|
||||
products = new Collection<Product>(this)
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
const ProductCollection = model
|
||||
.define("ProductCollection", {
|
||||
id: model.id({ prefix: "pcol" }).primaryKey(),
|
||||
title: model.text().searchable(),
|
||||
handle: model.text(),
|
||||
metadata: model.json().nullable(),
|
||||
products: model.hasMany(() => Product, {
|
||||
mappedBy: "collection",
|
||||
}),
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_collection_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "pcol")
|
||||
|
||||
if (!this.handle && this.title) {
|
||||
this.handle = kebabCase(this.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_collection_handle_unique",
|
||||
on: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductCollection
|
||||
|
||||
@@ -1,88 +1,26 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
Rel,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import {
|
||||
DALUtils,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import Product from "./product"
|
||||
|
||||
const imageUrlIndexName = "IDX_product_image_url"
|
||||
const imageUrlIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: imageUrlIndexName,
|
||||
tableName: "image",
|
||||
columns: ["url"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
imageUrlIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "image" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductImage {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
url: string
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_image_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@Property({ columnType: "integer", default: 0 })
|
||||
rank: number
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
columnType: "text",
|
||||
onDelete: "cascade",
|
||||
fieldName: "product_id",
|
||||
mapToPk: true,
|
||||
})
|
||||
product_id: string
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
persist: false,
|
||||
})
|
||||
product: Rel<Product>
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "img")
|
||||
}
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "img")
|
||||
}
|
||||
}
|
||||
const ProductImage = model
|
||||
.define(
|
||||
{ tableName: "image", name: "ProductImage" },
|
||||
{
|
||||
id: model.id({ prefix: "img" }).primaryKey(),
|
||||
url: model.text(),
|
||||
metadata: model.json().nullable(),
|
||||
rank: model.number().default(0),
|
||||
product: model.belongsTo(() => Product, {
|
||||
mappedBy: "images",
|
||||
}),
|
||||
}
|
||||
)
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_product_image_url",
|
||||
on: ["url"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductImage
|
||||
|
||||
@@ -1,87 +1,27 @@
|
||||
import {
|
||||
DALUtils,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import { ProductOption, ProductVariant } from "./index"
|
||||
|
||||
const optionValueOptionIdIndexName = "IDX_option_value_option_id_unique"
|
||||
const optionValueOptionIdIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: optionValueOptionIdIndexName,
|
||||
tableName: "product_option_value",
|
||||
columns: ["option_id", "value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
optionValueOptionIdIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_option_value" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductOptionValue {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
value: string
|
||||
|
||||
@ManyToOne(() => ProductOption, {
|
||||
columnType: "text",
|
||||
fieldName: "option_id",
|
||||
mapToPk: true,
|
||||
nullable: true,
|
||||
onDelete: "cascade",
|
||||
const ProductOptionValue = model
|
||||
.define("ProductOptionValue", {
|
||||
id: model.id({ prefix: "optval" }).primaryKey(),
|
||||
value: model.text(),
|
||||
metadata: model.json().nullable(),
|
||||
option: model
|
||||
.belongsTo(() => ProductOption, {
|
||||
mappedBy: "values",
|
||||
})
|
||||
.nullable(),
|
||||
variants: model.manyToMany(() => ProductVariant, {
|
||||
mappedBy: "options",
|
||||
}),
|
||||
})
|
||||
option_id: string | null
|
||||
|
||||
@ManyToOne(() => ProductOption, {
|
||||
nullable: true,
|
||||
persist: false,
|
||||
})
|
||||
option: ProductOption | null
|
||||
|
||||
@ManyToMany(() => ProductVariant, (variant) => variant.options)
|
||||
variants = new Collection<ProductVariant>(this)
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_option_value_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "optval")
|
||||
this.option_id ??= this.option?.id ?? null
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_option_value_option_id_unique",
|
||||
on: ["option_id", "value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductOptionValue
|
||||
|
||||
@@ -1,93 +1,29 @@
|
||||
import {
|
||||
DALUtils,
|
||||
Searchable,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import { Product } from "./index"
|
||||
import ProductOptionValue from "./product-option-value"
|
||||
|
||||
const optionProductIdTitleIndexName = "IDX_option_product_id_title_unique"
|
||||
const optionProductIdTitleIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: optionProductIdTitleIndexName,
|
||||
tableName: "product_option",
|
||||
columns: ["product_id", "title"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
optionProductIdTitleIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_option" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductOption {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
title: string
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
columnType: "text",
|
||||
fieldName: "product_id",
|
||||
mapToPk: true,
|
||||
nullable: true,
|
||||
onDelete: "cascade",
|
||||
const ProductOption = model
|
||||
.define("ProductOption", {
|
||||
id: model.id({ prefix: "opt" }).primaryKey(),
|
||||
title: model.text().searchable(),
|
||||
metadata: model.json().nullable(),
|
||||
product: model.belongsTo(() => Product, {
|
||||
mappedBy: "options",
|
||||
}),
|
||||
values: model.hasMany(() => ProductOptionValue, {
|
||||
mappedBy: "option",
|
||||
}),
|
||||
})
|
||||
product_id: string | null
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
persist: false,
|
||||
nullable: true,
|
||||
.cascades({
|
||||
delete: ["values"],
|
||||
})
|
||||
product: Product | null
|
||||
|
||||
@OneToMany(() => ProductOptionValue, (value) => value.option, {
|
||||
cascade: [Cascade.PERSIST, "soft-remove" as any],
|
||||
})
|
||||
values = new Collection<ProductOptionValue>(this)
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_option_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "opt")
|
||||
this.product_id ??= this.product?.id ?? null
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_option_product_id_title_unique",
|
||||
on: ["product_id", "title"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductOption
|
||||
|
||||
@@ -1,73 +1,25 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import {
|
||||
DALUtils,
|
||||
Searchable,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import Product from "./product"
|
||||
|
||||
const tagValueIndexName = "IDX_tag_value_unique"
|
||||
const tagValueIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: tagValueIndexName,
|
||||
tableName: "product_tag",
|
||||
columns: ["value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
tagValueIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_tag" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductTag {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
value: string
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_tag_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@ManyToMany(() => Product, (product) => product.tags)
|
||||
products = new Collection<Product>(this)
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "ptag")
|
||||
}
|
||||
}
|
||||
const ProductTag = model
|
||||
.define(
|
||||
{ tableName: "product_tag", name: "ProductTag" },
|
||||
{
|
||||
id: model.id({ prefix: "ptag" }).primaryKey(),
|
||||
value: model.text().searchable(),
|
||||
metadata: model.json().nullable(),
|
||||
products: model.manyToMany(() => Product, {
|
||||
mappedBy: "tags",
|
||||
}),
|
||||
}
|
||||
)
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_tag_value_unique",
|
||||
on: ["value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductTag
|
||||
|
||||
@@ -1,67 +1,22 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import { Product } from "@models"
|
||||
|
||||
import {
|
||||
DALUtils,
|
||||
Searchable,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/framework/utils"
|
||||
|
||||
const typeValueIndexName = "IDX_type_value_unique"
|
||||
const typeValueIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: typeValueIndexName,
|
||||
tableName: "product_type",
|
||||
columns: ["value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
typeValueIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_type" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductType {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
value: string
|
||||
|
||||
@Property({ columnType: "json", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
const ProductType = model
|
||||
.define("ProductType", {
|
||||
id: model.id({ prefix: "ptyp" }).primaryKey(),
|
||||
value: model.text().searchable(),
|
||||
metadata: model.json().nullable(),
|
||||
product: model.hasMany(() => Product, {
|
||||
mappedBy: "type",
|
||||
}),
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_type_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "ptyp")
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_type_value_unique",
|
||||
on: ["value"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductType
|
||||
|
||||
@@ -1,187 +1,69 @@
|
||||
import {
|
||||
createPsqlIndexStatementHelper,
|
||||
DALUtils,
|
||||
generateEntityId,
|
||||
Searchable,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import { Product, ProductOptionValue } from "@models"
|
||||
|
||||
const variantSkuIndexName = "IDX_product_variant_sku_unique"
|
||||
const variantSkuIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: variantSkuIndexName,
|
||||
tableName: "product_variant",
|
||||
columns: ["sku"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const variantBarcodeIndexName = "IDX_product_variant_barcode_unique"
|
||||
const variantBarcodeIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: variantBarcodeIndexName,
|
||||
tableName: "product_variant",
|
||||
columns: ["barcode"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const variantEanIndexName = "IDX_product_variant_ean_unique"
|
||||
const variantEanIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: variantEanIndexName,
|
||||
tableName: "product_variant",
|
||||
columns: ["ean"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const variantUpcIndexName = "IDX_product_variant_upc_unique"
|
||||
const variantUpcIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: variantUpcIndexName,
|
||||
tableName: "product_variant",
|
||||
columns: ["upc"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const variantProductIdIndexName = "IDX_product_variant_product_id"
|
||||
const variantProductIdIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: variantProductIdIndexName,
|
||||
tableName: "product_variant",
|
||||
columns: ["product_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
variantProductIdIndexStatement.MikroORMIndex()
|
||||
variantSkuIndexStatement.MikroORMIndex()
|
||||
variantBarcodeIndexStatement.MikroORMIndex()
|
||||
variantEanIndexStatement.MikroORMIndex()
|
||||
variantUpcIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product_variant" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductVariant {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
title: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
sku?: string | null
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
barcode?: string | null
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
ean?: string | null
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
upc?: string | null
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
allow_backorder?: boolean = false
|
||||
|
||||
@Property({ columnType: "boolean", default: true })
|
||||
manage_inventory?: boolean = true
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
hs_code?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
origin_country?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
mid_code?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
material?: string | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true })
|
||||
weight?: number | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true })
|
||||
length?: number | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true })
|
||||
height?: number | null
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true })
|
||||
width?: number | null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@Property({
|
||||
columnType: "integer",
|
||||
nullable: true,
|
||||
default: 0,
|
||||
const ProductVariant = model
|
||||
.define("ProductVariant", {
|
||||
id: model.id({ prefix: "variant" }).primaryKey(),
|
||||
title: model.text().searchable(),
|
||||
sku: model.text().searchable().nullable(),
|
||||
barcode: model.text().searchable().nullable(),
|
||||
ean: model.text().searchable().nullable(),
|
||||
upc: model.text().searchable().nullable(),
|
||||
allow_backorder: model.boolean().default(false),
|
||||
manage_inventory: model.boolean().default(true),
|
||||
hs_code: model.text().nullable(),
|
||||
origin_country: model.text().nullable(),
|
||||
mid_code: model.text().nullable(),
|
||||
material: model.text().nullable(),
|
||||
weight: model.number().nullable(),
|
||||
length: model.number().nullable(),
|
||||
height: model.number().nullable(),
|
||||
width: model.number().nullable(),
|
||||
metadata: model.json().nullable(),
|
||||
variant_rank: model.number().default(0).nullable(),
|
||||
product: model
|
||||
.belongsTo(() => Product, {
|
||||
mappedBy: "variants",
|
||||
})
|
||||
.nullable(),
|
||||
options: model.manyToMany(() => ProductOptionValue, {
|
||||
pivotTable: "product_variant_option",
|
||||
mappedBy: "variants",
|
||||
joinColumn: "variant_id",
|
||||
inverseJoinColumn: "option_value_id",
|
||||
}),
|
||||
})
|
||||
variant_rank?: number | null
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
onDelete: "cascade",
|
||||
fieldName: "product_id",
|
||||
mapToPk: true,
|
||||
})
|
||||
product_id: string | null
|
||||
|
||||
@ManyToOne(() => Product, {
|
||||
persist: false,
|
||||
nullable: true,
|
||||
})
|
||||
product: Product | null
|
||||
|
||||
@ManyToMany(() => ProductOptionValue, "variants", {
|
||||
owner: true,
|
||||
pivotTable: "product_variant_option",
|
||||
joinColumn: "variant_id",
|
||||
inverseJoinColumn: "option_value_id",
|
||||
})
|
||||
options = new Collection<ProductOptionValue>(this)
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_variant_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "variant")
|
||||
this.product_id ??= this.product?.id ?? null
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_product_variant_product_id",
|
||||
on: ["product_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_variant_sku_unique",
|
||||
on: ["sku"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_variant_barcode_unique",
|
||||
on: ["barcode"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_variant_ean_unique",
|
||||
on: ["ean"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_variant_upc_unique",
|
||||
on: ["upc"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default ProductVariant
|
||||
|
||||
@@ -1,222 +1,86 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Collection,
|
||||
Entity,
|
||||
Enum,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { model, ProductUtils } from "@medusajs/framework/utils"
|
||||
|
||||
import {
|
||||
createPsqlIndexStatementHelper,
|
||||
DALUtils,
|
||||
generateEntityId,
|
||||
ProductUtils,
|
||||
Searchable,
|
||||
toHandle,
|
||||
} from "@medusajs/framework/utils"
|
||||
import ProductCategory from "./product-category"
|
||||
import ProductCollection from "./product-collection"
|
||||
import ProductImage from "./product-image"
|
||||
import ProductOption from "./product-option"
|
||||
import ProductTag from "./product-tag"
|
||||
import ProductType from "./product-type"
|
||||
import ProductImage from "./product-image"
|
||||
import ProductOption from "./product-option"
|
||||
import ProductVariant from "./product-variant"
|
||||
import ProductCategory from "./product-category"
|
||||
import ProductCollection from "./product-collection"
|
||||
|
||||
const productHandleIndexName = "IDX_product_handle_unique"
|
||||
const productHandleIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: productHandleIndexName,
|
||||
tableName: "product",
|
||||
columns: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const productTypeIndexName = "IDX_product_type_id"
|
||||
const productTypeIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: productTypeIndexName,
|
||||
tableName: "product",
|
||||
columns: ["type_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
const productCollectionIndexName = "IDX_product_collection_id"
|
||||
const productCollectionIndexStatement = createPsqlIndexStatementHelper({
|
||||
name: productCollectionIndexName,
|
||||
tableName: "product",
|
||||
columns: ["collection_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
})
|
||||
|
||||
productTypeIndexStatement.MikroORMIndex()
|
||||
productCollectionIndexStatement.MikroORMIndex()
|
||||
productHandleIndexStatement.MikroORMIndex()
|
||||
@Entity({ tableName: "product" })
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class Product {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text" })
|
||||
title: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
handle?: string
|
||||
|
||||
@Searchable()
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
subtitle?: string | null
|
||||
|
||||
@Searchable()
|
||||
@Property({
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
const Product = model
|
||||
.define("Product", {
|
||||
id: model.id({ prefix: "prod" }).primaryKey(),
|
||||
title: model.text().searchable(),
|
||||
handle: model.text(),
|
||||
subtitle: model.text().searchable().nullable(),
|
||||
description: model.text().searchable().nullable(),
|
||||
is_giftcard: model.boolean().default(false),
|
||||
status: model
|
||||
.enum(ProductUtils.ProductStatus)
|
||||
.default(ProductUtils.ProductStatus.DRAFT),
|
||||
thumbnail: model.text().nullable(),
|
||||
weight: model.text().nullable(),
|
||||
length: model.text().nullable(),
|
||||
height: model.text().nullable(),
|
||||
width: model.text().nullable(),
|
||||
origin_country: model.text().nullable(),
|
||||
hs_code: model.text().nullable(),
|
||||
mid_code: model.text().nullable(),
|
||||
material: model.text().nullable(),
|
||||
discountable: model.boolean().default(true),
|
||||
external_id: model.text().nullable(),
|
||||
metadata: model.json().nullable(),
|
||||
variants: model.hasMany(() => ProductVariant, {
|
||||
mappedBy: "product",
|
||||
}),
|
||||
type: model
|
||||
.belongsTo(() => ProductType, {
|
||||
mappedBy: "product",
|
||||
})
|
||||
.nullable(),
|
||||
tags: model.manyToMany(() => ProductTag, {
|
||||
mappedBy: "products",
|
||||
pivotTable: "product_tags",
|
||||
}),
|
||||
options: model.hasMany(() => ProductOption, {
|
||||
mappedBy: "product",
|
||||
}),
|
||||
images: model.hasMany(() => ProductImage, {
|
||||
mappedBy: "product",
|
||||
}),
|
||||
collection: model
|
||||
.belongsTo(() => ProductCollection, {
|
||||
mappedBy: "products",
|
||||
})
|
||||
.nullable(),
|
||||
categories: model.manyToMany(() => ProductCategory, {
|
||||
pivotTable: "product_category_product",
|
||||
mappedBy: "products",
|
||||
}),
|
||||
})
|
||||
description?: string | null
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
is_giftcard!: boolean
|
||||
|
||||
@Enum(() => ProductUtils.ProductStatus)
|
||||
@Property({ default: ProductUtils.ProductStatus.DRAFT })
|
||||
status!: ProductUtils.ProductStatus
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
thumbnail?: string | null
|
||||
|
||||
@OneToMany(() => ProductOption, (o) => o.product, {
|
||||
cascade: ["soft-remove"] as any,
|
||||
.cascades({
|
||||
delete: ["variants", "options", "images"],
|
||||
})
|
||||
options = new Collection<ProductOption>(this)
|
||||
|
||||
@Searchable()
|
||||
@OneToMany(() => ProductVariant, (variant) => variant.product, {
|
||||
cascade: ["soft-remove"] as any,
|
||||
})
|
||||
variants = new Collection<ProductVariant>(this)
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
weight?: number | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
length?: number | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
height?: number | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
width?: number | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
origin_country?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
hs_code?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
mid_code?: string | null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
material?: string | null
|
||||
|
||||
@Searchable()
|
||||
@ManyToOne(() => ProductCollection, {
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
fieldName: "collection_id",
|
||||
mapToPk: true,
|
||||
onDelete: "set null",
|
||||
})
|
||||
collection_id: string | null
|
||||
|
||||
@ManyToOne(() => ProductCollection, {
|
||||
nullable: true,
|
||||
persist: false,
|
||||
})
|
||||
collection: ProductCollection | null
|
||||
|
||||
@ManyToOne(() => ProductType, {
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
fieldName: "type_id",
|
||||
mapToPk: true,
|
||||
onDelete: "set null",
|
||||
})
|
||||
type_id: string | null
|
||||
|
||||
@ManyToOne(() => ProductType, {
|
||||
nullable: true,
|
||||
persist: false,
|
||||
})
|
||||
type: ProductType | null
|
||||
|
||||
@ManyToMany(() => ProductTag, "products", {
|
||||
owner: true,
|
||||
pivotTable: "product_tags",
|
||||
index: "IDX_product_tag_id",
|
||||
})
|
||||
tags = new Collection<ProductTag>(this)
|
||||
|
||||
@OneToMany(() => ProductImage, (image) => image.product_id, {
|
||||
cascade: [Cascade.PERSIST, Cascade.REMOVE],
|
||||
})
|
||||
images = new Collection<ProductImage>(this)
|
||||
|
||||
@ManyToMany(() => ProductCategory, "products", {
|
||||
owner: true,
|
||||
pivotTable: "product_category_product",
|
||||
})
|
||||
categories = new Collection<ProductCategory>(this)
|
||||
|
||||
@Property({ columnType: "boolean", default: true })
|
||||
discountable: boolean
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
external_id?: string | null
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@Index({ name: "IDX_product_deleted_at" })
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
deleted_at?: Date
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata?: Record<string, unknown> | null
|
||||
|
||||
@OnInit()
|
||||
@BeforeCreate()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "prod")
|
||||
this.type_id ??= this.type?.id ?? null
|
||||
this.collection_id ??= this.collection?.id ?? null
|
||||
|
||||
if (!this.handle && this.title) {
|
||||
this.handle = toHandle(this.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
.indexes([
|
||||
{
|
||||
name: "IDX_product_handle_unique",
|
||||
on: ["handle"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_type_id",
|
||||
on: ["type_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
name: "IDX_product_collection_id",
|
||||
on: ["collection_id"],
|
||||
unique: false,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default Product
|
||||
|
||||
Reference in New Issue
Block a user