|
|
@@ -1,6 +1,6 @@
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
import { InjectConnection } from '@nestjs/typeorm';
|
|
|
-import { Connection } from 'typeorm';
|
|
|
+import { Connection, FindConditions, In } from 'typeorm';
|
|
|
|
|
|
import { CreateProductInput, UpdateProductInput } from '../../../../shared/generated-types';
|
|
|
import { ID, PaginatedList } from '../../../../shared/shared-types';
|
|
|
@@ -21,16 +21,27 @@ import { translateDeep } from '../helpers/utils/translate-entity';
|
|
|
|
|
|
import { ChannelService } from './channel.service';
|
|
|
import { FacetValueService } from './facet-value.service';
|
|
|
+import { ProductCategoryService } from './product-category.service';
|
|
|
import { ProductVariantService } from './product-variant.service';
|
|
|
import { TaxRateService } from './tax-rate.service';
|
|
|
|
|
|
@Injectable()
|
|
|
export class ProductService {
|
|
|
+ private readonly relations = [
|
|
|
+ 'featuredAsset',
|
|
|
+ 'assets',
|
|
|
+ 'optionGroups',
|
|
|
+ 'channels',
|
|
|
+ 'facetValues',
|
|
|
+ 'facetValues.facet',
|
|
|
+ ];
|
|
|
+
|
|
|
constructor(
|
|
|
@InjectConnection() private connection: Connection,
|
|
|
private channelService: ChannelService,
|
|
|
private assetUpdater: AssetUpdater,
|
|
|
private productVariantService: ProductVariantService,
|
|
|
+ private productCategoryService: ProductCategoryService,
|
|
|
private facetValueService: FacetValueService,
|
|
|
private taxRateService: TaxRateService,
|
|
|
private listQueryBuilder: ListQueryBuilder,
|
|
|
@@ -38,21 +49,22 @@ export class ProductService {
|
|
|
private eventBus: EventBus,
|
|
|
) {}
|
|
|
|
|
|
- findAll(
|
|
|
+ async findAll(
|
|
|
ctx: RequestContext,
|
|
|
- options?: ListQueryOptions<Product>,
|
|
|
+ options?: ListQueryOptions<Product> & { categoryId?: ID },
|
|
|
): Promise<PaginatedList<Translated<Product>>> {
|
|
|
- const relations = [
|
|
|
- 'featuredAsset',
|
|
|
- 'assets',
|
|
|
- 'optionGroups',
|
|
|
- 'channels',
|
|
|
- 'facetValues',
|
|
|
- 'facetValues.facet',
|
|
|
- ];
|
|
|
-
|
|
|
+ let where: FindConditions<Product> | undefined;
|
|
|
+ if (options && options.categoryId) {
|
|
|
+ where = {
|
|
|
+ id: In(await this.getProductIdsInCategory(options.categoryId)),
|
|
|
+ };
|
|
|
+ }
|
|
|
return this.listQueryBuilder
|
|
|
- .build(Product, options, { relations, channelId: ctx.channelId })
|
|
|
+ .build(Product, options, {
|
|
|
+ relations: this.relations,
|
|
|
+ channelId: ctx.channelId,
|
|
|
+ where,
|
|
|
+ })
|
|
|
.getManyAndCount()
|
|
|
.then(async ([products, totalItems]) => {
|
|
|
const items = products.map(product =>
|
|
|
@@ -70,8 +82,9 @@ export class ProductService {
|
|
|
}
|
|
|
|
|
|
async findOne(ctx: RequestContext, productId: ID): Promise<Translated<Product> | undefined> {
|
|
|
- const relations = ['featuredAsset', 'assets', 'optionGroups', 'facetValues', 'facetValues.facet'];
|
|
|
- const product = await this.connection.manager.findOne(Product, productId, { relations });
|
|
|
+ const product = await this.connection.manager.findOne(Product, productId, {
|
|
|
+ relations: this.relations,
|
|
|
+ });
|
|
|
if (!product) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -147,6 +160,22 @@ export class ProductService {
|
|
|
return assertFound(this.findOne(ctx, productId));
|
|
|
}
|
|
|
|
|
|
+ private async getProductIdsInCategory(categoryId: ID): Promise<ID[]> {
|
|
|
+ const facetValueIds = await this.productCategoryService.getFacetValueIdsForCategory(categoryId);
|
|
|
+ const qb = this.connection
|
|
|
+ .getRepository(Product)
|
|
|
+ .createQueryBuilder('product')
|
|
|
+ .select(['product.id'])
|
|
|
+ .innerJoin('product.facetValues', 'facetValue', 'facetValue.id IN (:...facetValueIds)', {
|
|
|
+ facetValueIds,
|
|
|
+ })
|
|
|
+ .groupBy('product.id')
|
|
|
+ .having('count(distinct facetValue.id) = :idCount', { idCount: facetValueIds.length });
|
|
|
+
|
|
|
+ const productIds = await qb.getRawMany().then(rows => rows.map(r => r.product_id));
|
|
|
+ return productIds;
|
|
|
+ }
|
|
|
+
|
|
|
private async getProductWithOptionGroups(productId: ID): Promise<Product> {
|
|
|
const product = await this.connection
|
|
|
.getRepository(Product)
|