Is there any possible way to remove a url rule from the urlManager on the go?
I know I can add a rule via urlManager->addRules(), but how do I delete them?
Thanks.
EDIT:
My aim is to generate the rules dynamically based on the subdomain.
For example, a subdomain of certain type would have a rule <catalog>/<brand>/<product> leading to a certain product, while the other would have a different rule. Also, the subdomains are also dynamic, meaning that I can't create any static conditions.
You have all rules here:
Yii::app()->urlManager->rules;
That's an associative array, you can easily loop through it and manipulate it.
It would be great, if you could provide more details about why you need such behavior.
In common, you can extend default CUrlManager class and add needed functions. After that you can specify this new, let's say, CustomUrlManager in config.php as implementation for your urlManager.
But again - what problem do you try to solve?
Unfortunately, there wasn't a clear way to solve this problem.
The inner rules array _rules is declared private, and there are no methods for working with it apart from addRules(). This method, however, only allows you to append the processed rules to the start/end of the _rules array. You won't be able to delete the rules you've added, too.
The solution I came up with is bad practice, but there was no other way of working around my issue (that I know of).
Declare a method inside CUrlManager
protected function clearInnerRules() {
$this->_rules = array(); // clears the inner rules array
}
In the child class UrlManager that is used as the manager for the Yii app, declare:
public function reprocessRules() {
Yii::app()->cache->delete(self::CACHE_KEY); // remove anything rule-connected from cache
$this->clearInnerRules();
$this->processRules();
}
Using the onBeginRequest event, add rules to the placeholders in the array:
// code
Yii::app()->urlManager->rules['rulePlaceholder'] = $newRule;
Yii::app()->urlManager->reprocessRules();
// code
Done. Now I can easily delete the rules or replace them at any moment (provided, this moment is before the request). Sometimes you just gotta do what you gotta do, I guess.
Related
I already have a GET route with an URI /projects/{id} which displays Infos of a project with a given id. I also have a GET index route (/projects), which shows all my projects.
My problem is that I currently try to create different indexes (for example one which only displays the projects where I am assigned [e.g. on /projects/mines], or the projects which are pending administrator approval [e.g. on /projects/proposals], and still others displays).
So I want to know if I can have two GET routes /projects/{id}and /projects/{display_mode} which will be calling two differents methods of my ProjectController (respectively show and index).
Thanks for your help! :)
You may have one route /projects which returns all projects as default.
If there is query parameter like
/projects?displayMode=proposals
then you can apply filters.
In your controller it would look something like this
$projects = Project::query();
if ($request->query('displayMode') == 'proposals')
$projects->where('pending', true)
return $projects->get();
You can add multiple filters too in the same way
I'm not sure about specific Laravel options for the route definitions (sorry!), but if the {id} will always be an integer and {display_mode} will always have non-digits in it, you could keep just one route, but do the conditional handling in your controller. Just have the mainAction do something likeā¦
return preg_match('/^\d+$/', $param) ? idHelperAction($param) : displayModeHelperAction($param);
Then create those two helper functions and have them return whatever you want.
$param is supposed to be whatever you get from that route parameter -- /projects/{param}.
That should call the idHelperAction for routes where $param is all digits and nothing else; otherwise, it should call the displayModeHelperAction. Either way, it sends the same $param to the helper function and returns whatever that helper function returns -- effectively splitting one route definition into two possible actions.
Of course, you might have to add some context in the code sample. If the functions are all defined in the same class, you might need to use $this->idHelperAction($param) or self::idHelperAction($param) (and the same with the other helper action), depending on whether it's static or not; or tell it where to find the functions if you put them in another class, etc., etc. -- all the normal contextual requirements.
Is it OK to use
$id = $request->get('some_id');
instead of setting some parameters in Routes AND Controller like:
Route::get('some_page/{parameters}', 'controllerName#functionName');
function functionName($parameters)
{
$id = $parameters;
}
Appreciation
Of course it's good. When you're using GET, both ways are similar and if you like to use $request->get() for some reason, it's totally ok.
If you're using Form, it's the only right way. Plus, you can create custom Request class to use it for validation and other operations:
https://laravel.com/docs/master/validation#form-request-validation
They have two fundamentally different goals.
Using $request->get() is a way to retrieve a value from inside the php's REQUEST object regardless of its association with routing pattern you use.
Following HTTP's standards, you probably use $_GET to read some value without it changing the database [significantly] and you use $_POST to write data to you server.
While {pattern} in routing ONLY and ONLY should be used as a way for your application to locate something, some resource(s); in other words, its only goal is to help you route something in your server.
Nevertheless, in certain cases, such as /user/{id} the value of {id} might encounter some overlapping as to whether be treated as a route parameter or as a key of $_REQUEST.
Things such as tokens, filters criteria, sorting rules, referrers (when not significantly) etc. can be read right from $_REQUEST without interfering them into routing pattern of you application.
In most of the tutorials for PHP MVC design structures, a router class is used to take user input and look up the right controller in order to process that input accurately. The input normally takes the form of a url. For example http://example.com/foo/bar/ would ask the router to find the controller named foo and fire the method bar.
PHP also has an auto-include function which requires a consistent naming system to used for your classes. For example, if I wanted to get an instance of the class Foo, I would need the file to be called Foo.php and inside would be a class called Foo.
Unfortunately, in tandem, these mechanisms place competing demands on the naming system. The url is very 'front facing' and as such, clients will often need it to reflect the content of a particular page. For example, if I have a page that gives directions to a venue, the client may need this page to be at http://example.com/venues/directions/. Later the content may change and the number of venues is reduced to 1 and now the client wishes the url to read http://example.com/venue/directions/. Obviously, this is a very trivial example but I think the need to change urls occasionally is clear.
Since the urls are very tightly connected to the controller classes, a change to a url means the class file name, the class name itself and any instances of that class will all require changing. For more complex systems this seems very time consuming, to me.
What sort of solutions are there to this problem? It seems to me that a directory of sorts will be necessary in which relations between urls and controllers are stored such that any url could be used to call any controller regardless of what either is named. Thus we would have a venues controller and a venues url and when the client requests a change in the url, we just make a change in the directory translating the new "venue" into "venues".
I think the best way to do this would be in a database table but this then requires an extra query for every user request. Is a database the right way to implement a directory? Is a directory even the best way to solve the above problem? Is this problem even a problem in the first place???
Some solutions I have seen to this is to explicitly specify a routing "connection" to a controller/action.
For example, in NiceDog, you can specify routes like
R('venues?/directions')
->controller('VenueController')
->action('directionsAction')
->on('GET');
Allowing a regular expression to match the URL. (The expression above will match venue/directions or venues/directions) I like this method, because it decouples the controller naming scheme from the URL naming scheme.
Another more configuration-based approach would be to have some kind of explicit mapping:
$routes = array(
'venues?' =>
array('controller'=>'VenueController','action'=>'indexAction'),
'venues?/directions' =>
array('controller'=>'VenueController','action'=>'directionsAction')
);
And then a simple dispatch function:
function dispatch($url,$routes) {
foreach ($routes as $pattern=>$map) {
if (preg_match($pattern,$url,$matches)) {
$controller = new $map['controller']();
$action = $map['action'];
call_user_func_array(
array($controller,$action),
array_slice($matches,1)
);
return true;
}
}
throw new Exception('Route not found.');
}
A good way to solve this problem is for the MVC framework to allow you to specify your own routing rules. For example, see URI Routing in the CodeIgniter framework's documentation.
To go along with your example, this would allow you to remap requests for /venues/ to /venue.
Rather than using controller/action/key1/value1/key2/value2 as my URL, I'd like to use controller/action/value1/value2. I think I could do this by defining a custom route in my Bootstrap class, but I want my entire application to behave this way, so adding a custom route for each action is out of the question.
Is this possible? If so, how would I then access valueN? I'd like to be able to define the parameters in my action method's signature. e.x.:
// PostsController.php
public function view($postID) {
echo 'post ID: ' . $postID;
}
I'm using Zend Framework 1.9.3
Thanks!
While I don't think it's possible with the current router to allow N values (a fixed number would work) you could write a custom router that would do it for you.
I would question this approach, however, and suggest that actually listing all of your routes won't take long and will be easier in the long run. A route designed as you've suggested would mean that either your named parameters are always in the same order, i.e.
/controller/action/id/title/colour
or that they are almost anonymous
/controller/action/value1/value2/value3
With code like
$this->getRequest()->getParam('value2'); //fairly meaningless
Does it have to be N or can you say some finite value? For instance can you imagine that you'll never need more than say 5 params? If so you can set up a route:
/:controller/:action/:param0/:param1/:param2/:param3/:param4
Which will work even if you don't specify all 5 params for every action. If you ever need 6 somewhere else you can just add another /:paramN onto the route.
Another solution I've worked with before is to write a plugin which parses the REQUEST_URI and puts all the extra params in the request object in the dispatchLoopStartup() method. I like the first method better as it makes it more obvious where the params are coming from.
I'm using URI re-routing in CI to make better URLS. An example here would be:
$route['users/(:any)'] = "users/index/$1";
The aim here is to get rid of the index from URL. This works well. However it stops me from being able to access any functions in the users controller, for example
mywebsite.com/users/messages
Just redirects to the users/index. Is there a way around this?
Define the list of methods you wish to keep then let the rest wildcard match:
$route['users/(messages|login|something)'] = "users/$1";
$route['users/(:any)'] = "users/index/$1";
Hi I'm not familiar with CI but i have a similar routing system. The (:any) works as a sort of catch all. When my router checks the routing rules it stops checking if it has found an exact match. So then the answer would be to just add another functions route before the catch all. Like
$route['users'] = "users/index/";
$route['users/messages/(:any)'] = "users/checkmessages/$1";
$route['users/(:any)'] = "users/$1";
Not sure how CI handles this but i can think of something like the first URL part is the class and the second the function. The router or the controller module should have the intelligence to start calling the function even without the routing table.
The routing table should only be used in case of "other callable names" like i did above with the messages/checkmessages thingy.
hope that gets you going.