Now our post model is taking shape
and it's able to save a new post,
update an existing post, and delete a post.
There are two functionalities that are missing,
which we absolutely also need to add.
And that's the functionality to fetch a list of posts
and the functionality to get a single post in the end.
Now let's start with fetching a list of posts.
What we can do for that is we can add another method.
But now, the trick is
that we will actually not instantiate this post-class
before we wanna call this method.
Because whilst it made sense to call save and delete
on an instantiated post, so on our concrete post object,
if we're about to fetch a list of post objects,
we have no single post object yet.
And to make this work,
JavaScript classes have another feature
which is called static methods.
You can add a method, like fetchAll, for example,
and add the static keyword in front of that.
The special thing about static methods is
that you don't call them on the instantiated object,
so on the object that's based on the class,
but instead on the class itself.
And we can also turn this into an async method like this.
So back in blog.js, where I now do need all my posts,
which would be for the admin page here.
There, I'm fetching all my posts.
We can now copy this line of code here,
go back to post.js, and add it in there.
And then, here we can just return all those posts like that.
Now back in blog.js, again,
this means that we can get all the posts by calling Post,
so using the class which we created,
and which we are importing under that name
in this blog.js file, .fetchAll.
And that's the difference to save and delete.
We called save and delete on existing posts objects,
like here in line 116, in my case.
We first created the post and then called save
on that created post.
Now, we're not doing that.
Instead, we're calling fetchAll directly
on the class itself.
So I can await this here,
and then I will still have my posts.
But the logic for fetching to posts now resides
in a different place.
It now resides inside of our model
with the help of such a static method.
And that's why static methods,
a new feature which we haven't learned about before,
can also be very helpful if you wanna use a class,
not just as a blueprint,
but also for grouping related functionalities together.
Because that's what we're doing here in the end.
We're grouping all the posts-related functionalities
together into one class.
And with the help of static methods,
we can also include functionalities that don't act
on a single post that already exists,
but that are instead only generally related to posts
and working with posts.
Now, just as I have the static fetchAll method
for fetching all posts,
we can, therefore, also add another static method
for fetching a single post by its ID.
Though here, for a single post, we could, of course,
argue that we instead create a fetch function,
a fetch method like this, which is not static,
where just as with deleting,
we check if we have an ID, and if we don't, we just return.
Because we need that ID to then populate this post object,
which we created with the data that was fetched
from the database.
So that means that in blog.js,
where I am fetching the data for a single post,
here in this get route,
I am copying, or cutting, this logic,
and I will execute this here in my post model now.
And that is my post, which I get here.
And now I can set this .title,
so of this class or of the object that was created based
on this class equal to post.title,
so equal to the title I fetched from the database.
And just to make this clear,
this is, again, my postDocument.
So we can use that to clarify that here I got
back my document from the database.
And I set this content equal to postDocument.content.
And then, we could call the fetch method on a post
that we instantiated with an ID,
but without a title or content,
because we're setting this dynamically in here then.
So then, in blog.js, here,
we could again create a post by calling new Post.
We don't need to create the post ID here.
Instead, we pass null for the title of the content,
and we pass req.params.id for the ID.
And then, we call post.fetch here
to trigger that fetch function we just added
to use that internally-stored ID
to fetch all the other data that belongs to this post.
Now, I will await that, and then thereafter, we continue.
Now this check here won't do the trick anymore though,
because now post will always exist,
but that doesn't necessarily tell us
whether fetching the data will succeed.
That's not too problematic though,
because this check your wasn't
entirely correct anymore anyways.
Therefore, here,
we wanna update this check
and maybe check if post.title is not set,
or if not post.content,
which means we created a post object,
but we were not able to populate that
with title and content.
And there would be other ways
of implementing this logic as well,
but this should do the trick.
And with all of that,
we now outsourced all our post data-related logic
into the post model, as it should be.
Now in blog.js, we can get rid of that import to db,
and we can get rid of the objectId creation here
and of the MongoDB import.
Because that is now all taking place in the post model file.
And that's the idea behind the post model file.
Now if I save this, this crashes.
Let's see what's wrong.
My mistake.
When you add a static method, that should be async.
async has to come after static.
So it's static async instead of async static.
We have to swap that.
So it's static async.
And with that, if we now reload, that's looking good.
If I visit the admin page,
all the posts are still fetched without new errors.
So that's working.
And if we view a single post, that fails though,
and that fails because postId is not defined.
And that's just a mistake from my side again,
here in the post model, when we fetch.
Of course, when I find a post,
this should not look for postId.
This doesn't exist here.
Instead, we wanna use this ID here.
So same mistake as before, sorry.
But that is also, I guess, a bit helpful
because you see that such errors are happening.
It's easy to overlook something like this,
and it's always the error messages,
which you wanna have a look at
to see why things are failing.
With that added though, if I now reload,
populating this forum works correctly again.
So therefore, now that is working.
Now there is one change we have to implement though,
and that is related to how we are fetching a single post.
We are now doing that with help of this fetch method,
which we added,
which populates the existing post object with the data.
Now there is a subtle issue with that,
which can be really hard to spot.
When we rely on this post object based on our post class,
then the ID of the post is stored
in an ID property, not _id.
The implication of that is that in all the places
where we rely on our own post object
instead of that document fetched from the database,
we now must not work with _id anymore,
but with just ID.
And in detail, that's the case in our single-post EJS file.
Here, I'm configuring the submit URL
for my updating post route.
And there, I'm accessing post._id.
Now keep in mind
that the single-post EJS file is actually rendered
in this get /posts/:id/edit route.
And there, I populate post with that post I created here.
So with my own object based on my own class.
And therefore, the ID in my own object is not stored
in a ._id field, but in just a .id.
So in single-post.ejs, we have to work with just id here.
Otherwise, we would get an error
if we do try to update a post.
This does not affect any other pages
because in no other pages,
we send our own class-based object into the view.
But here we are doing it,
and therefore we need to ensure that we update this
so that when we reload this detail page,
and I do update this post here, saving this works.
Otherwise, it would have failed.
But that's the only thing.
A new post can be created without issues
because there we're not using any ID of any existing object.
But it was important for updating.
And with that,
we now switched to using a model for our posts.
We can do the same for the auth part here,
but I will do that later.
Instead, let's now first move on to the controller part
and see how we implement that
and which other refactorings we can apply
to our post-related routes.