Skip to content

Commit 191a4ff

Browse files
committed
wip: many to many
1 parent 4f6d12b commit 191a4ff

File tree

9 files changed

+88
-36
lines changed

9 files changed

+88
-36
lines changed

src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,44 @@ import React from "react";
44
import * as sessionActions from "@/backend/services/session.actions";
55
import { notFound } from "next/navigation";
66
import { persistenceRepository } from "@/backend/persistence-repositories";
7-
import { eq, and } from "@/backend/persistence/persistence-where-operator";
7+
import {
8+
eq,
9+
and,
10+
manyToManyJoin,
11+
leftJoin,
12+
} from "@/backend/persistence/persistence-where-operator";
13+
import { Article, Tag, User } from "@/backend/models/domain-models";
14+
import { DatabaseTableName } from "@/backend/persistence/persistence-contracts";
815

916
interface Props {
1017
params: Promise<{ uuid: string }>;
1118
}
1219
const page: React.FC<Props> = async ({ params }) => {
1320
const sessionUserId = await sessionActions.getSessionUserId();
1421
const _params = await params;
15-
22+
// eq("author_id", sessionUserId)
1623
const [article] = await persistenceRepository.article.findRows({
1724
limit: 1,
18-
where: and(eq("id", _params.uuid), eq("author_id", sessionUserId)),
25+
where: and(eq("id", _params.uuid)),
26+
columns: ["id", "title", "handle"],
27+
joins: [
28+
leftJoin<Article, User>({
29+
as: "author",
30+
joinTo: DatabaseTableName.users,
31+
localField: "author_id",
32+
foreignField: "id",
33+
columns: ["id", "name", "username"],
34+
}),
35+
],
1936
});
2037

2138
if (!article) {
2239
throw notFound();
2340
}
2441

25-
return <ArticleEditor uuid={_params.uuid} article={article} />;
42+
return <pre>{JSON.stringify(article, null, 2)}</pre>;
43+
44+
// return <ArticleEditor uuid={_params.uuid} article={article} />;
2645
};
2746

2847
export default page;

src/app/[username]/[articleHandle]/opengraph-image.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { Article, User } from "@/backend/models/domain-models";
22
import { persistenceRepository } from "@/backend/persistence-repositories";
3-
import {
4-
eq,
5-
joinTable,
6-
} from "@/backend/persistence/persistence-where-operator";
3+
import { eq, leftJoin } from "@/backend/persistence/persistence-where-operator";
74
import { ImageResponse } from "next/og";
85
import { readFile } from "node:fs/promises";
96
import { join } from "node:path";
@@ -33,7 +30,7 @@ export default async function Image(options: ArticlePageProps) {
3330
columns: ["title", "excerpt", "cover_image", "body"],
3431
limit: 1,
3532
joins: [
36-
joinTable<Article, User>({
33+
leftJoin<Article, User>({
3734
as: "user",
3835
joinTo: "users",
3936
localField: "author_id",

src/app/sitemaps/articles/sitemap.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { persistenceRepository } from "@/backend/persistence-repositories";
33
import {
44
and,
55
eq,
6-
joinTable,
6+
leftJoin,
77
neq,
88
} from "@/backend/persistence/persistence-where-operator";
99
import type { MetadataRoute } from "next";
@@ -14,7 +14,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
1414
columns: ["handle", "updated_at"],
1515
limit: -1,
1616
joins: [
17-
joinTable<Article, User>({
17+
leftJoin<Article, User>({
1818
as: "user",
1919
joinTo: "users",
2020
localField: "author_id",

src/backend/persistence/persistence-contracts.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,25 @@ export type CompositeWhere<T> = {
5353
};
5454
export type WhereCondition<T> = SimpleWhere<T> | CompositeWhere<T>;
5555

56-
export interface IPersistenceJoin {
56+
export interface IPersistenceLeftJoin {
5757
as: string;
5858
joinTo: string;
5959
localField: string;
6060
foreignField: string;
6161
columns: string[];
6262
}
6363

64+
export interface IPersistenceManyToManyJoin {
65+
as: string;
66+
pivotTable: DatabaseTableName;
67+
localField: string;
68+
foreignField: string;
69+
columns: string[];
70+
}
71+
6472
export interface IPersistentPaginationPayload<T> {
6573
where?: WhereCondition<T>;
66-
joins?: IPersistenceJoin[];
74+
joins?: IPersistenceLeftJoin[];
6775
orderBy?: Array<IPersistentOrderBy<T>>;
6876
columns?: Array<keyof T>;
6977
limit?: number;
@@ -88,7 +96,7 @@ export interface IPagination<T> {
8896
where?: WhereCondition<T>; // No longer allows arrays
8997
columns?: Array<keyof T>;
9098
orderBy?: Array<IPersistentOrderBy<T>>;
91-
joins?: IPersistenceJoin[];
99+
joins?: IPersistenceLeftJoin[];
92100
}
93101

94102
//------------------------------------

src/backend/persistence/persistence-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
IPersistenceJoin,
2+
IPersistenceLeftJoin,
33
IPersistentOrderBy,
44
SimpleWhere,
55
WhereCondition,
@@ -144,7 +144,7 @@ export const buildOrderByClause = <T>(
144144
return `ORDER BY ${orderByConditions.join(", ")}`;
145145
};
146146

147-
export const buildJoinClause = <T>(joins?: Array<IPersistenceJoin>) => {
147+
export const buildJoinClause = <T>(joins?: Array<IPersistenceLeftJoin>) => {
148148
if (!joins || joins.length === 0) {
149149
return {
150150
joinConditionClause: "",

src/backend/persistence/persistence-where-operator.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
* Drizzle-style operator functions with direct generic approach
33
*/
44

5-
import { IPersistenceJoin } from "./persistence-contracts";
5+
import {
6+
DatabaseTableName,
7+
IPersistenceLeftJoin,
8+
} from "./persistence-contracts";
69

710
// Type for all possible operators
811
type Operator =
@@ -186,13 +189,13 @@ export function desc<T>(key: keyof T): IPersistentOrderBy<T> {
186189
};
187190
}
188191

189-
export function joinTable<LOCAL_MODEL, FOREIGN_MODEL>(payload: {
192+
export function leftJoin<LOCAL_MODEL, FOREIGN_MODEL>(payload: {
190193
as: string;
191194
joinTo: string;
192195
localField: keyof LOCAL_MODEL;
193196
foreignField: keyof FOREIGN_MODEL;
194197
columns: Array<keyof FOREIGN_MODEL>;
195-
}): IPersistenceJoin {
198+
}): IPersistenceLeftJoin {
196199
return {
197200
as: payload.as,
198201
joinTo: payload.joinTo,
@@ -201,3 +204,19 @@ export function joinTable<LOCAL_MODEL, FOREIGN_MODEL>(payload: {
201204
columns: payload.columns.map((col) => col.toString()),
202205
};
203206
}
207+
208+
export function manyToManyJoin<LOCAL_MODEL, FOREIGN_MODEL>(payload: {
209+
as: string;
210+
pivotTable: DatabaseTableName;
211+
localField: keyof LOCAL_MODEL;
212+
foreignField: keyof FOREIGN_MODEL;
213+
columns: Array<keyof FOREIGN_MODEL>;
214+
}): IPersistenceLeftJoin {
215+
return {
216+
as: payload.as,
217+
joinTo: payload.pivotTable,
218+
localField: payload.localField.toString(),
219+
foreignField: payload.foreignField.toString(),
220+
columns: payload.columns.map((col) => col.toString()),
221+
};
222+
}

src/backend/persistence/persistence.repository.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
buildOrderByClause,
1414
buildSetClause,
1515
buildWhereClause,
16+
sql,
1617
toSnakeCase,
1718
} from "./persistence-utils";
1819
import { removeNullOrUndefinedFromObject } from "@/lib/utils";

src/backend/services/article.actions.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import {
99
and,
1010
desc,
1111
eq,
12-
joinTable,
12+
leftJoin,
1313
like,
14+
manyToManyJoin,
1415
neq,
1516
notInArray,
1617
or,
@@ -199,6 +200,7 @@ export async function updateArticle(
199200
published_at: input.is_published ? new Date() : null,
200201
},
201202
});
203+
202204
return article;
203205
} catch (error) {
204206
handleRepositoryException(error);
@@ -229,6 +231,11 @@ export async function updateMyArticle(
229231
metadata: input.metadata,
230232
},
231233
});
234+
// a 1, t 1
235+
// a 1, t 2
236+
// a 1, t 3
237+
238+
// a-> (2,3,4)
232239

233240
if (input.tag_ids) {
234241
await persistenceRepository.articleTag.deleteRows({
@@ -238,8 +245,8 @@ export async function updateMyArticle(
238245
),
239246
});
240247

241-
await input.tag_ids.forEach((tag_id) => {
242-
persistenceRepository.articleTag.createOne({
248+
input.tag_ids.forEach(async (tag_id) => {
249+
await persistenceRepository.articleTag.createOne({
243250
article_id: article.id,
244251
tag_id: tag_id,
245252
});
@@ -294,7 +301,7 @@ export async function findRecentArticles(
294301
orderBy: [desc("published_at")],
295302
columns: ["id", "title", "handle"],
296303
joins: [
297-
joinTable<Article, User>({
304+
leftJoin<Article, User>({
298305
as: "user",
299306
joinTo: "users",
300307
localField: "author_id",
@@ -337,7 +344,7 @@ export async function articleFeed(
337344
"excerpt",
338345
],
339346
joins: [
340-
joinTable<Article, User>({
347+
leftJoin<Article, User>({
341348
as: "user",
342349
joinTo: "users",
343350
localField: "author_id",
@@ -386,7 +393,7 @@ export async function userArticleFeed(
386393
"excerpt",
387394
],
388395
joins: [
389-
joinTable<Article, User>({
396+
leftJoin<Article, User>({
390397
as: "user",
391398
joinTo: "users",
392399
localField: "author_id",
@@ -429,19 +436,20 @@ export async function articleDetailByHandle(article_handle: string) {
429436
"updated_at",
430437
],
431438
joins: [
432-
joinTable<Article, User>({
439+
leftJoin<Article, User>({
433440
as: "user",
434441
joinTo: "users",
435442
localField: "author_id",
436443
foreignField: "id",
437444
columns: ["id", "name", "username", "profile_photo"],
438445
}),
439-
// joinTable<Article, Tag>({
440-
// as: "tags",
441-
// joinTo: "tags",
442-
// localField: "author_id",
443-
// foreignField: "id",
444-
// }),
446+
manyToManyJoin<Article, Tag>({
447+
as: "tags",
448+
pivotTable: DatabaseTableName.article_tag,
449+
localField: "id",
450+
foreignField: "id",
451+
columns: ["id", "name", "icon", "color", "description"],
452+
}),
445453
],
446454
limit: 1,
447455
});
@@ -476,7 +484,7 @@ export async function articleDetailByUUID(uuid: string) {
476484
"updated_at",
477485
],
478486
joins: [
479-
joinTable<Article, User>({
487+
leftJoin<Article, User>({
480488
as: "user",
481489
joinTo: "users",
482490
localField: "author_id",

src/backend/services/series.action.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
asc,
77
desc,
88
eq,
9-
joinTable,
9+
leftJoin,
1010
} from "../persistence/persistence-where-operator";
1111
import { Article, Series, SeriesItem, User } from "../models/domain-models";
1212

@@ -31,7 +31,7 @@ export const getSeriesDetailByHandle = async (handle: string) => {
3131
where: eq("handle", handle),
3232
limit: 1,
3333
joins: [
34-
joinTable<Series, User>({
34+
leftJoin<Series, User>({
3535
as: "owner",
3636
joinTo: "users",
3737
localField: "owner_id",
@@ -46,7 +46,7 @@ export const getSeriesDetailByHandle = async (handle: string) => {
4646
orderBy: [asc("index")],
4747
limit: -1,
4848
joins: [
49-
joinTable<SeriesItem, Article>({
49+
leftJoin<SeriesItem, Article>({
5050
as: "article",
5151
joinTo: "articles",
5252
localField: "article_id",

0 commit comments

Comments
 (0)