ในโพสต์บล็อกก่อนหน้าของฉัน เราได้เจาะลึก TypeORM และสร้างแอปพลิเคชันสิ่งที่ต้องทำ CRUD พื้นฐาน ในโพสต์บนบล็อกนี้ เราจะดำเนินการสำรวจต่อไปโดยยกระดับแอปพลิเคชัน todo ขึ้นไปอีกระดับด้วยการเพิ่มความซับซ้อนของความสัมพันธ์แบบหนึ่งต่อกลุ่ม

ในบริบทของแอปพลิเคชัน Todo ความสัมพันธ์แบบหนึ่งต่อกลุ่มสามารถแสดงได้ดังนี้:

TodoList หนึ่งตัวสามารถมี TodoItem ได้หลายตัว

ตัวอย่างเช่น พิจารณาแอปพลิเคชัน Todo ที่ผู้ใช้สามารถสร้างรายการ Todo ได้หลายรายการ โดยแต่ละรายการจะมีชุดรายการ Todo รายการสิ่งที่ต้องทำแต่ละรายการจะถูกระบุโดยไม่ซ้ำกันด้วย ID และแต่ละรายการสิ่งที่ต้องทำจะเชื่อมโยงกับรายการสิ่งที่ต้องทำผ่านความสัมพันธ์ของคีย์นอก

ในสถานการณ์นี้ เราสามารถกำหนดคลาสเอนทิตี TodoList ด้วยคีย์หลัก id และคุณสมบัติ name ที่แสดงถึงชื่อของรายการ นอกจากนี้เรายังสามารถกำหนดคลาสเอนทิตี TodoItem ด้วยคีย์หลัก id คุณสมบัติ description ที่แสดงถึงคำอธิบายงาน และคีย์ภายนอก todoListId ที่อ้างอิง id ของ TodoList

@Entity()
export class TodoList {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => TodoItem, todoItem => todoItem.todoList)
  todoItems: TodoItem[];
}

@Entity()
export class TodoItem {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  description: string;

  @ManyToOne(() => TodoList, todoList => todoList.todoItems)
  todoList: TodoList;

  @Column()
  todoListId: number;
}

ในตัวอย่างก่อนหน้านี้ เราใช้ตัวตกแต่ง @OneToMany และ @ManyToOne ที่ได้รับจาก TypeORM เพื่อสร้างความสัมพันธ์แบบหนึ่งต่อกลุ่มระหว่างเอนทิตี TodoList และ TodoItem มัณฑนากร @OneToMany ใช้เพื่อกำหนดด้านผกผันของความสัมพันธ์ในคลาส TodoList ในขณะที่มัณฑนากร @ManyToOne กำหนดด้านที่เป็นเจ้าของของความสัมพันธ์ในคลาส TodoItem

ด้วยการกำหนดความสัมพันธ์ทั้งสองด้าน เราจึงสามารถรับประกันความสอดคล้องของข้อมูลได้ดีขึ้น ทำให้การสืบค้นข้อมูลเข้าใจง่ายขึ้น และปรับปรุงการจัดระเบียบโค้ด ด้วยการตั้งค่านี้ เราสามารถดึงข้อมูลเอนทิตี TodoItem ทั้งหมดที่เกี่ยวข้องกับเอนทิตี TodoList เฉพาะได้อย่างง่ายดายโดยการสืบค้นคุณสมบัติ todoItems ของออบเจ็กต์ TodoList

มามุ่งเน้นไปที่มัณฑนากร @OneToMany ซึ่งใช้เพื่อกำหนดความสัมพันธ์แบบหนึ่งต่อกลุ่มระหว่างสองเอนทิตีใน TypeORM ไวยากรณ์สำหรับมัณฑนากรนี้คือ:

@OneToMany(type => RelatedEntity, relatedEntity => relatedEntity.entityProperty)

ส่วน “type =› RelatedEntity” ระบุประเภทของเอนทิตีที่เกี่ยวข้อง ซึ่งกำหนดด้าน “กลุ่ม” ของความสัมพันธ์ ในกรณีของเรา เอนทิตีที่เกี่ยวข้องคือ TodoItem ส่วน “ relatedEntity = >> relatedEntity.entityProperty” ระบุคุณสมบัติในเอนทิตีที่เกี่ยวข้องซึ่งแสดงถึงด้าน “หนึ่ง” ของความสัมพันธ์ ในตัวอย่างของเรา คุณสมบัติคือ todoList ซึ่งแสดงถึงเอนทิตี TodoList ที่มีเอนทิตี TodoItem จำนวนมาก

ด้วยการใช้มัณฑนากรนี้ TypeORM สามารถแมปเอนทิตีทั้งสองและสร้างความสัมพันธ์ระหว่างกัน เป็นผลให้เราสามารถดึงข้อมูลที่เกี่ยวข้องได้อย่างง่ายดายและรับประกันความสอดคล้องของข้อมูลที่ดีขึ้น ทำให้โค้ดของเราจัดระเบียบและบำรุงรักษาได้ง่ายขึ้น

ตอนนี้เราได้กำหนดเอนทิตี TodoList และ TodoItem แล้ว เราสามารถดำเนินการใช้ฟังก์ชัน CRUD ด้วยความสัมพันธ์แบบหนึ่งต่อกลุ่มได้ ขั้นตอนต่อไปคือการกำหนด TodoListService ของเรา ซึ่งจะมีเมธอด CRUD:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TodoList } from './entities/todo-list.entity';

@Injectable()
export class TodoListService {
  constructor(
    @InjectRepository(TodoList)
    private readonly todoListRepository: Repository<TodoList>,
  ) {}

  async findAll(): Promise<TodoList[]> {
    return this.todoListRepository.find();
  }

  async create(name: string, items: TodoItem[]): Promise<TodoList> {
    const todoList = new TodoList();
    todoList.name = name;
    todoList.todoItems = items;
  
    return this.todoListRepository.save(todoList);
  }

  async update(id: number, updateTodoListDto: UpdateTodoListDto): Promise<TodoList> {
    const todoList = await this.todoListRepository.findOne(id, { relations: ['todoItems'] });
    if (!todoList) {
      throw new NotFoundException(`Todo list with id ${id} not found`);
    }
  
    const { name, todoItems } = updateTodoListDto;
  
    if (name) {
      todoList.name = name;
    }
  
    if (todoItems) {
      // Remove existing todo items
      todoList.todoItems = [];
  
      // Create new todo items and associate them with the todo list
      for (const todoItemDto of todoItems) {
        const todoItem = new TodoItem();
        todoItem.name = todoItemDto.name;
        todoItem.done = todoItemDto.done;
        todoItem.todoList = todoList;
        await this.todoItemRepository.save(todoItem);
        todoList.todoItems.push(todoItem);
      }
    }
  
    return this.todoListRepository.save(todoList);
  }

  async delete(id: number): Promise<void> {
    await this.todoListRepository.delete(id);
  }
}

เมธอด findAll ดึงเอนทิตี TodoList ทั้งหมด เมธอด create สร้างเอนทิตี TodoList ใหม่ด้วยชื่อที่กำหนดและรายการเอนทิตี TodoItem เมธอด update อัปเดตเอนทิตี TodoList ที่มีอยู่ด้วย ID ที่กำหนดและคุณสมบัติที่ระบุในออบเจ็กต์ UpdateTodoListDto รวมถึงรายการเอนทิตี TodoItem ที่เกี่ยวข้อง เมธอด delete จะลบเอนทิตี TodoList ที่มีอยู่ด้วย ID ที่กำหนด

สุดท้ายนี้ เราสามารถกำหนด TodoListController ของเราเพื่อจัดการคำขอ HTTP ได้:

import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { TodoListService } from './todo-list.service';
import { CreateTodoListDto } from './dto/create-todo-list.dto';
import { UpdateTodoListDto } from './dto/update-todo-list.dto';
import { TodoList } from './entities/todo-list.entity';

@Controller('todo-lists')
export class TodoListController {
  constructor(private readonly todoListService: TodoListService) {}

  @Get()
  async findAll(): Promise<TodoList[]> {
    return this.todoListService.findAll();
  }

  @Post()
  async create(@Body() createTodoListDto: CreateTodoListDto): Promise<TodoList> {
    return this.todoListService.create(createTodoListDto.name, createTodoListDto.todoItems);
  }

  @Put(':id')
  async update(
    @Param('id') id: number,
    @Body() updateTodoListDto: UpdateTodoListDto,
  ): Promise<TodoList> {
    return this.todoListService.update(id, updateTodoListDto);
  }

  @Delete(':id')
  async delete(@Param('id') id: number): Promise<void> {
    return this.todoListService.delete(id);
  }
}

เราได้กำหนดตัวควบคุม API ด้วยจุดสิ้นสุดสี่จุดซึ่งสอดคล้องกับการดำเนินการ CRUD ที่กำหนดไว้ใน TodoListService มัณฑนากร @Get ใช้สำหรับเมธอด findAll, มัณฑนากร @Post สำหรับ create, @Put มัณฑนากรสำหรับ update และ @Delete มัณฑนากรสำหรับ delete ตัวตกแต่ง @Body และ @Param ใช้เพื่อดึงข้อมูลจากเนื้อหาคำขอและพารามิเตอร์ URL ตามลำดับ

โปรดทราบว่าตัวควบคุมขึ้นอยู่กับอินสแตนซ์ของคลาส TodoListService ซึ่งถูกส่งผ่านไปยังตัวสร้างโดยใช้การฉีดการขึ้นต่อกัน

โดยสรุป ความสัมพันธ์แบบหนึ่งต่อกลุ่มใน TypeORM และ NestJS สามารถปรับปรุงฟังก์ชันการทำงานของแอปพลิเคชันของคุณได้อย่างมาก โดยช่วยให้คุณสร้างแบบจำลองข้อมูลที่ซับซ้อนและครอบคลุมมากขึ้นได้ ด้วยการทำความเข้าใจวิธีใช้ความสัมพันธ์นี้และเครื่องมือตกแต่งที่เกี่ยวข้อง คุณจะสามารถสร้างแอปพลิเคชันที่แข็งแกร่งและมีประสิทธิภาพมากขึ้นได้ ดังนั้น อย่าลังเลที่จะทดลองใช้ความสัมพันธ์แบบหนึ่งต่อกลุ่มในโปรเจ็กต์ถัดไปของคุณ!

หากคุณพบว่าข้อมูลนี้มีประโยชน์ โปรดสนับสนุนและติดตามฉันเพื่อรับการอัปเดตเพิ่มเติม🙂