a-ok Posted May 5, 2017 Share Posted May 5, 2017 I've been creating categories and tags via Pages and PageFields for a while now but the thing that has always bugged me is, if for example I have a section 'News' (/news/) and a page parent/child for news items 'News (categories)' (/news-categories/) then if you filter by a category then the URL slug is /news-categories/architecture/, for example... whereas it would be nice if the URL slug was /news/categories/architecture or similar. I know this creates a few anomalies because the categories aren't actually a child page of /news/ but I think it's a lot cleaner and makes more sense visually. I've been trying to think of the best way to achieve this. I could do a rewrite... or I could place the hidden page for categories within /news/ and work from there? I just wasn't sure if there was a solution or best practice that anyone on here has used before? I tend to have a few different category pages for different sections (News, Projects etc). Thanks guys! What I currently do: News ---Article 1 ---Article 2 News (categories) HIDDEN ---Category 1 ---Category 2 Would this work? News ---Article 1 ---Article 2 ---Category HIDDEN ------Category 1 ------Category 2 Link to comment Share on other sites More sharing options...
abdus Posted May 5, 2017 Share Posted May 5, 2017 You can use url segments to achieve this. 2 Link to comment Share on other sites More sharing options...
Macrura Posted May 5, 2017 Share Posted May 5, 2017 usually for areas like news, blog, events, the best practice is to check for the URL segments as mentioned by abdus. You can look at the blog module or blog profile to possibly see some functions for how to intercept the segment, and display the filtered posts. 2 Link to comment Share on other sites More sharing options...
Robin S Posted May 5, 2017 Share Posted May 5, 2017 Just adding to what abdus and Macrura have said: you use URL segments to do this, but you don't need to hook page paths or render any different page. Your category pages can use a template with only a title field and with no corresponding template file - these category pages only exist for the purpose of being selected in a Page Reference field in your news article pages. In your 'news' template (the template of the parent page of the news articles) you check for a URL segment and if there is one you use that in a selector to match only news articles that have that category selected. You can do a kind of sanitizing of the URL segment by first checking if there is any page by that name using your category template or under your category parent - if there isn't then you throw a 404. 3 Link to comment Share on other sites More sharing options...
a-ok Posted May 5, 2017 Author Share Posted May 5, 2017 Thanks all. Super appreciated. I have added 'category' as a URL segment (see attached) so in theory anything /news/category/ should work now. However, I'm guessing I still need to include a rewrite on the category template? I had a look at the other forum posts from ryan and noticed this... if ($input->urlSegment1 == 'contact') { // render the /about/contact/ page echo $pages->get('/about/contact/')->render(); return; } else if($input->urlSegment1) { // throw a 404 throw new Wire404Exception(); } But I am unsure how I can harness this? Link to comment Share on other sites More sharing options...
Robin S Posted May 5, 2017 Share Posted May 5, 2017 5 hours ago, oma said: I have added 'category' as a URL segment (see attached) so in theory anything /news/category/ should work now. No, doing this would only allow the exact URL segment 'category'. Instead, don't place any restrictions on the segment in the template settings and do your segment validation inside the 'news' template. 5 hours ago, oma said: However, I'm guessing I still need to include a rewrite on the category template? Rather than rewrite or render anything from a category page you can handle all the output within your 'news' template. Here is an example from a recent project: $limit = 5; $news_selector = "template=news_item, post_date<=today, limit=$limit, sort=sort"; $categories = $pages(1139)->children(); $segment_1 = $sanitizer->pageName($input->urlSegment1, true); // URL segments are already sanitized as page names but not to lowercase if($segment_1) { $current_category = $categories->get("name=$segment_1"); if($current_category) { // the segment is a valid news category $news_selector .= ", news_category=$current_category"; $page_title = "$current_category->title news"; } else { // the segment is invalid so throw 404 throw new Wire404Exception(); } } $news_items = $pages->find($news_selector); $total_pages = ceil($news_items->getTotal() / $limit); The effect of this is that if there is a valid URL segment then only news items from that category are listed, but if there is no URL segment then all news items are listed. 7 Link to comment Share on other sites More sharing options...
a-ok Posted May 6, 2017 Author Share Posted May 6, 2017 15 hours ago, Robin S said: No, doing this would only allow the exact URL segment 'category'. Instead, don't place any restrictions on the segment in the template settings and do your segment validation inside the 'news' template. Rather than rewrite or render anything from a category page you can handle all the output within your 'news' template. Here is an example from a recent project: $limit = 5; $news_selector = "template=news_item, post_date<=today, limit=$limit, sort=sort"; $categories = $pages(1139)->children(); $segment_1 = $sanitizer->pageName($input->urlSegment1, true); // URL segments are already sanitized as page names but not to lowercase if($segment_1) { $current_category = $categories->get("name=$segment_1"); if($current_category) { // the segment is a valid news category $news_selector .= ", news_category=$current_category"; $page_title = "$current_category->title news"; } else { // the segment is invalid so throw 404 throw new Wire404Exception(); } } $news_items = $pages->find($news_selector); $total_pages = ceil($news_items->getTotal() / $limit); The effect of this is that if there is a valid URL segment then only news items from that category are listed, but if there is no URL segment then all news items are listed. This is special. Thanks, Robin. I had no idea this was the best approach and makes a lot of sense (categories still exist in their own page tree outside news but can exist within via the URL). Thanks to everyone else for chiming in too! 1 Link to comment Share on other sites More sharing options...
a-ok Posted November 22, 2017 Author Share Posted November 22, 2017 Sorry to bring this up again but felt it was best to keep it under the same topic. I'm wondering if it's possible to do the same with multiple categories? Obviously this line would need to change... $current_category = $categories->get("name=$segment_1"); Any thoughts? Obviously the categories returns would be piped (for example, music|art) is that right? I don't mind using ids either... but just curious to know if you think it's possible. Link to comment Share on other sites More sharing options...
a-ok Posted November 26, 2017 Author Share Posted November 26, 2017 I’m guessing this isn’t possible. I’ve had a bit of a play and would need the URL to allow for more than one word in one segment and don’t think that’s possible? Link to comment Share on other sites More sharing options...
Robin S Posted November 26, 2017 Share Posted November 26, 2017 On 23/11/2017 at 9:52 AM, oma said: I'm wondering if it's possible to do the same with multiple categories? A URL segment can include any characters that are valid in a page name. Quote Page names by default support lowercase ASCII letters, digits, underscore, hyphen and period. So you could separate your category names with an underscore or period perhaps, then explode on that character and match each category to pages. But you'd be sort of fighting against the workings of URL segments so why not use a GET variable instead? Then you can separate your categories with any character you want... http://yourdomain.com/your-page/?categories=one|two ...and support for arrays is actually built-in... http://yourdomain.com/your-page/?categories[]=one&categories[]=two 2 Link to comment Share on other sites More sharing options...
szabesz Posted November 27, 2017 Share Posted November 27, 2017 I opted for this one 7 hours ago, Robin S said: So you could separate your category names with an underscore or period perhaps, then explode on that character and match each category to pages. here: https://www.szepelet.com/products/product-categories_cleansers/ for example:product-categories_body-care vsskin-concerns_body-care Where product-categories and skin-concerns are parent categories while body-care and body-care are different subcategories under their respective parents. However, these categories are sort of set in stone, meaning that they rarely change – if ever – and not manageable by the site editor by design. 2 Link to comment Share on other sites More sharing options...
a-ok Posted November 27, 2017 Author Share Posted November 27, 2017 12 hours ago, Robin S said: A URL segment can include any characters that are valid in a page name. So you could separate your category names with an underscore or period perhaps, then explode on that character and match each category to pages. But you'd be sort of fighting against the workings of URL segments so why not use a GET variable instead? Then you can separate your categories with any character you want... http://yourdomain.com/your-page/?categories=one|two ...and support for arrays is actually built-in... http://yourdomain.com/your-page/?categories[]=one&categories[]=two Thanks, Robin. I have come up with the following on my overview template (replacing the setup for URL segments and replacing it with a GET setup) $limit = 18; $query = "template=where-to-go-detail|our-guides-detail, sort=sort, limit=$limit"; if ($input->get("tags")) { var_dump($input->get("tags")); $currentTags = $input->get("tags"); $currentTags = $sanitizer->text($currentTags); $query .= ", tags={$currentTags}"; $articles = $pages->find($query); } else { $articles = $pages->find($query); } What do you think? Looks good? I would then use isset($currentTags) to check if a tag has been queried. Link to comment Share on other sites More sharing options...
a-ok Posted November 27, 2017 Author Share Posted November 27, 2017 What I can't work out is why this wouldn't work? $limit = 18; $articles = $pages->find("template=where-to-go-detail|our-guides-detail, sort=sort, limit=$limit"); if ($input->get("tags")) { // If GET ?tags= exists... $currentTags = $input->get("tags"); $currentTags = $sanitizer->text($currentTags); $articles->filter("tags={$currentTags}"); // Append original query with the tags selector if (count($articles)) { continue; } else { throw new Wire404Exception(); } } Link to comment Share on other sites More sharing options...
a-ok Posted November 27, 2017 Author Share Posted November 27, 2017 I believe when using 'filter->()' it's using IDs? So I had to do... $limit = 18; $articles = $pages->find("template=where-to-go-detail|our-guides-detail, sort=sort, limit=$limit"); if ($input->get("tags")) { // If GET ?tags= exists... $currentTags = $input->get("tags"); $currentTags = $sanitizer->text($currentTags); $articles->filter("tags.name=$currentTags"); // Append original query with the tags selector if (!count($articles)) throw new Wire404Exception(); } Link to comment Share on other sites More sharing options...
Robin S Posted November 27, 2017 Share Posted November 27, 2017 9 hours ago, oma said: I have come up with the following on my overview template (replacing the setup for URL segments and replacing it with a GET setup) $limit = 18; $query = "template=where-to-go-detail|our-guides-detail, sort=sort, limit=$limit"; if ($input->get("tags")) { var_dump($input->get("tags")); $currentTags = $input->get("tags"); $currentTags = $sanitizer->text($currentTags); $query .= ", tags={$currentTags}"; $articles = $pages->find($query); } else { $articles = $pages->find($query); } This is a better way to go than your later examples. It's more efficient to use the GET tags as part of the $pages->find() selector than to find a larger number of pages and then filter it. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now