Collections

So far we've been creating one entity at a time. What if we want to create multiple entities at once?

Let's recap our factories and persistence logic:

(ns fab.tutorial
  (:require [fabrikk.alpha.core :as fab]))

(defn admin-email []
  (str "admin-" (rand-int 10000) "@example.com"))

(def user
  (fab/->factory
   ::user
   {:primary-key :id
    :template {:id (fab/sequence)
               :name "John Smith"
               :email "john@example.org"
               :role "user"
               :verified true}
    :traits {:admin {:name (fab/derive :id (partial str "Admin-"))
                     :email admin-email
                     :role "admin"}
             :unverified {:verified false}}}))

(def post
  (fab/->factory
   ::post
   {:template {:id random-uuid
               :title "This one weird trick"
               :content "Some content goes here...."
               :author (fab/one ::user)
               :author-name (fab/derive [:author] :name)}}))

(defonce my-store (atom {}))

(def collect (fnil conj []))

(defmethod fab/persist! :my-store [factory-id entity]
  (let [persisted (assoc entity :id (case factory-id
                                      ::user (rand-int 1000000)
                                      ::post (random-uuid)))]
    (swap! my-store update factory-id collect persisted)
    persisted))
(fab/set-default-persistence :my-store)

Building Collections

We've already met the build-list function in Dependent Entities, but let's recap it here. It's a function that accepts a factory, a quantity, and the same build options as build and (as you might expect), builds that quantity of the entity. It's important to note that by default a new user is built for each post, which might not be what you want. Let's create a list of posts with the same user:

Here we can see that the map passed to the with build option are applied to each post, and they all have the same author and title.

Creating Collections

Now let's look at persisting multiple posts using create-list:

Again, by default fabrikk will create a separate user for each post. And we can create posts for one user with:

Mixing building and creation

The headline here is:

BE VERY CAUTIOUS WHEN MIXING build AND create

(hopefully the ugliness of this warning will make it stick out more in your mind)

The previous code block mixed build with create, let's look at why this might be a bit of a footgun:

Here we've built a user, and then used it in two calls to create-list. The first call will create a user and assign it as the author for each post, but when it comes to the second call, fabrikk has no way to link it to the previously created user. This means each list of posts has a different author. This might be what you want in some situations but might blow your foot off in others.

If you use the same built entity multiple times in one create call:

fabrikk is clever enough to create one user and point both fields to it.

Finally, when fabrikk creates an entity it marks it as created (in the metadata), and will not create it again if it's passed to a different create call.

The 'many' Directive

In some situations, you may want an entity to reference a list of entities. We can do this using the list version of the one directive: many. Let's introduce a new factory that uses it:

A cool thing about many is that the derive directive knows how to handle it, allowing you to access the entities in the list via their index:

Also, remember that we haven't created a concrete association between groups and users, we've just specified that the default group representation requires three users. We're free to override this:

Here we've built a group of posts instead of users. Since the post factory doesn't specify a primary-key the full post entity is used instead.

Last updated