Skip to content

Commit 7e90ddc

Browse files
authored
docs: Add guide on how to make an entity channel aware (#3209)
1 parent b8112be commit 7e90ddc

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed

docs/docs/guides/core-concepts/channels/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Many entities are channel-aware, meaning that they can be associated with a mult
3131

3232
- [`Asset`](/reference/typescript-api/entities/asset/)
3333
- [`Collection`](/reference/typescript-api/entities/collection/)
34-
- [`Customer`](/reference/typescript-api/entities/customer-group/)
34+
- [`Customer`](/reference/typescript-api/entities/customer/)
3535
- [`Facet`](/reference/typescript-api/entities/facet/)
3636
- [`FacetValue`](/reference/typescript-api/entities/facet-value/)
3737
- [`Order`](/reference/typescript-api/entities/order/)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
```

docs/sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const sidebars = {
9191
value: 'Advanced Topics',
9292
className: 'sidebar-section-header',
9393
},
94+
'guides/developer-guide/channel-aware/index',
9495
'guides/developer-guide/cache/index',
9596
'guides/developer-guide/dataloaders/index',
9697
'guides/developer-guide/db-subscribers/index',

packages/core/src/service/services/channel.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ export class ChannelService {
111111
/**
112112
* @description
113113
* Assigns a ChannelAware entity to the default Channel as well as any channel
114-
* specified in the RequestContext.
114+
* specified in the RequestContext. This method will not save the entity to the database, but
115+
* assigns the `channels` property of the entity.
115116
*/
116117
async assignToCurrentChannel<T extends ChannelAware & VendureEntity>(
117118
entity: T,
@@ -171,7 +172,7 @@ export class ChannelService {
171172

172173
/**
173174
* @description
174-
* Assigns the entity to the given Channels and saves.
175+
* Assigns the entity to the given Channels and saves all changes to the database.
175176
*/
176177
async assignToChannels<T extends ChannelAware & VendureEntity>(
177178
ctx: RequestContext,

0 commit comments

Comments
 (0)