A nopCommerce architecture question when doing customization

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
10 years ago
Finally had a chance to do customizations with services, found a 100% clean way of customizations without touching the core code. It only works for services or helpers, but better than nothing. At least no loads of partials to me.

http://tech.sunnyw.net/2013/12/nopcommerce-create-customized-service.html
10 years ago
Another great post, my friend! Funnily enough I accomplished the same thing a few days ago - plugged in my own ProductService sub-class that just logs all requests for "GetProductById" to log the productID to a text file, as a proof of concept.
I was surprised how easy and straightforward it was and it works great.

Ahhh I love the DI principle :)
10 years ago
Anyone know if there is a way to enforce the order in which DependencyRegistrar's are read?

I believe Autofac registration works in a 'last one wins' arrangement. Seems to me so long as the core one gets read first, you might not even have to fiddle with the official source; You could just implement your own DependencyRegistrar and be done.

*chuckling* just reading some SO posts about this, and order dependent registration apparently gives people the willies.

Answered my own question, from the official documentation

official documentation wrote:

You can create as many dependency registrar classes as you need. Each class implementing IDependencyRegistrar interface has an Order property. It allows you to replace existing dependencies. To override nopCommerce dependencies, set the Order property to something greater than 0. nopCommerce orders dependency classes and runs them in ascending order. The higher the number the later your objects will be registered.


So... there you go

Seems RouteProvider can work the same way, though it uses Priority, which confuses me in terms of which order it would execute in. I'd have to try it.

With that provision, I don't see any reason why you would ever use Controller inheritance or partials. Just roll your own and update the routing. Good thing is you only have to take care of the actions you need to override, not the entire class.
10 years ago
mattsoundworld wrote:
With that provision, I don't see any reason why you would ever use Controller inheritance or partials. Just roll your own and update the routing. Good thing is you only have to take care of the actions you need to override, not the entire class.


It's a pain - the controllers generally have helper methods that would need to be either refactored out into another class or duplicated into your own controller.

Also, when rendering a view from my own re-routed action, I had to prefix the viewpath with "../[OriginalControllerName]" otherwise it'd be trying to render views from the name of my own controller.

I also had to update any Html.Partial calls in these views, to append the original controller name, otherwise it'd be trying to render partials based on my new controller's name (e.g. re-mapped CatalogController.Product to my own CustomCatalogController and rendering ../Catalog/ProductTemplate.Simple then this view would try and render the partials from /CustomCatalog/_partial
10 years ago
sproaty wrote:


It's a pain - the controllers generally have helper methods that would need to be either refactored out into another class or duplicated into your own controller.


Fair point, but to go the inherited class route, don't you have to mark properties as protected? You could mark those utility methods as public and have at them. You'd create some dependencies because you'd have to new up a Controller for the purpose of using those utilities, but no more than inheriting or partialing.

sproaty wrote:

Also, when rendering a view from my own re-routed action, I had to prefix the viewpath with "../[OriginalControllerName]" otherwise it'd be trying to render views from the name of my own controller.


I certainly don't see that as a negative. It gives you the option to create the views you want OR use what is already there without modifying core code.

sproaty wrote:

I also had to update any Html.Partial calls in these views, to append the original controller name, otherwise it'd be trying to render partials based on my new controller's name (e.g. re-mapped CatalogController.Product to my own CustomCatalogController and rendering ../Catalog/ProductTemplate.Simple then this view would try and render the partials from /CustomCatalog/_partial


Sounds like a case where you'd be better off not writing an across the board /Catalog to /CustomCatalog route, and instead just use /Catalog/Action to /CustomCatalog/CustomAction.

Ex

routes.MapRoute("CustomCatalogAction", // Route name
                           "/NamedAction",          // URL with parameters
                            new { controller = "CustomCatalog", action = "CustomAction" }
                            // Parameter defaults );

routes.MapRoute("CustomCatalogAction2", // Route name
                           "/Catalog/NamedAction",          // URL with parameters
                            new { controller = "CustomCatalog", action = "CustomAction" }
                            // Parameter defaults );


You could also consider getting seriously crazy and looking at Action Filters, and making a decision based on the request context as to where to redirect the call. Sorta like here
10 years ago
mattsoundworld wrote:


Fair point, but to go the inherited class route, don't you have to mark properties as protected? You could mark those utility methods as public and have at them. You'd create some dependencies because you'd have to new up a Controller for the purpose of using those utilities, but no more than inheriting or partialing.




In my opinion, they should have been protected to start off with, but since nop favor partial approach, so they were private. There is another way to do it without changing them to protected, not so hard, but personally I favor protected.


mattsoundworld wrote:


I certainly don't see that as a negative. It gives you the option to create the views you want OR use what is already there without modifying core code.

Sounds like a case where you'd be better off not writing an across the board /Catalog to /CustomCatalog route, and instead just use /Catalog/Action to /CustomCatalog/CustomAction.

Ex

routes.MapRoute("CustomCatalogAction", // Route name
                           "/NamedAction",          // URL with parameters
                            new { controller = "CustomCatalog", action = "CustomAction" }
                            // Parameter defaults );

routes.MapRoute("CustomCatalogAction2", // Route name
                           "/Catalog/NamedAction",          // URL with parameters
                            new { controller = "CustomCatalog", action = "CustomAction" }
                            // Parameter defaults );


You could also consider getting seriously crazy and looking at Action Filters, and making a decision based on the request context as to where to redirect the call. Sorta like here



Nope, options to use new view are done by theme, another topic.

If write CustomCatalog instead of remarking all the old controller to something else, you lose SEO as your URL will look ugly now, you lose most of the original functionalities except your "extra".  There is another way to change how URL mapped in RouteProvider, but again, a bit of pain.



At the end of the day, you just need a way that you feel comfortable and easy to use, then turn it into your team standard.
10 years ago
sproaty wrote:
With that provision, I don't see any reason why you would ever use Controller inheritance or partials. Just roll your own and update the routing. Good thing is you only have to take care of the actions you need to override, not the entire class.

It's a pain - the controllers generally have helper methods that would need to be either refactored out into another class or duplicated into your own controller.

Also, when rendering a view from my own re-routed action, I had to prefix the viewpath with "../[OriginalControllerName]" otherwise it'd be trying to render views from the name of my own controller.

I also had to update any Html.Partial calls in these views, to append the original controller name, otherwise it'd be trying to render partials based on my new controller's name (e.g. re-mapped CatalogController.Product to my own CustomCatalogController and rendering ../Catalog/ProductTemplate.Simple then this view would try and render the partials from /CustomCatalog/_partial


I can totally feel the pain, that's why I was quite determine to spend the extra time for the inheritance route. With this freedom to inject more services, or customize some services in a customized controller, I can give more accurate estimates to the project that I do not need to worry about breaking existing functionalities.
10 years ago
mattsoundworld wrote:
Anyone know if there is a way to enforce the order in which DependencyRegistrar's are read?

I believe Autofac registration works in a 'last one wins' arrangement. Seems to me so long as the core one gets read first, you might not even have to fiddle with the official source; You could just implement your own DependencyRegistrar and be done.

*chuckling* just reading some SO posts about this, and order dependent registration apparently gives people the willies.

Answered my own question, from the official documentation


You can create as many dependency registrar classes as you need. Each class implementing IDependencyRegistrar interface has an Order property. It allows you to replace existing dependencies. To override nopCommerce dependencies, set the Order property to something greater than 0. nopCommerce orders dependency classes and runs them in ascending order. The higher the number the later your objects will be registered.

So... there you go

Seems RouteProvider can work the same way, though it uses Priority, which confuses me in terms of which order it would execute in. I'd have to try it.

With that provision, I don't see any reason why you would ever use Controller inheritance or partials. Just roll your own and update the routing. Good thing is you only have to take care of the actions you need to override, not the entire class.



Btw, that is a very interesting point. I have never done DI override before, will definitely look this up.

Thanks.
Sunny
10 years ago
Great topics of discussion guys - I'm writing a nopCommerce research guide for my boss to decide whether it's a viable platform for us to leverage and I've covered some of these areas - just trying to summarise a lot of my findings!

Sunny - I thought you'd managed to wire your custom service as a dependency replacement? In a plugin you can implement a DependencyRegistrar:


public class DependencyRegistrar : IDependencyRegistrar
{
        public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
        {
            builder.RegisterType<YourService>().As<IYourService>();
        }

        public int Order
        {
            get { return 0; }
        }
}



As far as overriding controllers go, it's either:

- Extend controller via plugin, make what's needed protected. Mark methods as virtual. Register existing URL route that you're overriding to your extended controller - deal with partials etc

or

- Make a partial controller class inside core, as described by carlosmartinezt - and use [Action] with another method with the same signature but with an extra paramter


I'm also going to be doing a lot of view customisation most likely, so I'm trying to find some limits. Just moving the shopping basket in the header outside of its containing <div> was tricky because it relied on information inside the model passed in by a controller, and a partial view was rendering this containing div.

Shopping cart link is inside "header-links" div, and I wanted to move it into "header" div so that I can style it differently and whatnot. However, to move it 'back' a layer I needed to move/duplicate existing code that populated the ShoppingBasketModel (which header-div used) into another controller. The "header" div was just static and didn't expect a model, so I had to create a controller for it to render a partial which will populate the shopping basket model.

It's a bit tricky to explain..

Looks like I'll be overriding a *lot* of the views - not sure how much of a pain upgrading will be in the future! I'm trying to find the simplest path - glad that others are on the case, too.
10 years ago
Can't edit posts - just wanted to say. I'm trying to make something for the customer that's probably totally unlike nop out of the box. The main thing I see with most of these sites produced is all the same feel...the shopping basket's in the same place. Always has the hover. The ajax cart thing. I'm not bashing nop here - it's just it looks like a great platform to build upon, rather than building an e-commerce site from the ground up.

I'm just worried about a few limits - the designs will be really different from the standard stuff. Due to view reliance on models, I'm going to have to move things around and doing so is going to be quite crazy due to the amount of changes needed. Just from my badly-explained example above about moviing the shopping cart HTML back a few divs took a fair while so I can imagine whole sections of many pages being quite complex.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.