I have built a site on zend-framework 1.9.7. I want to make friendly url for every page which has a URL similar to this : http://mysite/search/detail/id/124
I want the friendly URL to look like: http://mysite/search/detail/ram
Where "ram" is the name of user which has id=124
I have include RewriteRule ^name/(.*)/$ guid/$1 in .htaccess file, but it doesn't work.
I suggest you to take a look at the Zend Controller Quickstart which walks through the steps of setting up the standard routing (which already provides everything for nice URLs).
If you want more detailed Info on the Routing, then I suggest to take a look at Zend_Controller_Router's Manual.
Specifically I would handle your case through a Router Route, for example:
<?php
$router = Zend_Controller_Front::getInstance()->getRouter();
$detailsRoute = new Zend_Controller_Router_Route("search/detail/:name", array(
'controller' => 'search',
'action' => 'detail'
));
$router->addRoute('searchDetail', $detailsRoute);
The part :name is a parameter which gets filled with the value ram of you desired URL, and can be retrieved with $request->getParam('name'); later on.
Controllers in ZF have the functionality to be called from custom routes. You can find the documentation here. They give you a wide variety of options to choose the kind of route you want to use. It can be pretty URLs like those in blogs or even REST endpoints.
You don't have to mess with the htaccess file for this as all calls to non-static resources are directed through index.php anyway.
have a zend plugin that works very well for this.
<?php
/** dependencies **/
require 'Zend/Loader/Autoloader.php';
require 'Zag/Filter/CharConvert.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
//filter
$filter = new Zag_Filter_CharConvert(array(
'replaceWhiteSpace' => '-',
'locale' => 'en_US',
'charset'=> 'UTF-8'
));
echo $filter->filter('ééé ááá 90');//eee-aaa-90
echo $filter->filter('óóó 10aáééé');//ooo-10aaeee
the plugin is super easy to use.
hug!
Related
Been trying to figure this out for a few days now with no joy. Here's my setup: I have a CakePHP install in
/home/user/tools/cakephp
and a plugin at
/home/user/tools/cakephp/app/Plugin/MyPlugin
The Apache server setup is such that I've set the DocRoot to /home/user/tools, so browsing to
http://myserver.com/cakephp/my_plugin
works fine, but now my client wants to set it up so that
http://myserver.com/product-name
serves up the CakePHP plugin, and all subsequent routes are honoured. Has anyone had any experience setting something like this up? Has to be Apache, unfortunately, and can be done with a mixture of config/.htaccess (clients constraints).
Thanks
Stephen
You are saying you have to do this via the Apache config or a .htaccess file, why? A plugin can have either his own Config/routes.php or you could configure the routing in your app wide app/Config/routes.php file. We are doing the same for a plugin we are using in our application.
What we did:
In the app wide routes file (app/Config/routes.php) we set a variable we use as our base url for accessing the plugin. We set it in a variable so we can easily switch when we get collisions with other controllers or plugins, so we want to keep that flexibility by doing the following:
# set the webroot for the plugin, for ajax calls and the sake of usability
$MyPluginBase = '/product-name';
# And we have this set just in case we need it somewhere in our application
Configure::write('MyPlugin.base', $MyPluginBase);
Then we configure our custom routes:
Router::connect($MyPluginBase . '/:name', array(
'plugin' => 'my_plugin',
'controller' => 'my_plugin_products',
'action' => 'products'
));
Router::connect($MyPluginBase . '/some/other/url/*', array(
'plugin' => 'my_plugin',
'controller' => 'my_plugin_some_controller',
'action' => 'whatever'
));
Now we can access the pugin via the 'product-name' url.
But, when you simply need a controller/action wide solution for this, you could accomplish this by using the following two routes:
Router::connect($MyPluginBase . '/:controller/:action/*', array(
'plugin' => 'my_plugin'
));
Router::connect($MyPluginBase . '/*', array(
'plugin' => 'my_plugin',
'controller' => 'my_plugin_main_controller'
));
Please note that the order of Router::connect methods matters!
ps. After reading the question again, I saw you have set your DocumentRoot wrong for production. Consult the following page in the cookbook for clarification: http://book.cakephp.org/2.0/en/installation.html#production
Adding the following line to the htaccess file should work
RedirectMatch 301 cake/my_plugin(.*) /product-name$1
The above would resolve:
http://myserver.com/cakephp/my_plugin to http://myserver.com/product-name
http://myserver.com/cakephp/my_plugin/somelink to http://myserver.com/product-name/somelink
When you say "all subsequent routes are honoured", I assume you mean that http://myserver.com/product-name/foo/bar will work the same as http://myserver.com/cakephp/my_plugin/foo/bar.
If that is the case, and you have mod_aliasinstalled, all you need to do is provide an Alias directive in httpd.conf:
Alias /product-name /cakephp/my_plugin
This should be completely transparent to CakePHP; it will be unaware that this mapping is happening.
If you want to prevent direct requests to http://myserver.com/cakephp/..., you could also add an external redirect:
Redirect 301 /cakephp/my_plugin /product-name
(from http://httpd.apache.org/docs/current/mod/mod_alias.html#alias)
I'm new with Kohana and finding their documentation to be lacking (lots of incomplete writings, lots of broken links, etc.). I just want to create a route like so:
Route::set('test1', 'blah/<id>')
->defaults(array(
'controller' => 'Blah',
'action' => 'foo',
));
So if the URL is localhost/blah/8342342 it will run through this controller and action. The problem is I get a 404 error. If I change the URI in the Route::set to be blah/foo/<id> it works fine. I only want the /blah/ directory though, not 'blah/foo'. Is this possible or do you need to have both the controller and action in the URL?
Another question, does the first directory in your URI (in this case /blah) HAVE to match the controller name? For example, if the first directory in the URI is "blah/", does that mean my controller must be named "Blah.php"? From my tests it seems that this is the case, but I don't know why it would be set up that way. What if I wanted the URI "contact/" to go through controller Blah?
I am creating my own MVC framework in php as a means to learn more advanced programming. I've got the framework up and running however I have an issue regarding the current routing method. I want the framework to support a backend cms to compliment the front end website. The issue is that my routing structure works like so - mywebsite.com/controller/method/id
The routing engine sorts the information into an array like this
segments 0 => controller, 1 => method, 2 => id
Right now if I visit mywebsite.com/projects it takes me to what I have set up as an admin page. Not only do I want this to be accessible only from mywebsite.com/admin/projects, I want the mywebsite.com/projects to take me to frontend.
So if I want to visit mywebsite.com/projects I'd like it to render the "front" controller, pushing "projects" into the method. If I visit mywebsite.com/admin/projects I'd like it to load the projects controller.
Here's the current routing class in whole as follows.
<?php
class Request {
//url domain.com/controller/method/other
//holds url segments 0 => controller, 1 => method, 2 => other, etc
public $segments;
function __construct() {
$this->parse_globals();
}
function parse_globals(){
//$uri = preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $_SERVER['REQUEST_URI']));
$uri = (empty($_GET['rt'])) ? '' : $_GET['rt'];
$this->segments = array_filter(explode('/', $uri));
if (in_array("admin", $this->segments)) {
array_shift($this->segments);
}
print_r($this->segments);
//remove index php
if( reset($this->segments) == 'index.php') {
array_shift ($this->segments);
}
}
function controller(){
return $this->segment(0);
}
function method(){
return $this->segment(1);
}
function param( $str ){
return isset($_REQUEST[$str]) ? $_REQUEST[$str] : false;
}
function segment( $no ){
return isset($this->segments[$no]) ? $this->segments[$no] : false;
}
}
Instead of simply using explode() to separate the segments of URL, you could use a set of regular expression pattern.
For example, this following patter would try to match at firsts action, and, if action exists them check if there is controller set before it:
'/(:?(:?\/(?P<controller>[^\/\.,;?\n]+))?\/(?P<action>[^\/\.,;?\n]+))?/'
Most of PHP frameworks use different ways to generate such patterns, with simplified notations. This way you can set which parts for each pattern are mandatory and which optional. And it is also possible to provide fallback values for the optional parts.
That said ...
Before starting to make something so complicated as cms with framework, you might invest some additional time in researching OOP. I would recommend to at least watch lectures from Miško Hevery and Robert C. Martin. Just because you think, that you know how to write a class, does not mean, that you understands object oriented programming.
Update
I have listed few materials in this answer. You might find them useful,
Additionally here are two more lectures, that were not in answer above:
Clean Code I: Arguments
Clean Code III: Functions
My understanding is that there's three routing cases:
The basic one:
/<controller>/<action>/<parameters>
A special one for the admin panel (where "admin" would be a kind of separate module):
/<module>/<controller>/<action>/<parameters>
And finally a special case for "/projects" which maps to "/front/projects".
In that case, you need to make your routing class more flexible so that it can handle any kind of routing scheme. In a framework like Kohana, this would be done with rules such as:
Route::set('adminModule', 'admin/projects')
->defaults(array(
'controller' => 'projects',
'action' => 'admin',
));
Route::set('projectPage', 'projects')
->defaults(array(
'controller' => 'front',
'action' => 'projects',
));
Route::set('default', '(<controller>(/<id>(/<action>)))')
->defaults(array(
'controller' => 'index',
'action' => 'index',
));
Obviously this is just an example but you get the idea. Basically, you want to provide some sensible default routing (eg. controller/action/id) but you also need to allow users to configure other routes.
I am currently developing a php router which is targeted at extreme high performance. you probably might want to take a look.
https://github.com/c9s/Pux
We also provide a pattern compiler that is doing the same thing as yours. but you can write a simpler path instead of complex patterns.
e.g., you may write something like this:
/product/:id/:name [ id => '\d+', name => '\w+' ]
FYI:
Pux is 48.5x faster than symfony router in static route dispatching, 31x faster in regular expression dispatching. (with pux extension installed)
Pux tries not to consume computation time to build all routes dynamically (like Symfony/Routing). Instead, Pux compiles your routes to plain PHP array for caching, the compiled routes can be loaded from cache very fast.
With Pux PHP Extension support, you may load and dispatch the routes 1.5~2x faster than pure PHP Pux.
Trying to learn Kohana, coming from Asp.Net MVC 3. In MVC I am used to the default route (very similar to the default route in Kohana) working like this:
The default route matches any of these:
/
/Home
/Home/Index
I expected it to be the same in Kohana, but it seems all it matches is this:
/
Here's my setup in bootstrap.php:
Kohana::init(array(
'base_url' => '/kohana',
'index_file' => FALSE
));
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'home',
'action' => 'index',
));
So if I enter localhost/kohana in the address bar I get to the view called by controller home and action index (action_index). But if I enter localhost/kohana/home/index I get an error saying the object doesn't exist.
Why is this? Shouldn't I be able to enter controller and action in the url and get the correct routing? So basically I have no idea how to enter URLs to get to an action method...
Sorry if this is a stupid newbie question, but I can't figure it out Googling and looking at the Kohana docs... I've been sort of spoiled by the fact that Asp.Net MVC routing always just worked, so I never had to really learn about it...
Rename example.htaccess to .htaccess, open it and change the line RewriteBase / to RewriteBase /kohana/. Windows explorer will probably not allow for a file without the name, so you have to use another file manager (Total Commander for example).
I have a set of rather static pages wich I moved to the views/pages folder. The resulting *.ctp files are editable by my customer through CushyCMS (simplistic cms perfect for dummy proof editing). However CushyCMS generated preview links that obviously don't take CakePHP into account. I would like to solve this little problem with custom routing, but can't get my head around the details..
How can I dynamically connect the url http://localhost:8888/cake125/app/views/pages/test.ctp to http://localhost:8888/cake125/pages/test?
I added the following in my routes.php:
Router::connect('/pages/test.ctp', array(
'controller' => 'pages',
'action' => 'display', 'test'));
This works ok for connecting: http://localhost:8888/cake125/pages/test.ctp to http://localhost:8888/cake125/pages/test. Somehow following snibbet doesn't do the trick:
Router::connect('/app/views/pages/test.ctp', array(
'controller' => 'pages',
'action' => 'display', 'test'));
Ideally I'd like to have a single Router::connect statement which connects all /app/views/pages/*.ctp requests to the right place.
Finally I would also like to correctly handle google search results for the old version of the site. Like so:
Router::connect('/test.html', array(
'controller' => 'pages',
'action' => 'display', 'test'));
This works ok but I'd rather have anypage.html connect to /pages/anypage. Can anyone help with this?
Thanks in advance!
First, by virtue of having Cake in a subdirectory (/cake125), I think you may need to connect the /cake125/:controller/:action, rather than how you have it. Not 100%, though; Cake might be robust enough to handle that use case. If you have weird errors, I'd check that.
On with my answer:
I think you are somewhat misunderstanding how the Router class works. You connect URLs, not relative filesystem paths, using Router::connect. By default (which you may have erased, but it's pretty simple to fix), Cake will route requests to /pages/* to the PagesController::display() function, passing it one argument (the action listed in the http request).
So, to have the pages controller map /pages/one to the app/views/pages/one.ctp element, simply make sure that the following (default, i.e. Cake normally has this setup) line is in the routes config (and make sure that lines above it do not match that pattern):
Router::connect( '/pages/:action', array( 'controller' => 'pages', 'action' => 'display', :action);
This should ensure that PagesController::display( $action ) is invoked during the request, which is (I think) what you're after.
If your CMS generates preview links that you want to correctly re-route, I'd suggest adding a new route. E.g., if your CMS generates links like http://somesite.com/cms/preview/newly_edited_file, you can route it like this:
Router::connect( '/cms/preview/:action', array( 'controller' => 'pages', 'action' => 'display', :action );
For your second question: have a default rule in your routes (make it the last rule, and have it match *). It will then be configured to route all not found requests to your controller/action pair as requested. Try this:
Router::connect( '/:action', array( 'controller' => 'pages', 'action' => 'display', :action );
Major caveat this will break your existing routes. You will need to manually add an entry for each of your existing controllers (Router::connect( '/users/:action', ...etc...). If you google around you can find some clever solutions, such as having that list generated at runtime for you. But you will need to address "normal" routing, once you've added that catch-all (and make sure your catch-all is at the end of the routing file).
Also, if you want to parse URLs like /test.html, simply add a call to Router::parseExtensions(...) so that Cake will register .html as an extension for it to parse. Check the manual on that function for more info.
As others have pointed out how CakePHP Router works, I'll leave it at that.
For the second part of your question (handling old links), I'd suggest adding this to the end of your Routes list:
Router::connect( '/:page',
array (
'controller' => 'pages',
'action' => 'display',
),
array (
'pass' => array ('page'), // to pass the page as first arg to action
'page' => '.+\.html$', // to verify that it ends with .html
)
);
You'd unfortunately have to parse out the .html yourself though
How can I dynamically connect the url http://localhost:8888/cake125/app/views/pages/test.ctp to http://localhost:8888/cake125/pages/test?
Well, the thing is, you don't. :-)
What I mean by that is, you do not connect a URL to another URL. What you really do is, you make certain URLs trigger certain Controller functions (or Actions for short) which in turn may (or may not) render certain Views. By default it's all straight forward through naming conventions. The URL /foo/bar triggers the Controller Foo's Action bar and renders the View /views/foo/bar.ctp.
The PagesController is already a special case. The URL /pages/foo triggers the Controller Pages's Action display, passes it the parameter foo, which renders the View /views/pages/foo.ctp. Notice the difference in which Action is triggered.
Since there are a lot of steps inbetween, it's not a given that a certain URL corresponds to a particular file on the hard disk. The URL /foo/bar might trigger Controller Baz' Action doh which renders the View /views/narf/glob.ctp.
This makes translating http://localhost:8888/cake125/app/views/pages/test.ctp to render the file /views/pages/test.ctp somewhere between an uncertainty and a pain in the rear.
Edit:
Having said that, the particular problem in your case is that the base URL is http://localhost:8888/cake125/app/. You can invoke a Cake app from http://localhost:8888/cake125/, http://localhost:8888/cake125/app/ or http://localhost:8888/cake125/app/webroot. All three URLs will be handled by the same file cake125/app/webroot/index.php, if you use one of the shorter URLs the request will be "forwarded" (rewritten) via .htaccess rules.
So the Route you're trying to connect, the Route that Cake sees, is actually /views/pages/test.ctp.
Actually, my mistake, this might not be the problem, but it depends on your .htaccess files and server configuration.
It doesn't seem to make much sense in a CMS though, since every newly created page would need its own rule. So I'd recommend against trying to do so and rather hack Cushy to properly construct URLs using the Cake HtmlHelper or Router::url(). Failing that, connect all URLs with a catch-all rule to some Action, parse the URL there and render the correct View "manually".
Alternatively, use .htaccess files and rewrite rules to actually rewrite the URL into a normal Cake URL, so Cake doesn't have to worry about it. As said above though, this can be very fragile.