SearchFilter.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import type { Query as PiniaOrmQuery } from 'pinia-orm'
  2. import type { Ref } from 'vue'
  3. import type { ApiFilter } from '~/types/data'
  4. import type ApiResource from '~/models/ApiResource'
  5. import { SEARCH_STRATEGY } from '~/types/enum/data'
  6. import AbstractFilter from '~/services/data/Filters/AbstractFilter'
  7. import RefUtils from '~/services/utils/refUtils'
  8. export default class SearchFilter extends AbstractFilter implements ApiFilter {
  9. field: string
  10. filterValue: Ref<string | null>
  11. mode: SEARCH_STRATEGY
  12. /**
  13. * @param field
  14. * @param value
  15. * @param mode The 'search' strategy (exact [default], partial, start, end, word_start).
  16. * This strategy is defined API-side, but PiniaOrm needs to know how to handle this.
  17. * @see https://api-platform.com/docs/core/filters/
  18. * @param reactiveFilter
  19. */
  20. constructor(
  21. field: string,
  22. value: Ref<string | null>,
  23. mode: SEARCH_STRATEGY = SEARCH_STRATEGY.EXACT,
  24. reactiveFilter: boolean = false,
  25. ) {
  26. super(reactiveFilter)
  27. this.field = field
  28. this.filterValue = value
  29. this.mode = mode
  30. }
  31. protected search(value: string, filterValue: Ref<string | null>): boolean {
  32. if (filterValue.value === null) {
  33. return false
  34. }
  35. let wordStartRx: RegExp | null = null
  36. if (this.mode === SEARCH_STRATEGY.WORD_START) {
  37. wordStartRx = new RegExp(
  38. `^${filterValue.value}|\\s${filterValue.value}`,
  39. 'i',
  40. )
  41. }
  42. if (this.mode === SEARCH_STRATEGY.EXACT) {
  43. return value === filterValue.value
  44. } else if (this.mode === SEARCH_STRATEGY.IEXACT) {
  45. return value.toLowerCase() === filterValue.value.toLowerCase()
  46. } else if (this.mode === SEARCH_STRATEGY.PARTIAL) {
  47. return value.includes(filterValue.value)
  48. } else if (this.mode === SEARCH_STRATEGY.IPARTIAL) {
  49. return value.toLowerCase().includes(filterValue.value.toLowerCase())
  50. } else if (this.mode === SEARCH_STRATEGY.START) {
  51. return value.startsWith(filterValue.value)
  52. } else if (this.mode === SEARCH_STRATEGY.END) {
  53. return value.endsWith(filterValue.value)
  54. } else if (this.mode === SEARCH_STRATEGY.WORD_START) {
  55. return wordStartRx!.test(value)
  56. } else {
  57. throw new Error('Unrecognized mode')
  58. }
  59. }
  60. public applyToPiniaOrmQuery(
  61. query: PiniaOrmQuery<ApiResource>,
  62. ): PiniaOrmQuery<ApiResource> {
  63. const filterValue = RefUtils.castToRef(
  64. this.filterValue,
  65. this.reactiveFilter,
  66. )
  67. if (!filterValue.value) {
  68. return query
  69. }
  70. return query.where(this.field, (value: string) =>
  71. this.search(value, filterValue),
  72. )
  73. }
  74. public getApiQueryPart(): string {
  75. if (!this.filterValue.value) {
  76. return ''
  77. }
  78. return `${this.field}[]=${this.filterValue.value}`
  79. }
  80. }