Confusion of slug with route part "new" etc - symfony-1.4

I have a sfDoctrineRouteCollection:
foo:
class: sfDoctrineRouteCollection
options:
model: Foo
columns: slug
action: [list, show, new, create, edit, update, delete]
If someone now creates a new entity whose slug results in, for example, "new", the routing engine gets confused regarding the two routes /foo/new for creating new entities and /foo/new for showing the entity with slug "new".
What's a clean and elegant solution to this problem?
Of course, I could provide explicit route definitions, but that would make sfDoctrineRouteCollection kind of obsolete. A separate route definition for the "show" action would already solve the problem, for example, by adding a prefix like "/foo/:slug/show". I also could provide a custom slugify method intercepting slugs like "new" etc.
Do you know any clean and elegant solutions?

You can use segment_names option to change new to something else or you can use something more explicit like id or id-slug or disable with_show and add a show object action
object_actions: { show: [get, head] }

Related

Is it possible to specify forbidden values on a slug field?

I have an entity which have a slug field defined and managed using Gedmo Slug library.
Is it possible to easily define a list of unwanted values for this field (e.G. 'edit', 'new', 'delete'...) or do I need to make something all by myself?
You could use a SlugHandler I guess.
Take a look at this : http://atlantic18.github.io/DoctrineExtensions/doc/sluggable.html#slug-handlers
You could create your own SlugHandler, add some SlugHandlerOption with your forbidden words, and use a simple regex to check if the generated slug is valid.
If not, you could modify the generated slug or throw an exception.
I hope to understand your request. It lacks a bit of clarity.
In my opinion, the Gedmo librairy can not do this easily.
In your place, I will create a value that I will work according to your imperatives and I will let the annotation slug work with this value.

Flatting FOS ElasticaBundle nested fields

I have FOSElasticaBundle in my Symfony project. I have an entity mapped with ElasticaBundle that has some "simple fields", like
mappings:
createdAt:
type: "date"
and other fields that are IDs (like owner_id) to correlate to other entities, like
owner:
type: "nested"
properties:
fullname:
type: string
index: not_analyzed
because I need to have the user fullname searchable with ES/Kibana. This works but it created of course a nested field "owner.fullname" and this kind of fields are not searchable with Kibana (it's since years there are requests about it).
So I'm asking: is there a way to flat out that field so that I have a simple plain string field in ES named "owner_fullname" with no nested data?
Thanks
Answer to self.
You can create a method (or use existing where possible) in your class to return informations about related class.
So if you have a User class related to several Address class to store user's addresses, and you want to store in ES the default one, you can create a method "getDefaultAddress" in User class, something like
public function getDefaultAddress() {
return $this->addresses->getDefault()->getFormattedFlatString()
}
then map it to a field with elastica Bundle and use the "property_path:" descriptor to tell elastica where to get the content for that field.
In this way you have a simple field in ES and not a nested one.
I'm not sure I like this philosophy because you mix up code and external persistence layers, but it works, is clear and easy to maintain especially if you create dedicated methods with same prefix in your class, like esGetAddress, esGetPlace and so on.

Handling trivial and complex Actions with Routes

I would like to design our Controllers and Actions like this:
There is one Action for every Controller called "StaticPage(string page)" (thought about creating a BaseController to derive from it but for now, we will talk only about "Suite" as Controller).
This Action returns Views, that are containing mostly HTML code and no Model.
So instead of creating 20 Actions for 20 trivial Views, I would like one Action to manage and differ them by a parameter.
But on the same Controller, I would like them to have Actions that return Views with Model and do more complex and database related stuff. At first, I thought about following routes (Note: In this Scenario, there exists an area "Product"):
context.MapRoute(
"Product_default",
"Product/{controller}/{action}",
new { action = "index"}
);
context.MapRoute(
"StaticPage_Route",
"Product/{controller}/{page}",
new { action = "StaticPage", page = UrlParameter.Optional
);
So lets say we got the View TechnicalSpecification.cshtml and TrivialPage.cshtml and when I call
/Product/Suite/TechnicalSpecification
it should search and find the Action "TechnicalSpecification", but when I try
/Product/Suite/TrivialPage
it should also first search the Action "TrivialPage". But then, when it doesnt find it, it should treat it as an Parameter called page (like in the Routes I've mentioned above).
But I don't really know, how to manage this right.
Is there a way without setting an custom ActionFilter Attribute, that checks something in OnActionExecuted(...) and then does something to call the "StaticPage"-Action?
I found the solution myself.
Overriding HandleUnknownAction does the trick.
The code looks like this:
protected override void HandleUnknownAction(string actionName)
{
this.View(actionName).ExecuteResult(this.ControllerContext);
}
Also, the Route "StaticPage_Route" was not necessary anymore so I removed that route.
By this, I just need to put the trivial Views in the respective View-folder and the override method calls them if available.
With this I can save alot of trivial code and don't need to implement those sites.

Create route that will move the query string to part of the url

I'd like to change my routes so that instead of having:
/Users/Edit?UserID=1
I can do
/Users/Edit/1
How can I create a custom route to do that?
Also, can someone direct me to a good tutorial on routes? I don't wanna create a post every time I have a simple problem with routes.
Actually http://localhost/Users/Edit/1 can do the what you want without defining a new route. The default route structure is http://localhost/controllerName/actionMethodName/id.
So if you just use "id" instead of "UserID" in your action method. The number in the URL would be assigned to the parameter on action.
public ActionResult Edit(int id)
Here's a few lesson on routing. Take a look at them. It's a quite easy thing to understand.
http://www.asp.net/mvc/tutorials/asp-net-mvc-routing-overview-cs
http://www.asp.net/mvc/tutorials/creating-custom-routes-cs
http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
You will want to use the htaccess file. You put in a regular expression which will extract pieces of the file path as if it is part of the query string.
This is a link I googled it may do the trick.
You will want to do something like this:
routes.MapRoute(
"MyRoute",
"{controller}/{action}/{UserId}",
new { controller = "DefaultController",
action = "DefaultAction",
UserId = UrlParameter.Optional }
);

ASP.NET MVC url construction

Before I ask my question: I just started ASP.NET MVC, so an advanced answer will maybe hard to understand ;)
I'm having 3 tables (I'll add just a few of the fields below)
Shops (ShopID, Name)
Products (ProductID, Name, ShopID)
Comments (CommentID, ProductID, UserID, CommentText)
Now what I want to do is this:
www.site.com/Shops
//Gives me a list of shops
www.site.com/Shops/ShopName
//Gives me some details about the shop called ShopName and lists all the products that the shop has
www.site.com/Shops/ShopName/ProductName/
//Gives me all the comments of that product called ProductName from that specific shop called ShopName
www.site.com/Shops/ShopName/ProductName/ASpecificCommentHere
//Gives me a specific comment on that product of that shop
and other things like
www.site.com/Shops/ShopName/ProductName/AddComment
//Adds a new comment to that product from that shop
Now I only have a ShopsController which makes it possible for me to do something like this now:
www.site.com/Shops/
// Gives me a list of all shops
and
www.site.com/Shops/Details/123
// Gives me some details about the shop with ID 123 and should list its products.
But here is also the first problem: I don't want the ID number in the url like /Shops/123 but rather the name like /Shops/ShopName and I don't know if it's good to have /Details/ there in the url. Maybe /Shops/ShopName would be better without the Details part in between?
How should I do this?
Should I create also a Controller for my Products and for my Comments that I have 3 controllers in total?
Or should I just keep one ShopController to always get the first part of the url (so that it always starts with) site.com/Shops/ShopName/...
Thanks in advance
(and a short little question, should ViewModels be placed in the controllers directory?)
Kind a doing your work, but - whatever.
I would go with something like this (be warned - it's untested)...
As you already likely know - order is important.
www.site.com/Shops/ShopName/ProductName/AddComment =>
routes.MapRoute(
"Product",
"Shops/{shopName}/{productName}/{action}",
new { controller="comment"}
);
www.site.com/Shops/ShopName/ProductName/ASpecificCommentHere =>
routes.MapRoute(
"Product",
"Shops/{shopName}/{productName}/{commentName}",
new { controller="comment", action="details"}
);
www.site.com/Shops/ShopName/ProductName/AddProduct =>
routes.MapRoute(
"Product",
"Shops/{shopName}/{productName}/{action}",
new { controller="product"}
);
www.site.com/Shops/ShopName/ProductName/ =>
routes.MapRoute(
"Product",
"Shops/{shopName}/{productName}",
new { controller="product", action = "details"}
);
www.site.com/Shops/ShopName =>
routes.MapRoute(
"ShopDetails",
"Shops/{shopName}",
new { controller="shop", action = "details"}
);
www.site.com/Shops (NB: this route is 'global', for every controller) =>
routes.MapRoute(
"List",
"{controller}",
new { action = "list"}
);
Maybe this can be optimized or worse - it's wrong but it should give you some better understanding for sure.
Remember not to name shops/controllers/comments as according controller actions. That's an issue with this routing approach.
And i hope you noticed that i would create 3 different controllers (what might and might not be a good decision - that's based on expectable weight and complexity of controller logic).
Another tip - read about model binding. Using custom binders you could replace string parameters (entity names) with actual, already assembled objects. Maybe that wouldn't be worth for this particular case, but model binding surely is 2nd important and hardest thing to grasp when facing asp.net mvc.
And i lied, i wouldn't go with this because i prefer specifying controller specific routes using attributes directly on actions (omitted that because it seems more advanced).
Routing is like regular expressions. There's a moment when it just 'clicks' and seems natural and easy.
Already answered second question as comment to your question. :)
I would spend a little time researching the routing mechanism in MVC. This is where you set the formats for the URLs your application should receive. All the things you mentioned are possible...you just need to construct your route definitions accordingly. As far as being able to accept the name of the entity, rather than the ID, you can do that very easily by constructing your route so that it accepts names and changing the signatures on your action methods to accept strings instead of ints.
I don't know what the official stance on where ViewModels should be located. It's really up to you, and it depends on the ORM method you are implementing. I'm using LINQ-to-SQL, and having a very good time with it. I keep my LINQ-to-SQL model in my Models folder. I prefer to keep my ViewModels in a View subfolder under my Models folder. I've also got a Domain subfolder to hold the partial models for my entities, in case I want to implement calculated fields or set up the initial values for the record.

Resources