|
| 1 | +--- |
| 2 | +title: "Implementing ChannelAware" |
| 3 | +showtoc: true |
| 4 | +--- |
| 5 | + |
| 6 | +## Defining channel-aware entities |
| 7 | + |
| 8 | +Making an entity channel-aware means that it can be associated with a specific [Channel](/reference/typescript-api/channel/). |
| 9 | +This is useful when you want to have different data or features for different channels. First you will have to create |
| 10 | +an entity ([Define a database entity](/guides/developer-guide/database-entity/)) that implements the `ChannelAware` interface. |
| 11 | +This interface requires the entity to provide a `channels` property |
| 12 | + |
| 13 | +```ts title="src/plugins/requests/entities/product-request.entity.ts" |
| 14 | +import { DeepPartial } from '@vendure/common/lib/shared-types'; |
| 15 | +import { VendureEntity, Product, EntityId, ID, ChannelAware } from '@vendure/core'; |
| 16 | +import { Column, Entity, ManyToOne } from 'typeorm'; |
| 17 | + |
| 18 | +@Entity() |
| 19 | +class ProductRequest extends VendureEntity implements ChannelAware { |
| 20 | + constructor(input?: DeepPartial<ProductRequest>) { |
| 21 | + super(input); |
| 22 | + } |
| 23 | + |
| 24 | + @ManyToOne(type => Product) |
| 25 | + product: Product; |
| 26 | + |
| 27 | + @EntityId() |
| 28 | + productId: ID; |
| 29 | + |
| 30 | + @Column() |
| 31 | + text: string; |
| 32 | +// highlight-start |
| 33 | + @ManyToMany(() => Channel) |
| 34 | + @JoinTable() |
| 35 | + channels: Channel[]; |
| 36 | +// highlight-end |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +## Creating channel-aware entities |
| 41 | + |
| 42 | +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. |
| 43 | +This can be done by using the `ChannelService` which provides the `assignToCurrentChannel` helper function. |
| 44 | + |
| 45 | +:::info |
| 46 | +The `assignToCurrentChannel` function will only assign the `channels` property of the entity. You will still need to save the entity to the database. |
| 47 | +::: |
| 48 | + |
| 49 | +```ts title="src/plugins/requests/service/product-request.service.ts" |
| 50 | +import { ChannelService } from '@vendure/core'; |
| 51 | + |
| 52 | +export class RequestService { |
| 53 | + |
| 54 | + constructor(private channelService: ChannelService) {} |
| 55 | + |
| 56 | + async create(ctx: RequestContext, input: CreateRequestInput): Promise<ProductRequest> { |
| 57 | + const request = new ProductRequest(input); |
| 58 | + // Now we need to assign the request to the current channel (+ default channel) |
| 59 | +// highlight-next-line |
| 60 | + await this.channelService.assignToCurrentChannel(input, ctx); |
| 61 | + |
| 62 | + return await this.connection.getRepository(ProductRequest).save(request); |
| 63 | + } |
| 64 | +} |
| 65 | +``` |
| 66 | +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. |
| 67 | + |
| 68 | + |
| 69 | +## Querying channel-aware entities |
| 70 | + |
| 71 | +When querying channel-aware entities, you can use the [ListQueryBuilder](/reference/typescript-api/data-access/list-query-builder/#extendedlistqueryoptions) or |
| 72 | +the [TransactionalConnection](/reference/typescript-api/data-access/transactional-connection/#findoneinchannel) to automatically filter entities based on the provided channel id. |
| 73 | + |
| 74 | + |
| 75 | +```ts title="src/plugins/requests/service/product-request.service.ts" |
| 76 | +import { ChannelService, ListQueryBuilder, TransactionalConnection } from '@vendure/core'; |
| 77 | + |
| 78 | +export class RequestService { |
| 79 | + |
| 80 | + constructor( |
| 81 | + private connection: TransactionalConnection, |
| 82 | + private listQueryBuilder: ListQueryBuilder, |
| 83 | + private channelService: ChannelService) {} |
| 84 | + |
| 85 | + findOne(ctx: RequestContext, |
| 86 | + requestId: ID, |
| 87 | + relations?: RelationPaths<ProductRequest>) { |
| 88 | +// highlight-start |
| 89 | + return this.connection.findOneInChannel(ctx, ProductRequest, requestId, ctx.channelId, { |
| 90 | + relations: unique(effectiveRelations) |
| 91 | + }); |
| 92 | +// highlight-end |
| 93 | + } |
| 94 | + |
| 95 | + findAll( |
| 96 | + ctx: RequestContext, |
| 97 | + options?: ProductRequestListOptions, |
| 98 | + relations?: RelationPaths<ProductRequest>, |
| 99 | + ): Promise<PaginatedList<ProductRequest>> { |
| 100 | + return this.listQueryBuilder |
| 101 | + .build(ProductRequest, options, { |
| 102 | + ctx, |
| 103 | + relations, |
| 104 | +// highlight-next-line |
| 105 | + channelId: ctx.channelId, |
| 106 | + }) |
| 107 | + .getManyAndCount() |
| 108 | + .then(([items, totalItems]) => { |
| 109 | + return { |
| 110 | + items, |
| 111 | + totalItems, |
| 112 | + }; |
| 113 | + }); |
| 114 | + } |
| 115 | +} |
| 116 | +``` |
0 commit comments