Browse Source

operational POC of the new fetchCollection system

Olivier Massot 1 năm trước cách đây
mục cha
commit
45656be231

+ 34 - 0
services/data/Filters/OrderBy.ts

@@ -0,0 +1,34 @@
+import type { Query as PiniaOrmQuery } from 'pinia-orm'
+import type { ApiFilter } from '~/types/data'
+import ApiResource from '~/models/ApiResource'
+import { ORDER_BY_DIRECTION } from '~/types/enum/data'
+
+export default class OrderBy implements ApiFilter {
+  field: string
+  mode: ORDER_BY_DIRECTION
+
+  /**
+   * @param field
+   * @param mode
+   */
+  constructor(
+    field: string,
+    mode: ORDER_BY_DIRECTION = ORDER_BY_DIRECTION.ASC,
+  ) {
+    this.field = field
+    this.mode = mode
+  }
+
+  public applyToPiniaOrmQuery(
+    query: PiniaOrmQuery<ApiResource>,
+  ): PiniaOrmQuery<ApiResource> {
+    return query.orderBy(
+      (instance) => instance[this.field].toLowerCase(),
+      this.mode,
+    )
+  }
+
+  public getApiQueryPart(): string {
+    return `order[${this.field}]=${this.mode}`
+  }
+}

+ 42 - 10
services/data/Filters/Search.ts

@@ -3,38 +3,70 @@ import type { ApiFilter } from '~/types/data'
 import ApiResource from '~/models/ApiResource'
 import { SEARCH_STRATEGY } from '~/types/enum/data'
 
-/**
- *
- */
 export default class Search implements ApiFilter {
   field: string
-  value: Ref<string | number>
-  private mode: string
+  filterValue: Ref<string>
+  mode: SEARCH_STRATEGY
+  reactiveFilter: boolean
 
   /**
    * @param field
    * @param value
-   * @param mode The search strategy (exact [default], partial, start, end, word_start).
+   * @param mode The 'search' strategy (exact [default], partial, start, end, word_start).
    *             This strategy is defined API-side, but PiniaOrm needs to know how to handle this.
    *             @see https://api-platform.com/docs/core/filters/
+   * @param reactiveFilter Est-ce qu'on doit conserver la réactivité du filtre ? Concrètement, dans le cas d'une
+   *                       recherche textuelle, si le filtre est réactif, le résultat de la query Pinia-ORM sera
+   *                       filtré à chaque fois que le filtre est modifié (même sans refresh ou nouvel appel à
+   *                       fetchCollection). Si reactiveFilter est false (comportement par défaut), le résultat
+   *                       de la query ne sera mis à jour qu'en cas de nouvel appel à fetchCollection (ou à refresh()).
    */
   constructor(
     field: string,
-    value: Ref<string | number>,
+    value: Ref<string>,
     mode: SEARCH_STRATEGY = SEARCH_STRATEGY.EXACT,
+    reactiveFilter: boolean = false,
   ) {
     this.field = field
-    this.value = value
+    this.filterValue = value
     this.mode = mode
+    this.reactiveFilter = reactiveFilter
   }
 
   public applyToPiniaOrmQuery(
     query: PiniaOrmQuery<ApiResource>,
   ): PiniaOrmQuery<ApiResource> {
-    return query.where(this.field, this.value.value)
+    const filterValue = this.reactiveFilter
+      ? this.filterValue
+      : ref(this.filterValue.value)
+
+    let wordStartRx: RegExp | null = null
+    if (this.mode === SEARCH_STRATEGY.WORD_START) {
+      wordStartRx = new RegExp(`^${filterValue.value}|\\s${filterValue.value}`)
+    }
+
+    return query.where(this.field, (value: string) => {
+      if (this.mode === SEARCH_STRATEGY.EXACT) {
+        return value === filterValue.value
+      } else if (this.mode === SEARCH_STRATEGY.IEXACT) {
+        return value.toLowerCase() === filterValue.value.toLowerCase()
+      } else if (this.mode === SEARCH_STRATEGY.PARTIAL) {
+        return value.includes(filterValue.value)
+      } else if (this.mode === SEARCH_STRATEGY.IPARTIAL) {
+        return value.toLowerCase().includes(filterValue.value.toLowerCase())
+      } else if (this.mode === SEARCH_STRATEGY.START) {
+        return value.startsWith(filterValue.value)
+      } else if (this.mode === SEARCH_STRATEGY.END) {
+        return value.endsWith(filterValue.value)
+      } else if (this.mode === SEARCH_STRATEGY.WORD_START) {
+        return wordStartRx!.test(value)
+      } else {
+        throw new Error('Unrecognized mode')
+      }
+    })
   }
 
   public getApiQueryPart(): string {
-    return `${this.field}[]=${this.value.value}`
+    return `${this.field}[]=${this.filterValue.value}`
   }
 }

+ 4 - 16
services/data/Query.ts

@@ -1,5 +1,5 @@
 import type { Query as PiniaOrmQuery } from 'pinia-orm'
-import type { ApiFilter, OrderBy } from '~/types/data'
+import type { ApiFilter } from '~/types/data'
 import type ApiResource from '~/models/ApiResource'
 
 /**
@@ -9,27 +9,15 @@ import type ApiResource from '~/models/ApiResource'
  */
 export default class Query {
   protected filters: Array<ApiFilter> = []
-  protected orderBy: Array<OrderBy> = []
 
   /**
    * Add an ApiFilter to the query
    *
    * @param filter
    */
-  public addWhere(filter: ApiFilter) {
+  public add(filter: ApiFilter): this {
     this.filters.push(filter)
-  }
-
-  /**
-   * Add an OrderBy directive to the query
-   *
-   * @param field
-   * @param direction
-   */
-  public addOrderBy(field: string, direction: 'asc' | 'desc' = 'asc') {
-    const orderBy: OrderBy = { field, direction }
-
-    this.orderBy.push(orderBy)
+    return this
   }
 
   /**
@@ -38,7 +26,7 @@ export default class Query {
    * @see https://api-platform.com/docs/core/filters/
    */
   public getUrlQuery(): string {
-    const queryParts: string[] = []
+    const queryParts: string[] = ['page=1']
 
     this.filters.forEach((filter) => {
       const queryPart = filter.getApiQueryPart()

+ 2 - 1
services/data/entityManager.ts

@@ -216,8 +216,9 @@ class EntityManager {
 
     query = query ?? new Query()
     const urlQuery = query.getUrlQuery()
+    url += '?' + urlQuery
 
-    const response = await this.apiRequestService.get(url, urlQuery)
+    const response = await this.apiRequestService.get(url)
 
     // deserialize the response
     const apiCollection = HydraNormalizer.denormalize(response, model)

+ 0 - 6
types/data.d.ts

@@ -52,16 +52,10 @@ interface Collection {
 
 interface ApiFilter {
   field: string
-  value: Ref<string | number>
   applyToPiniaOrmQuery: (query: PiniaOrmQuery<ApiResource>) => PiniaOrmQuery<ApiResource>
   getApiQueryPart: () => string
 }
 
-interface OrderBy {
-  field: string
-  direction : 'asc' | 'desc'
-}
-
 interface EnumItem {
   value: string
   label: string

+ 7 - 0
types/enum/data.ts

@@ -16,4 +16,11 @@ export const enum SEARCH_STRATEGY {
   START = 'start',
   END = 'end',
   WORD_START = 'word-start',
+  IEXACT = 'iexact',
+  IPARTIAL = 'ipartial',
+}
+
+export const enum ORDER_BY_DIRECTION {
+  ASC = 'asc',
+  DESC = 'desc',
 }