vapor-docs/build/1.5/fluent/model.html

630 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Vapor Documentation</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quicksand:400,700,300">
<link rel="stylesheet" href="/1.5/styles/vapor-code.css">
<link rel="stylesheet" href="/1.5/styles/main.css">
</head>
<body>
<header>
<a class="logo" href="/1.5/">
<img src="/1.5/images/droplet.svg" alt="Vapor">
<h1>Vapor <em>Docs</em></h1>
</a>
<ul>
<li>
<a href="http://vapor.codes">Home</a>
</li>
<li>
<a href="http://example.vapor.codes">Example</a>
</li>
<li>
<a href="https://github.com/vapor/vapor">GitHub</a>
</li>
<li>
<a href="https://twitter.com/@codevapor">Twitter</a>
</li>
<li>
<a href="http://vapor.team">Slack</a>
</li>
</ul>
</header>
<nav>
<a href="#" class="toggle show"></a>
<a href="#" class="toggle close">&times;</a>
<div class="scroll">
<section>
<h3>Getting Started</h3>
<ul>
<li class="">
<a href="/1.5/getting-started/install-swift-3-macos.html">
Install Swift 3: macOS
</a>
</li>
<li class="">
<a href="/1.5/getting-started/install-swift-3-ubuntu.html">
Install Swift 3: Ubuntu
</a>
</li>
<li class="">
<a href="/1.5/getting-started/install-toolbox.html">
Install Toolbox
</a>
</li>
<li class="">
<a href="/1.5/getting-started/hello-world.html">
Hello, World
</a>
</li>
<li class="">
<a href="/1.5/getting-started/manual.html">
Manual
</a>
</li>
<li class="">
<a href="/1.5/getting-started/xcode.html">
Xcode
</a>
</li>
</ul>
</section>
<section>
<h3>Guide</h3>
<ul>
<li class="">
<a href="/1.5/guide/droplet.html">
Droplet
</a>
</li>
<li class="">
<a href="/1.5/guide/folder-structure.html">
Folder Structure
</a>
</li>
<li class="">
<a href="/1.5/guide/json.html">
JSON
</a>
</li>
<li class="">
<a href="/1.5/guide/config.html">
Config
</a>
</li>
<li class="">
<a href="/1.5/guide/views.html">
Views
</a>
</li>
<li class="">
<a href="/1.5/guide/leaf.html">
Leaf
</a>
</li>
<li class="">
<a href="/1.5/guide/controllers.html">
Controllers
</a>
</li>
<li class="">
<a href="/1.5/guide/middleware.html">
Middleware
</a>
</li>
<li class="">
<a href="/1.5/guide/validation.html">
Validation
</a>
</li>
<li class="">
<a href="/1.5/guide/provider.html">
Provider
</a>
</li>
<li class="">
<a href="/1.5/guide/sessions.html">
Sessions
</a>
</li>
<li class="">
<a href="/1.5/guide/hash.html">
Hash
</a>
</li>
<li class="">
<a href="/1.5/guide/commands.html">
Commands
</a>
</li>
</ul>
</section>
<section>
<h3>Routing</h3>
<ul>
<li class="">
<a href="/1.5/routing/basic.html">
Basic
</a>
</li>
<li class="">
<a href="/1.5/routing/parameters.html">
Route Parameters
</a>
</li>
<li class="">
<a href="/1.5/routing/query-parameters.html">
Query Parameters
</a>
</li>
<li class="">
<a href="/1.5/routing/group.html">
Group
</a>
</li>
<li class="">
<a href="/1.5/routing/collection.html">
Collection
</a>
</li>
</ul>
</section>
<section>
<h3>Fluent</h3>
<ul>
<li class="">
<a href="/1.5/fluent/driver.html">
Driver
</a>
</li>
<li class="active">
<a href="/1.5/fluent/model.html">
Model
</a>
</li>
<li class="">
<a href="/1.5/fluent/query.html">
Query
</a>
</li>
<li class="">
<a href="/1.5/fluent/relation.html">
Relation
</a>
</li>
</ul>
</section>
<section>
<h3>Auth</h3>
<ul>
<li class="">
<a href="/1.5/auth/user.html">
User
</a>
</li>
<li class="">
<a href="/1.5/auth/middleware.html">
Middleware
</a>
</li>
<li class="">
<a href="/1.5/auth/request.html">
Request
</a>
</li>
<li class="">
<a href="/1.5/auth/protect.html">
Protect
</a>
</li>
</ul>
</section>
<section>
<h3>HTTP</h3>
<ul>
<li class="">
<a href="/1.5/http/request.html">
Request
</a>
</li>
<li class="">
<a href="/1.5/http/response.html">
Response
</a>
</li>
<li class="">
<a href="/1.5/http/body.html">
Body
</a>
</li>
<li class="">
<a href="/1.5/http/response-representable.html">
ResponseRepresentable
</a>
</li>
<li class="">
<a href="/1.5/http/responder.html">
Responder
</a>
</li>
<li class="">
<a href="/1.5/http/client.html">
Client
</a>
</li>
<li class="">
<a href="/1.5/http/server.html">
Server
</a>
</li>
<li class="">
<a href="/1.5/http/cors.html">
CORS
</a>
</li>
</ul>
</section>
<section>
<h3>WebSockets</h3>
<ul>
<li class="">
<a href="/1.5/websockets/droplet.html">
Droplet
</a>
</li>
<li class="">
<a href="/1.5/websockets/custom.html">
Custom
</a>
</li>
</ul>
</section>
<section>
<h3>Testing</h3>
<ul>
<li class="">
<a href="/1.5/testing/modules.html">
Modules
</a>
</li>
<li class="">
<a href="/1.5/testing/basic.html">
Basic
</a>
</li>
</ul>
</section>
<section>
<h3>Deploy</h3>
<ul>
<li class="">
<a href="/1.5/deploy/nginx.html">
Nginx
</a>
</li>
<li class="">
<a href="/1.5/deploy/supervisor.html">
Supervisor
</a>
</li>
</ul>
</section>
<section>
<h3>Version (1.5)</h3>
<ul>
<li class="">
<a href="/1.5/switch/1_5.html">
1.5
</a>
</li>
<li class="">
<a href="/1.5/switch/2_0.html">
2.0
</a>
</li>
</ul>
</section>
</div>
</nav>
<main>
<a href="https://github.com/vapor/documentation/blob/master/CONTRIBUTING.md" class="edit">✎ Edit on GitHub</a>
<h1 id="model">Model</h1>
<p><code>Model</code> is the base protocol for any of your application's models, especially those you want to persist.</p>
<blockquote>
<p><code>Model</code> is only available in Vapor, the Fluent equivalent is <code>Entity</code></p>
</blockquote>
<h2 id="example">Example</h2>
<p>Let's create a simple <code>User</code> model.</p>
<pre><code class="language-swift">final class User {
var name: String
init(name: String) {
self.name = name
}
}</code></pre>
<p>The first step to conforming to <code>Model</code> is to import Vapor and Fluent.</p>
<pre><code class="language-swift">import Vapor
import Fluent</code></pre>
<p>Then add the conformance to your class.</p>
<pre><code class="language-swift">final class User: Model {
...
}</code></pre>
<p>The compiler will inform you that some methods need to be implemented to conform.</p>
<h3 id="id">ID</h3>
<p>The first required property is an identifier. This property will contain the identifier when the model is fetched from the database. If it is <code>nil</code>, it will be set when the model is saved.</p>
<pre><code class="language-swift">final class User: Model {
var id: Node?
...
}</code></pre>
<h3 id="node-initializable">Node Initializable</h3>
<p>The next requirement is a way to create the model from the persisted data. Model uses <code>NodeInitializable</code> to achieve this.</p>
<pre><code class="language-swift">final class User: Model {
init(node: Node, in context: Context) throws {
id = try node.extract("id")
name = try node.extract("name")
}
...
}</code></pre>
<p>The keys <code>id</code> and <code>name</code> are what we expect the columns or fields in the database to be named. The <code>extract</code> call is marked with a <code>try</code> because it will throw an error if the value is not present or is the wrong type.</p>
<h3 id="node-representable">Node Representable</h3>
<p>Now that we have covered initializing the model, we need to show how to save it back into the database. Model uses <code>NodeRepresentable</code> to achieve this.</p>
<pre><code class="language-swift">final class User: Model {
func makeNode(context: Context) throws -&gt; Node {
return try Node(node: [
"id": id,
"name": name
])
}
...
}</code></pre>
<p>When a <code>User</code> is saved, the <code>makeNode()</code> method will be called and the resulting <code>Node</code> will be saved to the database. The keys <code>id</code> and <code>name</code> are what we expect the columns or fields in the database to be named.</p>
<blockquote>
<p>In most of the cases you do not need to be concerned about <code>context</code> argument of the <code>makeNode(context:)</code> method. Its a part of the protocol that allows extensibility in more advanced or specific scenarios.</p>
</blockquote>
<h2 id="preparations">Preparations</h2>
<p>Some databases, like MySQL, need to be prepared for a new schema. In MySQL, this means creating a new table. Preparations are also equatable to migrations, as they can be used to alter schemas after they've already been created.</p>
<h3 id="prepare">Prepare</h3>
<p>Let's assume we are using a SQL database. To prepare the database for our <code>User</code> class, we need to create a table. If you are using a database like Mongo, you can leave this method unimplemented.</p>
<pre><code class="language-swift">final class User {
static func prepare(_ database: Database) throws {
try database.create("users") { users in
users.id()
users.string("name")
}
}
...
}</code></pre>
<p>Here we create a table named <code>users</code> that has an identifier field and a string field with the key <code>name</code>. This matches both our <code>init(node: Node)</code> and <code>makeNode() -&gt; Node</code> methods.</p>
<h3 id="revert">Revert</h3>
<p>An optional preparation reversion can be created. This will be run if <code>vapor run prepare --revert</code> is called.</p>
<pre><code class="language-swift">final class User {
static func revert(_ database: Database) throws {
try database.delete("users")
}
...
}</code></pre>
<p>Here we are deleting the table named <code>users</code>.</p>
<h3 id="preparations-as-migrations">Preparations as Migrations</h3>
<p>If you want to add a field to your table after you've already created the initial schema, you can create a struct or class that conforms to <code>Preparation</code> like so:</p>
<pre><code class="language-swift">
struct AddFooToBar: Preparation {
static func prepare(_ database: Database) throws {
try database.modify("bars", closure: { bar in
bar.string("foo", length: 150, optional: false, unique: false, default: nil)
})
}
static func revert(_ database: Database) throws {
}
}</code></pre>
<p>Then, in your Droplet setup, add this line: <code>drop.preparations.append(AddFooToBar.self)</code></p>
<h3 id="droplet">Droplet</h3>
<p>To run these prepations when the applications boots, you must add the Model to the <code>Droplet</code>.</p>
<pre><code class="language-swift">let drop = Droplet()
drop.preparations.append(User.self)</code></pre>
<blockquote>
<p>Note: Preparations must be appended before the Droplet is run.</p>
</blockquote>
<h2 id="full-model">Full Model</h2>
<p>This is what our final <code>User</code> model looks like:</p>
<pre><code class="language-swift">import Vapor
import Fluent
final class User: Model {
var id: Node?
var name: String
init(name: String) {
self.name = name
}
init(node: Node, in context: Context) throws {
id = try node.extract("id")
name = try node.extract("name")
}
func makeNode(context: Context) throws -&gt; Node {
return try Node(node: [
"id": id,
"name": name
])
}
static func prepare(_ database: Database) throws {
try database.create("users") { users in
users.id()
users.string("name")
}
}
static func revert(_ database: Database) throws {
try database.delete("users")
}
}</code></pre>
<h2 id="interacting">Interacting</h2>
<p>Now that <code>User</code> conforms to <code>Model</code>, it has a plethora of new methods like <code>find()</code>, <code>query()</code>, <code>makeJSON()</code> and more.</p>
<h3 id="fetch">Fetch</h3>
<p>Models can be fetched by their database identifier.</p>
<pre><code class="language-swift">let user = try User.find(42)</code></pre>
<h3 id="save">Save</h3>
<p>Newly created models can be saved to the database.</p>
<pre><code class="language-swift">var user = User(name: "Vapor")
try user.save()
print(user.id) // prints the new id</code></pre>
<h3 id="delete">Delete</h3>
<p>Persisted models with identifiers can be deleted.</p>
<pre><code class="language-swift">try user.delete()</code></pre>
<h2 id="model-vs-entity">Model vs. Entity</h2>
<p>Model has a couple of extra conformances that a pure Fluent entity doesn't have.</p>
<pre><code class="language-swift">public protocol Model: Entity, JSONRepresentable, StringInitializable, ResponseRepresentable {}</code></pre>
<p>As can be seen in the protocol, Vapor models can automatically convert to <code>JSON</code>, <code>Response</code>, and even be used in type-safe routing.</p>
<h2 id="options">Options</h2>
<p>Change the table/collection name</p>
<pre><code class="language-swift">static var entity = "new_name"</code></pre>
</main>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="/1.5/scripts/highlight.pack.js"></script>
<script>
hljs.registerLanguage("leaf", function (hljs) {
/* Author: Hale Chan <halechan@qq.com> */
return {
c: [
{
cN: 'function',
b: '#+',
e: '[)] ',
rB: true,
eE: false,
c : [
{
b: '[(]',
e: '[)]',
eE: true,
c: [
{
cN: 'string',
b: '"',
e: '"'
},
{
cN: 'title',
b: '[A-Za-z_][A-Za-z_0-9]*'
}
]
},
{
cN: 'keyword',
b: '#+([A-Za-z$_][0-9A-Za-z$_]*)?',
eP: true
},
]
}
]
};
});
</script>
<script>
$(function() {
// Syntax highlighting
hljs.initHighlightingOnLoad();
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$('body').addClass('safari');
}
$('h1, h2, h3, h4, h5, h6').each(function(key, item) {
var $item = $(item);
var id = $item.attr('id');
if (id) {
var link = $('<a>');
link.attr('href', '#' + id);
$item.wrap(link);
}
})
$('a.toggle').on('click', function(e){
e.preventDefault();
console.log('hi');
var body = $('body');
if (body.hasClass('toggled')) {
body.removeClass('toggled');
} else {
body.addClass('toggled');
}
})
var lastScroll = 0;
var isUp = false;
var win = $(window);
win.on('scroll', function(){
var scrollTop = win.scrollTop();
var scrollBottom = win.scrollTop() + win.height();
var docHeight = $(document).height();
if ( scrollTop < 0 || scrollBottom >= docHeight ){
return;
}
if (scrollTop > (lastScroll + 0) && scrollTop >= 0) {
if (!isUp) {
$('a.toggle.show').addClass('hide')
}
isUp = true;
lastScroll = scrollTop;
} else if (scrollTop < (lastScroll - 0)) {
if (isUp) {
$('a.toggle.show').removeClass('hide')
}
isUp = false;
lastScroll = scrollTop;
}
});
// scroll to the active nav item
$("nav .scroll").animate({
scrollTop: $("nav .active").offset().top - 157
}, 0);
// var lastScrollTop = 0;
// $(window).scroll(function(event){
// var st = $(this).scrollTop();
// if (st > lastScrollTop){
// $('a.toggle.show').addClass('hide')
// } else {
// $('a.toggle.show').removeClass('hide')
// }
// lastScrollTop = st;
// });
});
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-76177358-4', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>