1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Typormを使ってリレーションを組む

Posted at

目標

  • userテーブルとtodoテーブルをつなげる
  • どのユーザーがどんなタスクを持っているかを表示させる
  • ユーザーにつき複数のタスクがつくようにする

目的

  • typeormのリレーションの組み方を学ぶ
  • 1対多の紐付け方を学ぶ

手順

/users に対応するルートを別ファイルにする

routesフォルダーを作りそこにusers.tsを作る

// users.tsを以下のようにする
import { Request, Response } from 'express'

import { Router } from "express";
import { User } from '../entity/User'

const router = Router();

// ユーザー詳細
router.get("/:id", async (req: Request, res: Response) => {
  const results = await User.findOneOrFail(req.params.id);
  return res.json(results);
});
// 全ユーザーを表示
router.get("/", async (req: Request, res: Response) => {
  const results = await User.find();
  return res.json(results);
});
// ユーザーを登録
router.post("/", async (req: Request, res: Response) => {
  const user = await User.create(req.body);
  const results = await User.save(user);
  return res.json(results);
});
// ユーザー情報を更新
router.put("/:id", async (req: Request, res: Response) => {
  // User.findOne()を使用するとエラーになるのでFindOneOrFailを使用
  const user = await User.findOneOrFail(req.params.id);
  User.merge(user, req.body);
  const results = await User.save(user);
  return res.json(results);
});
// ユーザーを削除
router.delete("/:id", async (req: Request, res: Response) => {
  await User.delete(req.params.id);
  const message = "The user is deleted!!"
  return res.json(message);
});

export default router;
//index.txを以下のように変更

import express, { Request, Response, NextFunction } from 'express'
import todoRoutes from './routes/todos'
import userRoutes from './routes/users'
import "reflect-metadata";
import { createConnection } from "typeorm";
// create typeorm connection
createConnection()
  .then(() => {

    // create and setup express app
    const app = express()
    app.use(express.json());

    // register routes
    app.use('/todos', todoRoutes)
    app.use('/users', userRoutes)

    app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
      res.status(500).json({ message: err.message })
    })

    // start express server
    app.listen(3000, () => console.log('Server up at http://localhost:3000'))
  }).catch(error => console.log(error));

todoテーブルを作る

entityフォルダ内にTodo.tsをつくる

// entity/Todo.ts
import { BaseEntity, Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from "typeorm";

@Entity()
export class Todo extends BaseEntity {
  @PrimaryGeneratedColumn()
  readonly id?: number;

  @Column({ type: 'varchar' })
  title: string;

  @Column("text")
  description: string;

  @Column()
  isDone: boolean = false;

  @CreateDateColumn()
  readonly createdAt?: Date;

  @UpdateDateColumn()
  readonly updatedAt?: Date;

  // コンストラクタで初期化
  constructor(title: string, description: string) {
    super();
    this.title = title;
    this.description = description;
  }
}

次にUser.tsを共通するところがあるのでまとめていきます。

entity内でModel.tsを作る

// Model.ts
import { BaseEntity, Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from "typeorm";

@Entity()
export default abstract class Model extends BaseEntity {
  @PrimaryGeneratedColumn()
  readonly id?: number;

  @CreateDateColumn()
  readonly createdAt?: Date;

  @UpdateDateColumn()
  readonly updatedAt?: Date;

  // コンストラクタで初期化
  constructor(model?: Partial<any>) {
    super();
    Object.assign(this, model)
  }
}

User.tsとTodo.tsを変更する

// User.ts
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
import Model from "./Model";

@Entity()
export class User extends Model {
  @PrimaryGeneratedColumn()
  readonly id?: number;

  @Column({ type: 'varchar' })
  name: string;

  @Column({ type: 'varchar' })
  email: string;

  // コンストラクタで初期化
  constructor(name: string, email: string) {
    super();
    this.name = name;
    this.email = email;
  }
}
// Todo.ts
import { Entity, Column } from "typeorm";
import Model from "./Model"

@Entity()
export class Todo extends Model {
  @Column({ type: 'varchar' })
  title: string;

  @Column("text")
  description: string;

  @Column()
  isDone: boolean = false;

  // コンストラクタで初期化
  constructor(title: string, description: string) {
    super();
    this.title = title;
    this.description = description;
  }
}

###todos.tsの処理を整えてCRUDができるようにする

// todos.tsを以下のように変更する
import { Request, Response } from 'express'

import { Router } from "express";
import { Todo } from '../entity/Todo'

const router = Router();

// ユーザー詳細
router.get("/:id", async (req: Request, res: Response) => {
  const results = await Todo.findOneOrFail(req.params.id);
  return res.json(results);
});
// 全ユーザーを表示
router.get("/", async (req: Request, res: Response) => {
  const results = await Todo.find();
  return res.json(results);
});
// ユーザーを登録
router.post("/", async (req: Request, res: Response) => {
  const todo = await Todo.create(req.body);
  const results = await Todo.save(todo);
  return res.json(results);
});
// ユーザー情報を更新
router.put("/:id", async (req: Request, res: Response) => {
  // Todo.findOne()を使用するとエラーになるのでFindOneOrFailを使用
  const todo = await Todo.findOneOrFail(req.params.id);
  Todo.merge(todo, req.body);
  const results = await Todo.save(todo);
  return res.json(results);
});
// ユーザーを削除
router.delete("/:id", async (req: Request, res: Response) => {
  await Todo.delete(req.params.id);
  const message = "The todo is deleted!!"
  return res.json(message);
});

export default router;

###リレーションを組む

userIdカラムをtodoテーブルの加えるためにTodo.tsに次のコードを加える。
Many to oneの考え方です。

//Todo.ts
import { Entity, Column, ManyToOne, JoinColumn } from "typeorm";
import Model from "./Model"
import { User } from "./User" // 追加

@Entity()
export class Todo extends Model {
  @Column({ type: 'varchar' })
  title: string;

  @Column("text")
  description: string;

  @Column()
  isDone: boolean = false;
//----- 追加
  @Column()
  userId!: number;
  @ManyToOne(() => User, user => user.todos)
  user!: User;
//-----
  // userIdの初期化を追加
  constructor(title: string, description: string, userId: number) {
    super();
    this.title = title;
    this.description = description;
    this.userId = userId;
  }
}

User.tsにもTodo.tsと関係があることをコードを追加して表す。
こちらはone to manyの関係

import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from "typeorm";
import Model from "./Model";
import { Todo } from "./Todo";

@Entity()
export class User extends Model {
  @PrimaryGeneratedColumn()
  readonly id?: number;

  @Column({ type: 'varchar' })
  name: string;

  @Column({ type: 'varchar' })
  email: string;
//----- 追加
  @OneToMany(() => Todo, todo => todo.user)
  todos?: Todo[];
//----- 
  // コンストラクタで初期化
  constructor(name: string, email: string) {
    super();
    this.name = name;
    this.email = email;
  }
}

参考

TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.

TypeORM Tutorial (Seamless Typescript ORM)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?