We've got this nice add to cart button.
This button currently doesn't do anything.
Let's make sure it does something.
And for this,
I'll start simple by giving it a class of BTN,
so that it at least looks differently.
And now that it looks differently,
I'd like to connect it to some logic
so that when we click it,
we actually take that product for which we click it,
we add that to the cart
and that cart has to be stored on the server side somehow.
And that cart has to be connected to this user,
even if the user is not logged in.
We can make this work with sessions, thankfully.
And then I also want to show a number
next to the cart here in the navigation,
so that we see how many items are in the cart.
And when we click cart,
I of course want to have a detail page where we can see
the entire cart contents, and we can see the cart items
and we can even manage the cart items from in there.
That's the plan.
And for this to achieve this goal,
we of course get a couple of things to do.
For example, we'll need a new model
because we'll have brand new data
with which we work on the backend.
We have the cart with which we work there.
Hence I'll add a cart.model.js file in the models folder.
And in addition, in controllers,
I'll add the cart.controller.js file,
since we'll have cart-related controller actions
that we need.
Now let's start in the cart model,
Here, I'll create my cart class,
as I've done it for other kinds of models as well.
And I will add a constructor here,
so that we can create instances
based on this model in our code.
Now, every cart that we create in our code,
ultimately you should be created with some items,
because whilst a cart might be empty
the first time we visit a page,
once we navigate around on the page,
and we start adding some items,
I want to be able to populate a cart on the backend
for a given user,
with the items the user added to the cart before.
And you'll see how that works throughout this section.
So then I add an items property with the this keyword,
and we set this equal to the items we get here
as a parameter value.
And actually I will use a feature here
about which we learned in this course as well.
I'll give this items parameter a default value like this,
so that if this cart is created without any values
passed into the constructor,
we automatically use an empty array for our items,
so that the first time we create a cart for a given user
it's empty, it has no items in there.
That's the first step.
The next thing I want to add here
is an add item method,
which allows us to add a new item, a new product,
to this cart.
So here in the end,
I expect to get the product object full of product data
as a parameter value.
Now here for the cart, unlike for the product is on,
we will not talk to an individual carts collection
in the database.
We could do that,
but instead here, I plan on storing the cart
inside of a user's session.
Keep in mind that in a user session you can store data.
We are storing data in a session, actually,
when we authenticate in our util folder
in the authentication.js file,
we are writing data into the session
to store the user ID there.
And we can not just store the user ID there.
We can also store the cart there.
The advantage of this is that our session is automatically
tied to a specific visitor,
because that's how sessions work.
Every visitor of a website gets his or her own session,
and a cookie with an ID of that session,
that allows the backend to connect requests,
incoming requests to a given session.
And since we therefore have such a session
for every visitor, even visitors who are not logged in,
we can store the cart in that session,
and we'll then have a cart per visitor
without forcing the visitor to log in.
And of course the visitors should be able
to add items to a cart without logging in first.
So therefore I'll store to cart in a session,
not in a database, and therefore here in add item,
I will not store any data anywhere in the database.
Instead. I really just want to manipulate my items property,
which is an array and add the product
that we're adding to cart to this array.
Now you could think that all we have to do
is call this item's push,
and push a product into this array like this.
Every array in JavaScript has a push method,
and this does what the name implies.
It pushes a new element into that array.
It adds it at the end of the array.
This would kind of work,
but it also means
that if we add the same product multiple times,
we actually start adding multiple items to this array,
even though it's the same product.
And I would like to consolidate the same product.
I would like to aggregate product data in the cart,
so that if we add the same item, the same product,
to the cart, multiple times,
we only have one item in this array.
But in that item,
we keep track of the quantity of the item,
so did we still know that product X
has been added five times,
but we don't have five items in the array,
but instead we store a quantity of five
for that given product.
And this means that pushing is only correct
if a product is not part of this items array yet.
Therefore what we'll have to do here in add item,
is we'll have to check if that product
is already part of the items array.
And we can do this in a couple of different ways.
One way of achieving this
would be to use a regular for loop with this syntax,
where we start with a simple helper variable set to zero.
And then after the semicolon,
we have the condition,
which defines how long we want to loop,
in this case, as long as I
is smaller than this items length.
And then after another of semicolon,
we have code that should execute after every loop iteration,
which in this case is I plus plus to increment I by one,
so that it gets bigger and bigger after every iteration,
until we don't meet this condition anymore,
and therefore, we exit the loop.
With that, I'm looping from I to zero,
up to the number of items we have in the items array,
minus one, because this is smaller,
and not smaller or equal.
And that of course means that we can get access
to the items that are stored
in the items property array here,
by accessing this dot items
by that I, which we can use as an index.
And this will be zero in the first iteration,
one in the next iteration.
And this gives us access to all the items we have
in this array.
We could use a for of loop,
but I actually want to get access to the index here.
That's why I'm using this regular for loop instead.
I also want to practice working with that for loop
with this kind for loop again,
because here in the for loop, we now of course,
want to check if the item we are currently looking at,
and we will look at all the items,
thanks to this loop logic.
If that item has, let's say, a stored product,
so a product that's attached to this item,
which ID is equal to the product ID
of the product we're trying to add here.
Now this assumes that every item in this items array
will have a product property, which holds a nested object,
which is of kind product.
And that is simply something we can implement
in the next steps.
It would mean that the item we push into items array
is not just a product, but instead an object,
where product is a nested object, under a product key.
And therefore, we might want to change this now.
And actually before we continue in the for loop,
add a cart item constant here, maybe,
which is a regular JavaScript object created on the fly
with the object literal notation,
where we add a product key which points at this product,
in addition, I'll add a quantity key,
and I'll set it to one here.
We might change this down here,
but I will set it to one as a default,
assuming that it's the first time we add a given product.
And I'll also add a total price here,
which is the total price for this item,
which could be more than one product.
It's one initially, but it could be more later.
And then for initially of course, the price,
the total price, is product dot price,
since we assumed that just one product gets added.
Now that's just the initial state.
We will change it in this for loop.
But it's this cart item, which I want to push here.
And therefore, that's structure our cart items we'll have.
And here's this product property,
which points at the nested product object,
which I use in this if check to get the ID
of this nest of product.
And I compare data to the ID of the product,
which is about to be added.
Now, if I have a match here.
So if for a given item I'm looking at,
the next product that you should be equal
to the product ID of the product I want to add,
then I know that the product I want to add
is already part of the cart.
And in that case,
I'll go to my cart item here,
to this constant,
and I'll change some of these properties.
I'll set the quantity
to cart item dot quantity plus one, for example,
increasing it by one.
in case you wonder why I am able to edit a object
that's marked as const,
please note that I also talked about this
earlier in the course.
This works due to the way JavaScript objects and arrays
are managed under the hood.
They are managed as so-called reference values,
which means in a constant or variable,
we don't store the object or array itself,
instead, just the memory address,
so the address in memory of that object or array.
And that address hasn't changed.
That's why it's still a const,
because I'm not storing the object,
but just a pointer to debt place in memory,
where the object is stored.
And that pointer hasn't changed.
Hence this is a constant, and I can still use that pointer,
which is what I'm doing here,
to change the object that is stored at that address.
That is simply how
so-called reference values in JavaScript work.
And objects and arrays are reference values.
Numbers and strings are not objects, and arrays are.
So that's why I am able to add it here.
Attached you'll find a link to an article and a video
that explains this in greater detail
in case it's still unclear.
So here I'm setting this cart item quantity.
And of course,
now I also want to update the total price,
and that's the old cart item total price,
plus the product dot price of course, like this,
because I added one item,
one entity of that product to my cart item.
So now I update this cart item like this.
And now I have to replace the item in the items array
with that updated cart item.
The product still is the product.
We don't need to override that.
But we did override quantity and total price.
And now you're inside of this if check,
of course, now that I have a match,
and I updated my cart item,
I can reach out to this items for the given index.
That's why I'm using this kind of for loop,
so that I have access to the index of the item
I'm trying to replace like this.
And I replaced this with my updated cart item.
That's what I do here.
So that's how we do add a product,
if it already is part of the cart.
And here after this line, I actually want to return here.
I don't want to continue with this line of code,
for example,
because now I already did add my product
simply by updating an existing item.
And therefore I return,
because the rest of this loop and of this function
shouldn't execute anymore.
And that's what return will achieve here,
since we now returned from this add item function.
So this is a add item function that should do the trick.
Now, of course,
we need a controller action that will trigger this function,
and then a route that points at this controller action.
So Dale for let's go to the cart controller now,
and add a new function, add cart item
could be the name of that function.
And of course we want to make it available
outside of this file.
And it won't be the only action.
Hence I'll export an object, where I export it like this,
make it available like this, outside of the file.
And we get the request and the response here.
And with that, we now need to access the cart of that user.
So we need to dive into the session that belongs to a user
and access the cart that's stored there,
and manipulate that cart.
We need to call the add item method there.
Now for this, I will actually add an extra middleware,
which will name cart dot JS,
which has the job of looking at an incoming request,
and determining whether it's coming from a user
who already has a cart or who doesn't have a cart yet.
And either way,
the cart should then be initialized correctly.
So therefore I'll add a function, initialize cart here
like this.
And there, I will export this initialize cart function
in this cart dot JS middleware file.
And here we get the regular middleware parameter values,
rec, res, and next,
and then here I'll add a cart variable,
because I want to check if we don't have a cart yet,
let's say if in the request session,
the cart property is undefined, as it will be initially
if a user visits this page for the first time.
And in that case, I'll set my cart equal to new cart.
And here I want to use my model.
So I will import my cart model,
by requiring going up one level, dive into models,
cart model.
Then we can initialize a cart like this here,
a new empty cart with no items.
That's where our default empty array
for the items parameter value will become handy.
ELLs, if we find a cart in the session already,
we know that for this user,
we did create a cart in the past.
And in that case,
my cart variable will be set equal to new cart,
but I'll take the items
with which I want to initialize that cart,
from the cart that was already stored in my session.
So therefore here we can now access
rec dot session dot cart dot items,
because a cart object when stored in a session
should be off this kind of cart.
Hence we should have our items property,
because data is what our blueprint says for cart objects.
So we should be able to access the items property here
in the middleware,
and then create a new cart object based on that.
And then I want to make that available
for this request response cycle
in all the other middleware functions and my views,
with help of the good old rest dot locals property
provided by express,
where I will add a cart property, which points at my cart,
which is either a brand new cart,
or a cart that was reinitialized
based on an old cart object
that was stored in the session.
I can't use rec session dot cart just like this,
because A, it might not exist yet,
that's why we have this case,
and B even if it does exist,
the way data is stored in a session
works such that any methods
that might've been attached to an object
are not stored there.
So it would be a cart object that has the items,
but not the methods which we defined in our blueprint.
That's why I'm re initializing it as a brand new object,
based on our blueprint,
where I just take the items
that are stored in the old cart already,
so that we have a new cart object in memory,
in our backend code, based on the old cart data.
And then I make that available with res locals cart,
and then thereafter, I call next,
so that the request
can travel on to the next middleware in line.
Now we need to activate that middleware in app JS.
And here we can now include our cart middleware
by requiring going up one level,
diving into the middleware folder,
the cart middleware like this.
And we then activate it somewhere in our code,
but definitely after we initialize the session.
So let's say, here,
we activate the cart middleware by pointing at it,
so that express can then execute
that cart middleware function we just defined for us.
And this now ensures that there definitely will be
a res locals cart property.
This property will now definitely exist,
because our own middleware guarantees it.
And that means that in the cart controller,
in add cart item,
we cannot access res locals cart.
And this will be an object based on our cart blueprint,
which means we can call, add item here like this,
because in our cart model, we added this add item method,
this method here,
We can call this in the cart controller now.
And to add item,
we need to pass the product that should be added.