I am new to Laravel (first project ) but not new to PHP. I am creating a housing listing website with laravel 5.2. I need to create a location based listing such as /listing/country/province/city/UNIQUEIDENTIFIED
I would like to setup the views as follows:
/listing/{country}/{province}/{city}/UNIQUEIDENTIFIED
and the Controller should return all houses based on the specified variables.
If NO CITY is specified, I return listings on provincial level.
How do we set up dynamic views properly in a SEO friendly URL structure?
I found a much easier way to parse the URL inside the Controller, and then return the outcome to the view as follows:
public function getListing2(Request $request){
$maurl = $request->path();
$url_parts = parse_url($maurl);
$path_parts = explode('/', trim($url_parts['path'], '/'));
$var1 = $path_parts[0];
$var2 = $path_parts[1];
$var3 = $path_parts[2];
//DO my code
//Create the HTML object
return HTML OBJECT . . . . . . ; // :)
}
Related
Im using PHP MVC for my site and I have an issue with routing.
When I go to the index (front page), I use http://www.example.com or http://www.example.com/index.
When I go to the contact page, I use http://www.example.com/contact.
When I go to the services or about pages, I use http://www.example.com/content/page/services or http://www.example.com/content/page/about.
My index and contact pages have their own controllers because they are static pages. But the services and about pages are pulled from my db, thus dynamic. So I created a controller, named it content and just pass the parameters needed to get whatever page I want.
I want to make my URLs more consistent. If I go to the services or about pages, I want to use http://www.example.com/services or http://www.example.com/about.
How can I change my routing to meet this requirement? I ultimately would like to be able to create pages in my db, and then pull the page with a URL that looks like it has its own controller. Instead of having to call the content controller to get it to work.
Below are my controllers and what methods they contain, as well as my routing code.
Controllers:
IndexController
function: index
ContentController
function: page
function: sitemap
ContactController
function: index
function: process
Routing
class Application
{
// #var mixed Instance of the controller
private $controller;
// #var array URL parameters, will be passed to used controller-method
private $parameters = array();
// #var string Just the name of the controller, useful for checks inside the view ("where am I ?")
private $controller_name;
// #var string Just the name of the controller's method, useful for checks inside the view ("where am I ?")
private $action_name;
// Start the application, analyze URL elements, call according controller/method or relocate to fallback location
public function __construct()
{
// Create array with URL parts in $url
$this->splitUrl();
// Check for controller: no controller given ? then make controller = default controller (from config)
if (!$this->controller_name) {
$this->controller_name = Config::get('DEFAULT_CONTROLLER');
}
// Check for action: no action given ? then make action = default action (from config)
if (!$this->action_name OR (strlen($this->action_name) == 0)) {
$this->action_name = Config::get('DEFAULT_ACTION');
}
// Rename controller name to real controller class/file name ("index" to "IndexController")
$this->controller_name = ucwords($this->controller_name) . 'Controller';
// Check if controller exists
if (file_exists(Config::get('PATH_CONTROLLER') . $this->controller_name . '.php')) {
// Load file and create controller
// example: if controller would be "car", then this line would translate into: $this->car = new car();
require Config::get('PATH_CONTROLLER') . $this->controller_name . '.php';
$this->controller = new $this->controller_name();
// Check for method: does such a method exist in the controller?
if (method_exists($this->controller, $this->action_name)) {
if (!empty($this->parameters)) {
// Call the method and pass arguments to it
call_user_func_array(array($this->controller, $this->action_name), $this->parameters);
} else {
// If no parameters are given, just call the method without parameters, like $this->index->index();
$this->controller->{$this->action_name}();
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
} else {
header('location: ' . Config::get('URL') . 'error');
}
}
// Split URL
private function splitUrl()
{
if (Request::get('url')) {
// Split URL
$url = trim(Request::get('url'), '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
// Put URL parts into according properties
$this->controller_name = isset($url[0]) ? $url[0] : null;
$this->action_name = isset($url[1]) ? $url[1] : null;
// Remove controller name and action name from the split URL
unset($url[0], $url[1]);
// rebase array keys and store the URL parameters
$this->parameters = array_values($url);
}
}
}
In order to do this you should map your urls to controllers, check following example:
// route mapping 'route' => 'controller:method'
$routes = array(
'/service' => 'Content:service'
);
also controller can be any php callable function.
Answer Version 2:
Brother in the simplest mode, let's say you have an entity like below:
uri: varchar(255), title: varchar(255), meta_tags: varchar(500), body: text
and have access to StaticPageController from www.example.com/page/ url and what ever it comes after this url will pass to controller as uri parameter
public function StaticPageController($uri){
// this can return a page entity
// that contains what ever a page needs.
$page = $pageRepository->findByUri($uri)
// pass it to view layer
$this->renderView('static_page.phtml', array('page' => $page));
}
I hope this helps.
In my zf2 controller I want to retrieve the application base URL (for example http://example.com).
I tried the following call but it returns an empty string.
$this->request->getBasePath();
How can I then get the http://example.com part of URL in my controller?
I know this is not the prettiest way of doing it but, hey, it works:
public function indexAction()
{
$uri = $this->getRequest()->getUri();
$scheme = $uri->getScheme();
$host = $uri->getHost();
$base = sprintf('%s://%s', $scheme, $host);
// $base would be http://example.com
}
Or if you don't mind shortening everything you could do it in two lines:
public function indexAction()
{
$uri = $this->getRequest()->getUri();
$base = sprintf('%s://%s', $uri->getScheme(), $uri->getHost());
}
I'm not sure if there's a native way but you can use the Uri instance from Request.
You can take this snippet as a workaround until you've found a better solution:
$basePath = $this->getRequest()->getBasePath();
$uri = new \Zend\Uri\Uri($this->getRequest()->getUri());
$uri->setPath($basePath);
$uri->setQuery(array());
$uri->setFragment('');
$baseUrl = $uri->getScheme() . '://' . $uri->getHost() . '/' . $uri->getPath();
This works in the controller context. Note that in line 2, the Uri instance from the request is cloned in order not to modify the request's uri instance directly (to avoid side-effects).
I'm not happy with this solution but at least, it is one.
// Edit: Forgot to add the path, fixed!
I'm working on a project built in codeigniter that makes heavy use of routes and the remap function to rewrite urls. The current implementation is confusing and messy.
Essentially this is what the designer was trying to accomplish:
www.example.com/controller/method/arg1/
TO
www.example.com/arg1/controller/method/
Can anyone suggest a clean way of accomplishing this?
This actually only needs to happen for one specific controller. It's fine if all other controllers need to simply follow the normal /controller/model/arg1... pattern
Just to give you an idea of how the current code looks here is the 'routes' file: (not really looking into any insight into this code, just want to give you an idea of how cluttered this current setup is that I'm dealing with. I want to just throw this away and replace it with something better)
// we need to specify admin controller and functions so they are not treated as a contest
$route['admin/users'] = 'admin/users';
$route['admin/users/(:any)'] = 'admin/users/$1';
$route['admin'] = 'admin/index/';
$route['admin/(:any)'] = 'admin/$1';
// same goes for sessions and any other controllers
$route['session'] = 'session/index/';
$route['session/(:any)'] = 'session/$1';
// forward http://localhost/ball/contests to controller contests method index
$route['(:any)/contests'] = 'contests/index/$1';
// forward http://localhost/ball/contests/vote (example) to controller contests method $2 (variable)
$route['(:any)/contests/(:any)'] = 'contests/index/$1/$2';
// forward http://localhost/ball/contests/users/login (example) to controller users method $2 (variable)
$route['(:any)/users/(:any)'] = 'users/index/$1/$2';
// if in doubt forward to contests to see if its a contest
// this controller will 404 any invalid requests
$route['(:any)'] = 'contests/index/$1';
$route['testing/'] = 'testing/';
And the remap function that goes with it:
public function _remap($method, $params = array()){
// example $params = array('ball', 'vote')
// params[0] = 'ball', params[1] = 'vote'
/*
* Write a detailed explanation for why this method is used and that it's attempting to accomplish.
* Currently there is no documentation detailing what you're trying to accomplish with the url here.
* Explain how this moves the contest name url segment infront of the controller url segment. Also
* explain how this works with the routing class.
* */
$count = count($params);
if($count == 0){ // no contest specified
redirect('http://messageamp.com');
return;
}
$contest_name = $params[0];
unset($params[0]); //remove the contest name from params array because we are feeding this to codeigniter
if($count < 2) // no method specified
$method = 'index';
else{
$method = $params[1];
unset($params[1]);
}
//We need to scrap this, lazy-loading is a best-practice we should be following
$this->init(); //load models
//make sure contest is valid or 404 it
if(!$this->central->_check_contest($contest_name)){
show_404();
return;
}
$this->data['controller'] = 'contests';
$this->data['method'] = $method;
$this->data['params'] = $params;
// call the function if exists
if(method_exists($this, $method)){
return call_user_func_array(array($this, $method), $params);
}
show_404(); // this will only be reached if method doesn't exist
}
To get something like this:
www.example.com/controller/method/arg1/ TO www.example.com/arg1/controller/method/
You could do this in your routes.php config:
$route['(:any)/(:any)/(:any)'] = "$2/$3/$1";
However, if you want to have all of your other classes stick to the default routing, you would need to create routes for each of them to overwrite this default route:
$route['controller_name/(:any)'] = "controller_name/$1";
I'd like to obtain a list of all controllers in a Codeiginiter project so I can easily loop through each of them and add defined routes. I can't seem to find a method that will give me what I'm after ?
Here is the code snippet from the routes.php file where I would like to access the array: -
// I'd like $controllers to be dynamically populated by a method
//
$controllers = array('pages', 'users');
// Loop through each controller and add controller/action routes
//
foreach ($controllers as $controller) {
$route[$controller] = $controller . '/index';
$route[$controller . '/(.+)'] = $controller . '/$1';
}
// Any URL that doesn't have a / in it should be tried as an action against
// the pages controller
//
$route['([^\/]+)$'] = 'pages/$1';
UPDATE #1
To explain a little more what I'm trying to achieve.. I have a Pages controller which contains pages such as about, contact-us, privacy etc. These pages should all be accessible via /about, /contact-us and /privacy. So basically, any action/method in the Pages controller should be accessible without having to specify /pages/<action>.
Not sure if I'm going about this the right way ?
Well to directly answer to coding question, you can do this:
foreach(glob(APPPATH . 'controllers/*' . EXT) as $controller)
{
$controller = basename($controller, EXT);
$route[$controller] = $controller . '/index';
$route[$controller . '/(.+)'] = $controller . '/$1';
}
Buuuuuut this may not work out to be the most flexible method further down the line.
There are a few other ways to do it. One is to create a MY_Router and insert
$this->set_class('pages');
$this->set_method($segments[0]);
before/instead of show_404();
That will send /contact to /pages/contact, but only if no controllers, methods, routes are mapped to first.
OOOOOOORRRRRR use Modular Separation and add the following to your main routes.php
$routes['404'] = 'pages';
What I'd like to do is take the route for the current action along with any and all of the route and query string parameters, and change a single query string parameter to something else. If the parameter is set in the current request, I'd like it replaced. If not, I'd like it added. Is there a helper for something like this, or do I need to write my own?
Thanks!
[edit:] Man, I was unclear on what I actually want to do. I want to generate the URL for "this page", but change one of the variables. Imagine the page I'm on is a search results page that says "no results, but try one of these", followed by a bunch of links. The links would contain all the search parameters, except the one I would change per-link.
Edit:
Ok I got a better idea now what you want. I don't know whether it is the best way but you could try this (in the view):
url_for('foo',
array_merge($sf_request->getParameterHolder()->getAll(),
array('bar' => 'barz'))
)
If you use this very often I suggest to create your own helper that works like a wrapper for url_for.
Or if you only want a subset of the request parameters, do this:
url_for('foo',
array_merge($sf_request->extractParameters(array('parameter1', 'parameter3')),
array('bar' => 'barz'))
)
(I formated the code this way for better readability)
Original Answer:
I don't know where you want to change a parameter (in the controller?), but if you have access to the current sfRequest object, this should do it:
$request->setParameter('key', 'value')
You can obtain the request object by either defining your action this way:
public function executeIndex($request) {
// ...
}
or this
public function executeIndex() {
$request = $this->getRequest();
}
For symfony 1.4 I used:
$current_uri = sfContext::getInstance()->getRouting()->getCurrentInternalUri();
$uri_params = $sf_request->getParameterHolder()->getAll();
$url = url_for($current_uri.'?'.http_build_query(array_merge($uri_params, array('page' => $page))));
echo link_to($page, $url);
Felix's suggestion is good, however, it'd require you to hard core the "current route"..
You can get the name of the current route by using:
sfRouting::getInstance()->getCurrentRouteName()
and you can plug that directly in url_for, like so:
url_for(sfRouting::getInstance()->getCurrentRouteName(),
array_merge($sf_request->extractParameters(array('parameter1', 'parameter3')),
array('bar' => 'barz'))
)
Hope that helps.
With the same concept than Erq, and thanks to his code, I have made the same with some small changes, since my URL needs to convert some characters. Its generic though and should work with most forms, in order to save the parameters the user has chosen to search for.
public function executeSaveFormQuery(sfWebRequest $request)
{
$sURLServer = "http://";
$sURLInternalUri = "";
$page = "";
$sURLInternalUri = sfContext::getInstance()->getRouting()->getCurrentInternalUri();
$suri_params = $request->getParameterHolder()->getAll();
$sParams = http_build_query(array_merge($suri_params));
$dpos = strpos($sURLInternalUri, "?");
$sURLConsulta[$dpos] = '/';
$sURL = substr($sURLInternalUri, $dpos);
$dpos = strpos($sURL, "=");
$sURL[$dpos] = '/';
$sURLFinal = $sURLServer . $sURL . '?' . $sParams;
//$this->redirect($this->module_name . '/new');
self::executeNew($request, $sURLFinal);
//echo "var_dump(sURLFinal): ";
//var_dump($sURLFinal);
//echo "<br></br>";
//return sfView::NONE;
}
In executeNew, as easy as:
public function executeNew(sfWebRequest $request, $sURLQuery)
{
//$sURLQuery= "http://";
if ($sURLQuery!= "")
{
$this->form = new sfGuardQueryForm();
//echo "var_dump(sURLQuery)";
//var_dump($sURLQuery);
//echo "<br></br>";
$this->form->setDefault('surl', $sURLQuery);
}
else
{
$this->form = new sfGuardQueryForm();
}
}
echo $sf_context->getRequest()->getUri();