I have a question about the site im making in the prosess of learning Yii. This site is a non-public site, means the site will only show the main index page if you are logged in, else it will show a login page.
My main layout is for when a user is logged on. I also made a login layout, with just a simple login form.
How do I implement this logic:
app starts
app checks if logged in
logged in -> show main layout, if not show login layout
…the "first application" tutorial shows the main layout no matter if you are logged in or not, and only checks login for certain actions. Cant wrap my head around a solution here.
Ok I’m gonna have to take this at face value, cause I really cant understand why the protected/controller folder is more accessable to end-users than protected/components. Yes it is a base class, but still a controller, and controllers stay in the controller folder, no?
What you are saying just goes against what i have learned so far, i cant make sense of it. If time allows, feel free to elaborate or point me to some spesific documentation.
And thanks for effort with Yii and your time for helping out!
I had a feeling there should be an easier way of doing it, and I think I might have found it. The SiteController.php contains an action called actionIndex which renders the index. What i did was add a login-check there, like this:
public function actionIndex()
{
if (Yii::app()->user->isGuest) {
$this->actionLogin();
} else {
$this->render('index');
}
}
Then in the action actionLogin i say to use another layout like this $this->layout = 'login_layout';
This does exactly what I want to do, is there any security holes or other issues in doing it this way? Seems a lot more easy to me than the other opition.
Damn. You are of course very right. Even so, I will keep it for now and be very carefull with the other controllers. When I feel more comfortable with the inner workings of Yii, I will try implement what you suggest, cause right now I find it hard to wrap my head around, too many questions.
Hot Tip! Anyone who knows and understand what qiang means fully, maby you can make a short contrib to the cookbook?
OK, so I've spend a lot of time on yii these past 2 weeks, and feel a little more comfortable with how things work. I have tried to implement qiang's first suggestion, as I now see the smartness behind it. I have not however reached the final goal, but I think I'm on the right track.
First I made a BaseController.php and put it in components:
<?php
class BaseController extends CController
{
public function init()
{
if (Yii::app()->user->isGuest) {
$this->actionLogin();
}
}
public function actionLogin()
{
$this->layout = 'login';
$form=new LoginForm;
// collect user input data
if(isset($_POST['LoginForm']))
{
$form->attributes=$_POST['LoginForm'];
// validate user input and redirect to previous page if valid
if($form->validate())
$this->redirect(Yii::app()->user->returnUrl);
}
// display the login form
$this->render('login',array('form'=>$form));
}
}
?>
The actionLogin was moved from SiteController.php (from “My first app…”). Now all controllers must extend BaseController, and the login-check is done. How ever, if a user is not logged on, the actionIndex from the SiteController is still rendered. How can I make it so that if a user is not logged on, the actionIndex (or any other action called for in some controller) is not carried out?
OK, so I put the actionLogin function back into SiteController.php. I still dont understand how a parent class can call a function from a child class, ie: $this->actionLogin() being called from BaseController, though it exist in SiteController…but somewho it works.
More important, it doesn't stop the initial actionIndex to render the index even though user is not logged in.
My code for reference:
<?php
class SiteController extends BaseController
{
public function actionIndex()
{
$this->render('index');
}
public function actionLogin()
{
$this->layout = 'login';
$form=new LoginForm;
// collect user input data
if(isset($_POST['LoginForm']))
{
$form->attributes=$_POST['LoginForm'];
// validate user input and redirect to previous page if valid
if($form->validate())
$this->redirect(Yii::app()->user->returnUrl);
}
// display the login form
$this->render('login',array('form'=>$form));
}
}
class BaseController extends CController
{
public function init()
{
if (Yii::app()->user->isGuest) {
$this->actionLogin();
}
}
?>
Should I have an else statement in init() to kill the rest of the cycles?
Good, for a second my whole concept of OOP was shaking. Ok, so I tried what you suggested (a redirect cuts the current cycle, which is why its better than calling another function, i assume).
But, now we are in an infinite loop… BaseController checks for login, and if not redirects to site/login. However, the action site/login is within SiteController which extends BaseController!
I can guess a way to solve this: Make a LoginController under controllers that just extends CController (not BaseController).
I'm unfamiliar with beforeAction and loginRequred, but found this in the docs:
Quote
CController: beforeAction() - This method is invoked right before an action is to be executed (after all possible filters.)
CWebUser: loginRequired() - Redirects the user browser to the login page.
I wasn't quite sure on how to use this, but after a lot of trial and failure, I got it working like this: (ditching the initial idea with replacing the init() function)
OK, lets see if I get what’s going on here… Im not logged in when app call the actionIndex in SiteController. BUT before this action is carried out, the parent BaseController gets to carry out beforeAction, which basicly says: If you are not logged in and your action is NOT ‘site/login’ then do the loginRequired().
Is my understanding correct?
How is loginRequired() invoked since it obviously doesn’t create a loop like the redirect did?
PS. I plan to make a "howto" of these things i learn, thats why i ask of all the details, so i can understand what im talking about.
PPS. Might I add that this was a very nice and easy way to solve my problem, thanks a lot
Your understanding is correct. You may take a look at API manual about CWebUser::loginRequired(). It is a convenient alternative to the redirect() call directly.