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

Issue 219 #237

Merged
merged 1 commit into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(Relationships): Reduce relationship lines by combining relations…
…hip names to and from end model, then skipping end model. Fix issue with misrepresented one/one ot zero/one relationships
  • Loading branch information
keonik committed Aug 22, 2023
commit b68927948ef60173d7ed2f5ca46592e8877333f8
2 changes: 1 addition & 1 deletion ERD.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion __tests__/issues/124.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ test('Zero to many relationship', async () => {
// User has zero to many posts
expect(svgContent).toContain('author');
expect(svgContent).toContain('marker-start="url(#ZERO_OR_MORE_START)"');
expect(svgContent).toContain('marker-end="url(#ZERO_OR_ONE_END)"');
expect(svgContent).toContain('marker-end="url(#ZERO_OR_MORE_END)"');
});
2 changes: 1 addition & 1 deletion __tests__/issues/138.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ test('id not override before key name', async () => {
/id="text-entity-UserSetting([^\><]*)-attr-\d-name"([^<\>]*)\>user_id<\/text\>/
);
// UserSetting has a relation to User
expect(svgContent).toMatch(/<text([^><]*)\>user<\/text\>/);
expect(svgContent).toMatch(/<text([^><]*)\>userSettings \/ user<\/text\>/);
// expect(svgContent).toContain('>user</text>');
});
25 changes: 25 additions & 0 deletions prisma/issues/219.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
generator erd {
provider = "node ./dist/index.js"
output = "../../__tests__/219.svg"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model DailySchedule {
id Int @id @default(autoincrement())
day String
startTime String
endTime String
schedule Schedule @relation(fields: [scheduleId], references: [id])
scheduleId Int
}

model Schedule {
id Int @id @default(autoincrement())
name String
timezone String
dailySchedules DailySchedule[]
}
63 changes: 50 additions & 13 deletions src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,37 @@ ${
.join('\n\n');

let relationships = '';
let skipModels: string[] = [];
// https://mermaid.js.org/syntax/entityRelationshipDiagram.html#relationship-syntax
/*
Value (left) Value (right) Meaning
|o o| Zero or one
|| || Exactly one
}o o{ Zero or more (no upper limit)
}| |{ One or more (no upper limit)
*/
for (const model of modellikes) {
for (const field of model.fields) {
const isEnum = field.kind === 'enum';
if (isEnum && (tableOnly || ignoreEnums)) {
continue;
}

const relationshipName = `${isEnum ? 'enum:' : ''}${field.name}`;
const thisSide = `"${model.dbName || model.name}"`;
const otherSide = `"${
if (field.relationName) {
// skip models that have already been processed
if (skipModels.includes(field.relationName)) {
continue;
} else if (field.relationName) {
// add to skip list
skipModels.push(field.relationName);
}
}

const thisSide = model.dbName || model.name;
const otherSide =
modellikes.find((ml) => ml.name === field.type)?.dbName ||
field.type
}"`;
field.type;
const relationshipName = `${isEnum ? 'enum:' : ''}${field.name}`;
// normal relations
if (
(field.relationFromFields &&
Expand Down Expand Up @@ -175,20 +193,37 @@ ${
} else if (!otherField?.isRequired) {
thisSideMultiplicity = 'o|';
}

relationships += ` ${thisSide} ${thisSideMultiplicity}--${otherSideMultiplicity} ${
const otherSideRelationName = otherField?.name
? ` / ${otherField.name}`
: '';
relationships += ` "${thisSide}" ${thisSideMultiplicity}--${otherSideMultiplicity} "${
otherModel?.dbName || otherSide
} : "${relationshipName}"\n`;
}" : "${relationshipName + otherSideRelationName}"\n`;
}
// many to many
else if (
modellikes.find(
(m) => m.name === field.type || m.dbName === field.type
) &&
field.relationFromFields?.length === 0
// && field.relationToFields?.length
) {
relationships += ` ${thisSide} o{--}o ${otherSide} : "${field.name}"\n`;
const otherSideRelationName =
modellikes
.find(
(m) =>
m.name === field.type || m.dbName === field.type
)
?.fields.find((f) => f.type === model.name)?.name ?? '';
console.log(
'many to many relationship',
field.name,
otherSideRelationName
);
relationships += ` ${thisSide} o{--}o ${otherSide} : "${
field.name
}${
otherSideRelationName ? ' / ' + otherSideRelationName : ''
}"\n`;
}
// composite types
else if (field.kind == 'object') {
Expand All @@ -198,7 +233,7 @@ ${
.replace(/^_/, 'z_') // replace leading underscores
.replace(/\s/g, '') // remove spaces === otherSide
);
console.log(otherSide, otherSideCompositeType);

if (otherSideCompositeType) {
// most logic here is a copy/paste from the normal relation logic
// TODO extract and reuse
Expand All @@ -220,10 +255,12 @@ ${
} else if (!otherField?.isRequired) {
thisSideMultiplicity = 'o|';
}

const otherSideRelationName = otherField?.name
? ` / ${otherField.name}`
: '';
relationships += ` ${thisSide} ${thisSideMultiplicity}--${otherSideMultiplicity} ${
otherSideCompositeType.dbName || otherSide
} : "${relationshipName}"\n`;
} : "${relationshipName}${otherSideRelationName}"\n`;
}
}
}
Expand Down