Now that you know how to structure your controller, let's talk a bit about a feature in SFRA that allows us to extend these controllers.
Right after I said extend you might have thought: why do we need a feature for that? Can't we just change the controllers' code and that's it? If that happened, you should check the previous module again where we explain why this can be a bad idea.
Remember the server import we were using to do get and post endpoints? Well, it turns out it has other functions we can call in order to extend existing functions.
I keep saying extend, extend but I forgot to explain what does it actually mean, right? By extending we mean you can modify the behavior of a controller without actually touching the original file.
To do that, you will need to create a new cartridge and inside that cartridge, you will create a new controller.
Let's suppose you want to extend the Cart.js controller to add some extra logic, it doesn't matter what.
First, let's create a new cartridge. Or you can use the one you created in the previous module. Once the cartridge is created, let's replicate the folder structure we have in the app_storefront_base cartridge and create our controller in the correct folder only with the basic code that all controllers need.
The next step is to specify the controller we want to extend. There are 2 ways to do that, it depends only if your controller file has the same name as the one you want to modify or not.
SFRA server module provides a few ways for us to extend a controller. You can add extra logic:
server.append: Whenever you need to add some extra logic at the end of your function you should use the append method. Your code will be run right after the next() function call
server.prepend: Whenever you need to add some extra logic at the beginning of your function you should use the prepend method. Your code will be run as the 1st line of the code in the function.
server.replace: Whenever you are in a situation where it is not enough to add some extra logic at the beginning or at the end of the function and you need to actually change the behavior of the existing code you will end up using server.replace.
For example, imagine that you want to optimize the function or remove part of the code because the requirements changed. You can't just add code at the end or at the beginning to fix it, you need to rewrite it. That's where you will use server.replace
this.on("route:BeforeComplete"...: This is another way to add extra functionality to the end of your function without using server.append. Open your Cart.js controller and check the end of the GetProduct endpoint. Everything inside this function will be executed at the end of the middleware, in case you might expect your function to be appended.
Any code you put inside this function, you must consider that you are in a different scope, which means you don't have access to the variables outside this function. To be able to process them, you must use setViewData outside the function and use getViewData inside this function to get them back.
In practice, you will use this function only when you need to add extra logic to your controller without having to create a new controller file, replicate your endpoint with append and so on.
One of the most important things to know when extending controllers is to know how to retrieve the contents of the response object.
Let's just refresh things a bit. Remember when we discussed that your functions will render pages or return JSON objects? And that when we render pages we can send data to the page? We call it ViewData object
Now consider the following: the original function was executed and data was sent to the page, everything ok so far. But now you need to send more data to that page in the endpoint that you are extending. How do you send more data? You must not do a second render.
To fix this problem, the response object provides 2 special methods. They are getViewData and setViewData. Their names are self-explanatory. One allows us to retrieve the current ViewData object and the other to set the extra content to the ViewData object.