You can find the Gist of movies references in this lesson here:
https://gist.github.com/tomgobich/2f29d392c67c9cc01dfa14e42f2995d5
Tapping into Model Factory States
In this lesson, we'll dive a little bit deeper into Model Factories by introducing factory states. We'll also learn how we can use the tap method to alter a factory result prior to it persisting into the database
- Author
- Tom Gobich
- Published
- Mar 15, 24
- Duration
- 9m 15s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
Tapping into Model Factory States
-
(upbeat music)
-
So it'd be great if we actually had real movie titles
-
inside of our application using faker information
-
rather than just song names
-
because now our application feels more like a song database
-
rather than a movie database.
-
So I've taken movies listed from the National Film Registry
-
and I've normalized this into a JavaScript
-
or TypeScript array of objects containing a title
-
and a released year.
-
So today we're gonna take this list
-
and use it to seed our information
-
inside of our application.
-
So let's go ahead and hide our browser back away.
-
Now we could use this list of titles
-
directly inside of our Model Factory,
-
but it would be great to also be able to generate out
-
random movies that are future dated and the like
-
since our list stops at 2017.
-
So instead what we'll do is just use it
-
directly inside of our fake seeder.
-
It will just generate out one movie per record
-
that we have inside of this list.
-
Oops, I actually forgot to copy that list.
-
So let's dive back into this gist that I have here,
-
click on raw and let's Control or Command + A,
-
Control or Command + C to give that a copy.
-
Okay, now we can hide that back away.
-
And inside of our database directory,
-
let's go ahead and right click, new file,
-
and let's create a folder.
-
We could call this data, we could call this faker.
-
I'm just gonna call this data for now, movies.ts.
-
And let's go ahead and paste that list in
-
and give it a save so that it formats.
-
Cool, you see up here at line one,
-
we're exporting const movies.
-
So within our fake seeder, we can import that
-
and let's make use of it inside of a separate method
-
within here.
-
So we'll call this async, we'll make it private
-
and we'll call it create movies.
-
Okay, now we still wanna make use of our Movie Factory.
-
So we'll await movie factory.createMany
-
and we'll wanna create one movie record per movie
-
that we have inside of our data list here.
-
So we wanna import our movies, so we'll type out movies
-
and hit tab to auto import that from database data movies
-
and provide it its array length.
-
Now, currently this would create one movie
-
with any random song name as its title.
-
What we wanna do instead is tap into this
-
prior to its actual creation.
-
And with this tap method, we can provide a callback function
-
that's provided a row, which in this case
-
is going to be an instance of our movie model class.
-
It'll also provide us a factory context
-
and a factory builder.
-
So within here, we can do row,
-
provide that as a callback method
-
and now if we type out row,
-
we'll see that this looks exactly like a typical model would
-
so we can type out title and overwrite the title
-
prior to this record being persisted into the database
-
because tap is run just before it is persistent.
-
So within our tap callback method,
-
what we wanna do is grab the index
-
of the current movie that we're on.
-
So let's do let index equals zero.
-
We'll want to index plus plus at the end of this
-
to increment that.
-
We can go ahead and plop our movie into a variable.
-
So we'll do const movie equals movies.index
-
and then we can set the current movie title
-
by calling movie.title.
-
Now we can do something similar
-
with the release data as well.
-
Since we have the released year,
-
so we can use faker by extracting that
-
out of our factory context just like so.
-
We can do faker.date.
-
We can use the between method to start dates
-
that the random date should be between.
-
Since we only have the released year,
-
we can set this to a random date within that year
-
so that all of our movies aren't using the same month
-
and day as we're looping over them here.
-
So we'll say from and let's grab an instance
-
of a LuxonDateTime object set to the movies released year.
-
So we'll say released equals date time,
-
importing that from Luxon just like so.
-
So we'll say date time dot,
-
and we can just set this to now
-
and then overwrite the year to our movie.releasedYear.
-
Cool, so now this object is set to a LuxonDateTime
-
set to the current movies released year.
-
So meaning we can do released.startOf
-
to go to the start of the year
-
and then to released.endOf
-
to go to the end of the year.
-
Now we have a couple of issues here to fix.
-
Firstly, faker.date is going to return back
-
an actual JavaScript date,
-
whereas our released.dat is expecting a LuxonDateTime.
-
So we can wrap this in dateTime from JS date
-
to ingest the faker.date in as a LuxonDateTime.
-
Give that a save.
-
And now we have our second issue,
-
which is that faker in the from and to
-
is expecting a JavaScript date or a string or a number.
-
So we have a couple of options there.
-
We can either do toISO, toString, toFormat,
-
or we can just do toJSDate
-
to convert that to a JavaScript date.
-
We'll just go ahead with that one, cool.
-
So now as we loop through the movies that we're creating,
-
one per record inside of our movie array here,
-
we'll increment our index
-
so that we can get an instance of the movie
-
that we're currently on inside of our tap callback,
-
overwrite the record's title with the actual movie title,
-
and overwrite the released out with some random date
-
between January 1st and December 31st
-
of the current released year for that particular movie.
-
Before we give this a go,
-
let's go ahead and move our other movie factories,
-
creation calls inside of our createMovies
-
so that it's true to the method's name.
-
So we'll go ahead and plop these down at the end
-
after we run through our real movies, cool.
-
We can also clean this particular movie factory
-
up a little bit.
-
So instead of merging in
-
every time that we wanna create a movie that's released,
-
we can instead specify a state directly on the movie factory
-
and then just call that state here
-
so that we don't need to merge the state in
-
every single time.
-
So within our movie factory, we can scroll down here,
-
do .state, give the state a name.
-
So we could do released.
-
And then the second argument to this
-
is going to be very similar to the tap method.
-
It's going to provide us in the row for the movie
-
prior to it being persisted into the database.
-
And then we also get the factory context
-
and factory builder as well.
-
So we can extract faker out of this too.
-
So we can do pretty much the exact same thing
-
that we just did inside of our fake cedar
-
to specify a movie has been released
-
for whatever factory we're running this with.
-
So we could do row.statusId to set that to
-
movieStatuses.released,
-
and then row.releasedAt equals dateTime.fromJSDate.
-
We need to import that from Luxon.
-
So dateTime, hit tab to auto import that from Luxon,
-
just like so.
-
And then we can use faker.date.
-
And set this to sometime in the past
-
since the movie will have been released.
-
We can also do one for releasing soon.
-
So if we give that a copy and a paste,
-
we could do releasing soon,
-
or just soon if you want to keep that nice and short.
-
We can keep the movie status to released,
-
and this will give the scope that we defined
-
on our movie model a nice test case.
-
So we can set the release date to soon
-
rather than sometime in the past
-
so that our movie status is set to released,
-
but the release date is still sometime soon there
-
in the future.
-
So although the movie is set to released,
-
it's not actually physically released
-
because the release date has not been met yet.
-
Let's do one more.
-
So let's do one for post-production here.
-
So we'll do state name of post-production,
-
set the movie status to post-production,
-
and we can leave the faker date to soon there.
-
Okay, give that a save,
-
jump into our faker cedar,
-
and now we can replace this merge call here
-
instead by applying one of our states.
-
And for this, we just need to provide in the state name.
-
So here we were creating released movies,
-
so we'll call apply there with released as the argument,
-
and now we're creating our released movies
-
without having to merge in information for that.
-
We can give that a copy,
-
paste it two more times
-
because we also have releasing soon
-
as well as post-production that we can create as well.
-
So we give that a save.
-
Lastly, we need to actually call our create movie method.
-
So up here within our run method,
-
all we wanna do is await this create movies, just like so.
-
Awesome.
-
Now we're ready to go ahead and roll back our database,
-
rerun everything and reseed our database as well.
-
Now, one thing to note before we do that though,
-
is that you don't wanna place this data directory
-
inside of cedars because whenever we run our seed command,
-
Athonis JS is going to try and auto import
-
whatever cedar files we have within here,
-
and if it's not an actual cedar,
-
it's going to result in an error.
-
So we've placed our data directory
-
directly underneath database here
-
for a very particular reason,
-
even though we're only using it inside of our cedars.
-
Okay, so let's go ahead and hide our text editor back away.
-
Let's reset our database.
-
So node ace migration reset.
-
This is going to roll back
-
all of the migrations that we have,
-
regardless of what batch number it was run within.
-
Okay, let's go ahead and node ace migration run
-
to rerun those migrations.
-
All right, and lastly, let's go ahead and node ace db seed.
-
And we've run into an error.
-
Now, here it's saying it cannot insert into users
-
and specifically it's complaining
-
about the user's role ID foreign key constraint.
-
Essentially, we're trying to insert into users
-
because our fake cedar is running prior to our start cedar.
-
So our roles don't actually exist
-
at the time that we're trying to insert
-
into our user's database.
-
So what we need to do is specify
-
that we want the start cedar to run before our fake cedar,
-
so that those roles and the information
-
that we're creating inside of our start cedar
-
actually exists whenever we get to our fake cedar.
-
So let's go ahead and node ace migration reset
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!