4

I am using CakePHP 2.4

I have a url for e.g. /sent?note=123&test=abc

I want to remove the note parameter while giving me the rest of the url back. i.e.

/sent?test=abc

I have a piece of code that works but only for query parameters. I would like to find out how to improve my code so that it works with:

  1. named parameters
  2. passed parameters
  3. hashtag

E.g.

/sent/name1:value1?note=123&test=abc#top

This is the code I have written so far. https://github.com/simkimsia/UtilityComponents/blob/master/Controller/Component/RequestExtrasHandlerComponent.php#L79

UPDATE PART III:

Let me illustrate with more examples to demonstrate what I mean by a more generic answer.

The more generic answer should assume no prior knowledge about the url patterns.

Assuming given this url

/sent/name1:value1?note=123&test=abc

I want to get rid of only the query parameter note and get back

/sent/name1:value1?test=abc

The more generic solution should work to give me back this url.

Another example. This time to get rid of named parameters.

Assuming given this url again

/sent/name1:value1?note=123&test=abc

I want to get rid of name1 and get back:

/sent?note=123&test=abc

Once again, the more generic solution should be able to accomplish this as well.

UPDATE PART II:

I am looking for a more generic answer. Assuming the web app does not know the url is called sent. You also do not know if the query parameters contain the word note or test. How do I still accomplish the above?

I want to be able to use the same code for any actions. That way, I can package it into a Component to be reused easily.

UPDATE PART I:

I understand that hashtag will not be passed to PHP. So please ignore that.

Clues on how to get the values from the hashtag:

Community
  • 1
  • 1
Kim Stacks
  • 10,202
  • 35
  • 151
  • 282
  • The hashtag is client side only (it's never sent to PHP) so it will never be part of the request URL. – dcro Aug 28 '13 at 13:35
  • Got it. I will edit my question then. – Kim Stacks Aug 28 '13 at 13:42
  • when you say `"more generic solution"` do you mean one that works outside the framework? – Angel S. Moreno Sep 04 '13 at 16:45
  • @AngelS.Moreno I am okay with a solution that works with CakePHP. if there is one for CakePHP and one for raw PHP, that would be even more awesome. :) – Kim Stacks Sep 05 '13 at 09:14
  • @AngelS.Moreno By more generic solution, I mean compared to the one I already have. Mine works ONLY if i remove query parameters from a url that has query parameters. If there is a solution that can remove query parameters from a url that has query, named and other kinds of parameters. that would be "more generic" than mine. Also if the solution can remove named parameters from a url that has a bunch of different types of parameters. It is also more generic than mine. – Kim Stacks Sep 05 '13 at 09:17
  • @AngelS.Moreno Also the "more generic" was in response to the answer given by Manu whose answer clearly only works with the parameters "note", "test" etc. the solution i am looking for should work regardless the names of the parameters. – Kim Stacks Sep 05 '13 at 09:19

3 Answers3

2

What about using mod_rewrite ?

You can handle your URLS in an other way :

<IfModule mod_rewrite.c>  
  RewriteEngine on  
  RewriteRule ^/sent/name:(.*)?note=(.*)&test=([az-AZ])(#(.*))$ /sent/name:$1/note:$2/test:$3$4
</IfModule>

I'm not sure about the regex, but this may pass variables to cakePHP in a clean way (but I haven't tested it, though)

[EDIT]

But if you want to work without knowing urls patterns, then you can use the $this->request array : with an URL like

action/id:10/test:sample?sothertest=othersample&lorem=ipsum

I can get all the arguments using this in my controller :

// In your controller's method
$arguments= array_merge_recursive($this->request->named,$this->request->query);

Then, $arguments will be an array containing both named and passed params :

array(
    'id' => '10',
    'test' => 'sample',
    'sothertest' => 'othersample',
    'lorem' => 'ipsum'
)

Is it better ?

[EDIT 2] If you know which parameter you have to get rid of, and directly redirect to the new URL, this should work:

action/id:10/test:sample?knownParam=value&lorem=ipsum

or with

action/id:10/knownParam:value?othertest=othersample&lorem=ipsum

In your controller/appController action:

    // Name of the known param
    $knownParam = 'knownParam';
    // Arguments
    $arguments = array_merge_recursive($this->request->named, $this->request->query);
    if (key_exists($knownParam, $arguments)) {
        // Unset in named params:
        unset($arguments[$knownParam]);
        // Creating url:
        $url = array(
            'admin' => $this->request->params['prefix'],
            'plugin' => $this->request->params['plugin'],
            'controller' => $this->request->params['controller'],
            'action' => $this->request->params['action']
        );
        // Adding args
        foreach ($arguments as $k => $v) {
            $url[$k] = $v;
        }
        // Redirect
        $this->redirect($url);
    }

This will redirect both urls to

action/id:10/param1:value1/param2:value2

without the "know param"...

  • I am looking for a more generic answer. Assuming you do not know the url is called sent. You also do not know if the query parameters contain the word note or test. How do I still accomplish the above? – Kim Stacks Sep 04 '13 at 08:24
  • so let say, i want to remove lorem, I am supposed to get back action/id:10/test:sample?sothertest=othersample, how does your code do that? – Kim Stacks Sep 05 '13 at 09:20
  • @kimsia : Maybe I don't understand the question really well... There's only one known parameter to remove from the query and you don't know it it's a named or passed parameter, am I right ? So maybe, you can check both passed and named arrays, remove the known param and redirect the user ? – Manuel Tancoigne Sep 05 '13 at 15:21
  • Thanks for your effort. I appreciate it greatly. but I want to get back the url exactly MINUS the known parameter. I would definitely know that the known Parameter is either a named or a query parameter. – Kim Stacks Sep 06 '13 at 07:40
  • Hi Manu, thanks for your help. I finally figured out the answer. – Kim Stacks Sep 06 '13 at 17:10
1

Let us say you have created the following routes:

Router::connect('/projects/:id/quotations/:quotation_id/*', 
    array(
        'controller' => 'quotations', 
        'action' => 'get_all_by_project', "[method]" => "GET"),
    array(
        'pass' => array('id', 'quotation_id'),
        'id' => '[0-9]+',
        'quotation_id' => '[0-9]+'
    ),
    array(
        'named' => array(
            'name1',
            'name2',
            'name3'
        )
    )
);

In this route:

  • Passed parameters will be the compulsory parameters id and quotation_id obeying the order as the first and second passed parameter
  • Named parameters will be the optional parameters name1, name2, and name3.
  • Query parameters will, of course, be optional as well and depend on what you actually have in the url.
  • you need the asterisk at the end so that the named parameters can pass through

Let us assume the following pretty url and the ugly url of the same action:

/projects/1/quotations/23/name2:value2/name3:value3/name1:value1?note=abc&test=123 (pretty)

/quotations/get_all_by_project/1/23/name2:value2/name3:value3/name1:value1?note=abc&test=123 (ugly)

First part of the answer: Let us consider only the scenario of removing the query parameter note.

We should get back

/projects/1/quotations/23/name2:value2/name3:value3/name1:value1?test=123 (pretty)

/quotations/get_all_by_project/1/23/name2:value2/name3:value3/name1:value1?test=123 (ugly)

The following Component method will work. I have tested it on both the ugly and pretty urls.

public function removeQueryParameters($parameters, $here = '') {
    if (empty($here)) {
        $here       = $this->controller->request->here;
    }
    $query          = $this->controller->request->query;
    $validQueryParameters   = array();

    foreach($query as $param=>$value) {
        if (!in_array($param, $parameters)) {
            $validQueryParameters[$param] = $value;
        }
    }

    $queryString = $this->_reconstructQueryString($validQueryParameters);

    return $here . $queryString;
}

protected function _reconstructQueryString($queryParameters = array()) {
    $queryString = '';
    foreach($queryParameters as $param => $value) {
        $queryString .= $param . '=' . $value . '&';
    }

    if (strlen($queryString) > 0) {
        $queryString = substr($queryString, 0, strlen($queryString) - 1);
        $queryString = '?' . $queryString;
    }
    return $queryString;
}

This is how you call the Component method.

$newUrl = $this->RequestExtrasHandler->removeQueryParameters(array('note'));

RequestExtrasHandler is the name of Component I wrote that has the above method.

Second part of the answer: Let us consider only the scenario of removing the named parameter name2.

We should get back

/projects/1/quotations/23/name3:value3/name1:value1?test=123 (pretty)

/quotations/get_all_by_project/1/23/name3:value3/name1:value1?test=123 (ugly)

The following Component method will work. I have tested it on both the ugly and pretty urls.

public function removeNamedParameters($parameters, $here = '') {
    if (empty($here)) {
        $here   = $this->controller->request->here;
    }
    $query      = $this->controller->request->query;
    $named      = $this->controller->request->params['named'];

    $newHere    = $here;
    foreach($named as $param=>$value) {
        if (in_array($param, $parameters)) {
            $namedString = $param . ':' . $value;
            $newHere = str_replace($namedString, "", $newHere);
        }
    }

    $queryString = $this->_reconstructQueryString($query);

    return $newHere . $queryString;
}

This is how you call the Component method.

$newUrl = $this->RequestExtrasHandler->removeNamedParameters(array('name2'));

RequestExtrasHandler is the name of Component I wrote that has the above method.

Third part of the answer: After I realized that passed parameters are compulsory, I found that there is no real business need to remove passed parameters if at all.

Another problem is that unlike named parameters and query parameters, passed parameters tend not to have the keys present in the $this->controller->request->params['pass']

$this->controller->request->params['pass'] is usually in the form of a numerically indexed array.

Hence, there is huge challenge to take out the correct passed parameters.

Because of that, I will not create any method to remove passed parameters.

Check out the code here in details:

https://github.com/simkimsia/UtilityComponents/blob/d044da690c7b83c72a50ab97bfa1843c14355507/Controller/Component/RequestExtrasHandlerComponent.php#L89

Kim Stacks
  • 10,202
  • 35
  • 151
  • 282
0

maybe simple php functions can do what you want

$url =  '/sent?note=123&test=abc'; //for example
$unwanted_string = substr($url, 0,strrpos($url,'&') + 1);
$unwanted_string = str_replace('/sent?', '', $unwanted_string);
$url = str_replace($unwanted_string, '', $url);
Essam Elmasry
  • 1,212
  • 11
  • 11