Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Models · ZewoGraveyard/SQL Wiki · GitHub
Skip to content
Dan Appel edited this page Apr 6, 2016 · 6 revisions

SQL has ORM-style functionality, focusing on efficiency, transparency and safety. Your specific connector should have a Model protocol, that extends SQL.Model. Let's go through how to implement it.

Create your basic model struct. Using structs are good practice, as SQL modifies your object when saving and updating by replacing the entire struct. Using a class is not recommended.

import PostgreSQL
structArtist{letid:Int?
// Note the optionality. Fields that are marked `NOT NULL` in your schema should be non-optional.
varname:Stringvargenre:String?
// You probably want your own initializer
init(name:String, genre:String){self.id =nilself.name = name
self.genre = genre
}}

In order to conform to the Model protocol, we have to extend Artist. You can put this code directly in your struct if you like.

extensionArtist:Model{
// Define the fields of your model.
// Make sure to name it `Field` and have it conform to `String` and `FieldType`
enumField:String,FieldType{case Id ="id"case Name ="name"case Genre ="genre"}
// Table name corresponding to your model
staticlettableName:String="artists"
// The field for the primary key of the model
staticletfieldForPrimaryKey:Field=.Id
// The fields used when constructing your model.
// You don't have to match all the values in your `Field` enum, unless you want to.
// NOTE: Leave this out to select all fields.
staticletselectFields:[Field]=[.Id,.Name,.Genre
]
// The model needs to know the value of your primary key.
// You can name the actual variable of your struct representing the primary key
// to `primaryKey`, or provide it like shown below.
// NOTE: You can use any type as your primary key, as long as it conforms to `SQLDataConvertible`
varprimaryKey:Int?{return id
}
// Add the ability to construct the model from a row
init(row:Row)throws{
id =try row.value(Artist.field(.Id))
name =try row.value(Artist.field(.Name))
genre =try row.value(Artist.field(.Genre))}
// Define which values are allowed to be persisted.
// Note whether your properties are mutable.
varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.Genre: genre
]}}

While this solution is generally more verbose than most ORM's, there's no magic, it's very clear what's going on. Let's also create an Album model.

structAlbum{structError:ErrorType{letdescription:String}letid:Int?varname:StringvarartistId:Intinit(name:String, artist:Artist)throws{guardlet artistId = artist.id else{throwError(description:"Artist doesn't have an id yet")}self.name = name
self.artistId = artistId
self.id =nil}}extensionAlbum:Model{enumField:String,FieldType{case Id ="id"case Name ="name"case ArtistId ="artist_id"}staticlettableName:String="albums"staticletfieldForPrimaryKey:Field=.Id
staticletselectFields:[Field]=[.Id,.Name,.ArtistId
]varprimaryKey:Int?{return id
}init(row:Row)throws{
id =try row.value(Album.field(.Id))
name =try row.value(Album.field(.Name))
artistId =try row.value(Album.field(.ArtistId))}varpersistedValuesByField:[Field:SQLDataConvertible?]{return[.Name: name,.ArtistId: artistId
]}}

After creating our models, we can use a more safe way of creating queries.

Using models to create queries

Artist.selectQuery.join(Album.self, type:.Inner, leftKey:.Id, rightKey:.ArtistId)Artist.selectQuery.limit(10).offset(1)Artist.selectQuery.orderBy(.Descending(.Name),.Ascending(.Id))

Filtering queries using models

When working with model queries, you use Model.field(), specifying your models Field enum to get the declared fields.

Artist.selectQuery.filter(Artist.field(.Id)==1 || Artist.field(.Genre)=="rock")

Fetch & get models

Using model selects, you can call fetch and first

letartists=tryArtist.selectQuery.fetch(connection) // [Artist]
letartist=tryArtist.selectQuery.first(connection) // Artist?
letartist=tryArtist.get(1, connection: connection) // Artist?

Saving models

You can insert new models either by simply providing a dictionary with fields and values

letartist=tryArtist.create([.Name:"AC/DC",.Genre:"rock"], connection: connection)

Or, you can construct a model using your own initializers

varartist=Artist(name:"Kendrick Lamar", genre:"hip hop")try artist.create(connection)

You can simply call save

varartist=Artist(name:"Hocus pocus", genre:"hip hop")try artist.save(connection)

save will either call insert or update depending on whether the model has a primary key.

Deleting models

try artist.delete(connection: connection)

Validating before saving or creating

Add a method with the following signature to your models. Throw an error if validation failed. Not throwing an error indicates that the validation passed.

Validations are called before willSave(), willUpdate(), and willCreate()

func validate()throws{guard name =="david"else{throwMyError("Name has to be 'david'")}}

Persistence hooks

You can use any of the following hooks in your model.

  • willSave & didSave
  • willUpdate & didUpdate
  • willCreate & didCreate
  • willDelete & didDelete
  • willRefresh & didRefresh
extensionArtist{func willSave(){}func didSave(){}}

For updates the flow is: willSave() -> willUpdate -> (UPDATE) -> didUpdate() -> willRefresh() -> (REFRESH) -> didRefresh() -> didSave() For creates, the flow is similar, except the updates are creates.

Clone this wiki locally