Not sure if I properly wrote the subject but anyway.
Since you can create specific routes with different parameters for eg:
_search:
pattern: /page/{category}/{keyword}
defaults: { _controller: Bundle:Default:page, category: 9, keyword: null }
is there any way from a form with GET method to get to that route specific url format?
At the moment the url is like /page?category=2?keyword=some+keyword
As such is not passing to the route format as you may noticed.
What do I need to do to get it working through this specific format? I really have no idea how to rewrite the page url to match the route settings for the specific url. Even in plain php was stumbled on this ...
Thanks in advance.
It's the default behavior of HTML forms with GET method. You will need to build that URL yourself.
Backend way
Drawback: It makes two requests to the server instead of one
Advantage: It's more maintainable because the URL is built using the routing service
Your routing file
_search:
pattern: /page/{category}/{keyword}
defaults: { _controller: Bundle:Default:page, category: 9, keyword: null }
_search_endpoint:
pattern: /page
defaults: { _controller: Bundle:Default:redirect }
Your controller
public function redirectAction()
{
$category = $this->get('request')->query->get('category');
$keyword = $this->get('request')->query->get('keyword');
// You probably want to add some extra check here and there
// do avoid any kind of side effects or bugs.
$url = $this->generateUrl('_search', array(
'category' => $category,
'keyword' => $keyword,
));
return $this->redirect($url);
}
Frontend way
Using Javascript, you can build the URL yourself and redirect the user afterwards.
Drawback: You don't have access to the routing service (although you could use the FOSJsRoutingBundle bundle)
Advantage: You save one request
Note: You will need to get your own query string getter you can find a Stackoverflow thread here, below I'll use getQueryString on the jQuery object.
(function (window, $) {
$('#theFormId').submit(function (event) {
var category, keyword;
event.preventDefault();
// You will want to put some tests here to make
// sure the code behaves the way you are expecting
category = $.getQueryString('category');
keyword = $.getQueryString('keyword');
window.location.href = '/page/' + category + '/' + keyword;
}):
})(window, jQuery);
You could add a second route that will just match /page
then in the controller you can get the defaults. and merge them with any that are passed.
Take a look at a similar question I answered for some code examples.
KendoUI Grid parameters sending to a symfony2 app
I've too encountered this issue and I managed to resolve it with a slightly different solution.
You could also reroute like #Thomas Potaire suggested, but in the same controller, beginning your controller with :
/**
* #Route("/myroute/{myVar}", name="my_route")
*/
public function myAction(Request $request, $myVar = null)
{
if ($request->query->get('myVar') !== null) {
return $this->redirectToRoute('my_route', array(
'myVar' => str_replace(' ','+',$request->query->get('myVar')) // I needed this modification here
));
}
// your code...
}
Related
Can I get the controller action from given URL?
In my project, I will have different layout used for admin and normal users. i.e.
something.com/content/list - will show layout 1.
something.com/admin/content/list - will show layout 2.
(But these need to be generated by the same controller)
I have added filter to detect the pattern 'admin/*' for this purpose. Now I need to call the action required by the rest of the URL ('content/list' or anything that will appear there). Meaning, there could be anything after admin/ it could be foo/1/edit (in which case foo controller should be called) or it could be bar/1/edit (in which case bar controller should be called). That is why the controller name should be generated dynamically from the url that the filter captures,
So, I want to get the controller action from the URL (content/list) and then call that controller action from inside the filter.
Can this be done?
Thanks to everyone who participated.
I just found the solution to my problem in another thread. HERE
This is what I did.
if(Request::is('admin/*')) {
$my_route = str_replace(URL::to('admin'),"",Request::url());
$request = Request::create($my_route);
return Route::dispatch($request)->getContent();
}
I could not find these methods in the documentation. So I hope, this will help others too.
You can use Request::segment(index) to get part/segment of the url
// http://www.somedomain.com/somecontroller/someaction/param1/param2
$controller = Request::segment(1); // somecontroller
$action = Request::segment(2); // someaction
$param1 = Request::segment(3); // param1
$param2 = Request::segment(3); // param2
Use this in your controller function -
if (Request::is('admin/*'))
{
//layout for admin (layout 2)
}else{
//normal layout (layout 1)
}
You can use RESTful Controller
Route:controller('/', 'Namespace\yourController');
But the method have to be prefixed by HTTP verb and I am not sure whether it can contain more url segment, in your case, I suggest just use:
Route::group(array('prefix' => 'admin'), function()
{
//map certain path to certain controller, and just throw 404 if no matching route
//it's good practice
Route::('content/list', 'yourController#yourMethod');
});
I have set up friendly URLs for a few search result pages, using a custom route for each:
friendly_search_resultpage:
url: /products/my-friendly-alias
param:
module: products
action: search
querystring: searchattribute
querystring2: searchattribute2
This works fine, but when doing the search directly (i.e. browsing to /products/search?querystring=search...) I want to set a <link rel="canonical"> containing the matching friendly URL. This will help Google understand the relation and that there isn't duplicate content.
I put my friendly URL route at the top of routes.yml and hoped for a magic match, but URL parameters aren't recognised in the checking done by symfony. I have dug into sfRoute, with no luck. Is it possible?
I ended up writing a custom routing class to use for these routes, which is executed when url_for() is called. Here is the code:
<?php
class mySearchFriendlyRoute extends sfRoute
{
public function matchesParameters($params, $context = array())
{
// I can't find symfony sorting parameters into order, so I do
// (so that foo=x&bar=y is treated the same as bar=y&foo=x)
ksort($params);
$mine = $this->defaults;
ksort($mine);
if ($params == $mine) {
return true;
}
return false;
}
}
To use, add class: mySearchFriendlyRoute to the routes.yml entry.
Try to use Your own Route:
friendly_search_resultpage:
class: YourRouteClassName
...
And overload sfRoute::generate() there by concrete cannonicalization (return the canonical URL).
This redirects with 301 in my project upon of last sf1.4 revision.
There will be several high profile links for customers to focus on, for example:
Contact Us # domain.com/home/contact
About the Service # domain.com/home/service
Pricing # domain.com/home/pricing
How It Works # domain.com/home/how_it_works
Stuff like that. I would like to hide the home controller from the URL so the customer only sees /contact/, not /home/contact/. Same with /pricing/ not /home/pricing/
I know I can setup a controller or a route for each special page, but they will look the same except for content I want to pull from the database, and I would rather keep my code DRY.
I setup the following routes:
Route::get('/about_us', 'home#about_us');
Route::get('/featured_locations', 'home#featured_locations');
Which work well, but I am afraid of SEO trouble if I have duplicate content on the link with the controller in the URL. ( I don't plan on using both, but I have been known to do dumber things.)
So then made routes like these:
Route::get('/about_us', 'home#about_us');
Route::get('/home/about_us', function()
{
return Redirect::to('/about_us', 301);
});
Route::get('/featured_locations', 'home#featured_locations');
Route::get('/home/featured_locations', function()
{
return Redirect::to('/featured_locations', 301);
});
And now I have a redirect. It feels dumb, but it appears to be working the way I want. If I load the page at my shorter URL, it loads my content. If I try to visit the longer URL I get redirected.
It is only for about 8 or 9 special links, so I can easily manage the routes, but I feel there must be a smart way to do it.
Is this even an PHP problem, or is this an .htaccess / web.config problem?
What hell have I created with this redirection scheme. How do smart people do it? I have been searching for two hours but I cannot find a term to describe what I am doing.
Is there something built into laravel 4 that handles this?
UPDATE:
Here is my attempt to implement one of the answers. This is NOT working and I don't know what I am doing wrong.
application/routes.php
Route::controller('home');
Route::controller('Home_Controller', '/');
(you can see the edit history if you really want to look at some broken code)
And now domain.com/AboutYou and domain.com/aboutUs are returning 404. But the domain.com/home/AboutYou and domain.com/home/aboutUs are still returning as they should.
FINAL EDIT
I copied an idea from the PongoCMS routes.php (which is based on Laravel 3) and I see they used filters to get any URI segment and try to create a CMS page.
See my answer below using route filters. This new way doesn't require that I register every special route (good) but does give up redirects to the canonical (bad)
Put this in routes.php:
Route::controller('HomeController', '/');
This is telling you HomeController to route to the root of the website. Then, from your HomeController you can access any of the functions from there. Just make sure you prefix it with the correct verb. And keep in mind that laravel follows PSR-0 and PSR-1 standards, so methods are camelCased. So you'll have something like:
domain.com/aboutUs
In the HomeController:
<?php
class HomeController extends BaseController
{
public function getAboutUs()
{
return View::make('home.aboutus');
}
}
I used routes.php and filters to do it. I copied the idea from the nice looking PongoCMS
https://github.com/redbaron76/PongoCMS-Laravel-cms-bundle/blob/master/routes.php
application/routes.php
// automatically route all the items in the home controller
Route::controller('home');
// this is my last route, so it is a catch all. filter it
Route::get('(.*)', array('as' => 'layouts.locations', 'before' => 'checkWithHome', function() {}));
Route::filter('checkWithHome', function()
{
// if the view isn't a route already, then see if it is a view on the
// home controller. If not, then 404
$response = Controller::call('home#' . URI::segment(1));
if ( ! $response )
{
//didn't find it
return Response::error('404');
}
else
{
return $response;
}
});
They main problem I see is that the filter basically loads all the successful pages twice. I didn't see a method in the documentation that would detect if a page exists. I could probably write a library to do it.
Of course, with this final version, if I did find something I can just dump it on the page and stop processing the route. This way I only load all the resources once.
applicaiton/controllers/home.php
public function get_aboutUs()
{
$this->view_data['page_title'] = 'About Us';
$this->view_data['page_content'] = 'About Us';
$this->layout->nest('content', 'home.simplepage', $this->view_data);
}
public function get_featured_locations()
{
$this->view_data['page_title'] = 'Featured Locations';
$this->view_data['page_content'] = 'Featured properties shown here in a pretty row';
$this->layout->nest('content', 'home.simplepage', $this->view_data);
}
public function get_AboutYou()
{
//works when I return a view as use a layout
return View::make('home.index');
}
Is it possible to restrict a Symfony 2 route for XHR requests only? I want to declare routes, which are only accessible via AJAX.
I do not want to put some extra lines into each AJAX-specific-actions like that:
if ($request->isXmlHttpRequest()) {
// do something
} else {
// do something else
}
I want to define:
one rule for AJAX requests
one rule for GET/POST requests to the same URL
in order to get around having conditions like above.
I know this question is a bit older but meanwhile a new way to achieve this was introduced in Symfony 2.4.
Matching Expressions
For an ajax restriction it would look like this:
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
condition: "request.isXmlHttpRequest()"
Also possible in Annotation:
/**
* ContactAction
*
* #Route("/contact", name="contact", condition="request.isXmlHttpRequest()")
*/
My advice would be to define your own router service instead of default, which would extend from Symfony\Bundle\FrameworkBundle\Routing\Router, and redefine method resolveParameters() with implementing your own logic for handling additional requirements.
And then, you could do something like this in your routing:
your_route:
pattern: /somepattern
defaults: { somedefaults }
requirements:
_request_type: some_requirement
I'm not sure that you can prevent the request taking place, however you can check for an XHR request in the Controller by checking the current Request
The code would look like this:
if ($request->isXmlHttpRequest()) {
// ...
}
This is not 100% reliable, due to among other things, browser inconsistencies and the possibility of proxy interference. However it is the predominant method of checking for an asynchronous request and is recommended by many. If you are cr
URL Parameter
An alternative would be to add a parameter in your URL to identify the request as asynchronous. This is achieved by adding ?ajax=1 to your URL. Then, check for the parameter with:
$AjaxRequest = $request->getParameter('ajax');
If($AjaxRequest == 1) {
//...
}
Of course, at this point you could also create a specific Route e.g. /ajax/index/.
No, you cannot. It is not depend on which framework you are using, AJAX requests basically are just requests to a server. There is no 100% reliable solution, just "hacks".
What you're looking for does not exist in Symfony routing configuration.
Request::isXmlHttpRequest is even not 100% reliable, and checks HTTP headers put by your JavaScript library :
It works if your JavaScript library set an X-Requested-With HTTP header.
It is known to work with Prototype, Mootools, jQuery.
You can use the requirements for reach the described result.
So, suppose that you're defining routes onto yml format, you have to do something like this
my_route:
pattern: /path/to/route
defaults: { _controller: CompanyBundle:Controller:Action, _format: html }
requirements:
_format: xmlhttp /* not sure about the correct format because
i've never checked about */
And you can, of course, use _method: POST or _method: GET
I wanna make a simple URL to Controller mapping very much like what Symfony2 does. But that's all I want from Symfony2, rest of it is just too much for me.
For those who don't know what Symfony2 does:
blog_home:
pattern: /blog
defaults: { _controller: BlogBundle:Blog:index }
blog_show:
pattern: /blog/{slug}
defaults: { _controller: BlogBundle:Blog:show }
in a YAML config file.
YAML doesn't matter to me at all. I just wanna achieve the same functionality. To be able to map custom URLs to controller functions.
Maybe an open source mapping class or routing framework? Maybe some tutorials? Ideas to make my own? Any suggestions would be helpful.
I should mention I'm no PHP whiz, I know just enough or maybe a little less than enough. Which is why I don't wanna get into a full featured framework.
This is my url routing framework:
function route($url, $map) {
foreach($map as $re => $fn) {
if(preg_match("~^$re$~", $url, $args)) {
list($class, $method) = explode(".", $fn);
return call_user_func_array(
array(new $class, $method),
array_slice($args, 1));
}
}
error_404();
}
The $map is an array whose keys are regular expressions to match the url against and the values are strings "ClassName.method", like
$map = array(
"/blog/(.+)" => "BlogController.show",
"/blog" => "BlogController.blog",
"/foobar/(\d+)/(\w+)" => "Foobar.stuff",
);
The routing function finds the first matching pattern, instantiates a class and calls a method passing regexp subgroups as arguments. So, an url "/foobar/123/hello" will be routed to Foobar->stuff(123, 'hello').