Repositories
To add some sort of backend to your Store
s, you can make use of fritz2’s repositories. fritz2 offers implementations of the repository-interfaces for two types of backends:
- LocalStorage
- REST
Serializer
When defining the data class you want to share via Repository
, you must define a Serializer
for it. You can of course use kotlinx-serialization for this. Annotate your data class (here Person
) with @Serializable
and then write the following serializer for your class:
object PersonSerializer : Serializer<Person, String> {
override fun read(msg: String): Person = Json.decodeFromString(Person.serializer(), msg)
override fun readList(msg: String): List<Person> = Json.decodeFromString(ListSerializer(Person.serializer()), msg)
override fun write(item: Person): String = Json.encodeToString(ToDo.serializer(), item)
override fun writeList(items: List<Person>): String = Json.encodeToString(ListSerializer(Person.serializer()), items)
}
Resource
Working with repositories requires the definition of a Resource
:
val personResource = Resource(
Person::_id, // function to extract the unique id from a given instance
PersonSerializer, // implementation of Serializer interface to write and read the entity
Person() // definition of an empty entity (to reset an edit form, e.g.)
)
Repositories
We differentiate two kinds of repositories:
- an
EntityRepository
deals with one single entity of a given type. It offers the usual CRUD-methods:load(entity, id)
addOrUpdate(entity)
delete(entity)
- a
QueryRepository
deals with aList
of instances of a given type. It offers the following methods to query or manipulate the content of the repository:query(entities, query)
addOrUpdate(entities, entity)
updateMany(entities, entitiesToUpdate)
delete(entities, id(s)
)
EntityRepository
To connect a Store
with a REST-backend for example, just add the repository-service and use its methods in your handler:
// use the defined resource from above
val entityStore = object : RootStore<Person>(personResource.emptyEntity) {
private val rest = restEntity(personResource, "https://your_url")
val load = handle<String> { person, id ->
rest.load(person, id)
}
val saveOrUpdate = handle { rest.saveOrUpdate(it) }
val delete = handle { rest.delete(it) }
init {
syncBy(saveOrUpdate)
}
}
syncBy
automatically calls the given Handler
on each update of your Store
.
QueryRepository
When creating a QueryRepository
, you can define a type describing the queries which are done by this repository, and a function for executing the query defined by a given instance of this query-type.
data class PersonQuery(val namePrefix: String? = null)
val queryStore = object : RootStore<List<Person>>(emptyList()) {
val rest = localStorageQuery<Person, String, PersonQuery>(personResource, "your prefix") { entities, query ->
if (query.namePrefix != null) entities.filter { it.name.startsWith(query.namePrefix) }
else entities
}
val query = handle(execute = rest::query)
val delete = handle<String>(execute = rest::delete)
init {
query(PersonQuery())
}
}
Of course the receiver and result of this function depend on the concrete implementation you use. For a RestQuery
, you have to build and fire the request according to your query-object. If you do not specify a function, all entities of the defined Resource will be returned.
val queryStore = restQuery<Person, String, PersonQuery>(personResource, "your url") { query ->
get(query.namePrefix?.let {"?name=$it"}.orEmpty())
}
For more information about the remote
parameter of restQuery
, please refer to the docs section HTTP-Calls.
Examples
You can see repositories of all types in action at