{"action":"created","issue":{"url":"https://api.github.com/repos/joomla/joomla-cms/issues/5444","labels_url":"https://api.github.com/repos/joomla/joomla-cms/issues/5444/labels{/name}","comments_url":"https://api.github.com/repos/joomla/joomla-cms/issues/5444/comments","events_url":"https://api.github.com/repos/joomla/joomla-cms/issues/5444/events","html_url":"https://github.com/joomla/joomla-cms/pull/5444","id":52142903,"number":5444,"title":"Routing: Implementing JComponentRouterAdvanced","user":{"login":"Hackwar","id":313866,"avatar_url":"https://avatars.githubusercontent.com/u/313866?v=3","gravatar_id":"","url":"https://api.github.com/users/Hackwar","html_url":"https://github.com/Hackwar","followers_url":"https://api.github.com/users/Hackwar/followers","following_url":"https://api.github.com/users/Hackwar/following{/other_user}","gists_url":"https://api.github.com/users/Hackwar/gists{/gist_id}","starred_url":"https://api.github.com/users/Hackwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Hackwar/subscriptions","organizations_url":"https://api.github.com/users/Hackwar/orgs","repos_url":"https://api.github.com/users/Hackwar/repos","events_url":"https://api.github.com/users/Hackwar/events{/privacy}","received_events_url":"https://api.github.com/users/Hackwar/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/joomla/joomla-cms/labels/PR-staging","name":"PR-staging","color":"c7def8"}],"state":"closed","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/joomla/joomla-cms/milestones/7","html_url":"https://github.com/joomla/joomla-cms/milestones/Joomla!%203.5.0","labels_url":"https://api.github.com/repos/joomla/joomla-cms/milestones/7/labels","id":944889,"number":7,"title":"Joomla! 3.5.0","description":"","creator":{"login":"rdeutz","id":467356,"avatar_url":"https://avatars.githubusercontent.com/u/467356?v=3","gravatar_id":"","url":"https://api.github.com/users/rdeutz","html_url":"https://github.com/rdeutz","followers_url":"https://api.github.com/users/rdeutz/followers","following_url":"https://api.github.com/users/rdeutz/following{/other_user}","gists_url":"https://api.github.com/users/rdeutz/gists{/gist_id}","starred_url":"https://api.github.com/users/rdeutz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rdeutz/subscriptions","organizations_url":"https://api.github.com/users/rdeutz/orgs","repos_url":"https://api.github.com/users/rdeutz/repos","events_url":"https://api.github.com/users/rdeutz/events{/privacy}","received_events_url":"https://api.github.com/users/rdeutz/received_events","type":"User","site_admin":false},"open_issues":20,"closed_issues":4,"state":"open","created_at":"2015-01-21T08:12:44Z","updated_at":"2015-03-26T00:04:31Z","due_on":null,"closed_at":null},"comments":32,"created_at":"2014-12-16T17:35:25Z","updated_at":"2015-03-26T07:35:09Z","closed_at":"2015-03-26T00:04:31Z","pull_request":{"url":"https://api.github.com/repos/joomla/joomla-cms/pulls/5444","html_url":"https://github.com/joomla/joomla-cms/pull/5444","diff_url":"https://github.com/joomla/joomla-cms/pull/5444.diff","patch_url":"https://github.com/joomla/joomla-cms/pull/5444.patch"},"body":"This PR implements a new base class for component routers, which can be used to create rules based routers that are dynamically extendable.\r\n\r\n### The idea\r\nComponents that stick to the Joomla MVC style should be able to create a fully working router with as little work as possible. At the same time, component routers should only handle views, since those are the only ones that you should be able to link to. If you have something that is not a view, it should execute and then redirect to a view again. This means that we only have to create SEF URLs for views. Views are pretty well defined, too.\r\n\r\n### How it works\r\nA router extends from this class and in its constructor, it registers all its views with a bunch of its data and the hierarchy between them. The idea is, that views form a tree hierarchy, where traversing along the tree towards the leafs gets you higher and higher in the hierarchy. In case of com_content, the articles and its article view would represent the leafs of the tree, the categories with their category view represent the nodes of the tree and the categories view, which displays all categories, would represent the root of the tree.\r\nSo we already have a kind of hierarchy in our views and we register them accordingly in our router. We start with the categories view. That has no id, has no parent view and is not nestable. Then we have the category view, which has the ID \"id\" in the query, has the categories view as a parent view and that view is nestable. This means that while traversing along the above described tree, we can have a node that is a category view and its child-nodes are also category views.\r\nBased on this, we can then find the right Itemid for example (by starting from the node given by our query, for example the article view and its ID and traversing along the tree to its root, all the while comparing the menu items that we have with the current node that we are in) and/or add the category hierarchy from that menu item to the URL and last but not least add the article slug to it.\r\nSince we are not registering \"we have categories and content items\" but a flexible hierarchy, you could in theory go as far as having categories that contain tags which again contain content items and those contain something else again. So the hierarchy is not limited to the category/content-item system that we have in Joomla right now, but allows for a lot more.\r\n\r\nA specific example could look like this:\r\n```php\r\n\tpublic function __construct()\r\n\t{\r\n\t\t$categories = new JComponentRouterViewconfiguration('categories');\r\n\t\t$this->registerView($categories);\r\n\t\t$category = new JComponentRouterViewconfiguration('category');\r\n\t\t$category->setKey('id')->setParent($categories)->setNestable()->addLayout('blog');\r\n\t\t$this->registerView($category);\r\n\t\t$article = new JComponentRouterViewconfiguration('article');\r\n\t\t$article->setKey('id')->setParent($category, 'catid');\r\n\t\t$this->registerView($article);\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('archive'));\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('featured'));\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('form'));\r\n\r\n\t\t/** Insert adding rules to process the URLs here **/\r\n\t}\r\n```\r\n\r\n### The rules\r\nBased on the hierarchy that you have to define above, we can then write rules that process the input query to transform it to the SEF URL that we want. You can see a rule that finds the right Itemid for a query in #5446. This means, that we don't need a component specific code for this in the ContentHelperRoute classes.\r\nSimilarly the current routers behavior can be combined into a generalized rule that works of of the hierarchy that was described above. That means, that we can summarize all the code in our core routers in those rules and instead of having 11 component routers, we would only have one rule that implements this specific logic. You can see this in #5533.\r\nI have to admit, those rules get pretty complex and difficult to maintain, but it would mean that no component developer needs to write his own router anymore, but can use the core rules instead.\r\n\r\n### The component router\r\nA specific component router could simply consist of the code that I posted above. Adding a plugin event to it and some further logic, and we would specifically only have this:\r\n```php\r\n/**\r\n * Content Router\r\n */\r\nclass ContentRouter extends JComponentRouterAdvanced\r\n{\r\n\tfunction __construct()\r\n\t{\r\n\t\t$categories = new JComponentRouterViewconfiguration('categories');\r\n\t\t$this->registerView($categories);\r\n\t\t$category = new JComponentRouterViewconfiguration('category');\r\n\t\t$category->setKey('id')->setParent($categories)->setNestable()->addLayout('blog');\r\n\t\t$this->registerView($category);\r\n\t\t$article = new JComponentRouterViewconfiguration('article');\r\n\t\t$article->setKey('id')->setParent($category, 'catid');\r\n\t\t$this->registerView($article);\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('archive'));\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('featured'));\r\n\t\t$this->registerView(new JComponentRouterViewconfiguration('form'));\r\n\t\tparent::__construct();\r\n\t}\r\n}\r\n```\r\nThis would be all that is needed for our core routers. There are however some things that I would propose for the future and that would allow to make the system more robust and a bit more flexible.\r\nIn order for the system to be able to retrieve the right data, I think it would be good to introduce router specific getters for things like the article ID/slug. So a com_content router would have a method getArticle() that either returns the ID of the article that the slug and the category ID belongs to or that returns the ID with the slug attached, based on the input. I'm not sure about the correct interface here yet. The getCategory() method is a generalized version for all category related views. The system that I have in mind expects an interface like that from JCategoryNode for all nestable views. Since I don't like the JCategoryNode implementation very much anymore, since its too category specific, I started a discussion on a node interface in #5227. Feel free to join in there for this part.\r\n\r\n### getPath()\r\nThe getPath() method in this class returns a path of views from the node/leaf that is given by the query to the root of the tree-hierarchy. This allows us to simply check if each entry for example corresponds to a menu item and thus set the right Itemid or to add a segment to our url from that matching menu-path item to our original node/leaf defined by the query. It's a helper method that I think a lot of rules will make use of.\r\n\r\n### Current issues\r\nSince there is no code yet in this PR to test this with, I have to point you to #5446 and #5533. I'd also like to ask for a code-review by the maintainers and merging this based on that code review.\r\n\r\nThis was made possible through the generous donation of the people mentioned in the following link via an Indiegogo campaign: http://joomlager.de/crowdfunding/5-contributors"},"comment":{"url":"https://api.github.com/repos/joomla/joomla-cms/issues/comments/86372583","html_url":"https://github.com/joomla/joomla-cms/pull/5444#issuecomment-86372583","issue_url":"https://api.github.com/repos/joomla/joomla-cms/issues/5444","id":86372583,"user":{"login":"Hackwar","id":313866,"avatar_url":"https://avatars.githubusercontent.com/u/313866?v=3","gravatar_id":"","url":"https://api.github.com/users/Hackwar","html_url":"https://github.com/Hackwar","followers_url":"https://api.github.com/users/Hackwar/followers","following_url":"https://api.github.com/users/Hackwar/following{/other_user}","gists_url":"https://api.github.com/users/Hackwar/gists{/gist_id}","starred_url":"https://api.github.com/users/Hackwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Hackwar/subscriptions","organizations_url":"https://api.github.com/users/Hackwar/orgs","repos_url":"https://api.github.com/users/Hackwar/repos","events_url":"https://api.github.com/users/Hackwar/events{/privacy}","received_events_url":"https://api.github.com/users/Hackwar/received_events","type":"User","site_admin":false},"created_at":"2015-03-26T07:35:09Z","updated_at":"2015-03-26T07:35:09Z","body":"Thank you!!"}}