-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add WP native search fetch strategy
- Loading branch information
Showing
11 changed files
with
316 additions
and
21 deletions.
There are no files selected for viewing
155 changes: 155 additions & 0 deletions
155
packages/core/src/data/strategies/SearchNativeFetchStrategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { getSiteBySourceUrl, addQueryArgs, getWPUrl } from '../../utils'; | ||
import { endpoints } from '../utils'; | ||
import { apiGet } from '../api'; | ||
import { PostEntity, QueriedObject } from '../types'; | ||
import { searchMatchers } from '../utils/matchers'; | ||
import { parsePath } from '../utils/parsePath'; | ||
import { FetchOptions, AbstractFetchStrategy, EndpointParams } from './AbstractFetchStrategy'; | ||
|
||
/** | ||
* The EndpointParams supported by the {@link SearchNativeFetchStrategy} | ||
*/ | ||
export interface SearchParams extends EndpointParams { | ||
/** | ||
* Current page of the collection. | ||
* | ||
* @default 1 | ||
*/ | ||
page?: number; | ||
|
||
/** | ||
* Maximum number of items to be returned in result set. | ||
* | ||
* @default 10 | ||
*/ | ||
per_page?: number; | ||
|
||
/** | ||
* Limit results to those matching a string. | ||
*/ | ||
search?: string; | ||
|
||
/** | ||
* Limit results to items of an object type. | ||
* | ||
* @default 'post' | ||
*/ | ||
type?: 'post' | 'term' | 'post-format'; | ||
|
||
/** | ||
* Limit results to items of one or more object subtypes. | ||
*/ | ||
subtype?: string; | ||
|
||
/** | ||
* Ensure result set excludes specific IDs. | ||
*/ | ||
exclude?: number[]; | ||
|
||
/** | ||
* Limit result set to specific IDs. | ||
*/ | ||
include?: number[]; | ||
} | ||
|
||
/** | ||
* The SearchNativeFetchStrategy is used to fetch search results for a given search query | ||
* Uses the native WordPress search endpoint. | ||
* | ||
* Note that custom post types and custom taxonomies should be defined in `headless.config.js` | ||
* | ||
* This strategy supports extracting endpoint params from url E.g: | ||
* - `/page/2/` maps to `{ page: 2 }` | ||
* - `/searched-term/page/2` maps to `{ search: 'searched-term', page: 2 }` | ||
* | ||
* @see {@link getParamsFromURL} to learn about url param mapping | ||
* | ||
* @category Data Fetching | ||
*/ | ||
export class SearchNativeFetchStrategy< | ||
T extends PostEntity = PostEntity, | ||
P extends SearchParams = SearchParams, | ||
> extends AbstractFetchStrategy<T[], P> { | ||
path: string = ''; | ||
|
||
locale: string = ''; | ||
|
||
getDefaultEndpoint() { | ||
return endpoints.search; | ||
} | ||
|
||
getDefaultParams(): Partial<P> { | ||
return { _embed: true, ...super.getDefaultParams() } as P; | ||
} | ||
|
||
/** | ||
* This strategy automatically extracts taxonomy filters, date filters and pagination params from the URL | ||
* | ||
* It also takes into account the custom taxonomies specified in `headless.config.js` | ||
* | ||
* @param path The URL path to extract params from | ||
* @param params | ||
*/ | ||
getParamsFromURL(path: string, params: Partial<P> = {}): Partial<P> { | ||
const config = getSiteBySourceUrl(this.baseURL); | ||
|
||
// Required for search lang url. | ||
this.locale = config.integrations?.polylang?.enable && params.lang ? params.lang : ''; | ||
|
||
return parsePath(searchMatchers, path) as Partial<P>; | ||
} | ||
|
||
/** | ||
* The fetcher function is overridden to disable throwing if not found | ||
* | ||
* If a search request returns not found we do not want to redirect to a 404 page, | ||
* instead the user should be informed that no posts were found | ||
* | ||
* @param url The url to parse | ||
* @param params The params to build the endpoint with | ||
* @param options FetchOptions | ||
*/ | ||
async fetcher(url: string, params: Partial<P>, options: Partial<FetchOptions> = {}) { | ||
const { burstCache = false } = options; | ||
let seo_json: Record<string, any> = {}; | ||
let seo: string = ''; | ||
|
||
// Request SEO data. | ||
try { | ||
const wpUrl = getWPUrl().replace(/\/$/, ''); // Ensure no double slash in url param | ||
const localeParam = this.locale ? `&lang=${this.locale}` : ''; | ||
|
||
const result = await apiGet( | ||
addQueryArgs(`${wpUrl}${endpoints.yoast}`, { | ||
url: `${wpUrl}/?s=${params.search}${localeParam}`, | ||
}), | ||
{}, | ||
burstCache, | ||
); | ||
|
||
seo = result.json.html ?? null; | ||
seo_json = { ...result.json.json }; | ||
} catch (e) { | ||
// do nothing | ||
} | ||
|
||
const queriedObject: QueriedObject = { | ||
search: { | ||
searchedValue: params.search ?? '', | ||
type: 'post', | ||
subtype: (params.postType as string) ?? 'post', | ||
yoast_head: seo, | ||
yoast_head_json: { | ||
...seo_json, | ||
}, | ||
}, | ||
}; | ||
|
||
const response = await super.fetcher(url, params, { ...options, throwIfNotFound: false }); | ||
|
||
return { | ||
...response, | ||
queriedObject, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { useFetch } from './useFetch'; | ||
|
||
import type { FetchHookOptions } from './types'; | ||
import { | ||
FetchResponse, | ||
getPostAuthor, | ||
getPostTerms, | ||
PageInfo, | ||
PostEntity, | ||
SearchParams, | ||
QueriedObject, | ||
SearchNativeFetchStrategy, | ||
} from '../../data'; | ||
import { getWPUrl } from '../../utils'; | ||
import { makeErrorCatchProxy } from './util'; | ||
import { useSearchResponse } from './useFetchSearch'; | ||
|
||
/** | ||
* The useFetchSearchNative hook. Returns a collection of search entities retrieved through the WP native search endpoint | ||
* | ||
* See {@link useSearchNative} for usage instructions. | ||
* | ||
* @param params The list of params to pass to the fetch strategy. It overrides the ones in the URL. | ||
* @param options The options to pass to the swr hook. | ||
* @param path The path of the url to get url params from. | ||
* | ||
* @category Data Fetching Hooks | ||
*/ | ||
export function useFetchSearchNative< | ||
T extends PostEntity = PostEntity, | ||
P extends SearchParams = SearchParams, | ||
>( | ||
params: P | {} = {}, | ||
options: FetchHookOptions<FetchResponse<T[]>> = {}, | ||
path = '', | ||
): useSearchResponse<T> { | ||
const { data, error, isMainQuery } = useFetch<T[], P>( | ||
params, | ||
useFetchSearchNative.fetcher<T, P>(), | ||
options, | ||
path, | ||
); | ||
|
||
if (error || !data) { | ||
const fakeData = { | ||
posts: makeErrorCatchProxy<T[]>('posts'), | ||
pageInfo: makeErrorCatchProxy<PageInfo>('pageInfo'), | ||
queriedObject: makeErrorCatchProxy<QueriedObject>('queriedObject'), | ||
}; | ||
return { error, loading: !data, data: fakeData, isMainQuery }; | ||
} | ||
|
||
const { result, pageInfo, queriedObject } = data; | ||
|
||
const posts = result.map((post) => { | ||
post.author = getPostAuthor(post); | ||
post.terms = getPostTerms(post); | ||
|
||
return post; | ||
}); | ||
|
||
return { data: { posts, pageInfo, queriedObject }, loading: false, isMainQuery }; | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
// eslint-disable-next-line no-redeclare | ||
export namespace useFetchSearchNative { | ||
export const fetcher = < | ||
T extends PostEntity = PostEntity, | ||
P extends SearchParams = SearchParams, | ||
>( | ||
sourceUrl?: string, | ||
defaultParams?: P, | ||
) => new SearchNativeFetchStrategy<T, P>(sourceUrl ?? getWPUrl(), defaultParams); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { PostEntity, SearchParams, FetchResponse } from '@headstartwp/core'; | ||
import { FetchHookOptions, useFetchSearchNative } from '@headstartwp/core/react'; | ||
import { usePrepareFetch } from './usePrepareFetch'; | ||
|
||
/** | ||
* The useSearchNative hook. Returns a collection of search entities retrieved through the WP native search endpoint. | ||
* | ||
* In order to automatically map URL params create a catch-all route named `[...path].js`. | ||
* You can create the catch-all at any level e.g: `pages/search/[[...path]].js` | ||
* | ||
* The `pages/search/[[...path]].js` route for instance would yield a URL like this: `/search/[term]/page/[number]`, `/search/[term]` etc | ||
* | ||
* @param params The parameters accepted by the hook | ||
* @param options Options for the SWR configuration | ||
* | ||
* @category Data Fetching Hooks | ||
*/ | ||
export function useSearchNative< | ||
T extends PostEntity = PostEntity, | ||
P extends SearchParams = SearchParams, | ||
>(params: Partial<P> = {}, options: FetchHookOptions<FetchResponse<T[]>> = {}) { | ||
const useFetchArguments = usePrepareFetch(params, options); | ||
|
||
return useFetchSearchNative( | ||
useFetchArguments.params, | ||
useFetchArguments.options, | ||
useFetchArguments.path, | ||
); | ||
} | ||
|
||
/** | ||
* @internal | ||
*/ | ||
// eslint-disable-next-line no-redeclare | ||
export namespace useSearchNative { | ||
export const { fetcher } = useFetchSearchNative; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.