So let's now make sure
that in createNewProduct,
we don't just log the submitted data,
but we actually store it in a database.
And since we use the MVC pattern,
of course, that database access and storage code
should go into a dedicated model
that models our product data and its behavior.
And hence, the models folder is the right place to go.
And in there, I'll add a product.model.js file.
We have the user model already
with all the user related logic.
Now we'll add a new model for the product related logic.
Now for the product model,
I'll start by defining a class
because just as before I wanted to find
a blueprint for all the product objects,
with which I will work later in my controller actions.
And there we can use this constructor method
to define which kind of data we wanna store,
where, when a product is created
before we then add other methods
that allow us to interact with that model.
Last but not least,
we don't just wanna have the model in this file,
but we wanna use it in other files,
so we should export it like this.
But now let's work on the model.
If we have a look at our form for adding a new product,
we see that a product has a title, a image,
the summary field, the price field,
and the description field.
And therefore it would makes sense
to accept these kinds of data here in the constructor.
Now we could accept multiple parameter values
like title, summary, price, and so on,
but here I'll choose an alternative approach
and just accept one parameter, the productData,
which I expect to be an object
that groups all the individual fields that I want together.
And then in the constructor, just as before with the user,
I, of course, do wanna store that data
that we get as a parameter value here
in that to be created product object
for which this class is the blueprint.
We can do that with the help of the this keyword,
as you learned.
And for example, add a title property
to the to be created object where I store productData.title.
And here I will simply use the name
that I also used in my new-product.ejs file,
so in admin, new-product.ejs.
This name, I'll use these names that are defined here
so that I don't accidentally use the wrong name
in some part of my website later,
but that I have a consistent pattern
of accessing the different names
for these different input values.
So that's the title,
then, of course, I wanna store the summary,
which I expect to get on productData.summary
and the price, which should be productData.price
and also the description,
which not surprisingly is found on productData.description,
and then also the image.
Now for this, I wanna access productData.image.
And what I expect to get here is the name of the image file.
That's the thing I want here.
I also want to add another field, the imagePath,
which is the full path to the image,
as it's stored on the server-side.
And for this, I'll create a string with backticks here
so that I can easily inject a dynamic value
into that string.
And the path here will be product-data/images/
and then with dollar sign, curly braces,
I'll inject a value into that string
and that is this.image or productData.image,
so the image name.
With that, I store the image name stand-alone
in case we would need that name somewhere.
And I store the path or I create the path
like this dynamically based on the image name,
since I know in which folder images will be stored here.
If you chose a different folder for storing images,
you, of course, have to adjust this.
I also will add another field, the imageUrl,
which should be the URL which is used on the front-end,
so instead of the HTML code for requesting that image.
And we haven't yet added any logic
for serving those uploaded images, but we will do that soon.
Here, I will then also construct a string
with backticks so that I can inject something into it.
And here, the path which should be used
for requesting the image from my server
could be /products/assets/images/
and then the name of the image.
Now it's up to you which path you define here,
because you will be the person, the developer,
who has to write the back-end code
for then finding the appropriate image
when a request is coming into the server,
targeting this path.
And we'll write this logic together in a couple of minutes.
And now with that, I'm storing all the productData
that I need to store in this product class
or in this product objects
that will be created based on that class
with help of this constructor method.
Now, of course, we will create product objects
with that class so that we can then also interact
with the database and save that data to the database.
And hence, I'll add another method here.
Let's say a save method, the name is up to you though,
which will have the job of talking to the database.
Therefore, I'll turn it into a async method,
because I wanna use the await keyword in there
since we'll be dealing with promises in there.
And I will also import my db object from data, database,
which allows me to interact with the database.
Now here in the save method,
we can then use this db object
by using db.getDb and then calling collection
and accessing the product's collection,
and then calling insertOne on that collection
to insert one new document.
Now, I wanna prepare that document, the productDocument,
if you wanna call it like this or productData.
And that will be a little helper object
which I create on the fly with this object literal notation,
as it's called, by just using these curly braces,
not based on any specific class.
And there, I wanna store the title of the product
and I'll use this.title here.
So the title stored in the title property
of this product object in which this method is placed,
that's the value that will be stored in this title property
of this helper object.
And then I'll add summary with this.summary,
price with this.summary,
not this.summary, with this.price,
then the description with this.description,
and also the image with this.image.
And please note that's just the image name.
That's why I'm creating this dedicated helper object,
because I don't wanna store all the data
I set up here in the constructor.
I don't wanna store the imagePath or imageUrl,
because I will always be able
to dynamically reconstruct this in my code.
And if we ever would move the images into a new folder
and we would have stored the full folder path
in the database, we would have to update all the entries
in the database to match the new folder path.
If we just store the image name and we derived a folder path
here dynamically in our code, instead of in the database,
we are more flexible when it comes to changing
our folder structure on the server.
That's why I just wanna store the image name
in the database and not the full path to the image,
though, you could of course do that as well,
if you want it to.
But then it's this productData object,
which I'll pass to insertOne,
and therefore, that's the data that will be used
for creating a new document in the database.
Now insertOne returns a promise, so I will await this
and we could also accept the result
we get there and look into it,
but here I don't really care about it.
And hence, I'm already done after awaiting for this.
Now we can use this model in the admin controller.
Here in createNewProduct,
we can use it to store a new product.
So I'll import the product class here,
first of all, at the top of this file, going up one level,
diving into models, product.model.
And then here in createNewProduct,
we can add a product constant by calling new Product
and to new Product, I then pass an object,
because in that constructor method
we expect to get one parameter value,
which is an object with all these fields here.
And therefore, I'll pass in this object,
which is basically an object
with all my request body fields,
hence I'll use the spread operator
to paste all that data from my request body
into this new object.
And in addition, an image field,
which if you recall is just the file name,
which I can get on req.file.
And then if you look at the log from earlier,
you see that in there, you had the file name field
with that generated file name.
That's what I wanna store here,
so here it's req.file.filename.
And that is then creating such a product for me.
Now it's not getting stored in the database yet.
For this, we have to call product.save instead.
Now save returns a promise because it's an async method
and you learned that all methods or functions
decorated with async will return promises.
And hence, if you wanna await for that before we redirect,
we have to turn this createNewProduct
controller action function into a async function
with async as well, so that we can use await here.
This could also fail,
so we might want to actually wrap try-catch around this
so that we catch any errors that could occur
whilst trying to save this.
And then we can use the third parameter value,
this next function, which we can accept
to call next in the catch block and forward to this error.
And then I wanna return
so that no other code thereafter executes.
This is how we can handle this error.
And you learned, we need to do this
because Express does not catch and handle errors
that are caused by promises on its own.
Now we redirect thereafter if everything worked
and therefore, we should now be able to save
some productData in the database.
Let's give this a try.
Here, I'll again enter a keyboard as a title,
and then choose my keyboard image here.
This is a great keyboard.
Sounds like a fitting summary.
Let's enter a price.
This is a first product
added via the admin interface.
It's a very useful keyboard
which I am using for recording this video.
Let's hit save, it redirects, get no error here.
And if we go back here to the Mongo shell
connected to the database, if I run db.products.find,
we should find our product here, and we do.
It has the auto assigned ID
and then all the data we entered.
Now, you might notice that price is stored as a string.
That's actually not something I want.
I wanna store it as a number,
but other than that, it's looking good.
Now to store it as a number,
what I'll do is in the product model,
I'll ensure that when I get price here,
I actually add a plus in front of it
which forces a conversion to a number.
That's a little trick, and that's all we need to do
with that price will be a number.
And since I use this.price down there,
which then will be a number, it will be stored as a number.
So to quickly see that if I go back
to that form from before, with all the values I entered,
I did that by using the back button in the browser.
And I quickly delete this product here
by using db.products.deleteOne.
And then I just look for the product with that ID.
That's what you learned before in the course,
in the MongoDB course section.
Then with no products being there,
if I now submit that again,
and I now find all products again.
Here is my product again,
but now price is not surrounded by quotes
and therefore it is a number.
So that is how we can add products
and how we can store them in the database.
That is amazing.
Now, as a next step,
we can make sure that here in this administration area,
we also show a list or a grid of products
where we can then also start editing or deleting them.