Skip to content

Commit

Permalink
Add detachAll method to @Siblings properties. (#438)
Browse files Browse the repository at this point in the history
  • Loading branch information
madsodgaard committed Jun 4, 2021
1 parent 24facdf commit 2eddf6f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 5 deletions.
23 changes: 23 additions & 0 deletions Sources/FluentBenchmark/Tests/SiblingsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ extension FluentBenchmarker {
try self.testSiblings_attach()
try self.testSiblings_detachArray()
try self.testSiblings_pivotLoading()
try self.testSiblings_detachAll()
}

private func testSiblings_attach() throws {
Expand Down Expand Up @@ -101,4 +102,26 @@ extension FluentBenchmarker {
XCTAssertEqual(earth.$tags.pivots.count, 2)
}
}

private func testSiblings_detachAll() throws {
try self.runTest(#function, [
SolarSystem()
]) {
let earth = try Planet.query(on: self.database)
.filter(\.$name == "Earth")
.first().wait()!

// verify tag count
try XCTAssertEqual(earth.$tags.query(on: self.database).count().wait(), 2)

try earth.$tags.detachAll(on: self.database).wait()

// check earth has tags removed
do {
let tags = try earth.$tags.query(on: self.database)
.all().wait()
XCTAssertEqual(tags.count, 0)
}
}
}
}
60 changes: 55 additions & 5 deletions Sources/FluentKit/Properties/Siblings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ public final class SiblingsProperty<From, To, Through>
where From: Model, To: Model, Through: Model
{
public enum AttachMethod {
// always create the pivot
/// Always create the pivot model
case always

// only create the pivot if it doesn't already exist
/// Only create the pivot if it doesn't already exist
case ifNotExists
}

Expand Down Expand Up @@ -62,6 +62,11 @@ public final class SiblingsProperty<From, To, Through>

// MARK: Checking state

/// Check whether a specific model is already attached through a sibling relationship.
///
/// - Parameters:
/// - to: The model to check whether it is attached through a pivot.
/// - database: The database to perform check on.
public func isAttached(to: To, on database: Database) -> EventLoopFuture<Bool> {
guard let toID = to.id else {
fatalError("Cannot attach unsaved model.")
Expand All @@ -70,6 +75,11 @@ public final class SiblingsProperty<From, To, Through>
return self.isAttached(toID: toID, on: database)
}

/// Check whether a specific model ID is already attached through a sibling relationship.
///
/// - Parameters:
/// - toID: The ID of the model to check whether it is attached through a pivot.
/// - database: The database to perform the check on.
public func isAttached(toID: To.IDValue, on database: Database) -> EventLoopFuture<Bool> {
guard let fromID = self.idValue else {
fatalError("Cannot check if siblings are attached to an unsaved model.")
Expand All @@ -84,6 +94,12 @@ public final class SiblingsProperty<From, To, Through>

// MARK: Operations

/// Attach an array model to this model through a pivot.
///
/// - Parameters:
/// - tos: An array of models to attach through a sibling releationship
/// - database: The database to perform the attachment on.
/// - edit: An optional closure to edit the pivot model before saving it.
public func attach(
_ tos: [To],
on database: Database,
Expand All @@ -105,6 +121,13 @@ public final class SiblingsProperty<From, To, Through>
}.create(on: database)
}

/// Attach a single model by creating a pivot model and specifying the attachment method.
///
/// - Parameters:
/// - to: The model to attach through a sibling releationship
/// - method: The attachment method to use when deciding whether to create the pivot.
/// - database: The database to perform the attachment on.
/// - edit: An optional closure to edit the pivot model before saving it.
public func attach(
_ to: To,
method: AttachMethod,
Expand All @@ -125,6 +148,12 @@ public final class SiblingsProperty<From, To, Through>
}
}

/// Attach a single model by creating a pivot model.
///
/// - Parameters:
/// - to: The model to attach through a sibling releationship
/// - database: The database to perform the attachment on.
/// - edit: An optional closure to edit the pivot model before saving it.
public func attach(
_ to: To,
on database: Database,
Expand All @@ -144,7 +173,11 @@ public final class SiblingsProperty<From, To, Through>
return pivot.save(on: database)
}


/// Detaches an array of models from this model by deleting each pivot.
///
/// - Parameters:
/// - tos: An array of models to detach from this model.
/// - database: The database to perform the attachment on.
public func detach(_ tos: [To], on database: Database) -> EventLoopFuture<Void> {
guard let fromID = self.idValue else {
fatalError("Cannot detach siblings relation to unsaved model.")
Expand All @@ -162,22 +195,39 @@ public final class SiblingsProperty<From, To, Through>
.delete()
}

/// Detach a single model by deleting the pivot.
///
/// - Parameters:
/// - to: The model to detach from this model.
/// - database: The database to perform the attachment on.
public func detach(_ to: To, on database: Database) -> EventLoopFuture<Void> {
guard let fromID = self.idValue else {
fatalError("Cannot attach siblings relation to unsaved model.")
fatalError("Cannot detach siblings relation from unsaved model.")
}
guard let toID = to.id else {
fatalError("Cannot attach unsaved model.")
fatalError("Cannot detach unsaved model.")
}

return Through.query(on: database)
.filter(self.from.appending(path: \.$id) == fromID)
.filter(self.to.appending(path: \.$id) == toID)
.delete()
}

/// Detach all models by deleting all pivots from this model.
public func detachAll(on database: Database) -> EventLoopFuture<Void> {
guard let fromID = self.idValue else {
fatalError("Cannot detach siblings relation from unsaved model.")
}

return Through.query(on: database)
.filter(self.from.appending(path: \.$id) == fromID)
.delete()
}

// MARK: Query

/// Returns a `QueryBuilder` that can be used to query the siblings.
public func query(on database: Database) -> QueryBuilder<To> {
guard let fromID = self.idValue else {
fatalError("Cannot query siblings relation from unsaved model.")
Expand Down

0 comments on commit 2eddf6f

Please sign in to comment.