CodeIgniter - Optional parameters - php

I'm building my first CodeIgniter application and I need to make URLs like follows:
controllername/{uf}/{city}
Example: /rj/rio-de-janeiro
This example should give me 2 parameters: $uf ('rj') and $city ('rio-de-janeiro')
Another URL possible is:
controllername/{uf}/{page}
Example: /rj/3
This example should give me 2 parameters: $uf ('rj') and $page (3)
In other words, the parameters "city" and "page" are optionals.
I can't pass something like '/uf/city/page'. I need always or 'city' OR 'page'.
But I don't know how to configure these routes in CodeIgniter configuration to point to same method (or even to different methods).

I've found the correct result:
$route['controllername/(:any)/(:any)/(:num)'] = 'ddd/index/$1/$2/$3';
$route['controllername/(:any)/(:num)'] = 'ddd/index/$1/null/$2'; // try 'null' or '0' (zero)
$route['controllername/(:any)'] = 'ddd/index/$1';
The Index method (inside "ControllerName") should be:
public function Index($uf = '', $slug = '', $pag = 0)
{
// some code...
if (intval($pag) > 0)
{
// pagination
}
if (!empty($slug))
{
// slug manipulation
}
}
Hope it helps someone.
Thank you all.

public function my_test_function($not_optional_param, $optional_param = NULL)
{
//do your stuff here
}
have you tried this?

For example, let’s say you have a URI like this:
example.com/index.php/mycontroller/myfunction/hello/world
example.com/index.php/mycontroller/myfunction/hello
Your method will be passed URI segments 3 and 4 (“hello” and “world”):
class MyController extends CI_Controller {
public function myFunction($notOptional, $optional = NULL)
{
echo $notOptional; // will return 'hello'.
echo $optional; // will return 'world' using the 1st URI and 'NULL' using the 2nd.
}
}
Reference: https://codeigniter.com/user_guide/general/controllers.html

Related

Laravel 8 routes to controllers. SEO friendly URL structure

I am trying to figure out how to achieve a specific URL structure in a Laravel 8 project and the necessary route to achieve this. What I want is:
// Example urls to listings in the business directory.
// These urls should be routed to the directory controller.
www.domain-name.com/example-business-name-d1.html
www.domain-name.com/example-business-name-d15.html
www.domain-name.com/example-business-name-d100.html
www.domain-name.com/example-business-name-d123.html
www.domain-name.com/example-business-name-d432.html
// Example urls to articles/posts in the blog.
// These urls should be routed to the posts controller.
www.domain-name.com/example-post-name-p5.html
www.domain-name.com/example-post-name-p11.html
www.domain-name.com/example-post-name-p120.html
www.domain-name.com/example-post-name-p290.html
www.domain-name.com/example-post-name-p747.html
// We want to avoid the more traditional:
www.domain-name.com/directory/example-business-name-1.html
www.domain-name.com/blog/example-post-name-5.html
This is because we don't want the strings “directory” or “blog” contained in the url for every listing or blog post. Search engine results work better without it.
So far I am using a catch-all route {any} at the bottom of the web.php routes file to “catch all” routes that get that far. I then manipulate the string provided by the path to get the ID and single character token from the end of the urls. I then have these 2 variables but can figure out how to pass these onto the right controllers!
Or am I being really dumb and there is a much better way of achieving this?
Route::get('{any}', function($any = null){
// Break up the url into seperate parts.
$pieces = explode("-", $any);
$pieces = array_reverse($pieces);
$piece = $pieces[0];
// Remove the .html
$piece = substr($piece, 0, -5);
// Get the two parts of the identifier.
$id = substr($piece, 1);
$token = substr($piece, 0, 1);
// Call correct controller based on the token.
switch ($token) {
case "d":
// HERE I WANT TO PASS THE ID ON TO THE SHOW ACTION OF THE DIRECTORY CONTROLLER
break;
case "p":
// HERE I WANT TO PASS THE ID ON TO THE SHOW ACTION OF THE POSTS CONTROLLER
break;
default:
return abort(404);
break;
}
});
I would split the path into 2 variables ($slug and $id) and directly pass it to the controller.
Route::get('{slug}-d{id}.html', 'DirectoryController#show')
->where(['slug' => '([a-z\-]+)', 'id' => '(\d+)']);
Route::get('{slug}-p{id}.html', 'PostController#show')
->where(['slug' => '([a-z\-]+)', 'id' => '(\d+)']);
And in your controllers
class DirectoryController
{
public function show(string $slug, int $id) {}
}
class PostController
{
public function show(string $slug, int $id) {}
}
I can see two ways of achieving this result:
Create an intermediate controller
Route::get('{path}', 'CheckPathController#redirect')
Then in your CheckPathController you do all the checks and your call the proper controller action:
public function redirect(Request $request, $path) {
// Your checks on $path, extract $id and content type
if($isPost) {
$controller = resolve(PostController::class);
return $controller->show($request, $id);
}
if($isBusiness) {
$controller = resolve(BusinessController::class);
return $controller->show($request, $id);
}
// No matches, error 404
abort(404);
}
Complex regex
see: https://laravel.com/docs/8.x/routing#parameters-regular-expression-constraints
I'm not a regexp master, this should be a basic was to match any {word}-{word}-...-p{id}.html pattern but it will break in case of unexpected chars
Route::get('{path}', 'PostController::show')
->where(['path' => '([\w]*-)*p[0-9]+\.html$']);
Route::get('{path}', 'BusinessController::show')
->where(['path' => '([\w]*-)*d[0-9]+\.html$']);
Note that in this case, you controller will receive the pull $path string, so you will need to extract the id there.
You can match the slug using regex
Route::get('/{any}', 'YourController#methodName')->where(['any' => '.*(-d(.*?)\.).*']);
Repeated with p
Then when you pickup your $site in your controller method you can use regex to grab the site.
public function methodName($site)
{
preg_match('/.*(-(d(.*?))\.).*/', $site, $parts); //or something similar, $parts[2] will have what you want
}
OR
This will give your controller method d{number} or p{number}
Route::get('/{site}', function($site) {
$code = preg_match('/.*(-(d(.*?)|p(.*?))\.).*/', $site, $parts) ? $parts[2] : null;
$controllerName = 'ControllerA';
if(isset($code) && !is_null($code) && Str::contains($code, 'p')) {
$controllerName = 'ControllerB';
}
$controller = app()->make('App\Http\Controllers\Application\\' . $controllerName);
return $controller->callAction('methodName', $params = ['code' => $code]);
})->where(['site' => '.*(-(d|p)(.*?)\.).*']);

Get URL parameters inside custom module

I've created a custom block like this:
class HelloBlock extends BlockBase implements BlockPluginInterface{
/**
* {#inheritdoc}
*/
public function build() {
$config = $this->getConfiguration();
$result = db_query('SELECT * FROM {test}');
return array(
'#theme' => 'world',
'#test' => $result
);
}
}
And I now want to programmatically get some parameter from the URL.
For example:
If the URL is http://localhost/drup/hello/5569 I want to get hold of the value 5569 inside my module.
I have tried arg(1) and drupal_get_query_parameters() but I got this error messages:
Call to undefined function `Drupal\hello\Plugin\Block\arg()`
and
Call to undefined function `Drupal\hello\Plugin\Block\drupal_get_query_parameters()`
How can I get the parameters?
Use \Drupal\Core\Routing;:
$parameters = \Drupal::routeMatch()->getParameters();
The named parameters are available as
$value = \Drupal::routeMatch()->getParameter('slug_name_from_route');
Where 'slug_name_from_router' comes from your routing.yml path property
path: '/your/path/{slug_name_from_route}'
If you want the raw parameter without any upcasting you can get
$value = \Drupal::routeMatch()->getRawParameter('slug_name_from_route');
I used to get the parameter value from URL (localhost/check/myform?mob=89886665)
$param = \Drupal::request()->query->all();
And applied in my select Query
$query = \Drupal::database()->select('profile_register', 'p1');
$query->fields('p1');
$query->condition('p1.mobileno', $edituseprof);
$query->condition('publishstatus', 'no');
$result = $query->execute()->fetchAll();
But on multiple parameter value, i am now successful(Ex: http://10.163.14.41/multisite/check/myform?mob=89886665&id=1)
$query = \Drupal::database()->select('profile_register', 'p1');
$query->fields('p1');
$query->condition('p1.mobileno', $edituseprof['mob']);
$query->condition('p1.ids', $edituseprof['id']);
$query->condition('publishstatus', 'no');
$result = $query->execute()->fetchAll();
arg() is deprecated in drupal 8, however we can get values like arg() function does in drupal 7 & 6
$path = \Drupal::request()->getpathInfo();
$arg = explode('/',$path);
print_r($arg); exit();
The output would be parameters in url except basepath or (baseurl),
Array
(
[0] =>
[1] => node
[2] => add
)
To get query parameter form the url, you can us the following.
If you have the url for example,
domainname.com/page?uid=123&num=452
To get "uid" from the url, use..
$uid = \Drupal::request()->query->get('uid');
To get "num" from the url, use..
$num = \Drupal::request()->query->get('num');
$route_match = \Drupal::service('current_route_match');
$abc = $route_match->getParameter('node'); //node is refrence to what you have written in you routing file i.e:
in something.routing.yml
entity.node.somepath:
path: '/some/{node}/path'
I have used {node} as arg(1). And I can access it by using *->getParameter('node');
Hope this will work.
If your url is like this below example
http://localhost/drupal/node/6666
Then you have to get the full url path by
$current_path = \Drupal::request()->getPathInfo();
then explode the path to get the arguments array.
$path_args = explode('/', $current_path);
Another example if value passed by a key in url like below where id contains the value
http://localhost/drupal?id=123
You can get the id by given drupal request
$id = \Drupal::request()->query->get('id');
Here's the example of accessing URL parameters and passing them to a TWIG template,
I am considering you have already created your module and required files and suppose "/test?fn=admin" is your URL
In Your .module file implement hook_theme and define variables and template name (Make sure you replace "_" with "-" when creating the template file)
function my_module_theme () {
return [
'your_template_name' => [
'variables' => [
'first_name' => NULL,
],
];
}
Now create your controller and put below code in it.
namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
class MyModule extends ControllerBase {
public function content(Request $request) {
return [
'#theme' => 'my_template',
'#first_name' => $request->query->get('fn'), //This is because the parameters are in $_GET, if you are accessing from $_POST then use "request" instead "query"
];
}
}
Now in your TWIG file which should be "my-template.html.twig" you can access this parameter as,
<h3>First Name: {{ first_name }}</h3>
And its done.
Hope this helps.
The Drupal docs are great on this: https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes
define your path variable in yaml
example.name:
path: '/example/{name}'
...
Add the variable in your method and use it
<?php
class ExampleController {
// ...
public function content($name) {
// Name is a string value.
// Do something with $name.
}
}
?>

replace a wildcard for url comparison

I need to check valid routes from a route files where i want to put a wildcard (or placeholder) for url part that is dynamic.
The router read all routes in that json format:
{"action" : "BlogController#showPost", "method" : "GET", "url" : "showPost/id/{}"}
I need when the comparsion occurs to change the holder {any} with the current value and maybe allow to put regex expression inside the {any} tag.
An url like this:
showPost/id/211 have to be compared with showPost/id/{} and should return true. If possible i would like to allow putting {'[0-9]\'} as optional param to ensure that the real value match a regex expression.
What best solution to do this?
The comparsison method is this:
public static function findAction($query) {
foreach (Router::getInstance()->routes as $route) {
if ($route->url == $query) {
return $route;
}
}
}
The $query contains /showPost/id/221 and the Router::getInstance()->routes->route->url contains showPost/id/{}
The post is related to this auto-solved question:
how to make nice rewrited urls from a router
I don't re-post router code in order to avoid duplication.
Thanks in advance
I found a solution using "?" as a wildcard for routes json file. Its not maybe the best way but actually works.
The method now replace (and try to check) the real path queries with ? and check the routes each cycle.
public static function findAction($query) {
//d($query);
$queryArray = explode("/", $query);
//d($queryArray);
foreach (Router::getInstance()->routes as $route) {
if ($route->url == $query) {
// replace current routes url with incoming url
$route->url = $query;
return $route;
} else {
$queryReplace = null;
foreach ($queryArray as $key => $value) {
if (strpos($route->url,"?")) {
$queryReplace = str_replace("?", $value, $route->url);
if($queryReplace == $query) {
$route->url = $query;
return $route;
}
}
}
}
I still would like to put {any or regex} but atm i did not found a solution to this.

Laravel 4 : How to pass multiple optional parameters

I am new to laravel and I am really struggling to understand how to pass multiple optional url parameters.
What is the standard way to code routes when passing 3 optional parameters to the controller?
Also is there a way to code a route to allow named parameters to be passed to the controller?
such as
public/test/id=1&page=2&opt=1
or
public/test/id=1/page=2/opt=1
Thanks for any help
If you have multiple optional parameters
Route::get('test',array('as'=>'test','uses'=>'HomeController#index'));
And inside your Controller
class HomeController extends BaseController {
public function index()
{
// for example public/test/id=1&page=2&opt=1
if(Input::has('id'))
echo Input::get('id'); // print 1
if(Input::has('page'))
echo Input::get('page'); // print 2
//...
}
}
Named parameters are usually done as route segments but without explicit naming. So for example you could o something like this:
Route:get('test/{id?}/{page?}/{opt?}', function ($id = null, $page = null, $opt = null) {
// do something
});
$id, $page and $opt are all optional here as defined by the ? in the segment definitions, and the fact that they have default values in the function. However, you'll notice there's something of a problem here:
They have to appear in the URL in the correct order
Only $opt is truly optional, $page must be supplied if $opt is, and $id must be if $page is
This is a limitation brought about by the way that Laravel maps the named segments to function/method parameters. You could theoretically implement your own logic to make this work, however:
Route:get('test/{first?}/{second?}/{third?}', function ($first = null, $second = null, $third = null) {
if ($first) {
list($name, $value) = #explode('=', $first, 2);
$$name = $value;
}
if ($second) {
list($name, $value) = #explode('=', $second, 2);
$$name = $value;
}
if ($third) {
list($name, $value) = #explode('=', $third, 2);
$$name = $value;
}
// you should now have $id, $page and $opt defined if they were specified in the segments
});
Not that this is a very naïve solution, relying on blind exploding by = as well as setting the name of an arbitrarily-inputted variable (which is obviously asking for trouble). You should add more checking to this code, but it should give you an idea of how to get over the aforementioned two problems.
It should probably be noted that this is kinda going against the 'right way' to do routing and URIs in Laravel, so unless you really need this functionality, you should rethink the way you are forming these URIs to a way that the Laravel framework is more set-up for.

Jump parameter in function

I have a controller in codeigniter called 'Main' with this function:
public function index ($id = false, $filter ='htmlentities') {
...
...
}
I want to call it through the URI 'jumping' the first parameter so that it remains 'false':
I would like to be able through the URI using something like:
'main/index/false/myfilter'
However this doesn't work, does anyone know how can I 'jump' this first parameter so that its value remains being false but i am able to change the filter?
(changing parameter order in function is NOT an option)
Just test for the string 'false' at the top of the controller method:
public function index ($id = false, $filter ='htmlentities') {
if ($id == 'false') {
$id = false;
}
// ...
}
Another possibility might be to use a custom route and simply discard the first parameter. In your app/config/routes.php file, add the rule:
$route['main/index/:filter'] = 'main/index/false/$1';
Now, when you visit 'main/index/myfilter', the $id parameter will be set to false (a string, not a boolean!)

Categories