I have an ODATA Service MyService with entities Customers,Products and Categories, where Navigation Property is defined from Customers to Products, and from Products to Categories.
I am confused about the correct form of Uri to do Navigation from Customer to Product to Categories.
Initially I gave MyService/Customers(1)/Products(10)/Categories, but it gave me an error.
Then I tried MyService/Customers(1)/Products/Categories which worked correctly. I saw many examples in the internet in the form of the uri which I tried initially But it does not work for me.
Can anyone explain about this?
The correct form of URI depends on the multiplicity of the navigation property Products. If a Customer can have multiple Products then the first form is correct. If a Customer can have at most one product then the second form is correct.
Perhaps in your service this navigation property has had the multiplicity set incorrectly.
The use of parenthesis to specify the resource key indicates that this is an OData v4 topic.
According to the specification OData v4: 4.3.3 URLs for Related Entities with Referential Constraints your syntax is compliant as long as Porducts is a collection type.
Include the relevant $metadata for the resources in question in the future to resolve this level of ambiguity.
If your second form of URL worked, then it usually indicates that the multiplicity of the Products navigation property is singular, making it no longer a collection and therefore you cannot filter by the key. In this case, as above you could use the $filter query option, but then you might as well have queried the Categories controller directly.
Even if the syntax is compliant and the resource navigation to Products IS a collection, in many implementations this type of specificity in navigation is NOT supported. Maybe because it is unnecessary but probably because it is a grey area in terms of intent so it is hard for code frameworks to enforce routing conventions or rules in this area by default.
For instance, should this query be evaluated by the Customers controller, but return a collection of Category resource, or should this be evaluated by the Products controller to return a collection of Category resource? Wouldn't it be easier to design the underlying controllers if the Category controller was the one to return and query the Category resources, rather than potentially adding this same support to all of the Customer,Product and Category controllers?
Given that the unique key of 10 has been provided for the expected Product resource to return, the Customer key of 1 is irrelevant.
If you wanted to deliberately return a Product resource with a key of 10, but only if it belongs to a Customer with a key of 1 then you could also specify that query in the following forms:
~/MyService/Products(10)/Categories?$filter=Product/CustomerFK eq 1
~/MyService/Products(10)/Categories?$filter=Product/Customer/PK eq 1
or you can query the Category controller directly:
~/MyService/Categories?$filter=Products/any(p:p/Customer/PK eq 1 and p/Category/PK eq 10)
Whilst service authors can choose to support multi level navigation as the specification suggests and OP has attempted, it is not always offered out of the box and as such we tend to forget to implement it at all. Its not always hard to do, often we just need to implement some simple routes, but due to the variability of the interpretation and implementation, I personally only ever support navigating 1 level deep but do not allow key selector at the child level.
If the service you are querying does not support it, then simply target the Category resources directly through the Cateogry or indirectly through the Product controller with an appropriate filter.
Related
I know its a question which has been asked hundreds of times before but I'm stressing over the naming scheme I adopt for Controllers in my ASP.Net MVC application.
Say, for example, I have a model called User of which I have the singular ability to view, update and delete. Likewise, I also have a collection of Users which I can search upon giving many results.
As the routing is of the form of {controller}/{action}/{parameter} then my 'English' head thinks that a URL of:
http://localhost/users/view/123 is not correct as the URL relates to a single product whereas:
http://localhost/user/view/123 is correct, as the user is viewing a single product
And likewise, with Product and Products:
http://localhost/product/edit/P123456
http://localhost/products/?searchQuery
This seems to make more sense (well, to me anyway) and thus means that I have to implement two Controllers for each Model but at least this means I know that the singular controller deals with a single entity whereas the plural version deals with multiple entities:
UserController -> Add, Edit, View, Delete
UsersController -> Find, Delete
I guess the only fly in the ointment is when I come across an entity which is the same singularly as plural but within the scope of my project, this could be quite a rare thing.
I'm sorry if this is mindless babble however I'd appreciate some input from users who've gone through this before.
It does not matter. Users are normally not navigation through your site by typing urls, but by clicking links, buttons, menu's, ....
I have the following two Doctrine (ORM) entities:
Product
Category
Category contains one or more segment (level):
category
category/sub-category
Product contains a name and possibly a color. A Product either has a color or not.
product
product/red
I'd like to have both Category pages and Product pages.
The Category URL should be made up as {locale}/{category_path}, e.g.
/en_US/category
/en_US/category/sub-category
The Product URL should be made up as {locale}/{category_path}/{product_path}, e.g.
/en_US/category/product
/en_US/category/product/red
/en_US/category/sub-category/product
/en_US/category/sub-category/product/red
The problem with using Symfony's routing is that there maybe matching confusion between the following routes since they have the same number of segments:
/en_US/category/sub-category
/en_US/category/product
Is this something I can use the CMF DynamicRouter for? If so, at a high level, what pieces do I need to build? Do I need to use a RouteProvider for each Entity?
to use the dynamic router out of the box, your entities need to be able to implement the RouteReferrersReadInterface. That is, they need to return route objects. And you will need to implement the RouteProviderInterface so that you can find the documents in the database.
With the Doctrine ORM, you probably want separate route entities (one per language) that contain the manifested path and link to the products / categories. Afaik the ORM can only link between known entities - one way to use the same route object would be to make category and product share a common base class that is a Mapped Super Class. The product color variants could be done as a variable part of the URL, so you end up with a pattern saying .../{color}
With the CMF route enhancer, you can configure different controllers for category and product pages.
A limited example of ORM routes is provided with the RoutingBundle. You might extend the base entity, or do your own starting with that. The documentation on symfony.com is often first mentioning how to work with the default storage of the CMF, the Doctrine PHPCR-ODM, but things should work with the ORM as well. Open issues on the cmf github project if something does not work.
Is there a general rule of thumb to how controllers should be organised?
Should Controllers only be created if they are linked to a domain model?
For instance if I have a 'Product' model, I would have a ProductController, which would have actions such as 'GetProductDetails' etc...
But what about things that don't have an actual model, such as searching for products, and returning multiple products on a page?
Since the Product model is the underlying model for all these interactions, should this functionality be included into the ProductController and have actions for searching and displaying multiple products, or should another be created for Search?
If you follow the pattern used by the scaffolding used in visual studio then yes you end up with one controller per entity so a product controller would have a actions that returned a list, a single product and an action for posting update to. In addition you might have additional search actions and any other product related actions. Which is just state and reinforce infer-on's answer.
However the reason why you would do this is that it means your code is easier to maintain - if you're looking for code to do with products you have one controller class to look in. You're also adhering to the principle of separation of concerns each controller is only concerned with one type of entity.
Further to this if your app grows much larger and you use an IoC / dependency injection pattern then you only need to inject one repository or business service per controller that is a search controller that offers methods to search for products and customers would need services or repositories for customer and products but a request might only be a customer search so the creation of the products repository was pointless hence you get inefficient and overly complex code. There are patterns to solve this issue but they involve even more code so to avoid this and to keep it simple stick to one root entity one controller.
You should manage every action which involve same resource with same controller, and you should implement that solution in accord with Richardson Maturity Model
A model (developed by Leonard Richardson) that breaks down the
principal elements of a REST approach into three steps. These
introduce resources, http verbs, and hypermedia controls.
so your API will be something like this:
/api/products GET Gets full list of all categories
/api/products/123 GET Gets the details for a single category
/api/products PUT Replaces the entire list of categories with
the one given
/api/products/123 PUT Update the specified category
/api/products POST Creates a new category
/api/products DELETE Deletes all categories
/api/products/123 DELETE Deletes the specified category
I understand that in MVC pattern and in REST services it is common to use URIs like /items/{id} but what is bad thing about using query parameters in the URI?
GET /items/{id} vs GET /items?id={id}
Further, lets say an entity has 'referenceId' field that points to some related (say parent) entity, and I need to create REST service to get all items for parent entity, which way is better:
GET(POST) /items/parent/{parentId}
or
GET(POST) /items?parent={parentId}
Will be grateful for insights that would help to resolve my subjective issues on constructing URLs for REST services.
I would use the following schemes.
/items/id
This uniquely addresses a resource of items with id id. We are not using parameters as a parameter to uniquely address this resource (as is the case with the other option). Just as
miguelcobain suggests.
/parent/id/items
Here id is an id to uniquely address a resource of parent and from those we collect/retrieve the items it references. From what you have said in the question it seems that parent references multiple items, like a container or collection.
The convention I use for this is to narrow down the scope going from left to right. Therefore in case items could be active or inactive. Thusly items have a property or attribute to be active or inactive. Narrowing down on this I get the following scheme:
/items/active
/parent/id/active
For your first question:
/items/{id} should retrieve a single resource with the specified id or 404 if it doesn't exist.
/items/?id={id} should retrieve an array (even if only one in the array) because you are querying the collection.
For your second question:
I agree with #miguelcobain's assessment - if the item is a specific resource/entity, just use the proper resource path to retrieve it.
To make this easier on the consumer, create a link header with rel="parent" and/or include the uri in the child resource. For an example of link headers, see GitHub's pagination api.
Of course, REST principles don't care about aesthetic details on URLs. It just imposes that every resource should be uniquely addressable.
Furthermore, using the query parameters to uniquely address something "kind of" violates the semantics of a "parameter", doesn't it? A parameter should be something optional, something additional and parameterized. Something like a detailed search on a collection of items, for example.
What you wrote may make sense in some cases. It depends.
In your example, is the item really a resource? If not, you could just do GET(POST) /parents/{parentId}.
If parent is, say, a boolean, and you want to search the items that have parent equals to true, then using the parameters makes sense. But since you're explicitly saying that you want a parent with a specific id, I assume that parent is a resource itself and I would uniquely address that resource using your option 1.
I hope I made myself clear.
It seems to me there are no rules to follow.
items/{id} - this convention is suitable for GET item by given id. If user doesn't provide id then it returns 404 status code.
items/id={id}&name={name} - this type of convention is suitable for search multiple items by given criteria. If no items are found, it is not a 404 situation, you simply say "I successfully found nothing matching your search criteria"
I'm having trouble understanding how one would access the sub entities of an aggregate root. From answers to my previous question I now understand that I need to identify the aggregate roots of my model, and then only setup repositories which handle these root objects.
So say I have an Order object that contains Items. Items must exist within and Order so the Order is the aggregate root. But what if I want to include as part of my site an OrderItem details page? The URL to this page may be something like /Order/ItemDetails/1234, where 1234 is the ID of the OrderItem. Yet this would require that I retrieve an Item directly by ID, and because it is not an aggregate root I should not have a OrderItemRepository that can retrive an OrderItem by ID.
Since I want to work with OrderItems independent of an Orders does that imply that OrderItem is not actually an aggregate of Order but another aggregate root?
I don't know your business rules, of course, but I can't think of a case where you would have an orderitem that doesn't have an order. Not saying you wouldn't want to "work with one" by itself, but it still has to have an order, imo, and the order is sort of in charge of the relationship; e.g. you would represent all this by adding or deleting items from an order.
In situations like this, I usually will still require access to the items through the order. It's pretty easy to setup, in URLs I would just do /order/123/item/456. Or, if item ordering is stored / important (which it normally is stored at least indirectly via the order of entry), you could do /order/123/item/1 to retrieve the first item on the order.
In the controller, then, I just retrieve the order from the OrderRepository and then access the appropriate item from there.
All that said, I do agree w/ Arnis that you don't always have to follow this pattern at all. It's a case-by-case thing that you should evaluate the tradeoffs before doing it.
In Your case, I would retrieve OrderItem directly by URL /OrderItem/1234.
I personally don't try to abstract persistence (I don't use repository pattern). Also - I don't follow repository per aggregate root principle. But I do isolate domain model from persistence.
Main reason for that is - it's near-impossible to abstract persistence mechanisms completely. It's a leaky abstraction (e.g. try specifying eager/lazy loading for ORM that lives underneath w/o polluting repository API).
Another reason - it does not matter that much in what way You report data. Reporting part is boring and relatively unimportant. Real value of application is what it can do - automation of processes. So it's much more important how Your application behaves, how it manages to stay consistent, how objects interact etc.
When thinking about this problem, it's good to remember Law of Demeter. The point is - it should be applied only if we explicitly want to hide internals. In Your case - we don't want to hide order items.
So - exploiting fact that we know that entity Ids are globally unique (as opposed to unique only in Order context) it's just a short-cut and there is nothing wrong with retrieving them directly.
Interestingly enough - this can be pushed forward.
Even behavior encapsulation can and should be loosened up too.
E.g. - it makes more sense to have orderItem.EditComments("asdf") than order.EditOrderItemComments(order.OrderItems[0], "asdf").