Yii framework urlManager rewrite rules - yii

I have a url that looks like this:
<controller>/<action>/param/value
and I want it to like something like this:
param/value
How can it be achieved?
I tried this rule but not sure if it's ok (controller is account and action is index).
'user/<user:.*>' => 'account/index/user/test'

If I uderstand your question correctly, you want to handle URL's like this:
mysite.domain/user/username123
And call actionIndex in AccountController with param User, which (in this case) equals "username123"
In this case you can try the rule below:
'user/<user:.*>' => 'account/index/<user>'
But maybe you will need to change the declaration if your action:
function actionIndex($user){
// code
}

I would avoid putting params into action signatures as yii doens't go about processing actions with mismatching signatures [gracefully] at all... In fact, putting $user in will bind that action to always need a $user specified and if you ever decide to change your functionality, tracking down why your action isn't being called would be harder than determining why your $_GET isn't set... I would suggest in stead of adding the $user into the signature, just do something as follows in your action.
//will always run on /user/<USER:.*>
function actionIndex(){
$user = isset($_GET['user'])?$_GET['user']:NULL;
if(!is_null($user)){
//your user specific account action..
}else{
//handle your error gracefully..
}
}
This approach lets your action be more versatile. The URL rule should be as follows:
'user/<user:.*>' => 'account/index/user/<user>' //user is defined as a get...
Hope that helps && happy coding!

Related

CakePHP 3.x I need to check prefix in model if admin

I want to check if the current address is admin area IN MODEL to change conditions:
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
debug($this->request['prefix']);
}
It's not working. I need only to access to request vars IN MODEL.
Thanks.
I resolve it by using $_SERVER variable. It's works good
$_SERVER['REQUEST_URI']
but I still need to add beforefind for each Model... while I need only general conditions for all queries... I really feel bad about the accessibility of cakephp
Brother you can use the class in model
use Cake\Network\Request;
and Get path if you want

Error 400 Your request is invalid in yii if i am creating link

I'm pretty new to Yii, so this might be a silly question. I must be missing something somewhere. Plz help me out.
I have just written a simple code while I'm learning Yii.
I have a spark controller which has an action that looks like this:
public function actionDownload($name){
$filecontent=file_get_contents('images/spark/'.$name);
header("Content-Type: text/plain");
header("Content-disposition: attachment; filename=$name");
header("Pragma: no-cache");
echo $filecontent;
exit;
}
Now I have enabled SEO friendly URLs and echo statement in the view file to display the returned download file name.
But when I go to the URL,
SITE_NAME/index.php/spark/download/db5250efc9a9684ceaa25cacedef81cd.pdf
I get error 400 - your request is invalid.
Plz let me know if I am missing something here.
Yii keeps an eye on the params you pass into an action function. Any params you pass must match what's in $_GET.
So if you're passing $name then you need to make sure there's $_GET['name'] available.
So check your URL manager in /config/main.php, and make sure you're declaring a GET var:
'spark/download/<name:[a-z0-9]+>' => 'site/download', // Setting up $_GET['name']
Otherwise change your function to the correct param variable:
public function actionDownload($anotherName){
}
Did you create the URL correct? You should do it best with
Yii::app()->createUrl('/spark/download',array('name'=>$filename));
Make sure if you are properly supplying the parameter name ($name) in your request.
Use
SITE_NAME/index.php/spark/download/name/db5250efc9a9684ceaa25cacedef81cd.pdf
or
SITE_NAME/index.php/spark/download?name=db5250efc9a9684ceaa25cacedef81cd.pdf
instead of just SITE_NAME/index.php/spark/download/db5250efc9a9684ceaa25cacedef81cd.pdf

Phalcon redirection and forwarding

Do I understand correctly that after doing $this->dispatcher->forward() or $this->response->redirect() I need to manually ensure that the rest of the code does't get executed? Like below, or am I missing something?
public function signinAction()
{
if ($this->isUserAuthenticated())
{
$this->response->redirect('/profile');
return;
}
// Stuff if he isn't authenticated…
}
After almost a year of working on a hardcore project that uses Phalcon beyond its capacity, I wanted to clarify a few things and answer my own question. To understand how to properly do redirects and forwards you need to understand a little about how the Dispatcher::dispatch method works.
Take a look at the code here, though it's all C mumbo-jumbo to most of us, its really well written and documented. In the nutshell this is what it does:
The dispatcher enters the while loop until the _finished property becomes true or it discovers a recursion.
Inside the loop, it immediately sets that property to true, so when it starts the next iteration it will automatically break.
It then gets the controller / action information, which are originally supplied by the router in the application, and does various checks. Before and after that it also completes a lot of event-related business.
Finally it calls the action method in the controller and updates the _returnedValue property with (guess what!) the returned value.
If during the action call you call Dispatcher::forward method, it will update the _finished property back to false, which will allow the while loop to continue from the step 2 of this list.
So, after you do redirect or forward, you need to ensure that you code doesn't get executed only if that is part of the expected logic. In other words you don't have to return the result of return $this->response->redirect or return $this->dispatcher->forward.
Doing the last might seem convenient, but not very correct and might lead to problems. In 99.9% cases your controller should not return anything. The exception would be when you actually know what you are doing and want to change the behaviour of the rendering process in your application by returning the response object. On top of that your IDE might complain about inconsistent return statements.
To finalise, the correct way to redirect from within the controller:
// Calling redirect only sets the 30X response status. You also should
// disable the view to prevent the unnecessary rendering.
$this->response->redirect('/profile');
$this->view->disable();
// If you are in the middle of something, you probably don't want
// the rest of the code running.
return;
And to forward:
$this->dispatcher->forward(['action' => 'profile']);
// Again, exit if you don't need the rest of the logic.
return;
You need to use it like this:
return $this->response->redirect('/profile');
or
return $this->dispatcher->forward(array(
'action' => 'profile'
))
Use send() like this
public function signinAction()
{
if ($this->isUserAuthenticated())
{
return $this->response->redirect('profile')->send();
}
}

How can I access query string parameters for requests I've manually dispatched in Laravel 4?

I'm writing a simple API, and building a simple web application on top of this API.
Because I want to "consume my own API" directly, I first Googled and found this answer on StackOverflow which answers my initial question perfectly: Consuming my own Laravel API
Now, this works great, I'm able to access my API by doing something like:
$request = Request::create('/api/cars/'.$id, 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
This is great! But, my API also allows you to add an optional fields parameter to the GET query string to specify specific attributes that should be returned, such as this:
http://cars.com/api/cars/1?fields=id,color
Now the way I actually handle this in the API is something along the lines of this:
public function show(Car $car)
{
if(Input::has('fields'))
{
//Here I do some logic and basically return only fields requested
....
...
}
I would assume that I could do something similar as I did with the query string parameter-less approach before, something like this:
$request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
BUT, it doesn't seem so. Long story short, after stepping through the code it seems that the Request object is correctly created (and it correctly pulls out the fields parameter and assigns id,color to it), and the Route seems to be dispatched OK, but within my API controller itself I do not know how to access the field parameter. Using Input::get('fields') (which is what I use for "normal" requests) returns nothing, and I'm fairly certain that's because the static Input is referencing or scoping to the initial request the came in, NOT the new request I dispatched "manually" from within the app itself.
So, my question is really how should I be doing this? Am I doing something wrong? Ideally I'd like to avoid doing anything ugly or special in my API controller, I'd like to be able to use Input::get for the internally dispatched requests and not have to make a second check , etc.
You are correct in that using Input is actually referencing the current request and not your newly created request. Your input will be available on the request instance itself that you instantiate with Request::create().
If you were using (as you should be) Illuminate\Http\Request to instantiate your request then you can use $request->input('key') or $request->query('key') to get parameters from the query string.
Now, the problem here is that you might not have your Illuminate\Http\Request instance available to you in the route. A solution here (so that you can continue using the Input facade) is to physically replace the input on the current request, then switch it back.
// Store the original input of the request and then replace the input with your request instances input.
$originalInput = Request::input();
Request::replace($request->input());
// Dispatch your request instance with the router.
$response = Route::dispatch($request);
// Replace the input again with the original request input.
Request::replace($originalInput);
This should work (in theory) and you should still be able to use your original request input before and after your internal API request is made.
I was also just facing this issue and thanks to Jason's great answers I was able to make it work.
Just wanted to add that I found out that the Route also needs to be replaced. Otherwise Route::currentRouteName() will return the dispatched route later in the script.
More details to this can be found on my blog post.
I also did some tests for the stacking issue and called internal API methods repeatedly from within each other with this approach. It worked out just fine! All requests and routes have been set correctly.
If you want to invoke an internal API and pass parameters via an array (instead of query string), you can do like this:
$request = Request::create("/api/cars", "GET", array(
"id" => $id,
"fields" => array("id","color")
));
$originalInput = Request::input();//backup original input
Request::replace($request->input());
$car = json_decode(Route::dispatch($request)->getContent());//invoke API
Request::replace($originalInput);//restore orginal input
Ref: Laravel : calling your own API

How do you check if the current page is the frontpage using YII?

Drupal has a function called "drupal_is_front_page". Does YII have something similar to deal with navigation in this way?
Unfortunately not. And while the information needed to piece this together is available, doing so is really more pain than it should be.
To begin with, the front page is defined by the CWebApplication::defaultController property, which can be configured as discussed in the definitive guide. But there's a big issue here: defaultController can in reality be any of the following:
a bare controller name, e.g. site
a module/controller pair, e.g. module/site
a controller/action pair, e.g. site/index
a module/controller/action tuple, e.g. module/site/index
If you have specified the defaultController as #4 (which is the same as #3 if your application does not include any modules) then everything is easy:
function is_home_page() {
$app = Yii::app();
return $app->controller->route == $app->defaultController;
}
The problem is that if defaultController is specified as #1 or #2 then you have to examine a lot of the runtime information to convert it to form #3 or #4 (as appropriate) so that you can then run the equality check.
Yii of course already includes code that can do this: the CWebApplication::createController method, which can accept any of the valid formats for defaultController and resolve that to a controller/action pair (where controller is dependent on the module, if applicable). But looking at the source doesn't make you smile in anticipation.
To sum it up: you can either assume that defaultController will always be fully specified and get the job done with one line of code, or borrow code from createController to determine exactly what defaultController points to (and then use the one line of code to check for equality).
I do not recommend looking into solutions based on URLs because the whole point of routes is that different URLs can point to the same content -- if you go that way, can never be sure that you have the correct result.
In my experience, there is no such function in Yii. However, you can retrieve the followings:
base url: Yii::app()->request->baseUrl
current URL : Yii::app()->request->requestUri.
current page controller with Yii::app()->getController()->getAction()->controller->id .
With these APIs, it should be possible to find out whether the current page is front page.
another simple idea:
in your action (that one you use to present your 'main front page'), you could set up a variable using a script in its view:
Yii::app()->getClientScript()->registerScript("main_screen",
"var main_front_page = true;",CClientScript::POS_BEGIN);
put that code in the "main view", (the rest view pages dont have this piece of code).
so when you need to check if a page is the "main page" you could check for it using javascript, quering for:
if(main_front_page){..do something..}.
if you need to recognize the main page in php (in server side), use the method proposed by Jon.
another solution, based on a common method for your controller:
Your controllers all of them must extend from CController, but, when you build a new fresh yii application Gii creates a base Controller on /protected/components/Controller.php so all your controllers derives from it.
So, put a main attribute on it, named:
<?php
class Controller extends CController {
public $is_main_front_page;
public function setMainFrontPage(){ $this->is_main_front_page = true; }
public function getIsMainFrontPage(){ returns $this->is_main_front_page==true; }
}
?>
well, when you render your main front page action, set up this core varible to true:
<?php
class YoursController extends Controller {
public function actionPrimaryPage(){
$this->setMainFrontPage();
$this->render('primarypage');
}
public function actionSecondaryPage(){
$this->render('secondarypage');
}
}
next, in any view, you could check for it:
<?php // views/yours/primaryview.php
echo "<h1>Main Page</h1>";
echo "is primary ? ".$this->getIsMainFrontPage(); // must say: "is primary ? true"
?>
<?php // views/yours/secondaryview.php
echo "<h1>Secondary Page</h1>";
echo "is primary ? ".$this->getIsMainFrontPage(); // must say: "is primary ? false"
?>