I'm new to CakePHP...
Can anyone help me about this:
I have a Controller that I want to maintain in the URL not search an action, i.e: site.com/controller/SKU01B not seek for action SQU01B but search by product with SKU, the same will occur when typed site.com/product/view/1.
I think:
$routes->connect('/site/SKU*', ['controller' => 'Site', 'action' => 'index']);
but it didn't work, nor with ? instead of *.
Can anyone help me how can I do it?
Use:
Router::connect('/sku/*', array('controller' => 'yourcontroller', 'action' => 'view'),array('pass' => array('id')));
and
function view($id=null){
// $id will be sku
// For example, http://www.example.com/product/12345 here $id is 12345
}
Related
I'm using CakePHP 3 and I want to paginate my users.
However when I click on the second page, the URL looks like /users?page=2 and I expect : /users/2.
I created this route in routes.php :
$routes->connect('/users/:page', ['controller' => 'users', 'action' => 'index'], ['page' => '[0-9]+']);
And in Users/index.ctp before the "prev" button I put :
<?php
$this->Paginator->options([
'url' => [
'controller' => 'users',
'action' => 'index'
]
]);
?>
Now when I click on page 2 for example, /users/2 opens and I got this error message (RuntimeException) :
Unable to locate an object compatible with paginate.
Did I miss something or where I made a mistake ?
Thanks for your help.
The PaginatorHelper has built in the url format, i.e. to use ?page=n. It will also do sorting such as users?page=2&sort=user_id&direction=asc. Your format of /users/{page} does not handle sorting.
If your REALLY want to stick to /users/{page} you'll have to override PaginatorHelper.
try this
in side your controller with paginator component . It works for me
$this->Paginator->paginate('Users')
for custom urlenter code here
u need to implement index action as
public function index($page = null){
$this->Paginator->settings = ['limit' => 15, 'page' => $page];
$this->set('users', $this->Paginator->paginate('Users'));
}
Is it possible to have pages route without the controller in the URL but still have other controllers work? Example:
Access pages like this: http://domain.com/about/
Instead of like this: http://domain.com/pages/about/
But still have access to http://domain.com/othercontroller/action/
Doing the following works for having the pages without /pages/ in the URL but if I try to access any other controller it doesn't work:
From: Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
To: Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'index'));
Is there a way to setup the Router so that it runs controller/action if it exists. If it does not it runs the pages controller/action?
I think the short answer is, no - it's not possible in the manner you're hoping*. Routing isn't really "logic" driven, so unless you can come up with a way to match the things you want in both respects you can't do "if controller exists, then _, else _" kind of thing.
*You could, however add each "page" as a row in your routes file. That would allow "about", "contact" ...etc to be accessed directly, while things that don't exactly match them are handled by the remaining routes.
I know I'm late, but here's my tip for someone looking for this.
In routes.php
foreach(scandir('../View/Pages') as $path){
if(pathinfo($path, PATHINFO_EXTENSION) == "ctp"){
$name = pathinfo($path, PATHINFO_FILENAME);
Router::connect('/'.$name, array('controller' => 'pages', 'action' => 'display', $name));
}
}
This will create a route for every ctp file in the View/Pages folder.
I actually solved this the opposite way from Dave's answer above, by adding a route for each controller, rather than each page. (I won't be adding new controllers very often, but I will be adding new content on a regular basis.)
// define an array of all controllers that I want to be able to view the index page of
$indexControllers = array('posts','events','users');
//create a route for each controller's index view
foreach ($indexControllers as $controller) {
Router::connect(
'/' . $controller,
array(
'controller' => $controller,
'action' => 'index'
)
);
}
//create a route to remove 'view' from all page URLs
Router::connect(
'/:title',
array(
'controller' => 'contents',
'action' => 'view'
),
array(
'pass' => array('title'),
'title' => '[a-z0-9_\-]*'
)
);
My link:
echo $link->link($planDetailsByCompany['PlanDetail']['name'],
array('controller' => 'plan_details', 'action' => 'view_benefit_schedule',
'id' => $planDetailsByCompany['PlanDetail']['id'],
'slug' => $planDetailsByCompany['PlanDetail']['name']));
My custom route:
Router::connect('/pd/:id-:slug',
array('controller' => 'plan_details', 'action' => 'view_benefit_schedule'),
array('pass' => array('id', 'slug'),
'id' => '[0-9]+'));
My url is displaying like so:
..pd/44-Primary%20Indemnity
I cannot determine how to remove the %20 and replace it with a "-". There is a space in the company name that is causing this. Is this possible within the CakePHP router functionality? If so, how? Or another method.
Geeze.. I just solved this!
In my link above, replace the 'slug' line with:
...'slug' => Inflector::slug($planDetailsByCompany['PlanDetail']['name'])...
The Inflector handles the spaces in the url. And my result url is:
...pd/44-Primary_Indemnity
I am using CakePHP 1.3. I have a Product model. on the DB table among others there are id and slug fields.
If I have a product that is id:37 and slug:My-Product-Title I want the URL for the product to be:
products/37/My-Product-Title
Instead of the standard:
products/view/37
I created a route that looks like this:
Router::connect(
'/products/:id/:slug',
array('controller' => 'products', 'action' => 'view'),
array('pass' => array('id'), 'id' => '[0-9]+')
);
Now I can go to http://server/products/37/My-Product-Title and it takes me to the right place.
But How do I get reverse routing to automatically build the correct URL in $HtmlHelper->link?
When I use:
echo $html->link(
'Product 37',
array('controller'=>'products', 'action' => 'view', 37)
);
It still outputs the standard products/view/37 url.
I don't believe that it's possible to be done auto-magically. The helper is just an "helper" who builds the link from the given parameters.
So the easiest method is to add another parameter in your link like so:
echo $html->link(
'Product 37',
array('controller'=>'products', 'action' => 'view', 37, $slug)
);
where the $slug is the data from the slug field.
Probably it could be done your idea, but you need to break the MVC pattern very badly :)
Edit:
Reading your question again I understood it well. See how should be done:
in your router.php add the following rule:
Router::connect(
'/product/*',
array('controller' => 'products', 'action' => 'view')
);
Please note that it's /product/* rather than /products/*
Your link should be done like this:
echo $html->link(
'Product 37',
array('controller'=>'products', 'action' => 'view', 37, 'my-product-title')
);
and the link would look like:
http://yourdomain.com/product/37/my-product-title
For me doing your suggestion is bad practice. Also I don't think it's good from SEO point of view redirecting always the user.
For routing:
Router::connect(
'/products/:id/:slug',
array('controller' => 'products', 'action' => 'view'),
array('pass' => array('id'), 'id' => '[0-9]+')
);
Your links should look like this:
echo $html->link(
'Product 37',
array('controller'=>'products', 'action' => 'view', 'id' => 37, 'slug' => 'my-product-title')
);
You have to add additional (key => value) to your array for each :param in your routing. Then magic will work
You should look at the following post regarding custom route classes.
The slug data doesn't need to be involved with the database at all - the field is a fake field used to simplify logic and lookups. This solution allows you to reverse route slugs, without needing a slug field in the models table.
http://42pixels.com/blog/slugs-ugly-bugs-pretty-urls
I am not sure how bad this is but with the following code in the ProductsController:
function view($id)
{
if( isset($_SERVER) && stristr($_SERVER["REQUEST_URI"],'view/') )
{
$this->Product->id = $id;
$slug = $this->Product->field('slug');
$this->redirect($id.'/'.$slug);
}
$data = $this->Product->find('first', array('conditions' => array('Product.id' => $id)));
$this->set("data", $data);
}
If the page is accesses via /view/id it automatically redirects them to the current page using /id/slug
Now I can just use the default link scheme:
echo $html->link(
'Product 37',
array('controller'=>'products', 'action' => 'view', 37)
);
and they will be redirected to the right URL.
Only problem is I am not sure how bad it is to have a redirect happening every time a user visits a product page?
Is it possible in CakePHP to have URL aliases in routes.php? Or by what other means can achieve something equivalent:
Lets assume I have some paginated views. Among the possible orderings there are particular ones I want to bind to a simple URL. E.g.:
http://example.com/headlines => http://example.com/posts/listView/page:1/sort:Post.created/direction:desc
http://example.com/hottopics => http://example.com/posts/listView/page:1/sort:Post.view_count/direction:desc etc.
How do I add parameters to a Router::connect()? Pseudo code:
Router::connect('/'.__('headlines',true),
array(
'controller' => 'posts',
'action' => 'listView'
'params' => 'page:1/sort:Post.created/direction:desc',
)
);
Note that the Router "translates" a URL into Controllers, Actions and Params, it doesn't "forward" URLs to other URLs. As such, write it like this:
Router::connect('/headlines',
array(
'controller' => 'posts',
'action' => 'listView'
'page' => 1,
'sort' => 'Post.created',
'direction' => 'desc'
)
);
I don't think '/'.__('headlines', true) would work, since the app is not sufficiently set up at this point to translate anything, so you'd only always get the word in your default language back. Also, you couldn't switch the language anymore after this point, the first use of __() locks the language.
You would need to connect all URLs explictly. To save you some typing, you could do this:
$headlines = array('en' => 'headlines', 'de' => 'schlagzeilen', ...);
foreach ($headlines as $lang => $headline) {
Router::connect("/$headline", array('controller' => ..., 'lang' => $lang));
}
That will create a $this->param['named']['lang'] variable, which you should use in the URL anyway.
Yes, it is possible... Bootstrap.php loads before routes so if you set there something like:
session_start();
if(isset($_SESSION['lng'])){
Configure::write('Config.language', $_SESSION['lng']);
}
...and in your app controller in beforeFilter:
$language = 'xy';
Configure::write('Config.language', $language);
$_SESSION['lng'] = $language;
So initial page render you prompt for language, redirect to xy.site.com or www.site.com/xy whatever you prefer. Now second render will change $language and on page links and set $_SESSION['lang']...
All router links like:
Router::connect(__('/:gender/search/:looking_for/*'), array('controller' => 'users', 'action' => 'search'));
will become:
Router::connect(__('/:gender/trazi/:looking_for/*'), array('controller' => 'users', 'action' => 'search'));
or:
Router::connect(__('/:gender/suche/:looking_for/*'), array('controller' => 'users', 'action' => 'search'));
100% tested, works in CakePHP 2.2. Also further improvement is possible if you put subdomain/language url parser in the bootstrap itself...