Cours 08 - Query & Relations
Contexte
Section intitulée « Contexte »Les types de relations entre les tables et l’utilisation des jointures ont déjà été couvertes.
Cette méthodes pour récupérer des données en joignant plusieurs tables demeurant la plus efficace et la meilleure pour perfectionner sa talents en SQL. Il existe cependant une autre façon de croiser des données en exploitant les relations entre les tables.
Mise en place
Section intitulée « Mise en place »Il faut premièrement s’assurer que Drizzle retrouve bien les différents schémas déclarés dans l’application. Une façon de faire est de créer un fichier de type barrel. Si ce n’est pas déjà fait, on crée un fichier index.js à l’intérieur du dossier /src/db/schema. La fonction de ce dernier sera de centraliser tous les schémas en les exportant depuis cet endroit:
// l'extension .js est nécessaire ici// (ce fichier est aussi utilisé pour la migration sans passer par Nextjs)export * from './schema.js';export * from './un-autre-fichier-de-schemas.js';export * from './un-troisieme-fichier-de-schemas.js'Dans un fichier /src/db/relations.js, on explicitera chacune des relations propres à chaque table. Nous compléterons ces relations dans la rubrique suivante.
import { defineRelations } from "drizzle-orm";import * as schema from "./schema";
export const relations = defineRelations(schema, (r) => ({}));Finalement, dans le fichier /src/db/index.js, on fournira les relations à Drizzle:
import "dotenv/config"; // pour lire les variables d'environnementimport { drizzle } from "drizzle-orm/libsql";import { relations } from "./relations";
// correspond au nom de la variable dans .envexport const db = drizzle({// logger: true,connection: { url: process.env.DATABASE_URL },relations,});Requêtes
Section intitulée « Requêtes »Une fois les schémas retrouvés, il sera possible de faire des requêtes à la bd en utilisant des queries (db.query.nomDeLaTable({})) plutôt que select.
// retourne tous les posts qui correspondent
export const getAllPosts = async () => { // retourne un tableau de résultats return db.query.postsTable.findMany({ columns: { id: false // <- ne sélectionne pas les id dans la requête par exemple }, orderBy: { id: 'asc', title: 'desc' }, limit: 5 })};
// ne retourne qu'un seul postexport const getPostById = async (postId) => { return db.query.postsTable.findFirst({ where: eq(postsTable.id, postId) })};Relations
Section intitulée « Relations »La rubrique Relations de la documentation de Drizzle est très complète. Ces relations permettent d’établir des liens explicites de type “un à un”, “un à plusieurs” et “plusieurs à plusiers”.
Une fois les schémas rédigés, il est nécesaire de reprendre chacune des tables pour déclarer leurs relations. Toutes les relations seront centralisées dans le fichier /src/db/relations.js.
Un à plusieurs
Section intitulée « Un à plusieurs »Pour reprendre l’exemple de la documentation du site, si on explore le liens entre un utilisateur et ses publications:
export const usersTable = sqliteTable('users', { id: integer().primaryKey(), name: text(),});
export const postsTable = sqliteTable('posts', { id: integer().primaryKey(), content: text(), authorId: integer('author_id').reference(() => usersTable.id, {onDelete: 'cascade'}),});- Un utilisateur possède plusieurs publications.
- Une publication appartient à un utilisateur.
Pour chaque table, on déclarera les relations en leur donnant un nom dans l’objet relations. Voir la déclaration par étape:
import { defineRelations } from "drizzle-orm";import * as schema from "./schema";
// On crée une propriété correspondant au nom des tables (voir les tables déclarées ci-haut)export const relations = defineRelations(schema, (r) => ({ usersTable: {}, postsTable: {}}));import { defineRelations } from "drizzle-orm";import * as schema from "./schema";
// On donne ensuite un nom à la relation à déclarer pour chaque tableexport const relations = defineRelations(schema, (r) => ({ usersTable: { posts: }, postsTable: { auteur: }}));import { defineRelations } from "drizzle-orm";import * as schema from "./schema";
//On utilise r.many ou r.one en fonction du type de relation en plus d'expliquer comment faire le lien (la jonction)// entre les deux tablesexport const relations = defineRelations(schema, (r) => ({ usersTable: { // usersTable.id doit être égal à postsTable.authorId posts: r.many.postsTable({ from: r.usersTable.id, to: r.postsTable.authorId }) }, postsTable: { // postsTable.authorIdd doit être égal à usersTable.id auteur: r.one.usersTable({ from: r.postsTable.authorId, to: r.usersTable.id }) }}));On peut déclarer plusieurs relations pour une même table. Ainsi, un utilisateur pourrait posséder plusieurs publications, mais n’appartenir qu’à un seul rôle:
export const relations = defineRelations(schema, (r) => ({ usersTable: { posts: r.many.postsTable({ from: r.usersTable.id, to: r.postsTable.authorId }), role: r.one.rolesTable({ from: r.usersTable.roleId, to: r.rolesTable.id }) }, // ... extrait }));Plusieurs à plusieurs
Section intitulée « Plusieurs à plusieurs »Les relations plusieurs à plusiers sont finalement une combinaison de relations “un à plusieurs” en les établissant pour chacune des trois tables impliquées:
export const postsTable = sqliteTable('posts', { id: integer().primaryKey(), content: text(), authorId: integer('author_id').reference(() => usersTable.id, {onDelete: 'cascade'}),});
export const tagsTable = sqliteTable('tags', { id: integer().primaryKey(), title: text().notNull()});
export const tagsToPostTable = sqliteTable('tags_posts', { postId: integer('post_id').reference(() => postsTable.id, {onDelete: 'cascade'}), tagId: integer('tag_id').reference(() => tagsTable.id, {onDelete: 'cascade'}),}, (t) => [ primaryKey({columns: [t.postId, t.tagId]}) // clé primaire composite (combinant les deux champs, doit donc rester unique)]);import { defineRelations } from "drizzle-orm";import * as schema from "./schema";
// On donne ensuite un nom à la relation à déclarer pour chaque tableexport const relations = defineRelations(schema, (r) => ({ postsTable: { tag: r.many.tagsTable({ from: r.postsTable.id.through(r.tagsToPostTable.postId), to: r.tagsTable.id.through(r.tagsToPostTable.tagId) }) }, tagsTable: { post: r.many.postsTable() }}));Requêtes (avec relations)
Section intitulée « Requêtes (avec relations) »Une fois les relations explicitement déclarées, il sera possible d’inclure les tables liées lors des requêtes de type db.query:
export const getAllPosts = async () => { // retourne un tableau de résultats return db.query.postsTable.findMany({ columns: { id: false // <- ne sélectionne pas les id dans la requête par exemple }, with: { author: true, // author est le nom de la relation déclarée ci-haut }, orderBy: (posts, {asc}) => [asc(posts.id)], limit: 5 });};