Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loading all relationships on a Model #93

Closed
M3psipax opened this issue Nov 5, 2021 · 5 comments · Fixed by #94
Closed

Loading all relationships on a Model #93

M3psipax opened this issue Nov 5, 2021 · 5 comments · Fixed by #94
Labels
enhancement New feature or request

Comments

@M3psipax
Copy link
Contributor

M3psipax commented Nov 5, 2021

I'd like to be able to load all relations when fetching the Model. Old vuex-orm could do it like this: model.query().withAllRecursive().

From what I can see in the docs, this is no longer or not yet possible.

Are there plans to implement this in this version or is there another way to do this?

@cuebit
Copy link
Member

cuebit commented Nov 5, 2021

Correct, it is yet to be implemented. For the time being, you'll want to be explicit with your relation loading.

Sorry for the inconvenience at this time.

@cuebit cuebit added the enhancement New feature or request label Nov 5, 2021
@M3psipax
Copy link
Contributor Author

M3psipax commented Nov 5, 2021

For a workaround, would it be feasible to approach it like this?

const q = store.$repo(model).query();
model.fields().forEach(f => {
    q.with(f);
    // do recursive stuff
})

I'm not sure how to iterate over the fields() return type: Record<string, Attribute>.
Could you help me out there?

@cuebit
Copy link
Member

cuebit commented Nov 5, 2021

I would patch the Query and Repository objects through a plugin. You'll then have full feature coverage on both objects.

The following assumes vue@next + vuex@next + @vuex-orm/core@next + typescript, however, the principal applies to other combinations of vue + vuex.

  1. Declare the new methods:
// e.g. types/vuex-orm.d.ts
import { EagerLoadConstraint } from '@vuex-orm/core'

declare module '@vuex-orm/core/dist/src/query/Query' {
  interface Query {
    withAll(callback?: EagerLoadConstraint): Query
    withAllRecursive(depth?: number): Query
  }
}

declare module '@vuex-orm/core/dist/src/repository/Repository' {
  interface Repository {
    withAll(callback?: EagerLoadConstraint): Query
    withAllRecursive(depth?: number): Query
  }
}
  1. Create the plugin:
// e.g. store/vuex-orm-plugin.ts
import { ModelFields, Relation, VuexORMPlugin } from '@vuex-orm/core'

export const plugin: VuexORMPlugin = {
  install: (_store, { Query, Repository }) => {
    // Non-breaking conditional required.
    if (!Object.prototype.hasOwnProperty.call(Query.prototype, 'withAll')) {
      Query.prototype.withAll = function (callback) {
        // Use bracket notation because `model` is a protected property at this time.
        const fields = this['model'].$fields() as ModelFields

        for (const name in fields) {
          if (fields[name] instanceof Relation) {
            // Push field to eager loading collection.
            this.with(name, callback)
          }
        }

        return this
      }
    }
    if (!Object.prototype.hasOwnProperty.call(Query.prototype, 'withAllRecursive')) {
      Query.prototype.withAllRecursive = function (depth = 3) {
        this.withAll((query) => {
          // Reduce depth through recursion.
          depth > 0 && query.withAllRecursive(depth - 1)
        })

        return this
      }
    }
    if (!Object.prototype.hasOwnProperty.call(Repository.prototype, 'withAll')) {
      Repository.prototype.withAll = function (callback) {
        return this.query().withAll(callback)
      }
    }
    if (!Object.prototype.hasOwnProperty.call(Repository.prototype, 'withAllRecursive')) {
      Repository.prototype.withAllRecursive = function (depth = 3) {
        return this.query().withAllRecursive(depth)
      }
    }
  }
}
  1. Use said plugin:
// e.g. store/index.ts
import { createStore } from 'vuex'
import VuexORM from '@vuex-orm/core'
import { plugin } from './vuex-orm-plugin'

VuexORM.use(plugin)

export default createStore({
  plugins: [VuexORM.install()]
})

Pretty much what the lib will implement lol. For a lesser approach, you can extract what you need from this and continue with your initial attempt. Hope this helps.

@M3psipax
Copy link
Contributor Author

M3psipax commented Nov 5, 2021

Funny thing is I'm just working on a pull request, but I don't know if the recursive method is enough for this because I don't know exactly how the eagerloads are processed:

  /**
   * Set to eager load all top-level relationships. Constraint is set for all relationships.
   */
  withAll(callback: EagerLoadConstraint = () => {}): Query<M> {
    const fields = this.model.$fields();
    for (let record in fields) {
      const relation = fields[record]
      if (relation instanceof Relation) {
        this.eagerLoad[record] = callback
      }
    }

    return this
  }

  /**
   * Set to eager load all relationships recursively.
   */
  withAllRecursive(depth = 3): Query<M> {
    this.withAll((query) => {
      // Reduce depth through recursion.
      depth > 0 && query.withAllRecursive(depth - 1)
    })

    return this
  }

@M3psipax
Copy link
Contributor Author

M3psipax commented Nov 5, 2021

So I just created a Pull Request that seems to work on the surface.

I just added a single unit test because unfortunately, I have not time for more right now, but maybe you could review the request, so I can make any appropriate changes when I get around to it.

#94

@cuebit cuebit linked a pull request Nov 8, 2021 that will close this issue
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants