.Net MVC route for undetermined number of folders (nested category structure) - regex

I'm exploring the possibility of using MVC for my next e-commerce site. One thing I can't seem to figure out is whether or not I can use the same URL convention I normally use. Currently, the URL for any product could be one of the following:
Category/SubCategory/Product1.html
Category/SubCategory/SubSubCategory/Product2.html
Category/SubCategory/SubSubCategory/Product3.html
Category/SubCategory/SubSubCategory/SubSubSubCategory/Product4.html
etc.
The issue I'm having is with the nested category structure. So far the only thing I've come up with is as follows:
routes.MapRoute(
"Products",
"{categories}/{productname}",
new { controller = "Product", action = "Details", productname = UrlParameter.Optional },
new { categories = #"\w+/\w+" }
);
I was hoping that {categories} could be matched with any one of the following which I could process to identify the right category that the product belongs to:
Sport/Tennis/Rackets/ProductA
Sport/Badminton/Rackets/ProductB
But the route shown above doesn't work correctly.
Does anyone know how this can be achieved, or if it can't be done?

The routing system allows you to define catchall parameters, which ignore slashes and capture
everything up to the end of a URL. Designate a parameter as being catchall by prefixing it with an
asterisk (*).
routes.MapRoute(null, "Articles/{*articlePath}",
new { controller = "Articles", action = "Show" }
);
You can only have one catchall parameter in a URL pattern, and it must be the last (i.e.,
rightmost) thing in the URL, since it captures the entire URL path from that point onward.
One Caveat though, it doesn’t capture anything from the query string as route objects only look at the
path portion of a URL.
Catchall parameters are useful if you’re letting visitors navigate through some kind of arbitrary
depth hierarchy, such as in a content management system (CMS).
You can use the RouteData object to extract information about the route. For your needs, you would probably create a custom route handler that parses the route data and calls the correct controller methods.

You need access to the individual segments of the URL so you need to divide the category segment into two segments. That would make it much easier.
Let's say we call Tennis and Badminton categories and Rackets within those categories as a product class
You need a way to access the category, productClass and productName parameters. Supposing that "Sport" is fixed in this case, I will do it like this:
routes.MapRoute(
"Products",
"sport/{category}/{productClass}/{productName}",
new { controller = "Product", action = "Details", productClass = UrlParameter.Optional, productName = UrlParameter.Optional }
);
Your action method will be something like this
public ActionResult Details(string category, string productClass, string productName){
//Do whatever you need to do in order to get the specified product
}

You could use Areas in MVC2
So it would read:
Area/Controller/View/id
So in your case it would end up being:
Sport being the area,
Tennis The controller,
Rackets the view,
ProductA being an ID or querystring,
http://www.asp.net/mvc/videos/aspnet-mvc-2-areas
Hope this makes sense.

Related

how to consider annotation groups in a ObjectConstructor

JMSSerializer comes with a Doctrine Object Constructor that does its job, but imagine an Entity with two properties forming a primary key:
UserBase
prop annotated with #ORM\Id and #Serializer\Groups({"1"})
- username
prop annotated with #ORM\Id and #Serializer\Groups({"2"})
- email
User extends UserBase
- other props here, no Id defined.
one property key is excluded by using group=1 while deserializing. The client potentially still sends both email and username. email should not be considered though.
Unfortunately, if you pass the two properties in the body, DoctrineObjectConstructor does not check if something is excluded by deserialization, so it tries to load the Entity from the DB, according to the two values:
foreach ($classMetadata->getIdentifierFieldNames() as $name) {
if ( ! array_key_exists($name, $data)) {
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
$identifierList[$name] = $data[$name];
}
What I would like to do is taking into account my annotated groups, so as to use the fallbackConstructor in case some property forming the identifier is missing.
As a starter this is a good point: I created my own service, by passing along the annotationDriver. Then, if the property forming the identifier is not associated with the actual group:
$classMetadata = $this->annotationDriver->loadMetadataForClass($metadata->reflection);
$classMetadata->properties //here groups are listed for each property
I can fall back to the fallbackConstructor, as if I had not passed that property in the body
...not so fast! My Entity User extends a UserBase where all my identifier are, so I should take into account hierarchy, possibly in a generic way.
Any hint?
Alright, JMSSerializer's Object Constructor does not consider serialization groups when determing identifiers. Hence, if you include all the IDs in your object, whether they are part of the actual context group or not, they will be counted in.
I created an alternative version of the Object in order to fix this misbehavior (at least for me). Hope it helps

How to efficiently store records as Ember query parameters

Background: The user can filter a list based on tags, every tag is an Ember Data model. The selected filtering tags should be stored in the URL via query parameters. I also want to display the selected tags in the interface.
What's the best way to go about this?
I'm pretty sure I only want to store the IDs, but if I do this I have to maintain a separate variable / computed property where I store the actual record of the ID which comes from the query parameters. This kind of duplication seems wrong to me.
Apart from this issue I don't know if I should use an array query parameter or build an comma separated string of the IDs. If I do the former I end up with ugly URLs like ?tags=%5B"3"%5D. But doing the later means doing even more work.
So what's your approach to this kind of problem? I hope I'm missing something obvious that doesn't have so many downsides :)
Easy!
Find out which non-alphanumeric characters are not getting encoded in URLs. Using this tool, I found these characters: ~!*()_-.
Pick one character that is not used in your tag names. Let's say it's *. Use it as a delimiter.
Use tag names for tag ids. Or at least enforce unique tag names.
Then you've got a nice readable query:
?tags=foo*bar*baz&search=quux
To implement custom query param serialization/deserialization, do this:
Ember.Route.reopen({
serializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
return `${value.join('*')}`;
}
return this._super(...arguments);
},
deserializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
return value.split('*');
}
return this._super(...arguments);
}
});
Demo: app, code.
If you must use numeric tag ids in query params (for example, your tag names aren't unique), then you need some more customization: app, code.

Can i hide the query params from the URL?

In my Ember.js Application, I am dealing with query params for list updates. I have one strange use case, in which I don’t the URL to be updated with certain query params. How can I achieve this?
I assume you want to reload your model with parameters that are different than the ones in your application route? And you keep your application route parameters synced using queryParams?
In your route's model function you can filter your model data by the same query params (that appear in the address bar) but you can add some logic that extracts additional parameters either from the controller or other place and these parameters the data fetching query. Example:
model: function(queryParams) {
var params = queryParams;
params.additional_filter = this.controllerFor('mycontroller').get('additional_filter');
return this.store.find('mymodel', params);
}
Also if you want to explicitly reload the model you will need to call Router.refresh() function.

Django: structuring a complex relationship intended for use with built-in admin site

I have a fairly complex relationship that I am trying to make work with the Django admin site. I have spent quite some time trying to get this right and it just seems like I am not getting the philosophy behind the Django models.
There is a list of Groups. Each Group has multiple departments. There are also Employees. Each Employee belongs to a single group, but some employees also belong to a single Department within a Group. (Some employees might belong to only a Group but no Department, but no Employee will belong only to a Department).
Here is a simplified version of what I currently have:
class Group:
name = models.CharField(max_length=128)
class Department
group = models.ForeignKey(Group)
class Employee
department = models.ForeignKey(Department)
group = models.ForeignKey(Group)
The problem with this is that the Department select box on the Employees page must display all Departments, because a group has not yet been set. I tried to rectify this by making an EmployeeInline for the GroupAdmin page, but it is not good to have 500+ employees on a non-paginated inline. I must be able to use the models.ModelAdmin page for Employees (unless there is a way to search, sort, collapse and perform actions on inlines).
If I make EmployeeInline an inline of DepartmentAdmin (instead of having a DepartmentInline in GroupAdmin), then things are even worse, because it is not possible to have an Employee that does not belong to a Group.
Given my description of the relationships, am I missing out on some part of the Django ORM that will allow me to structure this relationship the way it 'should be' instead of hacking around and trying to make things come together?
Thanks a lot.
It sounds like what you want is for the Department options to only be those that are ForeignKey'ed to Group? The standard answer is that the admin site is only for simple CRUD operations.
But doing what you're supposed to do is boring.
You could probably overcome this limitation with some ninja javascript and JSON.
So first of all, we need an API that can let us know which departments are available for each group.
def api_departments_from_group(request, group_id):
departments = Department.objects.filter(group__id=group_id)
return json(departments) # Note: serialize, however
Once the API is in place we can add some javascript to change the <option>'s on the department select...
$(function() {
// On page load...
if ($('#id_group')) {
// Trap when the group box is changed
$('#id_group').bind('blur', function() {
$.getJSON('/api/get-departments/' + $('#id_group').val() + '/', function(data) {
// Clear existing options
$('#id_department').children().remove();
// Parse JSON and turn into <option> tags
$.each(data, function(i, item) {
$('#id_department').append('<option>' + item.name + '</option>');
});
});
});
}
});
Save that to admin-ninja.js. Then you can include it on the admin model itself...
class EmployeeAdmin(models.ModelAdmin):
# ...
class Media:
js = ('/media/admin-ninja.js',)
Yeah, so I didn't test a drop of this, but you can get some ideas hopefully. Also, I didn't get fancy with anything, for example the javascript doesn't account for an option already already being selected (and then re-select it).

Search value in body tag in Sitecore

I want to implement search functionality, So my requirement is I want to search some keyword in body tag in all content page. I do not know how I can search keyword in body tag in Sitecore. Please guide me?
As Anton outlined, the concept of searching the Body tag is wrong for Sitecore. You want to think in terms of Content in fields of Items. Sitecore's ContentSearch is how you can achieve this.
Sitecore comes with default indexes out-of-the-box that you should use for the search. You should rebuild these via the Index Manager in the Content Editor and then base your search on the basic example I've outlined for you below.
public IEnumerable<Item> Search(string searchterm)
{
string indexName = "sitecore_web_index";
using (var index = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
IQueryable<SearchResultItem> query = index.GetQueryable<SearchResultItem>().Where(i => i.Content.Contains(searchterm)).Filter(predicate);
var searchResults = query.GetResults();
foreach (var hit in searchResults.Hits)
{
yield return hit.Document.GetItem();
}
}
}
jRobbins's answer is sensible (he get's my upvote). However, it is technically possible to index the content of the body tag. I would be cautious with this. I've seen it working well, but I've also seen it completely destroy the performance of a site.
The approach involves the creating a computed field in your index. You populate the computed field by making a web request to your newly published page and scraping the response body tag.
Here's are a couple of module that more or less does that:
https://github.com/efocus-nl/sitecorewebsearch
https://github.com/hermanussen/sitecore-html-crawler
If you can accept something a little less accurate, then you could loop through each of the components on your page and extract content from their datasources. That approach is discussed in this video:
http://www.techphoria414.com/Blog/2012/May/Sitecore_Page_Editor_Unleashed