From 2bdb21cf66a2c94436c160d58e736590edc1f8e3 Mon Sep 17 00:00:00 2001 From: Kevin Mattutat Date: Thu, 14 Nov 2024 15:21:27 +0100 Subject: [PATCH 1/5] docs: Added guide on how to make entities channel aware --- .../guides/core-concepts/channels/index.md | 2 +- .../developer-guide/channel-aware/index.md | 111 ++++++++++++++++++ .../src/lib/catalog/src/catalog.routes.ts | 1 + .../src/service/services/channel.service.ts | 5 +- 4 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 docs/docs/guides/developer-guide/channel-aware/index.md diff --git a/docs/docs/guides/core-concepts/channels/index.md b/docs/docs/guides/core-concepts/channels/index.md index a30305f067..72b9c4b10c 100644 --- a/docs/docs/guides/core-concepts/channels/index.md +++ b/docs/docs/guides/core-concepts/channels/index.md @@ -31,7 +31,7 @@ Many entities are channel-aware, meaning that they can be associated with a mult - [`Asset`](/reference/typescript-api/entities/asset/) - [`Collection`](/reference/typescript-api/entities/collection/) -- [`Customer`](/reference/typescript-api/entities/customer-group/) +- [`Customer`](/reference/typescript-api/entities/customer/) - [`Facet`](/reference/typescript-api/entities/facet/) - [`FacetValue`](/reference/typescript-api/entities/facet-value/) - [`Order`](/reference/typescript-api/entities/order/) diff --git a/docs/docs/guides/developer-guide/channel-aware/index.md b/docs/docs/guides/developer-guide/channel-aware/index.md new file mode 100644 index 0000000000..ba9bd29059 --- /dev/null +++ b/docs/docs/guides/developer-guide/channel-aware/index.md @@ -0,0 +1,111 @@ +--- +title: "Channel-aware Entities" +showtoc: true +--- + +# Defining channel-aware entities + +Making an entity channel-aware means that it can be associated with a specific [Channel](/reference/typescript-api/channel/). +This is useful when you want to have different data or features for different channels. First you will have to create +a base entity ([Define a database entity](/guides/developer-guide/database-entity/)) that implements the `ChannelAware` interface. +This interface requires the entity to have a `channels` property + +```ts title="src/plugins/requests/entities/product-request.entity.ts" +import { DeepPartial } from '@vendure/common/lib/shared-types'; +import { VendureEntity, Product, EntityId, ID, ChannelAware } from '@vendure/core'; +import { Column, Entity, ManyToOne } from 'typeorm'; + +@Entity() +class ProductRequest extends VendureEntity { + constructor(input?: DeepPartial) { + super(input); + } + + @ManyToOne(type => Product) + product: Product; + + @EntityId() + productId: ID; + + @Column() + text: string; + + @ManyToMany(() => Channel) + @JoinTable() + channels: Channel[]; +} +``` + +# Creating channel-aware entities + +Creating a channel-aware entity is similar to creating a regular entity. The only difference is that you need to assign the entity to the current channel. +This can be done by using the `ChannelService` which provides the `assignToCurrentChannel` helper function. + +:::info +The `assignToCurrentChannel` function will only assign the `channels` property of the entity. You will still need to save the entity to the database. +::: + +```ts title="src/plugins/requests/service/product-request.service.ts" +import { ChannelService } from '@vendure/core'; + +export class RequestService { + + constructor(private channelService: ChannelService) {} + + async create(ctx: RequestContext, input: CreateRequestInput): Promise { + const request = new ProductRequest(input); + // Now we need to assign the request to the current channel (+ default channel) + await this.channelService.assignToCurrentChannel(requestInput, ctx); + + return await this.connection.getRepository(ProductRequest).save(request); + } +} +``` +For [Translatable entities](/guides/developer-guide/translations/), the best place to assign the channels is inside the `beforeSave` input of the [TranslateableSave](/reference/typescript-api/service-helpers/translatable-saver/). + + +# Querying channel-aware entities + +When querying channel-aware entities, you can use the [ListQueryBuilder](/reference/typescript-api/data-access/list-query-builder/#extendedlistqueryoptions) or +the [TransactionalConnection](/reference/typescript-api/data-access/transactional-connection/#findoneinchannel)to automatically filter entities based on the provided channel id. + + +```ts title="src/plugins/requests/service/product-request.service.ts" +import { ChannelService, ListQueryBuilder, TransactionalConnection } from '@vendure/core'; + +export class RequestService { + + constructor( + private connection: TransactionalConnection, + private listQueryBuilder: ListQueryBuilder, + private channelService: ChannelService) {} + + findOne(ctx: RequestContext, + requestId: ID, + relations?: RelationPaths) { + return this.connection.findOneInChannel(ctx, ProductRequest, requestId, ctx.channelId, { + relations: unique(effectiveRelations) + }); + } + + findAll( + ctx: RequestContext, + options?: ProductRequestListOptions, + relations?: RelationPaths, + ): Promise> { + return this.listQueryBuilder + .build(ProductRequest, options, { + ctx, + relations, + channelId: ctx.channelId, + }) + .getManyAndCount() + .then(([items, totalItems]) => { + return { + items, + totalItems, + }; + }); + } +} +``` diff --git a/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts b/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts index 74ad6b287c..37ffb58da3 100644 --- a/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts +++ b/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts @@ -1,3 +1,4 @@ + import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Route } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; diff --git a/packages/core/src/service/services/channel.service.ts b/packages/core/src/service/services/channel.service.ts index 57e49e7ee8..216e1f1f2b 100644 --- a/packages/core/src/service/services/channel.service.ts +++ b/packages/core/src/service/services/channel.service.ts @@ -111,7 +111,8 @@ export class ChannelService { /** * @description * Assigns a ChannelAware entity to the default Channel as well as any channel - * specified in the RequestContext. + * specified in the RequestContext. This method will not save the entity to the database, but + * assigns the `channels` property off the entity. */ async assignToCurrentChannel( entity: T, @@ -171,7 +172,7 @@ export class ChannelService { /** * @description - * Assigns the entity to the given Channels and saves. + * Assigns the entity to the given Channels and saves all changes to the database. */ async assignToChannels( ctx: RequestContext, From ad4b53abcdd1d55de6d967838062dc3bd77e92aa Mon Sep 17 00:00:00 2001 From: Kevin Mattutat Date: Thu, 14 Nov 2024 15:28:54 +0100 Subject: [PATCH 2/5] chore: Removed unwanted newline character --- packages/admin-ui/src/lib/catalog/src/catalog.routes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts b/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts index 37ffb58da3..74ad6b287c 100644 --- a/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts +++ b/packages/admin-ui/src/lib/catalog/src/catalog.routes.ts @@ -1,4 +1,3 @@ - import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, Route } from '@angular/router'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; From 2e1ae2b533d28f3532f58447b665eeb74785c51b Mon Sep 17 00:00:00 2001 From: Kevin Mattutat Date: Thu, 14 Nov 2024 16:21:21 +0100 Subject: [PATCH 3/5] docs: Added page to sidebar --- docs/docs/guides/developer-guide/channel-aware/index.md | 2 +- docs/sidebars.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/developer-guide/channel-aware/index.md b/docs/docs/guides/developer-guide/channel-aware/index.md index ba9bd29059..31952789c5 100644 --- a/docs/docs/guides/developer-guide/channel-aware/index.md +++ b/docs/docs/guides/developer-guide/channel-aware/index.md @@ -1,5 +1,5 @@ --- -title: "Channel-aware Entities" +title: "Channelaware entities" showtoc: true --- diff --git a/docs/sidebars.js b/docs/sidebars.js index 4168204391..a641393ed2 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -91,6 +91,7 @@ const sidebars = { value: 'Advanced Topics', className: 'sidebar-section-header', }, + 'guides/developer-guide/channel-aware/index', 'guides/developer-guide/db-subscribers/index', 'guides/developer-guide/importing-data/index', 'guides/developer-guide/logging/index', From 2a3aee10ba608ce8045a8e21d698cd90e014b72a Mon Sep 17 00:00:00 2001 From: Kevin Mattutat Date: Fri, 15 Nov 2024 17:26:36 +0100 Subject: [PATCH 4/5] chore: Fixed typos and highlighted code snippets --- .../developer-guide/channel-aware/index.md | 25 +++++++++++-------- .../src/service/services/channel.service.ts | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/docs/guides/developer-guide/channel-aware/index.md b/docs/docs/guides/developer-guide/channel-aware/index.md index 31952789c5..f78a024b38 100644 --- a/docs/docs/guides/developer-guide/channel-aware/index.md +++ b/docs/docs/guides/developer-guide/channel-aware/index.md @@ -1,14 +1,14 @@ --- -title: "Channelaware entities" +title: "Implementing ChannelAware" showtoc: true --- -# Defining channel-aware entities +## Defining channel-aware entities Making an entity channel-aware means that it can be associated with a specific [Channel](/reference/typescript-api/channel/). This is useful when you want to have different data or features for different channels. First you will have to create -a base entity ([Define a database entity](/guides/developer-guide/database-entity/)) that implements the `ChannelAware` interface. -This interface requires the entity to have a `channels` property +an entity ([Define a database entity](/guides/developer-guide/database-entity/)) that implements the `ChannelAware` interface. +This interface requires the entity to provide a `channels` property ```ts title="src/plugins/requests/entities/product-request.entity.ts" import { DeepPartial } from '@vendure/common/lib/shared-types'; @@ -29,14 +29,15 @@ class ProductRequest extends VendureEntity { @Column() text: string; - +// highlight-start @ManyToMany(() => Channel) @JoinTable() channels: Channel[]; +// highlight-end } ``` -# Creating channel-aware entities +## Creating channel-aware entities Creating a channel-aware entity is similar to creating a regular entity. The only difference is that you need to assign the entity to the current channel. This can be done by using the `ChannelService` which provides the `assignToCurrentChannel` helper function. @@ -55,19 +56,20 @@ export class RequestService { async create(ctx: RequestContext, input: CreateRequestInput): Promise { const request = new ProductRequest(input); // Now we need to assign the request to the current channel (+ default channel) - await this.channelService.assignToCurrentChannel(requestInput, ctx); +// highlight-next-line + await this.channelService.assignToCurrentChannel(input, ctx); return await this.connection.getRepository(ProductRequest).save(request); } } ``` -For [Translatable entities](/guides/developer-guide/translations/), the best place to assign the channels is inside the `beforeSave` input of the [TranslateableSave](/reference/typescript-api/service-helpers/translatable-saver/). +For [Translatable entities](/guides/developer-guide/translations/), the best place to assign the channels is inside the `beforeSave` input of the [TranslateableSave](/reference/typescript-api/service-helpers/translatable-saver/) helper class. -# Querying channel-aware entities +## Querying channel-aware entities When querying channel-aware entities, you can use the [ListQueryBuilder](/reference/typescript-api/data-access/list-query-builder/#extendedlistqueryoptions) or -the [TransactionalConnection](/reference/typescript-api/data-access/transactional-connection/#findoneinchannel)to automatically filter entities based on the provided channel id. +the [TransactionalConnection](/reference/typescript-api/data-access/transactional-connection/#findoneinchannel) to automatically filter entities based on the provided channel id. ```ts title="src/plugins/requests/service/product-request.service.ts" @@ -83,9 +85,11 @@ export class RequestService { findOne(ctx: RequestContext, requestId: ID, relations?: RelationPaths) { +// highlight-start return this.connection.findOneInChannel(ctx, ProductRequest, requestId, ctx.channelId, { relations: unique(effectiveRelations) }); +// highlight-end } findAll( @@ -97,6 +101,7 @@ export class RequestService { .build(ProductRequest, options, { ctx, relations, +// highlight-next-line channelId: ctx.channelId, }) .getManyAndCount() diff --git a/packages/core/src/service/services/channel.service.ts b/packages/core/src/service/services/channel.service.ts index 216e1f1f2b..93d39bc687 100644 --- a/packages/core/src/service/services/channel.service.ts +++ b/packages/core/src/service/services/channel.service.ts @@ -112,7 +112,7 @@ export class ChannelService { * @description * Assigns a ChannelAware entity to the default Channel as well as any channel * specified in the RequestContext. This method will not save the entity to the database, but - * assigns the `channels` property off the entity. + * assigns the `channels` property of the entity. */ async assignToCurrentChannel( entity: T, From 5e2f8d845a7e03802caf7b2b421e4a42ec868390 Mon Sep 17 00:00:00 2001 From: Kevin Mattutat Date: Mon, 18 Nov 2024 15:44:38 +0100 Subject: [PATCH 5/5] fix: Fixed the missing implementation of ChannelAware in code snippet --- docs/docs/guides/developer-guide/channel-aware/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/guides/developer-guide/channel-aware/index.md b/docs/docs/guides/developer-guide/channel-aware/index.md index f78a024b38..e2fd7c731f 100644 --- a/docs/docs/guides/developer-guide/channel-aware/index.md +++ b/docs/docs/guides/developer-guide/channel-aware/index.md @@ -16,7 +16,7 @@ import { VendureEntity, Product, EntityId, ID, ChannelAware } from '@vendure/cor import { Column, Entity, ManyToOne } from 'typeorm'; @Entity() -class ProductRequest extends VendureEntity { +class ProductRequest extends VendureEntity implements ChannelAware { constructor(input?: DeepPartial) { super(input); }