Skip to content

Commit 146e0cc

Browse files
committed
Improvements
1 parent 53c9520 commit 146e0cc

14 files changed

+2064
-351
lines changed

CHANGELOG_2025-01-05.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# SchemaMagic Updates - January 5, 2025
2+
3+
## ?? Overview
4+
This update improves relationship visualization in SchemaMagic with better handling of nullable foreign keys (0..1 relationships) and cleaner rendering of self-referential and vertically stacked table relationships.
5+
6+
## ? Key Improvements
7+
8+
### 1. Enhanced 0..1 Relationship Demonstrations
9+
All sample DbContext files now include examples of **optional (nullable) foreign key relationships** to showcase the `0..1` relationship symbol:
10+
11+
#### **ECommerceDbContext.cs**
12+
- ? `Order.BillingAddressId` (nullable) - Orders may not have billing address yet
13+
- ? `Order.ShippingAddressId` (nullable) - Digital orders may not need shipping
14+
- ? `Order.ApprovedById` (nullable) - Orders may not be approved yet
15+
- ? `Customer.PreferredShippingAddressId` (nullable) - Customers may not have a preferred address
16+
- ? `Category.ParentCategoryId` (nullable) - Self-referential hierarchy
17+
18+
#### **BlogDbContext.cs**
19+
- ? `Post.ReviewedById` (nullable) - Posts may not be reviewed yet
20+
21+
#### **SimpleTestDbContext.cs**
22+
- ? `Company.PrimaryContactId` (nullable) - New companies may not have a primary contact yet
23+
- ? `User.ManagerId` (nullable) - Top-level users don't have managers (self-referential hierarchy)
24+
25+
### 2. Fixed Self-Referential Relationship Rendering ??
26+
**Problem**: Self-referential relationships (e.g., `Category.ParentCategoryId ? Category.Id`) had a "weird tail" with unnecessary intermediate path segments.
27+
28+
**Solution**: Simplified the path calculation to create a clean rectangular loop:
29+
- Exit from the left edge of the FK property
30+
- Move left by a fixed distance (200px)
31+
- Move vertically to the target row (Id property)
32+
- Enter back at the left edge
33+
34+
**Before**:
35+
```javascript
36+
// Complex path with unnecessary midpoint calculation
37+
const midY = fromY + ((toY - fromY) / 2) + SELF_REF_LOOP_HEIGHT;
38+
pathSegments = [M, L loopLeft, L midY, L toY, L target]; // 5 segments with weird tail
39+
```
40+
41+
**After**:
42+
```javascript
43+
// Clean rectangular path
44+
pathSegments = [
45+
M fromX fromY, // Start at FK
46+
L loopLeft fromY, // Go left
47+
L loopLeft toY, // Go vertically
48+
L toX toY // Enter at Id
49+
]; // 4 segments, clean loop
50+
```
51+
52+
### 3. Improved Vertical Stacking Routing ??
53+
**Problem**: When tables are stacked vertically (large vertical separation), relationship lines used long horizontal segments that looked unnatural.
54+
55+
**Solution**: Implemented **angled routing** for vertically separated tables:
56+
57+
#### Detection Logic
58+
```javascript
59+
const verticalSeparation = Math.abs(fromY - toY);
60+
const horizontalSeparation = toX - fromX; // or fromX - toX
61+
62+
if (verticalSeparation > 100 && horizontalSeparation > 100) {
63+
// Use angled routing
64+
}
65+
```
66+
67+
#### Angled Path Pattern (LEFT?RIGHT example)
68+
```javascript
69+
const midX = fromX + (horizontalSeparation * 0.4); // 40% of the way
70+
71+
pathSegments = [
72+
M fromX fromY, // Start at FK on right edge
73+
L midX fromY, // Move right 40% of the way
74+
L midX toY, // Angle vertically to target row
75+
L entryX toY, // Move horizontal toward target
76+
L toX toY // Enter at left edge
77+
];
78+
```
79+
80+
**Visual Result**:
81+
```
82+
OrderItem (left, top) ? Product (right, bottom)
83+
84+
[OrderItem]???????
85+
ProductId ? (exit right, move 40% horizontally)
86+
?
87+
? (angle down)
88+
?
89+
??????? [Product]
90+
Id
91+
```
92+
93+
This creates a more natural flow that follows the visual hierarchy of the schema.
94+
95+
### 4. Updated Both Path Functions
96+
Both `createCrowsFootRelationshipLine()` and `updateRelationships()` now use the same improved routing logic to ensure consistency when:
97+
- Tables are initially rendered
98+
- Tables are dragged to new positions
99+
- The view is zoomed or panned
100+
101+
## ?? Testing Recommendations
102+
103+
### Test Self-Referential Relationships
104+
1. Generate schema from `ECommerceDbContext.cs`
105+
2. Verify `Category.ParentCategoryId ? Category` has a clean rectangular loop on the left side
106+
3. No "weird tail" or extra path segments
107+
108+
### Test 0..1 Relationships
109+
1. Generate schema from any sample DbContext
110+
2. Look for nullable FK properties (marked with `?` in the type)
111+
3. Verify the relationship line shows the optional circle marker at the FK side
112+
4. Marker should be: `url(#many-optional-side)` or `url(#one-optional-side)`
113+
114+
### Test Vertical Stacking
115+
1. Generate schema from `ECommerceDbContext.cs`
116+
2. Drag `OrderItem` to the top-left and `Product` to the bottom-right
117+
3. Verify the `OrderItem.ProductId ? Product.Id` relationship uses angled routing
118+
4. Path should exit right, move partway, angle down, then move horizontal to target
119+
120+
### Test Standard Horizontal Layout
121+
1. Place tables side-by-side at similar vertical levels
122+
2. Verify routing still uses the standard horizontal path with marker offsets
123+
3. No unnecessary intermediate angles when tables are horizontally aligned
124+
125+
## ?? Files Modified
126+
127+
### Sample DbContext Files
128+
- ? `Samples/ECommerceDbContext.cs` - Added 4 new nullable FKs
129+
- ? `Samples/BlogDbContext.cs` - Added 1 new nullable FK
130+
- ? `SimpleTestDbContext.cs` - Added 1 new nullable FK
131+
132+
### Template JavaScript Files
133+
- ? `Templates/relationships.js`
134+
- Simplified self-referential loop path (lines 117-137)
135+
- Added angled routing for vertical separation (lines 138-175)
136+
- Updated `updateRelationships()` with same logic (lines 405-480)
137+
138+
## ?? Visual Improvements Summary
139+
140+
| Scenario | Before | After |
141+
|----------|--------|-------|
142+
| Self-referential | Complex loop with "tail" | Clean rectangular loop |
143+
| Vertical stacking | Long horizontal lines | Natural angled routing |
144+
| 0..1 relationships | Not well demonstrated | Multiple examples in samples |
145+
| Optional markers | Few examples | Visible in 7+ relationships |
146+
147+
## ?? Next Steps
148+
149+
### For Users
150+
1. Test the updated routing with your own DbContext files
151+
2. Provide feedback on the angled routing behavior
152+
3. Report any edge cases where routing looks incorrect
153+
154+
### For Development
155+
1. Consider making the angle percentage (currently 40%) configurable
156+
2. Add user preference for routing style (angled vs. orthogonal)
157+
3. Implement automatic layout optimization based on relationship density
158+
4. Add smooth path transitions when dragging tables
159+
160+
## ?? Notes
161+
162+
- All changes are backward compatible
163+
- Generated HTML files include the updated JavaScript
164+
- Routing calculations happen in real-time as tables are moved
165+
- The 100px threshold for vertical separation can be adjusted if needed
166+
167+
## ?? Known Limitations
168+
169+
1. Very complex schemas with many overlapping relationships may still have visual clutter
170+
2. The 40% midpoint is a heuristic and may not be optimal for all layouts
171+
3. Self-referential loops always go left; right-side loops are not yet supported
172+
173+
## ?? Tips for Best Results
174+
175+
- Arrange related tables vertically to leverage angled routing
176+
- Keep tables with many relationships spread out to reduce line overlap
177+
- Use the zoom controls to get an overview of complex schemas
178+
- Click tables to highlight their relationships and reduce visual noise

RELATIONSHIP_FIXES_2025-01-05.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Relationship Visualization Fixes - 2025-01-05
2+
3+
## Issues Fixed
4+
5+
### 1. ? Optional (0..1) Relationships Not Being Drawn
6+
7+
**Problem**: The circle indicator (O) for optional relationships on the "one" side was not appearing, even though nullable foreign keys existed in the schema (e.g., `Order.BillingAddressId`, `Customer.PreferredShippingAddressId`).
8+
9+
**Root Cause**: The `analyzeRelationshipCardinality()` function was only checking if the FK itself was nullable to determine the "from" side optionality, but wasn't properly propagating that information to the "to" side (the "one" side of the relationship).
10+
11+
**Fix**:
12+
- Updated `analyzeRelationshipCardinality()` to properly detect when a nullable FK means the "one" side is optional (0..1)
13+
- The `toOptional` flag now correctly identifies relationships like `Order -> Address?` where the address is optional
14+
- Added proper logging to show both `fromOptional` and `toOptional` detection
15+
16+
**Example**:
17+
- `Order.BillingAddressId (int?)` ? `Address` now correctly shows `O|` on the Address side
18+
- `Customer.PreferredShippingAddressId (int?)` ? `Address` now correctly shows `O|` on the Address side
19+
20+
### 2. ?? Color Mismatch When No Table Selected
21+
22+
**Problem**: When no table was selected, the crow's foot notation appeared in a different blue shade than the relationship lines, creating visual inconsistency.
23+
24+
**Root Cause**: The color was hardcoded in the `drawCrowsFootNotation()` function but wasn't synchronized with the relationship line styling.
25+
26+
**Fix**:
27+
- Ensured crow's foot notation uses the exact same blue color (`#2563eb`) as relationship lines
28+
- Added proper color inheritance when relationships are highlighted (purple) or dimmed (gray)
29+
- Updated dark mode colors to match relationship line dark mode styling
30+
31+
**Visual Result**:
32+
- Default state: Crow's feet and lines both use consistent blue (`#2563eb`)
33+
- Selected state: Crow's feet and lines both use purple (`#764ba2`)
34+
- Dimmed state: Crow's feet and lines both use gray (`#d1d5db`)
35+
36+
## Files Modified
37+
38+
1. **Templates/relationships.js**
39+
- `drawCrowsFootNotation()`: Added constants for circle drawing, improved logging
40+
- `analyzeRelationshipCardinality()`: Fixed optional relationship detection on "one" side
41+
- `updateRelationships()`: Re-analyzes relationships when updating to ensure optional indicators persist
42+
43+
## Testing Recommendations
44+
45+
Test with schemas that have:
46+
1. ? Nullable FKs (e.g., `Order.BillingAddressId?`)
47+
2. ? Self-referencing nullable FKs (e.g., `Category.ParentCategoryId?`)
48+
3. ? Multiple optional relationships from same entity
49+
4. ? Mix of required and optional relationships
50+
51+
### ECommerceDbContext Test Cases
52+
53+
Perfect test schema with these relationships:
54+
- `Order -> Customer` (required, 1:N) ? Shows `>|` and `|`
55+
- `Order -> Address?` via BillingAddressId (optional, 0..1:N) ? Shows `>?` and `|?`
56+
- `Order -> Address?` via ShippingAddressId (optional, 0..1:N) ? Shows `>?` and `|?`
57+
- `Order -> Customer?` via ApprovedById (optional, 0..1:N) ? Shows `>?` and `|?`
58+
- `Customer -> Address?` via PreferredShippingAddressId (optional, 0..1) ? Shows `|?` and `|?`
59+
- `Category -> Category?` via ParentCategoryId (optional, self-ref) ? Shows `>?` and `|?`
60+
61+
## Crow's Foot Notation Legend
62+
63+
| Notation | Meaning | Example |
64+
|----------|---------|---------|
65+
| `>|` | Many (required) | OrderItem ? Order (many items per order) |
66+
| `>?` | Many (optional) | N/A (uncommon - FK would be nullable) |
67+
| `\|` | One (required) | Order ? Customer (order must have customer) |
68+
| `\|?` or `O\|` | One (optional) | Order ? Address? (order may not have address yet) |
69+
70+
### Reading the Notation
71+
72+
For `Order -> Address?` (optional address):
73+
- Order side: `>` (many orders can reference same address)
74+
- Address side: `|?` (one address, but optional - order may not have one)
75+
76+
For `Customer -> Order` (required customer):
77+
- Customer side: `|` (one customer per order)
78+
- Order side: `>` (many orders per customer)
79+
80+
## Implementation Notes
81+
82+
### Optional Detection Logic
83+
84+
```javascript
85+
// From side (FK table) = many side
86+
fromOptional = FK is nullable (int? or string?)
87+
88+
// To side (PK table) = one side
89+
toOptional = FK is nullable (means the reference is optional)
90+
```
91+
92+
### Example: Order.BillingAddressId?
93+
94+
```csharp
95+
public class Order {
96+
public int? BillingAddressId { get; set; } // Nullable FK
97+
public Address? BillingAddress { get; set; } // Nullable navigation
98+
}
99+
```
100+
101+
Analysis:
102+
- FK is nullable ? `fromOptional = true` ? Order side shows circle
103+
- FK is nullable ? `toOptional = true` ? Address side shows circle
104+
- Result: `O>----|O` (both sides optional)
105+
106+
## Dark Mode Support
107+
108+
All crow's foot notation colors properly support dark mode:
109+
- Light mode: `#2563eb` (blue)
110+
- Dark mode: `#60a5fa` (lighter blue)
111+
- Highlighted: Matches selection overlay purple
112+
- Dimmed: Matches dimmed relationship gray
113+
114+
## Known Limitations
115+
116+
1. Circle size is fixed at 10px radius - may need adjustment for very small/large zoom levels
117+
2. Self-referencing optional relationships work correctly but may overlap if layout is tight
118+
3. Multiple optional relationships to same entity may have crowded notation
119+
120+
## Future Enhancements
121+
122+
- [ ] Add tooltip showing relationship type when hovering crow's feet
123+
- [ ] Support M:N relationships with different notation
124+
- [ ] Add animation when relationships are highlighted/dimmed
125+
- [ ] Configurable crow's foot size based on zoom level

0 commit comments

Comments
 (0)