I'm trying to figure out what the best way to go about rewriting urls to accommodate not only a directory structure, but appended actions for a menu for a site I'm building in PHP.
I have one page that dynamically loads content based on whatever "page" is being loaded. Includes are pulled based on what the page is. Right now that's tied to directories in my includes folder. So folder/page will include includes/folder/page.php.
If I have a structure like "root/page/action/param" that works fine as long as I say "whatever is in this place in the url is this." But I ran into trouble when I started adding folders that hold different levels of pages. If I explode page out to "folder/subfolder/page" then using placement to determine what is what goes out the window. I was thinking about using something like a hyphen as a delimiter to go "root/folder-subfolder1-subfolder2-page/action/param" so I can identify the page easily but I don't really like that option.
I also don't really like tying my includes directory structure to my urls but the only other way I would think to go about avoiding that would be to have an in-memory object to handle what exactly goes where. But I want to avoid that as I want the menu to be dynamic, based on an XML file that gets parsed and urls generated accordingly. I don't want the overhead of parsing the xml and not only generating URLs but also dynamically generating an in-memory object that has seemingly arbitrary rules.
Any suggestions?
PHPonTrax uses an implementation that I think would allow for this type of routing that you want, and it's open source so you could look at the code for some inspiration. (Note: I am not making any judgement as to the framework, I just have seen the code and think it might work for you).
Basically, you store your routes in an array and you provide some placeholder's as well so that you can have multiple possible controllers/actions/ids after a given path.
So if $routes[] is our array to hold everything, we could add:
$routes[0]['path'] = "foo/bar/:controller/:action";
$routes[0]['params'] = null;
$routes[1]['path'] = "baz/:action";
$routes[1]['params'] = array("controller"=>"blah");
$routes[2]['path'] = "catalog/:id";
$routes[2]['params'] = array("controller"=>"products", "action"=>"view");
$routes[3]['path'] = ":controller/:action/:id";
$routes[3]['params'] = null;
":controller", ":action", and ":id" are unique tokens that indicate what you are allowing a token to represent in the path. So "/baz/edit", "/baz/delete", "/baz/create", "/baz/funkify", etc are all valid provided controller "blah" has edit, delete, create, and funkify methods. If the path is "/catalog/1234", then '1234' is the id and it's a valid path if and only if you can find a product with id=1234. If your request is to "/products/dothis/12345", routes 0-2 don't match, but route 3 does, so you can then look for a controller file named "products", and provided it has a method named "dothis", then you can call "dothis(12345)". Obviously, if there's no "dothis" method, or '12345' can't be found, it's an invalid path and you can raise an exception. Note you need to provide a default route, and in this case it's $routes[3].
In your file structure, you can have:
/app
/controllers
/views
/etc, etc
Possible drawback is that in your rewrite, you send every request to one php script.
If this interests you: PHPonTrax. You will want to look at the router class and action_controller class (see the process_route() and recognize_route() methods).
Use Absolute Paths instead of relative in your menu html & scripts and you shouldn't have problems as it will always point to correct source.
Absolute Path Example (notice the /):
Home
Services
My Page
My Page 2
Related
I am working on a site and the builders have used a mix of php and html for links. For example:
<li>Variable Speed Drives</li>
<li>Corrosion Resistant Baseplates</li>
and
<li>MP Repair</li>
<li>MTA Repair</li>
The php is referenced in another file in this way:
<?php
$pdf_link = "../pdf/";
$external_pdf_link = "../../pdf/";
$video_link = "../video/";
$external_video_link = "../../video/";
?>
My concern is not knowing the function of the php, other than it being a placeholder, and given that the links work both ways, I don't want to break something because I am clueless to its purpose.
In doing my due diligence researching, I ran across this post, which is close, but still no cigar, Add php variable inside echo statement as href link address?. All of the research seems to be about how rather than why. This is the site, and they only used it for the "Downloads" links: http://magnatexpumps.com/
Thank you...
B
There is no right way. They are just different.
Let's forget the PHP for a while. If you have this link in a page:
<a href='about.html'/>About</a>
What will happen? The browser will change the URL of the document. If you are at the root of the site like: "www.example.com", will redirect to "www.example.com/about.html". If you are in a URL like "www.example.com/news/index.html" will redirect you to "www.example.com/new/about". That's why sometimes it is useful to have a variable before, to force a full path URL.
Another case of URL variable interpolation is when you have different systems running in the same url. In this case, you will have to append the system name in order to get to where you want. If you don't know where your application will run if it will run on the doc root, or in a subfolder, use a variable to indicate the base path.
I'm new to the world of API programming, I just have a bit of a side project at work at the moment and I'm learning as I write, so bear with me.
I'm unsure as to the best way to implement an API for multiple different functions. At the moment I just have a test script I run and an apache redirect that redirects anything under /api to this script, call it TestAPI.php (so /api/anything will redirect). I pass the path variable of the API to the script (so in that example the path would be 'anything').
At the moment I'm just writing it for 1 purpose, to look up some data based on the path, and eventually be about to update and delete etc with PUT/DELETE etc (it's restISH not restFUL). This is fine at the moment where everything redirects to this script, but what about if I need 2 different functions? So I want to look up a different data set? So for example now /api/data1 would go to the 1st set and /api/data2 would go to the second. This is where I start to get unsure.
Do I simply have 1 mega script that grows and grows so that /api/data1 and /api/data2 redirect to the same place (and thus handle any errors like 404s there). Or do I have a script for /api/data1 and /api/data2 with separate redirects to each, then a generic catchall for 404s (I would always like to return JSON/XML rather than HTML for a 404, so I need at least logic to return based on the Accept header).
As a 3rd option, do I have some sort of frontline controller that catches everything, then calls off to the sub components? So 1 script that is redirects to for anything under /api, which then calls off to the required components or 404s if it's an invalid path. This seems like the best way to do it to me, but I have no idea how. Do I have some section of the site that only that script can call, or do I use cURL from the frontline controller to the back end API sections (as I'd need to pass POST/PUT data I assume I'd have to use cURL, is there any other way?). How is this best implemented in Apache?
Yes, you use a front controller. The front controller can use convention like first thing after /api processes the request
i.e.
/api/firstprocessor/method1
/api/firstprocessor/method2
/api/secondprocessor/method14
You can check out Zend_Framework for an example of this in action, or it can be something as simple as
$name = 'Script_' . $this->generateCommandName($request->getPathVariable(1));
$this->executeScript($name, $request);
public function executeScript($class, Request $request) {
if (file_exists("scripts/".$class.'.php')) {
//include the script
require_once "scripts/".$class.'.php';
//execute the script
$command = new $class;
$command->execute($request);
}
}
Then all your scripts just have an execute method that uses $request to get $_GET or $_POST variables
I'm developing a niche social networking site that is going multilingual. That means our current URL structure will soon need to start using translated words for slugs like the following:
www.example.com/home becomes www.example.com/inicio
www.example.com/profile becomes www.example.com/perfil
www.example.com/help becomes www.example.com/ayuda
And so on. My question is: what's the best way to support this in a PHP application? For incoming requests, I thought a dictionary like the following in my router.php file would suffice:
<?php
$request = explode("/", trim($_SERVER['REQUEST_URI'], "/"));
// Dictionaries of page slugs.
$slugs = array(
'es' => array(
'inicio' => 'home',
'perfil' => 'profile',
'ayuda' => 'help',
)
// additional languages can be added here
);
// Rewrite any incoming (foreign) requests
if ($host=="www.example.es") { // to be made programmatic
$lang = "es"; // pick up from locale constant rather being hard-coded
if (array_key_exists($request[0], $slugs[$lang])) {
$request[0] = $slugs[$lang][$request[0]];
}
}
...
Which basically takes URL segments and matches them against an English counter-part if it exists. If not, then it will proceed as normal and most likely cause a 404 as a controller doesn't exist for URL segment.
Although this words, I need it to be backwards-compatible too. For example, when building URLs in my application.
Naturally, as the application is only English at the moment these are just hard-coded. So say, when fetching a User object I do the following:
<?php
class User {
function __construct($id) {
// fetch user details
$this->profile_url = ROOT . "/profile/" . $this->username;
}
}
What is the best method to then replace instances of "/profile/" being hard-coded to getting the translated version, i.e. "/perfil/" in the Spanish site?
I could always be wrong, but here goes...
The standard way to achieve multilingual websites is to use i18n dictionary/template techniques in which you have a separate dictionary for each language, and in some cases different templates.
However, in such cases, I have never seen anybody change the language of their URL's. URL's map a request to files on the server disk (generally speaking), and there for shouldn't change based on language if you can avoid it.
It is common to prefix the 'path' section of your URL with the language you are requesting - ie:
http://foo.bar/en-us/foobar.html
To summarize:
I wouldn't worry about translating your URLs as it isn't a standard practice (atleast, not that I have seen). Simply prefix the URL 'path' with a language denotation such as in the URL above.
I have something similar in an application I'm developing.
Each page has a unique ID which is matched to a slug, page title, meta description and such for each language in a table.
As for the people saying it's not standard practice and not to bother with it I disagree as having nicely translated URLs can help your SEO in different languages.
Well, a common pattern I've seen for php is to create a php file for each language and initialize a large dictionary in which the keys are the same for all languages.
Have a session variable called language which can initially be 'en' for english (or whatever you prefer), and then include using command "include(language . '/main.php');" in which you have a folder called 'en' which contains all php files to include for translations. If main gets too large, you can subdivide and include whichever translation serves your needs (for example a /en/forum.php for forum translations and a /en/blob.php for front page translations).
It has the tremendous advantage of being flexible and allowing you to control the language simply by modifying one session variable. You can even do tricks like detect browser language settings and assign language according to that if it hasn't already been defined rather than simply making it english.
I was thinking about this, as I'm going down this path now. I was going to use a similar method, but not the same...
I was going to have language "include" files with all the strings of the site. So...
/languages/en.php
would have all the correct strings for the English language, and the other language files could be dropped in as new translations are done.
In the en.php file, I was going to put in fields like this
define('PageTitleWelcomeMessage', 'Welcome to Foo');
And then call that static variable anywhere in the site. The en language would be defined in their profile
I could then call that variable like so:
echo PageTitleWelcomeMessage;
I built an client website with php script purchased recently and the support for script is pathetic, I want the permalinks of the script to be search engine friendly but they're not. They have spaces inbetween which are not at all indexed by search engine..
So, how can I change the permalinks? Thank you all..
If you've got 50 PHP files and an .htaccess file that come with this "script", you'll likely first have to find the programming path that flows through them. If you take a look at the .htaccess file, you should see some ModRewrite lines, which should end with a PHP file name. That's the script that's receiving (and decoding) the permalinks. That file would be a good place to start looking for a hook to rewrite the permalink structure. If you could post the source code (or put it somewhere like pastebin and post the link) for that file, I'd be glad to take a look.
From one of your other comments it sounds like at least part of the script is using the Smarty PHP template engine. If so, if you can find a folder that contains "cache", "templates", and "templates_c" folders (or similar), you can rule that one out as well; that would be the templates used to show the page, and not any of the decode/encode scripts.
EDIT: Looking at your .htaccess file, line 29 looks to the be one that deals with article permalinks, and it points to view.php, and converts what was the permalink into id and title GET variables. Post the source of view.php if you could, and we'll go from there.
EDIT 2 Okay, looking at view.php gained a little more insight. Primarily of which is that there is no decoding function; the Answerscript engine promptly discards the 'title' part of the permalink and only looks up a question by its ID (number following the pipe on the URL query; you can prove this on their demo page by changing the title part of the URL to anything else, and it will still fetch the right page). So, the good news is that there's no decoding function that needs to be updated when you change the encoding function. Unfortunately this does very little to tell us where the encoding function is in the scripts.
The only hint is that the view.php file includes a file called include/functions/import.php, which I'm presuming has function definitions for does_post_exist($PID), update_last_viewed($PID), update_your_viewed($USERID), and update_viewcount_question($PID). Let's see the source of that file to see if there's any other functions in there that would be used for importing. Also, how many files are in the include/functions/ folder? If there's only a few, post all their sources; likely the encoding function is defined in there. If there's a bunch, is there an export.php file in that folder (i.e. the opposite of import.php that was used by view.php)? Post that file's source as it likely has the encoding function.
EDIT 3 There they are: in the main.php file there's a trio of functions: seo_clean_titles, insert_seo_clean_titles, and seo_clean_titles2. insert_seo_clean_titles is a function to be called from within a Smarty template (search all files that have a .tpl extension for {insert name="seo_clean_titles" to see where that's used), and the difference between seo_clean_titles and seo_clean_titles2 is that the first echoes out the result, while the second returns it. However, all three have the line $title = str_replace(" ", "-", $title);, which should be turning all spaces in the title to hyphens. If you're not seeing that result, likely the code is not calling these functions at the right places. You can search through all the .php files and see if anywhere else there's a call to seo_clean_titles or seo_clean_titles2, and make sure the result is being actually used as the final URL.
Edit 4 To add ".html" to the end of all URLs: Here's the line in the template file linking to the question page:
{$ques[i].title|stripslashes}
Change that to:
{$ques[i].title|stripslashes}
and the links will have ".html" on the end. You'll then need to modify view.php to strip the ".html" back off again when parsing out the ID number: Right before $pid = intval($ph);, insert the following:
if (strtolower(substr($pid, -5)) == ".html") $pid = substr($pid,0,-5); // Remove ".html" if it exists
That should do it!
The urlencode function would take care of the spaces in the URL.
For example:
<?php
$base_url = 'http://example.com';
$category = urlencode('some thing');
$item = urlencode('Name of an item');
echo "<a href=\"{$base_url}/{$category}/{$item}/>{$item}</a>";
?>
This would make the link http://example.com/some+thing/Name+of+an+item/ which a search engine should be fine with.
And, assuming you are using URL rewriting (like mod_rewrite), the values should reach your PHP script as they were before the urlencode function. If not, you can revert them back with urldecode.
I use CodeIgniter, I'm happy with that, but I have a question.
I build my projects under /www/projectname/beta/... directory, so at my code, at many parts like including some images or css files or etc. I have to make ... src="/projectname/beta/... so when I complete the website, I need to edit so many pages to clear these /projectname/beta/ path and make it / for main root. or when I start new project with same base, first of all I need to edit these paths at all files.
now, how can I define a variable like
$projectbetapath =
"/projectname/beta/";
and have access from everywhere, like global. where can I add such line, and how can I access this var from everywhere?
Thanks!! appreciate!
Why don't you add a constant in your index.php file?
define('BETA_PATH', '/beta');
When the site leaves the beta stage you just do:
define('BETA_PATH', '');
There are two answers to your question:
Set your variables as array fields of $config in application/config/config.php and access them with $this->config->item('name');
Use the URL-helper (Or $this->config->item('base_url')) to get the current base path whenever you have to type in a path.
The second answer will give you full flexibility, you'll only have to modify the base URL in config.php if the project moves.