Normalizing State
const vendingMachine = [
{
id: 1,
name: 'Leela'
location: {
id: 1,
name: 'Lake View'
},
inventory: [
{
id: 1,
name: 'Yay Chips'
},
{
id: 2,
name: 'Chips of Cookies',
}
]
},
{
id: 2,
name: 'Farnsworth',
location: {
id: 1,
name: 'Lake View'
},
inventory: [
{
id: 1,
name: 'Yay Chips'
},
{
id: 3,
name: 'Bag of Bretzels',
}
]
}
]
Location
Inventory
Location
Inventory
Machine
Machine
const vendingMachine = [
{
id: 1,
name: 'Leela'
location: {
id: 1,
name: 'Lake View'
},
inventory: [
{
id: 1,
name: 'Yay Chips'
},
{
id: 2,
name: 'Chips of Cookies',
}
]
},
{
id: 2,
name: 'Farnsworth',
location: {
id: 1,
name: 'Lake View'
},
inventory: [
{
id: 1,
name: 'Yay Chips'
},
{
id: 3,
name: 'Bag of Bretzels',
}
]
}
]
Rename: Lakeview
Rename: Lakeview
The case for normalization
-
Data is duplicated and leaves room for update errors
-
Data is deeply nested and hard to update
-
Updates to nested data leads to complete re-renders of unrelated components
{
machines : {
byId : {
"machine1" : {
id : "machine1",
name: "Leela",
snacks : ["inventory1", "inventory2"]
},
"machine2" : {
id : "machine2",
name : "Farnsworth",
snacks : ["inventory1", "inventory3"]
}
},
allIds : ["machine1", "machine2"]
},
snacks: {
byId: {
"inventory1": {
id: "snack1",
name: "Yay Chips"
},
"inventory2": {
id: "snack2",
name: "Chips of Cookies"
},
"inventory3": {
id: "snack3",
name: "Bag of bretzels"
}
},
allIds: ["inventory1", "inventory2", "inventory3"]
}
}
Id | Name | Inventory |
---|---|---|
machine1 | Leela | inventory1, inventory2 |
machine2 | Farnsworth | inventory1, inventory3 |
Id | Name |
---|---|
inventory1 | yay chips |
inventory2 | chips of cookie |
inventory3 | bag of bretzels |
Machines
Inventory
{
machines : {
byId : {
"machine1" : {
...,
inventory : ["inventory1", "inventory2"]
},
"machine2" : {
...,
inventory : ["inventory1", "inventory3"]
}
},
},
inventory: {
byId: {
"inventory1": { ... },
"inventory2": { ... },
"inventory3": { ... }
}
},
},
inventoryInMachine: [
{
inventory_id: "inventory1",
machine_id: "machine1"
},
{
inventory_id: "inventory2",
machine_id: "machine1"
},
{
inventory_id: "inventory1",
machine_id: "machine2"
}
...
]
}
inventory and machine relationship table
Id | Name | Inventory |
---|---|---|
machine1 | Leela | inventory1, inventory2 |
machine2 | Farnsworth | inventory1, inventory3 |
Id | Name |
---|---|
inventory1 | yay chips |
inventory2 | chips of cookie |
inventory3 | bag of bretzels |
Machines
Inventory
Inventory in Machines
Inventory Id | MachineId |
---|---|
inventory1 | machine1 |
inventory2 | machine1 |
inventory1 | machine2 |
inventory3 | machine2 |
Enter: Vuex ORM
Vuex ORM is a plugin for Vuex to enable Object-Relational Mapping access to the Vuex Store. Vuex ORM lets you create "normalized" data schema within Vuex Store.
What is Vuex ORM?
Step 1: Define Data Model
Step 2: Insert Data into Model
Step 3: Query Data!
Organizing Data in Vuex ORM
Step 1: Define Data Model
class Machine extends Model {
static entity = 'machines'
static fields () {
return {
id: this.attr(null),
name: this.string('')
}
}
}
name of data entity
`this.$store
.state.entities`
Step 1: Define Data Model
class Inventory extends Model { static entity = 'inventory' static fields () { return { id: this.attr(null), name: this.string('') } } }
fields/attributes in data entity
Step 2: Insert Data into Model
Machine.insert({ data: [ { id: 1, name: "Leela" }, { id: 2, name: "Farnsworth" } ] })
Step 2: Insert Data into Model
Inventory.insert({ data: [ { id: 1, name: 'Yay Chips' }, { id: 2, name: 'Chips of Cookies' }, { id: 3, name: 'Bag of Bretzels' }, { id: 4, name: 'Corn Crisps' }, { id: 5, name: 'Triangle Chips' } ] })
Step 3: Query Data
// Fetch all machines. const machines = Machine.all()
// Fetch all snacks. const snacks = Snack.all()
├── src/ ├── components ├── App.vue
└── store.js ├── models
└── Machine.js ├── ... └── public/
models
Machine.js
Vuex ORM Folder Structure
import Vue from "vue";
import Vuex from "vuex";
import VuexORM from "@vuex-orm/core";
import Machine from "@/models/Machine";
Vue.use(Vuex);
const database = new VuexORM.Database();
database.register(Machine);
export default new Vuex.Store({
plugins: [VuexORM.install(database)],
state: {},
getters: {},
mutations: {},
actions: {
init() {
const machines = [...]
Machine.insert({ data: machines });
}
}
});
store.js
initialize DB
register model to DB
add VuexORM as a plugin
import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
import actions from "./actions";
import getters from "./getters";
import mutations from "./mutations";
import VuexORM from "@vuex-orm/core";
import Machine from "@/models/Machine";
Vue.use(Vuex);
const database = new VuexORM.Database();
database.register(Machine);
const store = new Vuex.Store({
plugins: [VuexORM.install(database)],
state,
getters,
mutations,
actions
});
if (store._actions.init) {
store.dispatch("init");
}
export default store;
store/index.js
import Machine from "@/models/Machine";
const init = () => {
//initialize db here //
const machines = [...]
Machine.insert({ data: machines });
};
main.js
Let's create a machine model in Vuex ORM
Exercise Time!
[step-0]
https://github.com/shortdiv/vuex-normalize-state
The power of an ORM is revealed when relationships are established.
In Vuex ORM, relationships are defined as attributes in Model's static fields.
Relationships in Vuex ORM
- One to One
- One to Many
- Many to One
- ....
Relationships in Vuex ORM
Id | Name | Location |
---|---|---|
machine1 | Leela | Lake View |
machine2 | Farnsworth | Wicker Park |
machine3 | Fry | Lake View |
Id | Name |
---|---|
location1 | Lake View |
location2 | Wicker Park |
location3 | Boystown |
Machines
Locations
Id | Name | Inventory |
---|---|---|
machine1 | Leela | Lake View |
machine2 | Farnsworth | Wicker Park |
machine3 | Fry | Lake View |
Id | Name |
---|---|
location1 | Lake View |
location2 | Wicker Park |
location3 | Boystown |
Machines
Machines in Location
Locations | MachineId |
---|---|
location1 | machine1, machine3 |
location2 | machine2 |
location3 |
Locations
class Location extends Model {
static entity = 'locations'
static fields () {
return {
id: this.attr(null),
name: this.string(''),
latlng: this.latlng(''), machines: this.hasMany(Machine, 'machine_id'), } } }
define one to many relationship
import Machine from "@/models/Machine";
const init = () => {
const machines = [
{
id: 1,
name: "Leela",
lastStocked: "Feb 2019",
condition: "Working",
location: "Lake View",
latlng: [-87.6554288, 41.94705],
inventory: []
},
...
]
Machine.insert({ data: machines });
};
export default {
init,
...
}
store/actions.js
import Machine from "@/models/Machine";
import Location from "@/models/Location";
const init = () => {
const machines = [
{
id: 1,
name: "Leela",
lastStocked: "Feb 2019",
condition: "Working",
location_id: 1,
inventory: [
},
...
]
const locations = [
{
id: 1,
name: "Lake View",
latlng: [-87.6554288, 41.94705]
},
...
]
Machine.insert({ data: machines });
Location.insert({ data: locations });
};
export {
init,
...
}
store/actions.js
Let's create a location model in Vuex ORM
Exercise Time!
[step-1]
https://github.com/shortdiv/vuex-normalize-state
Id | Name | Inventory |
---|---|---|
machine1 | Leela | Lake View |
machine2 | Farnsworth | Wicker Park |
machine3 | Fry | Lake View |
Id | Name | Locked Down |
---|---|---|
location1 | Lake View | false |
location2 | Wicker Park | false |
location3 | Boystown | false |
Machines
Machines in Location
Locations | MachineId |
---|---|
location1 | machine1, machine3 |
location2 | machine2 |
location3 |
Locations
Id | Name | Inventory |
---|---|---|
machine1 | Leela | Lake View |
machine2 | Farnsworth | Wicker Park |
machine3 | Fry | Lake View |
Id | Name | Locked Down |
---|---|---|
location1 | Lake View | false |
location2 | Wicker Park | true |
location3 | Boystown | false |
Machines
Machines in Location
Locations | MachineId |
---|---|
location1 | machine1, machine3 |
location2 | machine2 |
location3 |
Locations
Query Builders in Vuex ORM
// Get Query Builder instance. const query = Machine.query()
const machine = Machine .query() .where('location_id', 1).get()
get all machines at location_id 1, i.e. Lake View
Query Builders in Vuex ORM
├── src/ ├── components ├── App.vue
└── store.js ├── models
└── Machine.js
├── helpers
└── LocationQuery.js ├── ... └── public/
helpers
LocationQuery.js
import Location from "@/models/Location";
const getLocationById = location_id => {
const location = Location.query()
.where("id", parseInt(location_id))
.get();
return location[0];
};
export {
getLocationById,
};
helpers/locationQueries.js
helpers/locationQueries.js
<template>
<section class="action-section">
<div class="machine-information">
<div class="machine-id">
<h3>Location</h3>
<p>{{ selectedNeighborhood.name }}</p>
</div>
...
</div>
...
</section>
</template>
<script>
import {
getLocationById
} from "@/helpers/locationQueries";
export default {
name: "LocationView",
props: {
neighborhoodId: String // query param //
}
computed: {
selectedNeighborhood() {
return getLocationById(this.neighborhoodId);
}
}
}
</script>
components/LocationView.vue
Let's create some helper queries in Vuex ORM
Exercise Time!
[step-2]
https://github.com/shortdiv/vuex-normalize-state
class Snack extends Model { static entity = 'snacks' static fields () { return { id: this.attr(null), name: this.string(''), machines: this.belongsToMany( Machine, MachineSnack, 'snack_id', 'machine_id' ), } } }
class MachineSnack extends Model {
static entity = 'machineSnacks'
static primaryKey = ['snack_id', 'machine_id'] static fields () { return { snack_id: this.attr(null), name: this.string(''), machines: this.belongsToMany( Machine, MachineSnack, 'snack_id', 'machine_id' ), } } }
Exercise Time!
Let's work
Normalizing State
By shortdiv
Normalizing State
- 700