SearchCriteriaBuilder - call api không còn là ám ảnh

Magento hỗ trợ khá mạnh query phục vụ cho việc tìm kiếm, một bộ lọc tìm kiếm sẽ có cấu trúc cơ bản như sau:

searchCriteria[filter_groups][<index>][filters][<index>][field]=<field_name>
searchCriteria[filter_groups][<index>][filters][<index>][value]=<search_value>
searchCriteria[filter_groups][<index>][filters][<index>][condition_type]=<operator>

trong đó:

  • field là tên thuộc tính.
  • value giá trị cần tìm.
  • condition_type là điều kiện truy vấn.

Ví dụ bài toán tìm kiếm sản phẩm quần đùi của phụ nữ (WSH%) hoặc quần dài (WP%) với kích thước 29 sau đó giới hạn những sản phẩm có giá từ 40$ đến 49,99$ thì câu truy vấn url trông sẽ như sau:

GET <host>/rest/<store_code>/V1/products?
searchCriteria[filter_groups][0][filters][0][field]=sku&
searchCriteria[filter_groups][0][filters][0][value]=WSH%2529%25&
searchCriteria[filter_groups][0][filters][0][condition_type]=like&

searchCriteria[filter_groups][0][filters][1][field]=sku&
searchCriteria[filter_groups][0][filters][1][value]=WP%2529%25&
searchCriteria[filter_groups][0][filters][1][condition_type]=like&

searchCriteria[filter_groups][1][filters][0][field]=price&
searchCriteria[filter_groups][1][filters][0][value]=40&
searchCriteria[filter_groups][1][filters][0][condition_type]=from&

searchCriteria[filter_groups][2][filters][0][field]=price&
searchCriteria[filter_groups][2][filters][0][value]=49.99&
searchCriteria[filter_groups][2][filters][0][condition_type]=to

Mọi thứ trông vẫn ổn cho đến khi thấy …

và thấy …

Để cho đơn giản code thì ý tưởng là tạo ra 1 function giúp chuyển đổi truy vấn tìm kiếm của chúng ta thành chuỗi URL dài dòng trên.

Có 2 giai đoạn để thực thi ý tưởng trên:

  1. Biến đoạn code đơn giản thành một object json cho máy hiểu.
  2. Biến object json thành URL.

Start

Một object json mong muốn sẽ có dạng giống như response mà request trả về

Tạo interface filter item cho object
image

interface filterItemInterface {
    field: string // field is an attribute name.
    value: string // value specifies the value to search for.
    condition_type: conditionType
}

với conditionType

type conditionType =
    | 'eq' // Equals.
    | 'finset' // A value within a set of values
    | 'from' // The beginning of a range. Must be used with to
    | 'gt' // Greater than
    | 'gteq' // Greater than or equal
    | 'in' // In. The value can contain a comma-separated list of values.
    | 'like' // Like. The value can contain the SQL wildcard characters when like is specified.
    | 'lt' // Less than
    | 'lteq' // Less than or equal
    | 'moreq' // More or equal
    | 'neq' // Not equal
    | 'nfinset' // A value that is not within a set of values
    | 'nin' // Not in. The value can contain a comma-separated list of values.
    | 'notnull' // Not null
    | 'null' // Null
    | 'to' // The end of a range. Must be used with from

Tạo interface cho filters
image

interface filtersInterface {
    filters: filterItemInterface[]
}

Tương tự ta cho interface cho sort order

type directionType = 'ASC' | 'DESC'

interface sortOrderItemInterface {
    field: string
    direction?: directionType
}

Tổng hợp lại ta có interface SearchCriteria

interface SearchCriteriaInterface {
    filter_groups?: filtersInterface[]
    sort_orders?: sortOrderItemInterface[]
    pageSize: number
    currentPage: number
}

Phần code logic trông sẽ như sau:

export const addFilterItem = (
    field: string,
    value: string,
    condition_type: conditionType = 'eq',
): filterItemInterface => ({
    field,
    value,
    condition_type,
})

export const addSortOrderItem = (
    field: string,
    direction?: directionType,
): sortOrderItemInterface => ({
    field,
    direction,
})

export const addFilters = (
    ...filters: filterItemInterface[]
): filtersInterface => {
    return {
        filters,
    }
}

export const addFilterGroup = (...filter_groups: filtersInterface[]) =>
    filter_groups

export const addSortOrder = (...sort_orders: sortOrderItemInterface[]) =>
    sort_orders

Ở đây sử dụng 1 kỹ thuật gọi là Rest parameter của ES6, sử dụng dấu ... giúp function có thể nhận tham số không giới hạn và biến nó thành một mảng, ví dụ:

var print(...list) {
 console.log(list)
}
print('a','b','c')
// -> ['a','b','c']

Và đây là kết quả, tada :grin:

addFilterGroup(
    addFilters(
        addFilterItem('sku', 'WSH%29%', 'like'),
        addFilterItem('sku', 'WP%29%', 'like'),
    ),
    addFilters(addFilterItem('price', '40', 'from')),
    addFilters(addFilterItem('price', '49.99', 'to')),
)
// or
addSortOrder(addSortOrderItem('price', 'ASC')),

in log kết quả thu được


Lúc này ta đã hoàn thành mục tiêu đầu tiên là biến truy vấn thành chuỗi object.
Phần còn lại là biến object này thành chuỗi url, ta sử dụng thư viện qs, cách sử dụng:

qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'

Cuối cùng …

“Builder” của chúng ta trông sẽ như sau :grinning:

export const searchCriteriaBuilder = (
    pageSize: number,
    currentPage: number,
    filter_groups: filtersInterface[],
    sort_orders?: sortOrderItemInterface[],
) =>
    qs.stringify(
        {
            searchCriteria: {
                filter_groups,
                sort_orders,
                pageSize,
                currentPage,
            },
        },
        { arrayFormat: 'indices' },
    )

Với bài toán trên thì URL truy vấn sẽ trông như sau :sunglasses:

searchCriteriaBuilder(
    16, // pageSize
    1, // currentPage
    addFilterGroup(
        addFilters(
            addFilterItem('sku', 'WSH%29%', 'like'),
            addFilterItem('sku', 'WP%29%', 'like'),
        ),
        addFilters(addFilterItem('price', '40', 'from')),
        addFilters(addFilterItem('price', '49.99', 'to')),
    ),
    addSortOrder(addSortOrderItem('price', 'ASC')),
)

Kết quả log:

So sánh với truy vấn gốc thì hoàn toàn chính xác:

Reference:

5 Likes

Xịn quá a! Lúc đầu quả em viết như thế mà lỗi có mỗi index là lỗi fix mãi không ra

Đúng kiểu starter nên là nhiều tâm sự nhỉ :)) Keep up sharing nhé các em!

1 Like