Data: add selector to get related products (#43489)

* add getRelatedProducts to get related products

* changelog

* add a resolver to pull the related products

* minor jsdoc enhancement

* query Product params should be optional

* fix ES issue

* minor TS enhancement

* use createSelector() helper
This commit is contained in:
Damián Suárez 2024-01-11 12:56:59 -03:00 committed by GitHub
parent ac43fb2426
commit d0d056c60e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 36 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: add
Data: add selector to get related products

View File

@ -80,9 +80,11 @@ export const getItems = createSelector(
return null;
}
if ( query && query._fields ) {
if ( query && typeof query._fields !== 'undefined' ) {
const fields = query._fields;
return ids.map( ( id: IdType ) => {
return query._fields.reduce(
return fields.reduce(
( item: Partial< Item >, field: string ) => {
return {
...item,

View File

@ -20,9 +20,11 @@ export const getOrders = createSelector(
if ( ! ids ) {
return defaultValue;
}
if ( query._fields ) {
if ( query && typeof query._fields !== 'undefined' ) {
const fields = query._fields;
return ids.map( ( id ) => {
return query._fields.reduce(
return fields.reduce(
( product: PartialOrder, field: keyof PartialOrder ) => {
return {
...product,

View File

@ -78,6 +78,36 @@ export function* getProduct( productId: number ) {
}
}
export function* getRelatedProducts( productId: number ) {
try {
// Get the product.
const product: Product = yield resolveSelect(
STORE_NAME,
'getProduct',
productId
);
// Pick the related products IDs.
const relatedProductsIds = product.related_ids;
if ( ! relatedProductsIds?.length ) {
return [];
}
// Get the related products.
const relatedProducts: Product[] = yield resolveSelect(
STORE_NAME,
'getProducts',
{
include: relatedProductsIds,
}
);
return relatedProducts;
} catch ( error ) {
throw error;
}
}
export function* getProductsTotalCount( query: Partial< ProductQuery > ) {
try {
const totalsQuery = {

View File

@ -33,9 +33,11 @@ export const getProducts = createSelector(
if ( ! ids ) {
return defaultValue;
}
if ( query._fields ) {
if ( query && typeof query._fields !== 'undefined' ) {
const fields = query._fields;
return ids.map( ( id ) => {
return query._fields.reduce(
return fields.reduce(
(
product: PartialProduct,
field: keyof PartialProduct
@ -145,6 +147,31 @@ export const getPermalinkParts = createSelector(
}
);
/**
* Returns an array of related products for a given product ID.
*
* @param {ProductState} state - The current state.
* @param {number} productId - The product ID.
* @return {PartialProduct[]} The related products.
*/
export const getRelatedProducts = createSelector(
( state: ProductState, productId: number ): PartialProduct[] => {
const product = state.data[ productId ];
if ( ! product?.related_ids ) {
return [];
}
const relatedProducts = getProducts( state, {
include: product.related_ids,
} );
return relatedProducts || [];
},
( state, productId ) => {
return [ state.data[ productId ] ];
}
);
export type ProductsSelectors = {
getCreateProductError: WPDataSelector< typeof getCreateProductError >;
getProduct: WPDataSelector< typeof getProduct >;
@ -153,4 +180,5 @@ export type ProductsSelectors = {
getProductsError: WPDataSelector< typeof getProductsError >;
isPending: WPDataSelector< typeof isPending >;
getPermalinkParts: WPDataSelector< typeof getPermalinkParts >;
getRelatedProducts: WPDataSelector< typeof getRelatedProducts >;
} & WPDataSelectors;

View File

@ -170,7 +170,7 @@ export type ProductQuery<
Status = ProductStatus,
Type = ProductType
> = BaseQueryParams< keyof Product > & {
orderby:
orderby?:
| 'date'
| 'id'
| 'include'
@ -179,19 +179,19 @@ export type ProductQuery<
| 'price'
| 'popularity'
| 'rating';
slug: string;
status: Status;
type: Type;
sku: string;
featured: boolean;
category: string;
tag: string;
shipping_class: string;
attribute: string;
attribute_term: string;
tax_class: 'standard' | 'reduced-rate' | 'zero-rate';
on_sale: boolean;
min_price: string;
max_price: string;
stock_status: 'instock' | 'outofstock' | 'onbackorder';
slug?: string;
status?: Status;
type?: Type;
sku?: string;
featured?: boolean;
category?: string;
tag?: string;
shipping_class?: string;
attribute?: string;
attribute_term?: string;
tax_class?: 'standard' | 'reduced-rate' | 'zero-rate';
on_sale?: boolean;
min_price?: string;
max_price?: string;
stock_status?: 'instock' | 'outofstock' | 'onbackorder';
};

View File

@ -1,16 +1,16 @@
export type BaseQueryParams< Fields = string > = {
context: string;
page: number;
per_page: number;
search: string;
after: string;
before: string;
exclude: string;
include: string;
offset: number;
order: 'asc' | 'desc';
orderby: 'date' | 'id' | 'include' | 'title' | 'slug';
parent: number[];
parent_exclude: number[];
_fields: Fields[];
context?: string;
page?: number;
per_page?: number;
search?: string;
after?: string;
before?: string;
exclude?: string;
include?: string | number[];
offset?: number;
order?: 'asc' | 'desc';
orderby?: 'date' | 'id' | 'include' | 'title' | 'slug';
parent?: number[];
parent_exclude?: number[];
_fields?: Fields[];
};