Skip to content

Commit 82f6702

Browse files
authored
Merge pull request #130 from CodeForBaltimore/seancrim23/issue-121
Many-to-Many entities<->contacts
2 parents 6ff8af0 + 72a5a4f commit 82f6702

File tree

10 files changed

+489
-62
lines changed

10 files changed

+489
-62
lines changed

sequelize/data/contact.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
"number": "1234567890",
1313
"isPrimary": true
1414
}
15-
],
16-
"EntityId": "The Leftorium"
15+
]
1716
},
1817
{
1918
"name": "Moe Szyslak",
@@ -28,7 +27,6 @@
2827
"number": "0987654321",
2928
"isPrimary": true
3029
}
31-
],
32-
"EntityId": "Moes Tavern"
30+
]
3331
}
3432
]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
module.exports = {
3+
up: (queryInterface, Sequelize) => {
4+
return Promise.all([
5+
queryInterface.createTable('EntityContacts', {
6+
id: {
7+
type: Sequelize.UUID,
8+
primaryKey: true,
9+
allowNull: false,
10+
autoIncrement: false,
11+
},
12+
entityId: {
13+
type: Sequelize.UUID,
14+
references: {
15+
model: 'Entities',
16+
key: 'id',
17+
},
18+
onUpdate: 'CASCADE',
19+
onDelete: 'CASCADE',
20+
},
21+
contactId: {
22+
type: Sequelize.UUID,
23+
references: {
24+
model: 'Contacts',
25+
key: 'id',
26+
},
27+
onUpdate: 'CASCADE',
28+
onDelete: 'CASCADE',
29+
},
30+
relationshipTitle: {
31+
type: Sequelize.STRING,
32+
defaultValue: "default",
33+
},
34+
createdAt: {
35+
allowNull: false,
36+
type: Sequelize.DATE
37+
},
38+
updatedAt: {
39+
allowNull: false,
40+
type: Sequelize.DATE
41+
}
42+
}),
43+
queryInterface.removeColumn('Contacts', 'EntityId'),
44+
]);
45+
},
46+
down: (queryInterface, Sequelize) => {
47+
return Promise.all([
48+
queryInterface.dropTable('EntityContacts'),
49+
queryInterface.addColumn(
50+
'Contacts',
51+
'EntityId',
52+
{
53+
type: Sequelize.UUID,
54+
references: {
55+
model: 'Entities',
56+
key: 'id'
57+
},
58+
onUpdate: 'CASCADE',
59+
onDelete: 'SET NULL'
60+
}
61+
),
62+
]);
63+
}
64+
};

sequelize/seeders/04-demo-contact.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ module.exports = {
1212
element.updatedAt = new Date();
1313
element.email = JSON.stringify(element.email);
1414
element.phone = JSON.stringify(element.phone);
15-
16-
if (element.EntityId) {
17-
const entityId = await queryInterface.sequelize.query(
18-
`SELECT id FROM ${process.env.DATABASE_SCHEMA}."Entities" WHERE name = '${element.EntityId}'`
19-
);
20-
element.EntityId = entityId[0][0].id;
21-
}
2215
}
2316

2417
let i = 0;
@@ -50,10 +43,6 @@ module.exports = {
5043
];
5144
let name = randomWords();
5245

53-
let entityId = await queryInterface.sequelize.query(
54-
`SELECT id FROM ${process.env.DATABASE_SCHEMA}."Entities" WHERE name = '${entityNames[Math.floor(Math.random() * entityNames.length)]}'`
55-
);
56-
5746
let contact = {
5847
id: uuid(),
5948
createdAt: new Date(),
@@ -73,10 +62,7 @@ module.exports = {
7362
])
7463
};
7564

76-
if(entityId[0][0] !== undefined) contact.EntityId = entityId[0][0].id;
77-
7865
contacts.push(contact);
79-
8066
i++;
8167
} while (i < 18);
8268

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const uuid = require('uuid4');
2+
const randomWords = require('random-words');
3+
const contacts = require('../data/contact.json');
4+
const entities = require('../data/entity.json')
5+
6+
module.exports = {
7+
up: async queryInterface => {
8+
let entityContacts = [];
9+
let entityContact;
10+
let entitiesMutable = entities;
11+
let numberOfLinks;
12+
let entityToLinkIndex;
13+
for (const contact of contacts) {
14+
// links between 1 and 4
15+
numberOfLinks = Math.floor(Math.random()*4+1)
16+
for (let linkIndex=0; linkIndex<=numberOfLinks; linkIndex++) {
17+
entityToLinkIndex = Math.floor(Math.random()*entitiesMutable.length)
18+
if (entitiesMutable[entityToLinkIndex] != null) {
19+
entityContact = {
20+
id: uuid(),
21+
entityId: entitiesMutable[entityToLinkIndex].id,
22+
contactId: contact.id,
23+
relationshipTitle: randomWords(),
24+
createdAt: new Date(),
25+
updatedAt: new Date(),
26+
};
27+
entityContacts.push(entityContact)
28+
// delete entity from list so it doesn't link twice
29+
entitiesMutable.splice(entityToLinkIndex, 1)
30+
} else {
31+
break;
32+
}
33+
}
34+
}
35+
return queryInterface.bulkInsert('EntityContacts', entityContacts);
36+
},
37+
down: queryInterface => {
38+
return queryInterface.bulkDelete('EntityContacts', null, {});
39+
}
40+
};

src/models/contact.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UUIDV4 } from "sequelize";
2+
import models from './index';
23

34
const contact = (sequelize, DataTypes) => {
45
// Defining our contact table and setting Contact object.
@@ -17,13 +18,6 @@ const contact = (sequelize, DataTypes) => {
1718
},
1819
unique: true
1920
},
20-
EntityId: {
21-
type: DataTypes.UUID,
22-
references: {
23-
model: 'Entity',
24-
key: 'id'
25-
}
26-
},
2721
name : {
2822
type: DataTypes.STRING,
2923
required: true
@@ -44,7 +38,12 @@ const contact = (sequelize, DataTypes) => {
4438

4539
Contact.associate = models => {
4640
Contact.belongsTo(models.User);
47-
Contact.belongsTo(models.Entity);
41+
Contact.belongsToMany(models.Entity, {
42+
through: "EntityContact",
43+
as: "entities",
44+
foreignKey: "contactId",
45+
otherKey: "entityId"
46+
});
4847
}
4948

5049
Contact.findById = async (id) => {
@@ -71,6 +70,25 @@ const contact = (sequelize, DataTypes) => {
7170
return contact;
7271
};
7372

73+
Contact.findContactWithAssociatedEntities = async (contactId) => {
74+
const contactEntities = await Contact.findOne({
75+
where: { id: contactId },
76+
include: [{
77+
model: models.Entity,
78+
as: 'entities',
79+
required: false,
80+
attributes: ["id", "name", "type", "address", "phone", "email", "checkIn", "description", "attributes"],
81+
through: {
82+
model: models.EntityContact,
83+
as: 'entityContacts',
84+
attributes: ["relationshipTitle"]
85+
}
86+
}]
87+
});
88+
89+
return contactEntities;
90+
};
91+
7492
return Contact;
7593
};
7694

src/models/entity-contact.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { UUIDV4 } from "sequelize";
2+
3+
const entityContact = (sequelize, DataTypes) => {
4+
// Defining our EntityContact object.
5+
const EntityContact = sequelize.define('EntityContact', {
6+
id: {
7+
type: DataTypes.UUID,
8+
unique: true,
9+
primaryKey: true,
10+
defaultValue: UUIDV4
11+
},
12+
entityId: {
13+
type: DataTypes.UUID,
14+
references: {
15+
model: 'Entity',
16+
key: 'id'
17+
}
18+
},
19+
contactId: {
20+
type: DataTypes.UUID,
21+
references: {
22+
model: 'Contact',
23+
key: 'id'
24+
}
25+
},
26+
relationshipTitle: {
27+
type: DataTypes.STRING,
28+
defaultValue: "default",
29+
}
30+
},
31+
{
32+
schema: process.env.DATABASE_SCHEMA
33+
});
34+
35+
EntityContact.createIfNew = async (ec) => {
36+
let whereQuery = {
37+
entityId: ec.entityId,
38+
contactId: ec.contactId,
39+
}
40+
if (ec.relationshipTitle) {
41+
whereQuery.relationshipTitle = ec.relationshipTitle;
42+
}
43+
const ecObject = await EntityContact.findOne({
44+
where: whereQuery
45+
});
46+
if (!ecObject) {
47+
await EntityContact.create(ec)
48+
}
49+
}
50+
51+
return EntityContact;
52+
};
53+
54+
export default entityContact;

src/models/entity.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UUIDV4 } from "sequelize";
2+
import models from "./index";
23

34
const entity = (sequelize, DataTypes) => {
45
// Defining our entity table and setting Entity object.
@@ -40,7 +41,12 @@ const entity = (sequelize, DataTypes) => {
4041
});
4142

4243
Entity.associate = models => {
43-
Entity.hasMany(models.Contact);
44+
Entity.belongsToMany(models.Contact, {
45+
through: "EntityContact",
46+
as: "contacts",
47+
foreignKey: "entityId",
48+
otherKey: "contactId"
49+
});
4450
}
4551

4652
Entity.findById = async (id) => {
@@ -69,6 +75,25 @@ const entity = (sequelize, DataTypes) => {
6975
return contacts;
7076
}
7177

78+
Entity.findEntityWithAssociatedContacts = async (entityId) => {
79+
const entityContacts = await Entity.findOne({
80+
where: { id: entityId },
81+
include: [{
82+
model: models.Contact,
83+
as: 'contacts',
84+
required: false,
85+
attributes: ["id", "name", "phone", "email", "attributes", "email"],
86+
through: {
87+
model: models.EntityContact,
88+
as: "entityContacts",
89+
attributes: ["relationshipTitle"]
90+
}
91+
}]
92+
});
93+
94+
return entityContacts;
95+
};
96+
7297
return Entity;
7398
};
7499

0 commit comments

Comments
 (0)