SevenOutman / redux-eloquent

Query and mutate redux store in ORM style that you love.
MIT License
1 stars 0 forks source link
orm redux redux-eloquent

redux-eloquent

npm npm devDependency Status npm

Query and mutate your redux store in ORM style.

Usage

This simple example assumes you are familiar with using react redux and react-redux.

// First of all
import { rdbReducer /*, REDUCER_NAMESPACE */ } from 'redux-eloquent'

createStore({
  // name it 'rdb' or use REDUCER_NAMESPACE constant
  rdb: rdbReducer
})
// Your models.js
import { defineModel, primary, id } from 'redux-eloquent'

export const Author = defineModel('authors', {
  id,           // shorthand for id: primary(Number)
  name: String
})

export const Book = defineModel('books', {
  isbn: primary(String),
  title: String,
  author: Author
})
// Your dispatch function, e.g. the callback of a request
somehowRequestBooks()
  .then(result => {
      Book(dispatch).save(result)
  })
// Your component
function mapState2Props(state) {
  return {
    allBooks: Book(state).all()
  }
}

API

Basic

defineModel(key, fields = {})

Defines a model structure

export const Publisher = defineModel('publishers', { id, // shorthand for id: primary(Number) name: String }) export const Author = defineModel('authors', { id, // you can omit a field here if it's not a model // name: String }) export const Book = defineModel('books', { // defines a primary field that identifies a model instance isbn: primary(String), publisher: Publisher, author: Author, // define a model array field like this coauthors: [Author] })

// you can also add fields afterwards, // in case several models depends on each other Author.define({ books: [Book] })


### Mutator

A model is actually a function, which takes a store state or dispatch function as parameter.

When it takes a dispatch function, it returns a `Mutator` which has following methods.

#### `save(obj)`

Save a model entry. Note this method handles nested data.
- `obj` an `Object` with the structure of model definition
```js
import { Book } from './model.js'

// in your mapDispatch2Props(dispatch) maybe
Book(dispatch).save({
  isbn: 'abcdefg',
  // this will be saved into Author too
  author: {
    id: 15,
    name: 'John'
  }
})

saveBatch(array)

Save an array of model entries.

Querier

When model takes a store state, it returns a Querier which has following methods.

all()

Retrieves all entries of a model

import { Publisher } from './model.js'

// in your mapState2Props(state) maybe
Publisher(state).all()

find(id)

Retrieves entries by given id (or other primary field values)

// in your mapState2Props(state) maybe Book(state).find('abcdefg')


#### `recent()`

Retrieves entries that you added with `save()` method EXPLICITLY, which means
```js
import { Book, Author } from './model.js'

// in your mapState2Props(state) maybe
Book(state).recent() // returns the 'abcdefg' book that you `save()`d earlier
Author(state).recent() // does not return the 'id=15' author nested in that book

with(relation)

In rdbReducer there's no nested data, entries of different models are stored separately. So by default, Querier does not return nested data as well, nested models are represented by their primary field value.

Use with() to tell Querier which model fields you want to be retrieved as nested objects.

with() return the Querier itself so you can do chaining.

import { Book, Author } from './model.js'

// in your mapState2Props(state) maybe
Book(state).recent() // { isbn: 'abcdefg', author: 15 }
// 'author' is a Book's field's name, not a model key
Book(state).with('author').recent() // { isbn: 'abcdefg', author: { id: 15, name: 'John' }}

Helpers

primary(type)

Mark a field as primary. defineModel() actually accepts a third parameter as options, in which you can set a primary field as

import { defineModel } from 'redux-eloquent'

const Books = defineModel('books', {
  isbn: String,
  title: String
}, {
  primaryKey: 'isbn'
})

With primary() helper

import { defineModel, primary } from 'redux-eloquent'

const Books = defineModel('books', {
  isbn: primary(String),
  title: String
})

Handy right?

id

Short hand for primary(Number)

Advanced

bindStateDispatch({ getState, dispatch })

If you find this Model(state) and Model(dispatch) term inconvenient, you can bind a redux store to redux-eloquent and it will use the stores getState() and dispatch

import { bindStateDispatch, rdbReducer as rdb } from 'redux-eloquent'
import { Book } from './model.js'

const store = createStore(/*...rdb...*/)

bindStateDispatch(store)

// then you can query like this
// more interestingly, you can now use it anywhere in your component, not only in mapState2Props
Book.all()