Vapor버전 4.0을 기준으로 작성된 게시글입니다!
DB기능을 Vapor에서 Fluent라고 지칭하는것 같아요
MySQL인 경우
// dependencies에 추가
.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent-mysql-driver.git", from: "4.0.0-beta")
// target에 추가
.product(name: "Fluent", package: "fluent"),
.product(name: "FluentMySQLDriver", package: "fluent-mysql-driver")
위의 코드를 추가해주면
아래와 같이 되겠죠
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent-mysql-driver.git", from: "4.0.0-beta"),
],
targets: [
.target(
name: "App",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "Fluent", package: "fluent"),
.product(name: "FluentMySQLDriver", package: "fluent-mysql-driver"),
],
configure.swit에 디비를 사용하니까 설정을 추가해줘야해요
이제부터 디비관련 코드를 작성할때 import Fluent 빼먹지마시구요
import Fluent
import FluentMySQLDriver
// configures your application
public func configure(_ app: Application) throws {
// uncomment to serve files from /Public folder
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
// MARK: - Setting MySQL
try setUpMySQL(app)
// MARK: - Register Routes
try routes(app)
}
private func setUpMySQL(_ app: Application) throws {
app.databases.use(
.mysql(
hostname: "localhost",
username: "root",
password: "",
database: "vapor",
tlsConfiguration: .forClient(certificateVerification: .none)
), as: .mysql)
}
이렇게 만들어두고
터미널에서
create database vapor
을 입력하면 데이터베이스가 만들어진걸 확인할 수 있죠
이때 만약
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
이 에러가 보인다면
mysql 서버가 실행되지 않은상태인거에요
터미널에서
mysql.server start
를 실행해주고 다시 확인해보세용
확인을 어떻게하냐구요?
다운받아주세요~
(터미널에서 접속해서 명령어로 하셔도무방합니다~~)
.
.
.
데이터베이스를 만들었으니이제 테이블을 만들어야해요
이제부터 Model이라는 개념이 나와요
Model의 역할은 Database에서 Table이자 데이터라고 비유할 수 있어요
모델은 기본적으로 Codable을 채택하구있구요
모델을 만들때 빈 생성자를 꼭 만들어줘야해요
vapor에서 사용한다고 하네요
final class Galaxy: Model, Content {
// Name of the table or collection.
static let schema = "galaxies"
// Unique identifier for this Galaxy.
@ID(key: .id)
var id: UUID?
// The Galaxy's name.
@Field(key: "name")
var name: String
// Creates a new, empty Galaxy.
init() { }
// Creates a new Galaxy with all properties set.
init(id: UUID? = nil, name: String) {
self.id = id
self.name = name
}
}
Model을 채택하면
schema변수를 설정해주고
@ID를 이용해서
해당값으로 모델의 인스턴스를 고유하게 식별할 수 있도록 할 수있어요
@Field는
키값을 필요로 하고 값을 저장할 수 있는 데이터베이스의 필드 역할을 지정해주는 프로퍼티레퍼에요
이 키값이 db에서의 키값이 되고
변수값은 키에 대한 값으로 저장될거에요
Create
데이터를 생성하는 코드는 간단해요
해당 모델을 만들어서
.create(on: db) 를 통해 생성할 수 있어요
let galaxy = Galaxy(name: "Earth")
galaxy.create(on: database)
서버 요청에서 디비를 접근하려면 아래와같아요
database를 req.db로 접근할 수 있구요
EventLoopFuture은 아직은 잘 몰라두되요 이렇게하는구나 우선 넘어가세요
app.get("galaxies") { req -> EventLoopFuture<Galaxy> in
let galaxy = try req.query.decode(Galaxy.self)
return galaxy.create(on: req.db)
.map { galaxy }
}
/* 응답
{
"id": "B6CA2E50-D9CD-4E89-9BD7-3C119E119E28",
"name": "\"지구\""
}
*/
EventLoopFuture<Galaxy>를 출력하면
현재 모델에는
id와 name이 있으니 생성된 그값들이 나오네요
Read
All
전체배열
// query all
app.get("galaxies", "all") { req in
Galaxy.query(on: req.db).all()
}
/*
[
{
"id": "64AA8096-41B8-4D55-8F66-DA6653AFC31D",
"name": "\"태양\""
},
{
"name": "\"지구\"",
"id": "B6CA2E50-D9CD-4E89-9BD7-3C119E119E28"
}
]
*/
디비에 있는 모든데이터가 배열로 나오는 코드에요 간단하죠
Single Field
원하는 필드만 뽑아내고싶다면
// query single field all
app.get("galaxies", "name", "all") { req in
Galaxy.query(on: req.db).all(\.$name)
}
/*
[
"\"태양\"",
"\"지구\""
]
*/
키패스를 이용해서 원하는 프로퍼티에만 접근할 수 있어요
Field
원하는 필드만 가져오고 싶을 때 사용하구요
선택하지 않은 필드는 nil로 나와요
// field
app.get("galaxies", "field") { req in
return Galaxy.query(on: req.db)
.field(\.$name)
.all()
}
/*
[
{
"name": "지구지구",
"id": null
},
{
"name": "\"지구나라\"",
"id": null
},
{
"id": null,
"name": "\"태양\""
},
{
"name": "\"지구\"",
"id": null
},
{
"id": null,
"name": "지구나라"
}
]
*/
Filter
필터를 설정할 수도있어요
// query first
app.get("galaxies", "first") { req -> EventLoopFuture<Galaxy> in
let earth = Galaxy.query(on: req.db)
.filter(\.$name == req.query["name"]!)
.first()
.unwrap(or: Abort(.noContent))
return earth
}
요청에서 원하는 이름을 받아오고 해당된 이름을 모두 불러온뒤
처음에 나온 데이터만 반환해주는 코드에요
Group
filter를 중복해서사용하면 and연산을하지만
group을 사용하면 or연산이 가능해요
// query group
app.get("galaxies", "group") { req -> EventLoopFuture<[Galaxy]> in
let a: String = req.query["a"]!
let b: String = req.query["b"]!
let galaxy = Galaxy.query(on: req.db).group(.or) { group in
group.filter(\.$name == b).filter(\.$name == a)
}.all()
return galaxy
}
/*
[
{
"name": "지구지구",
"id": "32968A73-90A5-490C-B218-A4A150338550"
},
{
"name": "지구나라",
"id": "EACA8500-CAD1-4ECC-B42D-2730EBFD5595"
}
]
*/
////////////////////
// query filter
app.get("galaxies", "filter") { req -> EventLoopFuture<[Galaxy]> in
let a: String = req.query["a"]!
let b: String = req.query["b"]!
let earth = Galaxy.query(on: req.db)
.filter(\.$name == a)
.filter(\.$name == b)
.all()
return earth
}
/*
[]
*/
Unique
해당값에대한 유니크한 값만 나와요
같은 값이 있다면 무시되고 하나만 나오는거에요
// unique
app.get("galaxies", "unique") { req in
return Galaxy.query(on: req.db)
.field(\.$name)
.unique()
.all()
}
/*
[
{
"id": null,
"name": "지구지구"
},
{
"id": null,
"name": "\"지구나라\""
},
{
"name": "\"태양\"",
"id": null
},
{
"name": "\"지구\"",
"id": null
},
{
"name": "지구나라",
"id": null
}
]
*/
Range
반환 갯수 제한가능
// range
app.get("galaxies", "range") { req in
Galaxy.query(on: req.db)
.range(..<3)
.all()
}
Update
// update
app.get("galaxies", "update") { req in
Galaxy.query(on: req.db)
.set(\.$name, to: req.query["newName"]!)
.filter(\.$name == "지구지구")
.update().map { "update" }
}
위의 코드는
데이터베이스에서 지구지구 이름인 데이터를 새로 받은 newName으로 수정하는 코드에요
update함수는 set, filter, range를 지원해줘서 같이 사용할 수 있어요
set이 변경할 패스와 값을 지정해주고
마지막에 update()를 실행해야 적용이되요
수정후 성공포맷을 반환하고싶다면
// update
app.get("galaxies", "update") { req in
Galaxy.query(on: req.db)
.filter(\.$name == req.query["name"]!)
.set(\.$name, to: req.query["newName"]!)
.update().map { SimpleResponse(code: 200, msg: "수정성공") }
}
struct SimpleResponse: Content {
var code: Int
var msg: String
}
/*
{
"msg": "수정성공",
"code": 200
}
*/
Delete
delete함수엔 filter가 지원되구요
// delete
app.get("galaxies", "delete") { req in
Galaxy.query(on: req.db)
.filter(\.$name == req.query["name"]!)
.delete()
.map { SimpleResponse(code: 200, msg: "삭제성공")}
}
Paginate
페이징 기능도 중요하죠
vapor은 기본적으로 페이징을 지원해줘서 간편하게 사용할 수 있구요
함수구현시 paginate만 사용하고
요청url에
per
page
url query를 붙여서 원하는 위치를 접근 할 수 있어요
응답으로나오는 메타데이터의
per: 페이지에 보여줄수 있는 최대 갯수
page: 현재페이지
total: 테이블에 있는 데이터 전체갯수
// paginate
app.get("galaxies") { req in
Galaxy.query(on: req.db)
.paginate(for: req)
}
// http://127.0.0.1:8080/galaxies?page=1
{
"items": [
{
"id": "59A0DCE5-B994-4FBD-A793-4A78DD9410D1",
"name": "\"지구나라\""
},
{
"name": "\"태양\"",
"id": "64AA8096-41B8-4D55-8F66-DA6653AFC31D"
},
{
"name": "\"지구\"",
"id": "B6CA2E50-D9CD-4E89-9BD7-3C119E119E28"
},
{
"id": "EACA8500-CAD1-4ECC-B42D-2730EBFD5595",
"name": "지구나라"
}
],
"metadata": {
"per": 10,
"total": 4,
"page": 1
}
}
// http://127.0.0.1:8080/galaxies?page=1&per=3
{
"items": [
{
"id": "59A0DCE5-B994-4FBD-A793-4A78DD9410D1",
"name": "\"지구나라\""
},
{
"name": "\"태양\"",
"id": "64AA8096-41B8-4D55-8F66-DA6653AFC31D"
},
{
"name": "\"지구\"",
"id": "B6CA2E50-D9CD-4E89-9BD7-3C119E119E28"
}
],
"metadata": {
"per": 3,
"total": 4,
"page": 1
}
}
'iyOmSd > Title: Vapor' 카테고리의 다른 글
[Vapor] Schema CRUD 데이터베이스 테이블 수정 (0) | 2021.03.06 |
---|---|
[Vapor] Validation 유효성검사 (0) | 2021.03.06 |
[Vapor] Routing (경로 설정, Data받아오기) (0) | 2021.03.06 |
[Vapor] 프로젝트 기본설정 및 설치 (2) | 2021.03.06 |