CakePHP - Custom Route Controller - cakephp

Is there anyway for me to create dynamic custom routes? The goal is to allow users to specify any URL they want to route to any controllers/view/ structure.
If user want to create something as below:
/a_quick_brown_fox => foxes/view/42
/jumps_over => actions/view/42
/lazy_dog => dogs/view/42
And many others in the future without the need to edit routes.php I am unsure of a possible solution.
I wish to allow user to input something like below
Custom URL => [ ]
Controller => [ ]
ID for View => [ ]
I will store it in a table to allow for unique URL checking, and what not. To allow scalability for new controllers I am okay with having prefix to slugs such as /l/<slug>
I would then wish to insert some code that will retrieve the custom URL from table and allow the routing. Is it at all possible? Has anyone ever done it?

I'm not sure whether you can define it directly into the routing system as you propose, however you could do something like this.
First define all your applications controller/actions explicitly so that your users won't overwrite them.
Then define a catch all route that will route to a controller of your choosing
//default routes
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
//other
//custom route
Router::connect('/*', array('controller' => 'routes', 'action' => 'custom'));
Your routes_controller/custom_action will receive whatever parameters the url contains, simply do a lookup on your DB from there and redirect to the correct route defined in your database.
function custom() {
//get values via $this->params
}

The easiest way to do this is to have the routes controller do the saving of new routes and to cache saved routes. Each time a new route is added flush the cache and save it again.
Then create a custom Route class that will pull the cache entries out and process them in the routes.php

You can create custom route class, but I don't think you have access to POST data in it (you can access GET data and Cache, if that's enough). Probably the easiest way is redirect in the controller.

Related

Hiding the controller path in the URL

I have these URLs:
https://my.example.com/api/foo
https://my.example.com/api/bar
https://my.example.com/api/baz
In order to simplify it for the user and also to emphasise the different meaning I have set up these subdomains which have the same DocumentRoot as https://my.example.com/:
https://foo.example.com/
https://bar.example.com/
https://baz.example.com/
Due to the fact I am not capable to do the final step which means the URLs are still like this:
https://foo.example.com/api/foo
https://bar.example.com/api/bar
https://baz.example.com/api/baz
which isn't really simplifying it as the user has still to append api/<name> to the URL.
My question:
Is there a way to omit /api/<name> and redirect it somehow in the CakePHP controller so it's enough to enter https://foo.example.com/ and this would run the code in https://foo.example.com/api/foo?
I can check the current subdomain using $_SERVER ["SERVERNAME"] so this isn't the problem.
What makes it a bit more difficult:
The login page is as usually accessed through https://foo.example.com/users/login which means when /users/login is a part of the URL then the invisible redirect must NOT be triggered. However, I think I can prevent this checking the content of the $_SERVER variable.
I really hope you guys understand what I mean.
PS: I don't want to use Apache's redirect as this would display the final URL https://foo.example.com/api/foo in the browser.
Running CakePHP 4.1.4
I don't really see how this is more simple, if anything I'd find it to be less verbose, which is never good for a URL. Anyways, it sounds like you're looking for host bound routes, ie the _host option.
This would connect / to foo.example.com, bar.example.com, and baz.example.com, each to a different controller depending on the host:
$routes->scope(
'/',
function (\Cake\Routing\RouteBuilder $builder) {
$builder
->connect('/', ['controller' => 'Foo'])
->setHost('foo.example.com');
$builder
->connect('/', ['controller' => 'Bar'])
->setHost('bar.example.com');
$builder
->connect('/', ['controller' => 'Baz'])
->setHost('baz.example.com');
$builder
->connect('/users/login', ['controller' => 'Users', 'action' => 'login']);
}
);
The /users/login URL would connect to any host, as it's not being restricted, ie all of these URLs would work and point to the same controller and action:
foo.example.com/users/login
bar.example.com/users/login
baz.example.com/users/login
That could possibly introduce further problems, like for example cookies not working if they're not scoped for all subdomains, or duplicate content as far as SEO goes. So make sure that you keep your routes as specific as possible!
Also note that resource routing won't work properly with this, as it requires a path element, ie you cannot connect resources on just /, eg foo.example.com, it would have to be foo.example.com/resource-name, if you'd wanted to connect to the former, each resource route would need to be connected manually.
Finally, if you're not using named routes, then you'll have to use '_host' => 'foo.example.com' in your URL arrays if you want to generate URLs!
See also
Cookbook > Routing > Matching Specific Hostnames

Cakephp multi level routing

We have some site example.com and I want to make routes for different cities (eg.
example.com/city1, example.com/city2).
We also want to show all other controllers and methods for the current city (eg.
example.com/city1/:controller/:action, example.com/city2/:controller/:action).
This will look like 2 or more different sites that use the same methods in the controllers but will display different info according to the city.
A router prefix won't work for us, because there could be more than 10 cities, and it must use the same methods in the controllers.
We can add cities from the admin panel.
How can we make routes that will take cities from the database and display all the links properly?
Router::connect('/:city/:controller/:action/*', array());
Router::connect('/:city/:controller/*', array());
This will add a prefix to your routes, but all your URLs will be affected. There is probably an easier way too.
Instead of having example.com/users/login, you will need to have example.com/New-York/users/login.
If you want to generate all these links, let us know what kind of controllers and methods you have.
The routes I pasted work for any prefix (city name)
I'd suggest you change your url scheme a little bit, use
example.com/cities/city1 instead of example.com/city1. This change is not only making it easier for routing/rewriting, but it will also rule out the possibility of you adding a city that will accidentally have the same name as a controller!
(Of course there will be some way to keep your original naming scheme, but it will be harder to implement and have the risk of a collision with a controller name)
Once you've got the /cities part in there you can do something like this (sample code taken from CakePHP Routing and not tested, just to give you an idea):
// routes.php
Router::connect(
'/cities/:city/:controller/:action/:id',
array(),
array(
// order matters since this will simply map ":id" to
// $articleId in your action
'pass' => array('id', 'city'),
'id' => '[0-9]+'
)
);
in your controller:
public function view($articleId = null, $city = null) {
// some code here...
}

CakePHP Routing & HTML Helper Link Ambiguity

I have a site that uses CakePHP 2.x. There's a backend interface where actions use the standard Cake layouts and views, but several of the actions are also exposed to front end users as "dialogs" (same functionality, just a layout that can be put in iframe).
In app/Config/router.php I have added the following:
Router::connect('/dialog/:controller', array('action' => 'index'));
Router::connect('/dialog/:controller/:action');
Router::connect('/dialog/:controller/:action/**');
This works appropriately, but the problem starts when trying to use the HTML helper's link() method. If I try to create a link like:
$this->Html->link('edit account', array('controller' => 'users', 'action' => 'edit'));
I get the following:
edit account
When the link is within a dialog, this works great, but I don't want the non-dialog pages to link to the dialog.
How can I control which of the two URLs is used in a particular page?
Is there something I can call from within AppController once I know whether the page being rendered is a dialog or not, or even something in the call to link() that would allow me to override it.
I know there's the "prefix" option which would allow for URLs like /user/dialog_edit but I would like to maintain the /dialog/users/edit format if possible. I also know I can hard code the URL vs. passing controller/action/id/etc in an array, and I don't anticipate pathing/model names changing, but I'd like to do this the idiomatic way for CakePHP, if possible.

dynamic html redirects using cakephp routers

dynamic html redirects  using routers
in the beginning I had no categories and all of my pages were root
example:
http://domain/somepage
This was great, but as my content over the years grew I need to categorize my content
so I added some routes see below
//routes.php
Router::connect(
"/:category/:slug",
array('controller' => 'controllername', 'action' => 'view'),
array(
'name'=>'[-A-Z0-9]+',
'pass' => array('category','slug')
)
);
//end
this works great and accomplished what I needed to do, but there is one problem the search engines .I need to write 301's for all of my links and I have over 8K pages.
The solution cakesphp's Router::redirect
The issue I am now having is I cant figurer out how to redirect my old links. I can for example redirect all of the links to one category, but that wont cut it. I need to redirect all of my links to the new location.
I am trying to use routes.php router :: redirect
if I do this my code it redirects to the category, but not the slug
Router::redirect(
'/:slug/*',
array(
'pass' => array('category/:slug'))
result
http://domain/category/
how can I get cake to redirect to
http://domain/category/slug ?
instead of http://domain/category/
I had all of my links pointing to the root directory
http://domain/somepage
http://domain/anotherpage
http://domain/ect
I needed to add categories
such as
`
http://domain.com/phones/samsung.php
http://domain.com/books/cakephp.php
`
I didn’t want to use .htaccees file because
My hosing provide limits me to 100 redirects
and i have over 8K links i need to redirect
I am trying to use cakes router ::redirect function in the routes.php file.
the below code works only for one category it doesn’t do it dynamically like I would like it too.
I tried to create a router class that would do this for me like you suggested, but to be honest with you I am not an expert in cakephp. Its easy to learn and a great framework I just dont know how to make my own classes or components yet. I just haven’t found good documentation to do this yet.
//routes.php
$move='category/'. stripslashes_deep ($_SERVER['REQUEST_URI']); Router::redirect('/:slug/*',$move, array('status' => 302)); code
Your best bet is to use a custom route class.
Custom routing extends the CakeRoute class and will allow you to return/add/modify parameters that are passed over.
CakePHP Custom Route Classes - How to Pass Arguments?

cakePHP project, orgazization of folders, pages, functions

Im new to cakePHP, and Im wondering how a 'live' site does this.
I see 2 possibilities :
1) There is one controller with a bunch of pages (functions) in its (extended) AppController.
2) There are many controllers, each with a small number of pages (functions) in its (extended) AppController.
(You probably get my question already, but Im going to say it in another way too)
Should I put my contact page in a separate controller than my blog page? (I have a hunch the answer is yes.) Why?
You don't need to create a controller for everything. In fact, you shouldn't, because there are better ways around it. The more static pages you have, the more out of hand it can get.
For Static Pages
Copy pages_controller.php from the cake/libs/controller folder over to your app/controllers folder. Add the following piece of code to your display() action:
function display() {
...
$page = Inflector::slug($page);
if (method_exists($this, $page)) {
$this->$page();
}
$this->render(join('/', $path));
return;
}
Then, modify your routes.php file to add the various static pages:
Router::connect('/about', array('controller' => 'pages', 'action' => 'display', 'about'));
Router::connect('/contact', array('controller' => 'pages', 'action' => 'display', 'contact'));
Now, the contact form is a static page, but has some logic attached to it. So, you can head over to your PagesController and create an action specifically for this (or any other page that isn't merely static):
function contact() {
if (!empty($this->data)) {
...
}
}
Basically, the route directs your static page request to the PagesController's display() action. The display action checks if a method of the same name exists. If it does, it executes that action and displays the pages/{page}.ctp view.
For Non-Static Pages, eg. Blog
Now, this definitely needs a model. In fact, multiple models (Post hasMany Comment, Post HABTM Tag). In order to manipulate and access these different models, it's better that you place the code into a separate controller.
A lot of people like to name their controllers based on their URLs. For example, they name their controller as BlogController if they want a URL such as /blog.
A method that I prefer is using routing to get the URLs that I want, and keeping controllers named as per CakePHP conventions.
Eg. A PostsController would control the Post model and related models. But if I wanted the /blog URL to display a list of all the posts, I would write a route to point it to /posts/index.
Router::connect('/blog', array('controller' => 'posts', 'action' => 'index'));
You can have additional routes too. Example: /blog/2010/06/10/whats-in-a-post to point to /posts/view/.
Again, this is just an example of what's possible. In the end, you should stick to the methods that you think helps keep your code organized for both you and your team.

Resources