Parcourir la source

refactor reactiveFilter in EM filters

Olivier Massot il y a 1 an
Parent
commit
231a31a9f2

+ 9 - 7
pages/tests/poc_fetch_collection.vue

@@ -43,7 +43,7 @@ Exemple :
       v-model="page"
       :length="totalPages"
       :total-visible="7"
-      @update:modelValue="refresh"
+      @update:model-value="refresh"
     />
 
     <div class="d-flex flex-row">
@@ -54,13 +54,13 @@ Exemple :
 </template>
 
 <script setup lang="ts">
-import {useEntityFetch} from '~/composables/data/useEntityFetch'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import Query from '~/services/data/Query'
 import SearchFilter from '~/services/data/Filters/SearchFilter'
-import {ORDER_BY_DIRECTION, SEARCH_STRATEGY} from '~/types/enum/data'
+import { ORDER_BY_DIRECTION, SEARCH_STRATEGY } from '~/types/enum/data'
 import Country from '~/models/Core/Country'
-import OrderBy from "~/services/data/Filters/OrderBy"
-import PageFilter from "~/services/data/Filters/PageFilter";
+import OrderBy from '~/services/data/Filters/OrderBy'
+import PageFilter from '~/services/data/Filters/PageFilter'
 
 const { fetchCollection } = useEntityFetch()
 
@@ -79,7 +79,9 @@ query.add(new PageFilter(page, itemsPerPage))
 
 const { data, pending, refresh } = fetchCollection(Country, null, query)
 
-const totalPages = computed(() => data.value ? data.value?.pagination.last : 1)
+const totalPages = computed(() =>
+  data.value ? data.value?.pagination.last : 1,
+)
 
 console.log(data)
 // console.log(data.value!.items)
@@ -101,9 +103,9 @@ const onAddClick = () => {
 }
 
 const onRefreshClick = () => {
+  page.value = 1
   refresh()
 }
-
 </script>
 
 <style scoped lang="scss">

+ 16 - 0
services/data/Filters/AbstractFilter.ts

@@ -0,0 +1,16 @@
+import type { Ref } from 'vue'
+
+export default abstract class AbstractFilter {
+  reactiveFilter: boolean
+
+  /**
+   * @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()).
+   */
+  protected constructor(reactiveFilter: boolean = false) {
+    this.reactiveFilter = reactiveFilter
+  }
+}

+ 14 - 23
services/data/Filters/EqualFilter.ts

@@ -1,48 +1,36 @@
 import type { Query as PiniaOrmQuery } from 'pinia-orm'
+import type { Ref } from 'vue'
 import type { ApiFilter } from '~/types/data'
 import ApiResource from '~/models/ApiResource'
-import type { Ref } from "vue";
+import AbstractFilter from '~/services/data/Filters/AbstractFilter'
+import RefUtils from '~/services/utils/refUtils'
 
-export default class EqualFilter implements ApiFilter {
+export default class EqualFilter extends AbstractFilter implements ApiFilter {
   field: string
   filterValue: string | number | null | Ref<string | number | null>
-  reactiveFilter: boolean
 
   /**
    * @param field
    * @param value
-   * @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()).
+   * @param reactiveFilter
    */
   constructor(
     field: string,
     value: string | number | null | Ref<string | number | null>,
     reactiveFilter: boolean = false,
   ) {
+    super(reactiveFilter)
     this.field = field
     this.filterValue = value
-    this.reactiveFilter = reactiveFilter
-  }
-
-  protected filterValueToRef(): Ref<string | number | null> {
-    if (isRef(this.filterValue)) {
-      if (this.reactiveFilter) {
-        return this.filterValue
-      } else {
-        return ref(this.filterValue.value)
-      }
-    } else {
-      return ref(this.filterValue)
-    }
   }
 
   public applyToPiniaOrmQuery(
     query: PiniaOrmQuery<ApiResource>,
   ): PiniaOrmQuery<ApiResource> {
-    const filterValue = this.filterValueToRef()
+    const filterValue = RefUtils.castToRef(
+      this.filterValue,
+      this.reactiveFilter,
+    )
 
     if (filterValue.value === null) {
       return query
@@ -52,7 +40,10 @@ export default class EqualFilter implements ApiFilter {
   }
 
   public getApiQueryPart(): string {
-    const filterValue = this.filterValueToRef()
+    const filterValue = RefUtils.castToRef(
+      this.filterValue,
+      this.reactiveFilter,
+    )
 
     if (filterValue.value === null) {
       return ''

+ 1 - 1
services/data/Filters/OrderBy.ts

@@ -2,7 +2,7 @@ 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'
-import StringUtils from "~/services/utils/stringUtils";
+import StringUtils from '~/services/utils/stringUtils'
 
 export default class OrderBy implements ApiFilter {
   field: string

+ 7 - 20
services/data/Filters/PageFilter.ts

@@ -1,44 +1,31 @@
 import type { Query as PiniaOrmQuery } from 'pinia-orm'
 import type { ApiFilter } from '~/types/data'
 import ApiResource from '~/models/ApiResource'
-import { SEARCH_STRATEGY } from '~/types/enum/data'
+import RefUtils from '~/services/utils/refUtils'
 
 export default class PageFilter implements ApiFilter {
   page: Ref<number>
   itemsPerPage: Ref<number>
-  reactiveFilter: boolean
 
   /**
    * @param page
    * @param itemsPerPage
-   * @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(
-    page: Ref<number>,
-    itemsPerPage: Ref<number>,
-    reactiveFilter: boolean = false,
-  ) {
+  constructor(page: Ref<number>, itemsPerPage: Ref<number>) {
     this.page = page
     this.itemsPerPage = itemsPerPage
-    this.reactiveFilter = reactiveFilter
   }
 
   public applyToPiniaOrmQuery(
     query: PiniaOrmQuery<ApiResource>,
   ): PiniaOrmQuery<ApiResource> {
-    const page = this.reactiveFilter
-      ? this.page
-      : ref(this.page.value)
+    const page = RefUtils.castToRef(this.page, false)
 
-    const itemsPerPage = this.reactiveFilter
-      ? this.itemsPerPage
-      : ref(this.itemsPerPage.value)
+    const itemsPerPage = RefUtils.castToRef(this.itemsPerPage, false)
 
-    return query.offset(itemsPerPage.value * (page.value - 1)).limit(itemsPerPage.value)
+    return query
+      .offset(itemsPerPage.value * (page.value - 1))
+      .limit(itemsPerPage.value)
   }
 
   public getApiQueryPart(): string {

+ 10 - 12
services/data/Filters/SearchFilter.ts

@@ -2,12 +2,13 @@ import type { Query as PiniaOrmQuery } from 'pinia-orm'
 import type { ApiFilter } from '~/types/data'
 import ApiResource from '~/models/ApiResource'
 import { SEARCH_STRATEGY } from '~/types/enum/data'
+import AbstractFilter from '~/services/data/Filters/AbstractFilter'
+import RefUtils from '~/services/utils/refUtils'
 
-export default class SearchFilter implements ApiFilter {
+export default class SearchFilter extends AbstractFilter implements ApiFilter {
   field: string
   filterValue: Ref<string>
   mode: SEARCH_STRATEGY
-  reactiveFilter: boolean
 
   /**
    * @param field
@@ -15,11 +16,7 @@ export default class SearchFilter implements ApiFilter {
    * @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()).
+   * @param reactiveFilter
    */
   constructor(
     field: string,
@@ -27,20 +24,21 @@ export default class SearchFilter implements ApiFilter {
     mode: SEARCH_STRATEGY = SEARCH_STRATEGY.EXACT,
     reactiveFilter: boolean = false,
   ) {
+    super(reactiveFilter)
     this.field = field
     this.filterValue = value
     this.mode = mode
-    this.reactiveFilter = reactiveFilter
   }
 
   public applyToPiniaOrmQuery(
     query: PiniaOrmQuery<ApiResource>,
   ): PiniaOrmQuery<ApiResource> {
-    const filterValue = this.reactiveFilter
-      ? this.filterValue
-      : ref(this.filterValue.value)
+    const filterValue = RefUtils.castToRef(
+      this.filterValue,
+      this.reactiveFilter,
+    )
 
-    if (!filterValue) {
+    if (!filterValue.value) {
       return query
     }
 

+ 26 - 0
services/utils/refUtils.ts

@@ -0,0 +1,26 @@
+import type {UnwrapRef} from "vue";
+
+export default class RefUtils {
+  /**
+   * Convertit la valeur du filtre en référence. S'il s'agit déjà d'une ref,
+   * selon que `maintainReactivity` soit vrai ou faux, on conserve la référence existante
+   * ou bien on la recréé pour briser la réactivité.
+   *
+   * @param value
+   * @param maintainReactivity
+   */
+  static castToRef<T>(
+    value: T | Ref<T>,
+    maintainReactivity: boolean = true,
+  ): Ref<T> | Ref<UnwrapRef<T>> {
+    if (isRef(value)) {
+      if (maintainReactivity) {
+        return value
+      } else {
+        return ref(value.value as T)
+      }
+    } else {
+      return ref(value as T)
+    }
+  }
+}