Joomla router.php - custom urls - php

I have been spending quiet some time figuring out how this works - so thought lets ask the question here.
I do understand how to customize URL's in Joomla with the help of router.php - at least I thought so. It is simple to create something like this
domain.com/country/id
example:
domain.com/germany/12
However, you wouldn't know that the id stands for a city. So in this example lets assume the city with id 12 is Berlin.
So for my custom component (named: countries) I would like that the following is displayed:
for view=countries (1st level)
domain.com/country
i.e.:
domain.com/germany
for view=city (2nd level)
domain.com/country/city-id
i.e.:
domain.com/country/berlin-12
(or perhaps just: domain.com/country/berlin - but I think the ID is required for the custom component to work - and any related modules on the page that read the ID to know what to do)
What do I have so far:
function CountriesBuildRoute(&$query)
{
$segments = array();
//if(isset($query['view'])) {
// $segments[] = $query['view'];
// unset( $query['view'] );
//}
if (isset($query['task'])) {
$segments[] = implode('/',explode('.',$query['task']));
unset($query['task']);
}
if (isset($query['id'])) {
$segments[] = $query['id'];
unset($query['id']);
}
if (isset($query['name'])) {
$segments[] = $query['name'];
unset($query['name']);
}
unset( $query['view'] );
return $segments;
}
function CountriesParseRoute( $segments )
{
$vars = array();
$app =& JFactory::getApplication();
$menu =& $app->getMenu();
$item =& $menu->getActive();
// Count segments
$count = count( $segments );
//Handle View and Identifier
switch( $item->query['view'] )
{
case 'countries':
if($count == 1) {
$vars['view'] = 'city';
}
break;
case 'city':
$id = explode( ':', $segments[$count-2] );
$name = explode( ':', $segments[$count-1] );
$vars['id'] = $id[0].'-'.$name;
break;
}
return $vars;
}
The way I am calling city pages from view countries is the following:
<a href="<?php echo JRoute::_('index.php?option=com_countries&view=city&id=' . (int)$item->id) .'&name='. $item->city_name; ?>">
Would be amazing if someone can help ! Cheers

If you want to get ride of IDs from urls you will have to add every country menu item or create rooter that will search for item id within database (bad idea with big websites). This will also require setting your homepage to one of your component views. Its easiest way.
When you build router you need two functions. First that will return SEF url CountriesBuildRoute and second that will translate SEF url back to query CountriesParseRoute. It is harder then you actually think to write SEF at this level. I will not write you whole router but only point you to right direction.
In Joomla 1.5 it was easier to make smth you want. If you have time look in rooter from some Joomla 1.5 component like (com_weblinks). CountriesBuildRoute returns array that will build your URL. For example when you return $query array looking like this: array('country','berlin') url will look like you want: domain.com/country/berlin. But reversing that process (something you will do in CountriesParseRoute) gonna be harder. You will have to check if first segment is a country (if it is second should be city).
So in function CountriesBuildRoute check what view is passed and build $segments array directly like you want for your url or selected view to be. Remember that single element from that array will be single segment from URL.
In function CountriesParseRoute check if first array element is a country (db checking, cached countries list, there are many ways to do it) then you will have to do the same with second element from array(if it exists).
I always created BuildRoute first as I wanted. Then spend hours on making parse route as precise and effective as only could be. You can spend hours or even few days if you want to make good router.

Related

Adding a Secondary Root URL to WordPress Rewrites

I'm currently working on a project that has requirements of having multiple paths to the same contents. The project will have 4 different brands, for the sake of argument, we'll call those brand1, brand2, brand3, and brand4.
The desire is to generate rewrite rules for WordPress so that all of the following would work:
http://primaryurl.com/page-1 (Brand 1 is the base URL)
http://primaryurl.com/brand2/page-1
http://primaryurl.com/brand3/page-1
http://primaryurl.com/brand4/page-2
This would also be true of any custom post types that exists, posts, etc.
I've dug into WP_Rewrite a bit but haven't quite figured out how to solve this challenge and feel like I might just be missing something.
I believe this could be accomplished via a ton of add_rewrite_rule() calls but feel like there has to be a better way.
Thanks in advance for any advice on how to tackle this.
I wound up solving this with the following:
$brands = array( 'brand2','brand3','brand4' );
add_action( 'generate_rewrite_rules', function( $wp_rewrite ) {
global $brands;
$new_rewrite_rules = array();
foreach ($brands as $brand) {
foreach ($wp_rewrite->rules as $regex => $redirect) {
$new_rewrite_rules[$brand . '/' . $regex] = $redirect;
}
}
//All rewrite rules are expected to be set at this moment
$wp_rewrite->rules = array_merge($new_rewrite_rules, $wp_rewrite->rules);
} );

Trying to highlight current menu item in Drupal 8 using hook_preprocess_menu

I have the below function to create active trail functionality. So if I were to have /blog as a "parent" and a post of /blog/mypost, when on mypost the blog link would show as highlighted. I don't want to have to make menu items for all the blog posts. The problem is when caching is turned on (not using settings.local.php and debug turned off) the getRequestUri isn't changing on some pages. It seems to be cached depending on the page. It works fine with page caching turned off but I'd like to get this working with caching. Is there a better way to check for the current path and apply the active class?
function mytheme_preprocess_menu(&$variables, $hook) {
if($variables['theme_hook_original'] == 'menu__main'){
$node = \Drupal::routeMatch()->getParameter('node');
if($node){
$current_path = \Drupal::request()->getRequestUri();
$items = $variables['items'];
foreach ($items as $key => $item) {
// If current path starts with a part of another path i.e. a parent, set active to li.
if (0 === strpos($current_path, $item['url']->toString())) {
// Add active link.
$variables['items'][$key]['attributes']['class'] .= ' menu-item--active-trail';
}
}
}
}
}
I've also tried putting this into a module to try and see if I can get the current path to then do the twig logic in the menu--main.twig.html template but I have the same problem.
function highlight_menu_sections_template_preprocess_default_variables_alter(&$variables) {
$variables['current_path'] = $_SERVER['REQUEST_URI'];
}
After a very long time trying all sorts of things, I found an excellent module which addresses exactly this problem. Install and go, not configuration, it just works:
https://www.drupal.org/project/menu_trail_by_path
Stable versions for D7 and D8.
I tried declaring an active path as part of a custom menu block, and even then my declared trail gets cached. Assuming it's related to the "There is no way to set the active link - override the service if you need more control." statement in this changelog, though why MenuTreeParameters->setActiveTrail() exists is anybody's guess.
For the curious (and for me when I search for this later!), here's my block's build() function:
public function build() {
$menu_tree = \Drupal::menuTree();
$parameters = new MenuTreeParameters();
$parameters->setRoot('menu_link_content:700c69e6-785b-4db7-be49-73188b47b5a3')->setMinDepth(1)->setMaxDepth(1)->onlyEnabledLinks();
// An array of routes and menu_link_content ids to set as active
$define_active_mlid = array(
'view.press_releases.page_1' => 385
);
$route_name = \Drupal::request()->get(RouteObjectInterface::ROUTE_NAME);
if (array_key_exists($route_name, $define_active_mlid)) {
$menu_link = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(array('id' => $define_active_mlid[$route_name]));
$link = array_shift($menu_link);
$parameters->setActiveTrail(array('menu_link_content:' . $link->uuid()));
}
$footer_tree = $menu_tree->load('footer', $parameters);
$manipulators = array(
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $menu_tree->transform($footer_tree, $manipulators);
$menu = $menu_tree->build($tree);
return array(
'menu' => $menu,
);
}
[adding a new answer since this is a completely different approach than my earlier one]
If a CSS-based solution is acceptable, this seems to work okay:
.page-node-type-press-release {
a[data-drupal-link-system-path="press-room/press-releases"] {
// active CSS styles here
}
}

XML Url parameters+REST

A little more than a month ago I did a post involving the extraction the values of from a URL.
https://stackoverflow.com/questions/21231390/extracting-the-values-from-a-xml-url
It was a good answer and helped me a lot, but is there a way to generalize the code for any parameter given? The testing parameters won't be part of a formula, and this plugin I'm developing is supposed to be handled by Moodle teachers. They won't have access to my code to manually alter the parameters themselves. Here is the link.
By changing the rs parameter, I change the course. p(even/odd) and tp(semestral/anual/trimestral courses) parameters are about semesters and year is the year.
This is what I currently have.
function buildURL($year, $period, $typeperiod,$course)
{
return 'https://clip.unl.pt/sprs?lg=pt&year='.$year.'&uo=97747&srv=rsu&p='.$period.'&tp='.$typeperiod.'&md=3&rs='.$course.'&it=1030123459';
}
function doRequest_with_FileGetContents($url)
{
return download_file_content($url);
}
function processXML($xmlContent)
{
$xmlObj= new SimpleXMLElement($xmlContent);
$result=array();
foreach($xmlObj->unidade_curricular->inscritos->aluno as $aluno)
{
$result[]= $aluno->identificador;
}
return $result;
}
Testing parameters:
$year='2014';
$period='1';
$typeperiod='s';
$course='8145';
$url=buildURL($year,$period,$typeperiod,$course);
$content_b = doRequest_with_FileGetContents($url);
$dataClip = processXML($content_b);
I've tried to associate the id number of the course (course / course edit Settings/course id number) to the parameter rs, by typing:
$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
$idnumber = $course->idnumber;
and replace it in $url=buildURL($year,$period,$typeperiod,$idnumber); but the XML doesn't even process. Is this even possible to pull it off without a formal interface?

Make SEO sensitive URL (avoid id) Zend framework

i have url like this :
http://quickstart.local/public/category1/product2
and in url (category1/product2) numbers are id , categorys and products fetched from database attention to the id
id is unique
i need to the sensitive url like zend framework url. for example :http://stackoverflow.com/questions/621380/seo-url-structure
how i can convert that url to the new url like this
is there any way?!!
You'll need to store a unique value in your database with a field name such as 'url' or something similar. Every time you generate a new product you will have to create this unique url and store it with the product information. A common way to do this is to take the name of the product and make it url friendly:
public function generateUrl($name)
{
$alias = str_replace(' ', '-', strtolower(trim($name)));
return preg_replace('/[^A-Za-z0-9-]/', '', $alias);
}
Calling this method:
$url = $this->generateUrl("My amazing product!");
echo $url;
will output:
my-amazing-product
You'll need to check that the output from this function does not already exist in the database as you will use this value to query on instead of the id.
If you apply this logic to the categories as well, you can have easily readable and descriptive urls like the one below. You may need to tweak your routing before this works correctly though.
http://quickstart.local/public/awesome-stuff/my-amazing-product
You could use ZF's Zend_Controller_Router_Route. For example, to make similar url to those used by SO, one could define a custom route in an application.ini as follows (assuming you have controller and action called questions and show respectively):
resources.router.routes.questions.route = '/questions/:id/:title'
resources.router.routes.questions.type = "Zend_Controller_Router_Route"
resources.router.routes.questions.defaults.module = default
resources.router.routes.questions.defaults.controller = questions
resources.router.routes.questions.defaults.action = show
resources.router.routes.questions.defaults.id =
resources.router.routes.questions.defaults.title =
resources.router.routes.questions.reqs.id = "\d+"
Having such a route, in your views you could generate an url as follows:
<?php echo $this->url(array('id'=>621380,'title' => 'seo url structure'),'questions');
// results in: /myapp/public/questions/621380/seo+url+structure
//OR if you really want to have dashes in your title:
<?php echo $this->url(array('id'=>621380,'title' => preg_replace('/\s+/','-','seo url structure'),'questions');
// results in: /myapp/public/questions/621380/seo-url-structure
Note that /myapp/public/ is in the url generated because I don't have virtual hosts setup on my localhost nor any modifications of .htaccess made. Also note that you don't need to have unique :title, because your real id is in :id variable.
As a side note, if you wanted to make it slightly more user friendly, it would be better to have your url as /question/621380/see-url-structure rather than /questions/621380/see-url-structure. This is because under this url you would have only one question, not many questions. This could be simply done by changing the route to the following resources.router.routes.questions.route = '/question/:id/:title'.
EDIT:
And what to do with categories and products that you have in your question? So, I would define a custom route, but this time using Zend_Controller_Router_Route_Regex:
resources.router.routes.questions.route = '/questions/(\d+)-(d+)/(\w*)'
resources.router.routes.questions.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.questions.defaults.module = default
resources.router.routes.questions.defaults.controller = questions
resources.router.routes.questions.defaults.action = show
resources.router.routes.questions.map.1 = category
resources.router.routes.questions.map.2 = product
resources.router.routes.questions.map.3 = title
resources.router.routes.questions.reverse = "questions/%d-%d/%s"
The url for this route would be then generated:
<?php echo $this->url(array('category' => 6213,'product' => 80,'title' => preg_replace('/\s+/', '-', 'seo url structure')),'questions' ); ?>
// results in: /myapp/public/questions/6213-80/seo-url-structure
Hope this will help or at least point you in the right direction.

Site search with CodeIgniter?

I need to make a simple site search with pagination in it; could anyone tell me how to do it without affecting the URL structure? Currently I'm using the default CodeIgniter URL structure and I have removed index.php from it. Any suggestions?
You could just use a url like /search/search_term/page_number.
Set your route like this:
$route['search/:any'] = "search/index";
And your controller like this:
function index()
{
$search_term = $this->uri->rsegment(3);
$page = ( ! $this->uri->rsegment(4)) ? 1 : $this->uri->rsegment(4);
// some VALIDATION and then do your search
}
Just to update this question. It is probably best to use the following function:
$uri = $this->uri->uri_to_assoc()
and the result will then put everything into an associative array like so:
[array]
(
'name' => 'joe'
'location' => 'UK'
'gender' => 'male'
)
Read more about the URI Class at CodeIgniter.com
Don't quite understand what you mean by "affecting the url structure". Do you mean you'd want pagination to occur without the URL changing at all?
The standard pagination class in CI would allow you to setup pagination so that the only change in the URL would be a number on the end
e.g if you had 5 results to a page your urls might be
http://www.example.com/searchresults
and then page 2 would be
http://www.example.com/searchresults/5
and page 3 would be
http://www.example.com/searchresults/10
and so on.
If you wanted to do it without any change to the URL then use ajax I guess.
Code Igniter disables GET queries by default, but you can build an alternative if you want the url to show the search string.
Your url can be in the notation
www.yoursite.com/index.php/class/function/request1:value1/request2:value2
$request = getRequests();
echo $request['request1'];
echo $request['request2'];
function getRequests()
{
//get the default object
$CI =& get_instance();
//declare an array of request and add add basic page info
$requestArray = array();
$requests = $CI->uri->segment_array();
foreach ($requests as $request)
{
$pos = strrpos($request, ':');
if($pos >0)
{
list($key,$value)=explode(':', $request);
if(!empty($value) || $value='') $requestArray[$key]=$value;
}
}
return $requestArray ;
}
source: http://codeigniter.com/wiki/alternative_to_GET/

Categories