使用Vapor 2和PostgreSQL持久化数据

这是正在进行的“蒸气入门”系列的第3部分。

在本教程中,我们将结合到目前为止所学的知识,使用我们保存到数据库中的数据来创建一些网页。 在本教程中,我们将使用开源关系数据库PostgreSQL。 如果您对使用数据库或编写SQL代码不完全熟悉,请不要担心。 Vapor附带了Fluent,Fluent是一种Swift ORM(对象关系映射工具),它使数据库的使用变得异常简单。 虽然学习SQL和关系数据库很有帮助,但您会在本教程中注意到并不需要完成。

对于今天的教程,我们将制作一个简单的博客应用程序。 对于本教程,我们将使其保持简单,因此我们将不包括任何身份验证。 好吧,让我们潜入。

首先,让我们开始创建一个新的Vapor项目

步骤1 —生成一个新的Vapor项目

打开您的终端并输入vapor new blogger

第2步-生成 .xcodeproj 项目文件

在终端中,输入cd blogger && vapor xcode这将带我们进入blogger文件夹并生成一个xcode项目文件

步骤3 —安装自制软件

Homebrew是一个软件包管理器,可以帮助开发人员在其MacOS设备上安装工具。 复制并粘贴以下代码以安装Homebrew

 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 

第4步-安装Postgresql

在终端类型brew install postgresql

我已经安装了Postgresql,所以您的终端看起来与我的略有不同。

第5步–运行PostgreSQL

要启动Postgresql,请在终端中运行以下命令。

  postgres -D / usr / local / var / postgres 

输入此命令后,您应该看到以下内容:

现在您已经启动并运行了Postgresql,现在需要为全新的Blogger应用创建数据库。 如果您对SQL不太满意,请不要担心,我们不会大量使用它。

第6步-创建一个新数据库

在终端中,在新的终端窗口中键入psql以打开PostgreSQL控制台。 确保您不在postgreSQL记录器中。

现在您处于正确的控制台中,我们可以输入一些SQL(发音为Sequel或Ess-Cue-El)来创建一个新的数据库。 输入CREATE DATABASE blogger; 进入终端以生成数据库。 不要忘记分号。 对于Swift开发人员来说这可能很奇怪,但是分号在许多其他语言中很常见。

现在我们已经创建了一个数据库,让我们退出PostgreSQL控制台。 为此,请键入\q退出。

步骤7 —下载PostgreSQL客户端

即使您熟悉使用SQL,使用某种Postgresql GUI客户端也很有帮助。 这些客户端使使用PostgreSQL变得非常容易,因为您可以指向并单击以创建新的数据库,表等。我喜欢使用的是Postico。 您可以通过创建新的收藏夹并输入信息来连接到PostgreSQL数据库。

由于您在本地计算机上运行PostgreSQL,因此主机将为127.0.0.1 ,默认情况下,PostgreSQL将在端口5432上运行。 点击connect后,您将连接到数据库。

步骤8 —将蒸气连接到PostgreSQL

要将PostgreSQL与Vapor一起使用,我们将使用VaporPostgreSQL提供程序。 首先,什么是提供者?

Provider协议为向Vapor项目添加功能和第三方程序包创建了一种简单且可预测的方式。 —蒸气文件

要将提供程序添加到您的Vapor应用程序,您首先需要将该软件包添加到Vapor。 为此,我们Package.swift下行添加到Package.swift文件的底部

  .Package(URL:“ https://github.com/vapor/postgresql-provider",majorVersion:1、minor:0) 

您的Package.swift文件现在应如下所示

更新Package.swift文件后,必须运行vapor xcode以下载软件包文件中的所有软件包并重新生成Xcode项目。 打开blogger.xcodeproj并导航到main.swift 。 在import Vapor下方,添加import VaporPostgreSQL 。 在let drop = Droplet()添加以下内容:

 try drop.addProvider(VaporPostgreSQL.Provider.self) 

通过将Postgresql提供程序添加到小滴,我们为Vapor提供了利用数据库的功能。 最后,我们必须将一个postgresql.json文件添加到我们的Config/Secrets文件夹中。 该文件将包含我们数据库的配置信息。 由于我们将其添加到Secrets文件夹中,因此不会包含在任何git commit中。 首先,右键单击Config文件夹,单击New Folder,然后将此文件夹命名为Secrets 。 接下来,右键单击Secrets文件夹,单击“新建文件”,并将其命名为postgresql.json 。 在此文件内添加以下内容:

  { 
“主机”:“ 127.0.0.1”,
“ user”:“ johannkerr”,
“ password”:“”,
“数据库”:“博客”,
“端口”:5432
}

将用户更改为您计算机上的用户名。 (在终端中键入whoami以查找您的用户名)数据库值必须与您在psql创建的数据库名称匹配。

第9步-创建 Blogpost 模型

对于此应用程序,我们将创建一个简单的博客网站。 因此,每个“博客文章”都需要一个标题和一个正文。 首先创建一个名为Blogpost.swift的新文件。 确保已选中“ App目标,并且正在创建此文件时将其保存在“模型”文件夹中。

接下来,将以下代码添加到Blogpost.swift文件中:

 进口基金会 
进口蒸气
导入流利
 最终课程Blogpost:模型{ 

}

ModelFluent为我们提供的协议,它将帮助我们连接到Postgresql数据库。 在符合Model并满足其要求之前,让我们创建一些属性和一个初始化程序。 由于我们正在创建博客应用程序,因此让我们为每个博客文章指定标题和正文。 用以下内容替换您的类声明(保留导入):

 最终课程Blogpost:模型{ 
var标题:字符串
var body:字串

init(title:String,body:String){
self.title =标题
self.body =身体
}

}

要创建模型,我们还需要添加一个属性来存储每个博客文章的唯一ID。 在标题上方,添加var id: Node? 并且var exists: Bool = falseNode是Vapor中使用的中间数据表示形式。

节点是一种可传递的数据结构,可用于促进不同类型之间的转换。 —蒸汽节点文档

接下来,让我们通过添加必要的功能来符合Model 。 我强烈建议您还阅读有关模型的Vapor文档,以了解如何执行此操作。 Model是符合其他几个协议的协议,如NodeInitializableNodeRepresentable 。 为此,我们需要一个初始化程序,该初始化程序可以从持久化数据中创建模型。 在第一个初始化程序的下面添加以下代码。

  init(node:节点,在上下文中:Context)引发{ 
id =尝试node.extract(“ id”)
标题=尝试node.extract(“ title”)
身体=尝试node.extract(“ body”)
}

您在上面看到的字符串值必须与我们包含在数据库表中的列或字段名称相对应。 您应该注意使用throwstry 。 Swift作为一种语言试图尽其所能地确保安全并处理错误,Vapor遵循这种风格。 接下来,添加以下函数以符合NodeRepresentable

  func makeNode(context:Context)抛出->节点{ 
返回try Node(node:[
“ id”:id,
“标题”:标题,
“身体”:身体
])
}

保存博客文章时,将调用makeNode ,因此此处的字符串值还必须与数据库中的列名或字段名匹配。

您的Blogpost.swift文件现在应包含以下内容:

 进口基金会 
进口蒸气
导入流利
 最终课程Blogpost:模型{ 
var id:节点?
var存在:布尔=假
var标题:字符串
var body:字串

init(title:String,body:String){
self.title =标题
self.body =身体
}

init(node:节点,在上下文中:Context)引发{
id =尝试node.extract(“ id”)
标题=尝试node.extract(“ title”)
身体=尝试node.extract(“ body”)
}

func makeNode(context:Context)抛出->节点{
返回try Node(node:[
“ id”:id,
“标题”:标题,
“身体”:身体
])
}


}

接下来,我们必须遵循Preparation协议。 某些数据库(例如PostgreSQL和MySQL)需要在添加数据之前了解其结构。 我们需要为Blogpost类提供preparerevert功能。 将以下代码添加到Blogpost.swift下的Blogpost.swift文件中。

 静态函数prepare(_数据库:数据库)抛出{ 
尝试database.create(“ blogposts”){(blogposts)在
blogposts.id()
blogposts.string(“ title”)
blogposts.custom(“ body”,键入:“ text”)
}
}

静态函数还原(_数据库:数据库)抛出{
尝试database.delete(“ blogposts”)
}

prepare函数创建一个blogposts表,并为idtitlebody创建列。 title使用默认的string模式构造函数,该构造函数为其提供默认长度255个字符。 对于博客文章的正文,我们将要超过该限制,因此我们在PostgreSQL中使用了文本类型。 为此,我们使用自定义函数创建名称为“ body”,类型为“ text”的字段

revert功能使我们可以删除表。 如果要删除表,可以运行vapor run prepare --revert 。 现在,我们的Blogpost模型已完成,可以在蒸气项目中使用它。

第11步—准备数据库

现在我们已经连接了模型,我们需要告诉蒸气滴准备数据库。 导航到main.swift并将以下行添加到main.swift行以下: drop.preparations += Blogpost.self 。 按Cmd + R运行您的应用程序。 注意:请确保将目标从Blogger更改为App

应用程序运行后,它将使用Blogpost表准备数据库。

切换回Postico并连接到数据库以查看新创建的表。

现在我们已经创建了一个数据库和一个blogposts表,让我们创建一些将数据添加到我们的数据库的路由。

第10步-创建一些路线

让我们做一个简单的快速路线,以便我们测试创建一个新的博客文章。 用以下内容替换现有路由:

  drop.get {要求 
var blogpost = Blogpost(标题:“ Hello”,正文:“ World”)
试试blogpost.save()
返回尝试blogpost.makeJSON()
}

此代码在localhost创建一个路由,该路由创建Blogpost类的实例,将其保存到数据库,然后将其JSON表示返回给浏览器。 运行该应用程序,然后在http://127.0.0.1:8080打开浏览器。

打开Postico以确保您的博客文章已创建。 如果您已运行Postico,则可能需要单击刷新按钮。

现在,我们已经创建了一个非常简单的路由,让我们向液滴中添加另外两个路由。 第一条路线将仅生成一个表单,而第二条路线将收到将保存提交的博客帖子的帖子操作。

删除我们以前的路线,并将其替换为以下内容:

  drop.get {要求 
返回尝试drop.view.make(“ new”)
}

您将在上一教程中记住,该路由将返回从new.leaf文件创建的页面。 我们尚未创建该文件,因此让我们继续创建它。 右键单击“ Views文件夹,单击“新建文件”,然后将文件命名为new.leaf 。 将以下代码添加到new.leaf

  // 1 
#extend(“ base”)
  #export(“ head”){ 
我的博客应用
// 2
   
  } 
  #export(“ body”){ 
// 3
  









   


}

1。 首先,我们通过使用extend("base")扩展base.leaf文件。 这使我们可以将base.leaf用作其他文件的模板

2.然后,导出head并包括Bootstrap。 Bootstrap是一个广泛流行的HTML,CSS和JS框架,它将为我们提供一些现成的表单样式。

3.接下来,我们导出正文部分并添加提交表单。 我们的提交表单有一个帖子标题字段和一个帖子正文文本区域。 该表单将POST操作发送到/submit ,我们将最终构建它。

现在,我们已经建立了表单,让我们运行项目只是为了确保其正常工作。 如果您在我们的浏览器中打开应用程序,则应看到以下内容。 继续尝试提交博客。

由于我们尚未创建/submit路由,因此如果您单击Submit,您将收到404错误。 在最后一条路线的正下方,我们添加一条路线来处理提交。

  drop.post(“ submit”){要求 

警卫让title = req.formURLEncoded?[“ title”] ?. string,让body = req.formURLEncoded?[“ body”] ?. string else {
返回“缺少的字段”
}

var blogpost = Blogpost(标题:标题,正文:正文)
试试blogpost.save()

返回尝试blogpost.makeJSON()
}

上面的代码创建了一个Post请求路由。 从请求中,我们获取通过url编码形式提交的值,然后使用它们创建Blogpost实例,保存它,然后返回它的JSON表示形式。 继续并运行您的应用程序,然后提交简短的博客文章。 点击提交后,您应该会看到类似以下的内容。

我们的数据库表也已更新,以反映我们的提交。

步骤11 —查询数据库

将项目添加到数据库后,您可能希望能够检索到已保留的项目。 蒸气和流利为我们提供了非常简单的方式来获取信息。 让我们创建一条从数据库中获取所有博客文章的途径。

  drop.get(“ blogposts”){要求 

让blogposts =试试Blogpost.query()。all()
返回尝试blogposts.makeJSON()

}

我们在/blogposts处创建了一条GET路由,当该命中命中时,会调用Blogpost上的query函数,该函数创建一个Blogpost查询,然后再调用all()函数,该查询从数组中返回该查询的所有Blogpost。 一旦有了makeJSON()数组,就可以调用makeJSON()将其呈现给浏览器。 让我们运行我们的应用程序并导航到/blogposts

正如预期的那样,我们的路线同时显示了我们保存的两个博客文章。 如果我们想专门获取ID为2或任何ID的博客该怎么办? 让我们创建一条新的路线来处理这种情况。 在最后一条路线的正下方,添加以下代码。

  drop.get(“ blogposts”,Int.self){req,blogId在 

警卫让博客=尝试Blogpost.query()。filter(“ id”,blogId).first()else {返回“未找到博客”}

返回尝试blog.makeJSON()

}

在上面的代码中,我们创建了一个GET路由来处理blogposts/{blogId}请求。 这里的blogId是可以在浏览器中键入的数字,例如, blogposts/1应该返回标题为“ Hello”的博客。 代替使用query().all() ,我们使用带有两个参数的filter()函数:一个用于列名,例如idtitlebody和第二个用于我们要搜索的参数。 在这种情况下,我们希望根据使用url输入到浏览器中的数字进行过滤,因此我们使用blogId 。 我们使用first()函数获取数组中的第一项。 如果最初没有找到任何项目,这意味着过滤器未找到任何结果,因此,我们可以返回给用户指示未找到任何内容。 如果找到一个项目,则使用makeJSON()将其输出到浏览器。 运行您的代码,然后尝试导航到localhost:8080/blogposts/1 ,它应返回标题为“ Hello”的博客,并尝试导航到我们知道没有博客的localhost:8080/blogposts/3

瞧! 现在,我们将数据持久保存到数据库中,并且还从数据库中检索了该数据。 现在轮到您进一步扩展此示例了。 利用您在本教程和第2部分中学到的知识,为每个博客和列出的所有博客创建一个网页。 提示使用 Leaf的loop标签显示所有博客。 祝好运!

如果您正在寻找完整的代码,可以在我的Github上找到它。

Interesting Posts