Custom Attribute Searching in NopCommerce 3.3 Part 1 - The Dirty Way

nopCommerce is Asp.Net MVC online shopping cart system and is open source available for everyone to download with source code or ready to deploy package. The reason that i am writing this article is, sometimes, it gets tricky to customize an open source project and especially if you are new to it. Recently, i have a requirement of searching products in nopcommerce through attribute which isn't available and i have to do it out of the box. But also the problem, was time constraint, so i have to do it as soon as possible. Luckily, digging around nopcommerce, i have managed and found a shortcut way where i can acheieve the expected behavior easily. But before i show you, how i did it, i also want to say that i follow this way without keeping mind the nopcommerce guidlines. I just have to finish up and deliver the project as soon as i could. 

So, enough for the theory, take a cup of coffee and set back and relax and just watch me doing it :).
I have downloaded nopcommerce 3.3 (which is the latest version at the time of writing this article) and i have installed it a fresh copy. So, i will first create two categories;
categories
As you can see, i have only a couple of categories. Now, i will create a couple of sample products with a name, Product 1 and Product 2 (as shown in below) and associate it to For Sale category.
Products in Category
Good so far, now its time to create specification attributes which i will use it as a search and that's what this article focus on. So, i will go to admin -> Catalog -> Attributes -> Specification Attributes and Create the following attributes;
Specification Attributes
As you can see, i have create a couple of attributes (but you can create as much as you want, the technique will work of all) and it has associated values. Now i will associate these attributes to the product. In-Order to do that, i will go to admin -> Catalog -> Products -> Manage Products and edit product 1. There is a tab called, Specification attributes and i will see there the attributes (that i have just created a while ago);
Product Attributes
Here is another screen shot in LINQ Pad;
LinqPad
Ok, we are done here, now its time to jump to Razor view and define our searching criteria. First, i will show you how our search will look like. So here is the screen shot and you can see i am using bootstrap, i have already included in my theme.
Custom Attributes Search
We have categories, Location and Bathrooms (attributes) and price (using jquery ui range slider). So, let's jump to Home -> Index view and see how the html looks like;
            <div class="well">
                <div class="row-fluid">
                    @using (Html.BeginRouteForm("ProductSearch", FormMethod.Get, new { @class = "form-horizontal", id="quick-search" }))
                    {
                        <table class="table table-hover table-striped table-border">
                            <thead>
                                <tr>
                                    <th width="40%">Category</th>
                                    <th width="20%">Location</th>
                                    <th width="10%">Bathrooms</th>
                                    <th width="30%">
                                        <div class="inline" style="width: 45px; float: left;">
                                            Price
                                        </div>
                                        <div id="slider-range" style="width: 150px; margin-top: 5px; float: left;"></div>
                                    </th>
                                    <th width="5%"></th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td>
                                        <label class="radio inline">
                                            <input type="radio" name="Cid" value="1" />For Sale
                                        </label>
                                        <label class="radio inline">
                                            <input type="radio" name="Cid" value="2" />For Rent
                                        </label>
                                    </td>
                                    <td>
                                        <select class="span12" name="location">
                                            <option value="">Any</option>
                                            <option value="3">Abu Dhabi</option>
                                            <option value="1">Dubai</option>
                                            <option value="2">Sharjah</option>
                                        </select>
                                    </td>
                                    <td>
                                        <select class="span12" name="bathrooms">
                                            <option value="">Any</option>
                                            <option value="4">1</option>
                                            <option value="5">2</option>
                                            <option value="6">3</option>
                                            <option value="7">4</option>
                                            <option value="8">5</option>
                                        </select>
                                    </td>
                                    <td>
                                        <label class="inline">
                                            From
                                            <input class="price-from span4" id="Pf" name="Pf" type="text" value="" />
                                            To
                                            <input class="price-to valid span4" id="Pt" name="Pt" type="text" value="" />
                                        </label>
                                    </td>
                                    <td>
                                        <input type="submit" class="btn btn-primary pull-right btn-small" value="@T("Search")" />
                                    </td>

                                </tr>
                            </tbody>
                        </table>                  
                    }
                </div>
            </div>
So, what's this magic code for? well, first we have a form with method set to 'Get' because it will be a get request and then we have two radios and the name is 'Cid' which is necessary because nopCommerce get the query string value from this string. Similar way, we have two select inputs which is named as location and bathrooms and the attributes values is coming from db table which i insert it manually.
Now, its time to write jquery, to get the values and send it to search page.
<script>
    $(function () {
        var properTypeSelect = $('select[name="propertyType"]');
        var bathroomSelect = $('select[name="bathrooms"]');
        $('#quick-search').on('submit', function (e) {
            e.preventDefault();
            var categoryId = $('input[name="Cid"]:checked').val();
            if (categoryId == 'undefined' || isNaN(parseInt(categoryId))) {
                categoryId = '';
            }

            var propertyType = properTypeSelect.val();
            var location = parseInt($('select[name="location"]').val());

            var bathrooms = parseInt(bathroomSelect.val());
            var priceFrom = $('input[name="Pf"]').val();
            var priceTo = $('input[name="Pt"]').val();
            var url = '/search?As=true&Sid=true&Q=';
            url += '&Cid=' + categoryId;
            url += '&Type=' + propertyType;
            //url += '&Location=' + location;
            url += '&Specs=';
            if (!isNaN(propertyType) && propertyType > 0) {
                url += propertyType + ',';
            }
            if (!isNaN(location) && location > 0) {
                url += location + ',';
            }
            if (!isNaN(bathrooms) && bathrooms > 0) {
                url += bathrooms + ',';
            }
            var index = url.lastIndexOf(',');
            if (index > 0) {
                url = url.slice(0, index);
            }
            url += '&Pf=' + priceFrom;
            url += '&Pt=' + priceTo;
            url = encodeURI(url);
            window.location.href = url;
        });
        $("#slider-range").slider({
            range: true,
            min: 0,
            max: 100000,
            values: [0, 100000],
            slide: function (event, ui) {
                $("#Pf").val(ui.values[0]);
                $("#Pt").val(ui.values[1]);
            }
        });
    });
</script>

so as you can see, from the code its much clear that we are getting values from controls and passing to search page. Only from line 21 to 34, we are getting specs values and and appending ',' and the removing the last character and concatenating with other string and passing to search page. Now, if i press the search button, i will see the following query string parameters in browser and it means we are good to go for server side code.

Search Querystring

For searching, nopCommerce uses search action method which is available in catalog controller.
#region Searching

        [NopHttpsRequirement(SslRequirement.No)]
        [ValidateInput(false)]
        public ActionResult Search(SearchModel model, SearchPagingFilteringModel command)
        {
               /////rest of the code
        }
So in this action method i will get the query string and then split it with ',' and add it to a collection of integers (because it is expected by nopcommerce Search method). So, here is how the code looks like;
 var filteredSpecs = new List<int>();
 string specs = Request.QueryString["Specs"];
 if (!String.IsNullOrEmpty(specs))
 {
     var specsArray = specs.Split(',');
     foreach (var spec in specsArray)
     {
         if (!String.IsNullOrEmpty(spec))
         {
             filteredSpecs.Add(Convert.ToInt32(spec));
         }
     }
 }
//products
products = _productService.SearchProducts(
    categoryIds: categoryIds,
    manufacturerId: manufacturerId,
    storeId: _storeContext.CurrentStore.Id,
    visibleIndividuallyOnly: true,
    priceMin: minPriceConverted,
    priceMax: maxPriceConverted,
    keywords:model.Q,
    searchDescriptions: searchInDescriptions,
    searchSku: searchInDescriptions,
    searchProductTags: searchInProductTags,
    languageId:_workContext.WorkingLanguage.Id,
    filteredSpecs:filteredSpecs,             // here is the filtered specification list
    pageIndex: command.PageNumber - 1,
    pageSize: command.PageSize);
model.Products = PrepareProductOverviewModels(products).ToList();
We only need to do on more thing, which is to allow filtering from admin panel. So, i will go back to products and edit each of the product and then navigate to specification tab and allow filtering there;
Allow Filtering
That's it, now if we select again the location (Dubai) and press search, here is the expected result;
Product Result
As, you can see, its very easy to do customization in nopCommerce once you understand the structure of nopCommerce. That's it for now, next i will show you the right way and the dynamic way to do this searching. 



Write your own review
Bad Excellent