Nell’intento di aiutare i principianti ad avvicinarsi a questo meraviglioso framework 2.0
oggi voglio condividere un pò di codice, che mi sarebbe piaciuto trovare, quando ho iniziato a studiare le logiche nascoste dietro la realizzazione di un form, dietro al salvataggio di dati e all’autenticazione.
Sono convinto che un pezzo di codice completo e ben commentato, possa essere molto d’aiuto per chi deve ancora familiarizzare con la potenza di yii.
Se vi state studiando la parte di guida inerente i form, mi auguro possiate trovarlo utile.
Al termine di questa guida si potranno memorizzare nuovi utenti nel db dalla pagina
/index.php/accesso/register
e gestire il login da
/index.php/accesso/login
Ecco gli step da seguire:
-
creare la tabella user con relativa classe, dove memorizzeremo gli utenti. (vedi gli step 1 e 2 di yii rights tutorial)
-
tra le rules della classe User aggiungere:
array('username, password, email', 'unique'),
per evitare di inserire doppioni
- creare un nuovo modello denominato /protected/models/AccessForm.php che contiene tutta la logica di controllo dei form che vogliamo realizzare
<?php
/**
* LoginForm class.
* LoginForm è la struttura dati per mantenere i dati di struttura
* che vengono gestiti dalla view login che gestisce il form.
* Viene instanziato dalla function actionLogin() presente nel LoginController
* Questa unità è il frutto della lettura della documentazione:
* http://www.yiiframework.com/doc/guide/1.1/it/form.model
*/
class AccessForm extends CFormModel
{
//Posso inizializzare i valori che appariranno nel form a mio piacimento
public $email;
public $username='pi';
public $rememberMe=TRUE;
public $password;
public $password2;
private $_identity; //l'oggetto che si preoccupa di gestire l'autenticazione
/**
* Dichiara le regole di validazione.
* Le regole affermano che username e password sono obbligatorie,
* E la password deve essere gestita dalla funzione autentica.
*/
public function rules()
{
//Le regole di scrittura si trovano qui:
//http://www.yiiframework.com/doc/guide/1.1/it/form.model#declaring-validation-rules
return array(
// email è obbligatoria solo nello scenario register
array('email', 'required', 'on'=>'register'),
// email dev'essere un'email formalmente valida.
array('email', 'email'),
// username and password sono obbligatorie negli scenari login e register
array('username, password', 'required', 'on'=>'login, register'),
// RememberMe dev'essere un valore booleano
array('rememberMe', 'boolean'),
// username deve avere una lunghezza di caratteri compresa tra 3 e 12
array('username', 'length', 'min'=>3, 'max'=>12),
// la password dev'essere lunga almeno 6 caratteri
array('password', 'length', 'min'=>6, 'on'=>'register'),
// nello scenario register, la password dev'essere confontata con password2
array('password2', 'compare', 'compareAttribute'=>'password', 'on'=>'register'),
// la password dev'essere autenticata
//autentica è il nome della funzione che si preoccupa, di implementare l'autenticazione
array('password', 'autentica', 'on'=>'login'),
// la password dev'essere autenticata
//autentica è il nome della funzione che si preoccupa, di implementare l'autenticazione
array('password', 'utenteDisponibileSuRecord', 'on'=>'register'),
);
}
// Dichiara le etichette attributo.
public function attributeLabels()
{
return array(
'rememberMe'=>'Ricordati per la prossima volta',
);
}
/**
* Autentica utenza e password.
* Questa è il famoso validatore 'autentica' come dichiarato nelle regole().
*/
public function autentica($attribute,$params)
{
//Se non ci sono errori, posso procedere con i controlli sull'autenticazione
if(!$this->hasErrors())
{
//istanzio l'oggetto che si preoccupa di gestire l'autenticazione
$this->_identity=new UserIdentity($this->username,$this->password);
if(!$this->_identity->authenticate()) //se fallisce
$this->addError('password','username o password incorretti.'); //riporto l'errore sul form
}
}
/**
* Cerca di capire se è possibile memorizzare quell'utenza su db
* Questa è il famoso validatore 'utenteDisponibileSuRecord' come dichiarato nelle regole().
*/
public function utenteDisponibileSuRecord($attribute,$params)
{
//Se non ci sono errori, posso procedere con i controlli sull'autenticazione
if(!$this->hasErrors())
{
//creo un AR e indico che voglio lavorare con lo scenario register
$utente = new User('register');
//Dato che i campi del modello User si chiamano quasi tutti come quelli di questa classe,
//posso permettermi il lusso di fare così:
$utente->attributes = $this->attributes; //passo tutti i campi del modello all'active record
//l'unico campo che si chiama diversamente è questo:
$utente->remember = $this->rememberMe;
//Se non è possibile inserire il record
if(!$utente->validate()) {
//riporto gli errori specifici
$errors = $utente->getErrors();
$this->addErrors($errors);
}
}
}
/**
* Esegue il log dell'utente utilizzando il nome utente e password nel modello.
* @return boolean : true se il login va a buon fine
*/
public function login()
{
/* Questa funzione è esposta pubblicamente e messa a disposizione
* di chiunque volesse utilizzarla per autenticarsi
*/
if($this->_identity===null)
{
$this->_identity=new UserIdentity($this->username,$this->password);
$this->_identity->authenticate();
}
if($this->_identity->errorCode===UserIdentity::ERROR_NONE)
{
$duration = $this->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($this->_identity,$duration);
return true;
}
else
return false;
}
/**
* Esegue la registrazione del nuovo utente all'interno del db.
* @return boolean : true se riesce a registrarlo
*/
public function save()
{
/* Questa funzione è esposta pubblicamente e messa a disposizione
* di chiunque volesse memorizzare una nuova utenza su db
*/
//creo un AR e indico che voglio lavorare con lo scenario register
$utente = new User('register');
//Dato che i campi del modello User si chiamano quasi tutti come quelli di questa classe,
//posso permettermi il lusso di fare così:
$utente->attributes = $this->attributes; //passo tutti i campi del modello all'active record
//l'unico campo che si chiama diversamente è questo:
$utente->remember = $this->rememberMe;
//provo a salvare e restituisco l'esito del risultato
return $utente->save();
}
}
- creare il controller /protected/controllers/AccessoController.php che si preoccupa di mettere in comunicazione il model con la view
<?php
http://www.yiiframework.com/doc/guide/1.1/en/form.model#securing-attribute-assignments
class AccessoController extends Controller
{
public function actionIndex()
{
//se chiedono di accedere a /index.php/accesso/
//gli mostriamo la stessapagina di http://localhost/xampp/demo/index.php/accesso/login
//anche se sarebbe più pulito e corretto fare un redirect,
//in quanto verrebbe vista come una pagina doppia, che potrebbe portare confusione
//solo che non l'ho ancora studiato
$this->actionLogin();
}
public function actionLogin()
{
/* Dal paragrafo letto qui:
* http://www.yiiframework.com/doc/guide/1.1/en/database.ar#data-validation
* presumo che potrei inserire un nuovo record mediante la creazione del modello AR
* più o meno così:
* $model=new User('login'); //dove User è la classe che estende CActiveRecord e login rappresenterebbe il tipo di scenario
* ma dato che il login non deve salvare dati, scelgo di instanziare il modello dei form così:
*/
$model=new AccessForm('login');
//Se dal form hanno cliccato il tasto submit di login
if(isset($_POST['AccessForm']))
{
/* devo raccogliere i dati dell'input dell'utente.
E potrei farlo passando i dati dal form al modello ad uno ad uno in questo modo:
$model->username=$_POST['LoginForm']['username'];
$model->password=$_POST['LoginForm']['password'];
$model->rememberMe=$_POST['LoginForm']['rememberMe'];
ma così si fa decisamente prima: una riga soltanto!
*/
$model->attributes=$_POST['AccessForm']; //passo tutti i campi del form al modello
// se la convalida dell'input dell'utente è formalmente corretta
//e riesce a fare il login
if ( ($model->validate()) && ($model->login()) )
$this->redirect(Yii::app()->user->returnUrl); //reindirizza alla home page
else
// visualizza il modulo di login per mostrare l'errore
$this->render('login',array('model'=>$model));
} else
// visualizza il modulo di login per chiedere i dati
$this->render('login',array('model'=>$model));
}
public function actionRegister()
{
/* A differenza di actionLogin,
* questa volta istanzio il modello per lo scenario della registrazione
*/
$model=new AccessForm('register');
//Se dal form hanno cliccato il tasto submit di iscrizione
if(isset($_POST['AccessForm']))
{
$model->attributes=$_POST['AccessForm']; //passo tutti i campi del form al modello
// se la convalida dell'input dell'utente è formalmente corretta
//e riesce a memorizzare l'utente su db
if ( ($model->validate()) && ($model->save()) ) {
//$this->redirect(Yii::app()->user->returnUrl); //reindirizza alla pagina di ringraziamento
$this->render('grazie',array('model'=>$model));
} else
// visualizza il modulo di registrazione per mostrare l'errore
$this->render('register',array('model'=>$model));
} else
// visualizza il modulo di registrazione per chiedere i dati
$this->render('register',array('model'=>$model));
}
/* da qui... fantascienza, chissà a che servirà actions()
// Uncomment the following methods and override them if needed
/*
public function filters()
{
// return the filter configuration for this controller, e.g.:
return array(
'inlineFilterName',
array(
'class'=>'path.to.FilterClass',
'propertyName'=>'propertyValue',
),
);
}
public function actions()
{
// return external action classes, e.g.:
return array(
'action1'=>'path.to.ActionClass',
'action2'=>array(
'class'=>'path.to.AnotherActionClass',
'propertyName'=>'propertyValue',
),
);
}
*/
}
- creare le 3 viste in /protected/views/ che contengono il codice html che vede l’utente (la grafica)
register.php
<?php
//print_r($model);
//print_r($_REQUEST);
$this->breadcrumbs=array(
'Registrazione',
);?>
<h1><?php echo $this->id . '/' . $this->action->id; ?></h1>
<p>
Compila il form sottostante per iscriverti
</p>
<div class="form">
<?php $form=$this->beginWidget('CActiveForm'); ?>
<?php echo $form->errorSummary($model); ?>
<div class="row">
<?php echo $form->label($model,'email'); ?>
<?php echo $form->textField($model,'email') ?>
</div>
<div class="row">
<?php echo $form->label($model,'username'); ?>
<?php echo $form->textField($model,'username') ?>
</div>
<div class="row">
<?php echo $form->label($model,'password'); ?>
<?php echo $form->passwordField($model,'password') ?>
</div>
<div class="row">
<?php echo $form->label($model,'ridigita la password'); ?>
<?php echo $form->passwordField($model,'password2') ?>
</div>
<div class="row submit">
<?php echo CHtml::submitButton('Iscriviti'); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
grazie.php
<?php
$this->breadcrumbs=array(
'Grazie',
);?>
<h1><?php echo $this->id . '/' . $this->action->id; ?></h1>
<p>
Grazie <?php echo $model->username ?> per la tua iscrizione.
</p>
login.php
<?php
$this->breadcrumbs=array(
'Login',
);?>
<h1><?php echo $this->id . '/' . $this->action->id; ?></h1>
<p>
Ecco il login
</p>
<div class="form">
<?php $form=$this->beginWidget('CActiveForm'); ?>
<?php echo $form->errorSummary($model); ?>
<div class="row">
<?php echo $form->label($model,'username'); ?>
<?php echo $form->textField($model,'username') ?>
</div>
<div class="row">
<?php echo $form->label($model,'password'); ?>
<?php echo $form->passwordField($model,'password') ?>
</div>
<div class="row rememberMe">
<?php echo $form->checkBox($model,'rememberMe'); ?>
<?php echo $form->label($model,'rememberMe'); ?>
</div>
<div class="row submit">
<?php echo CHtml::submitButton('Login'); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
Vi invito a provare il codice e a postare i vostri miglioramenti.
Penso possa essere un ottimo modo didattico di imparare e crescere tutti insieme più velocemente.
Ad esempio mi piacerebbe capire come poter implementare l’invio di una mail all’utente in fase di registrazione, per assicurarci che l’utente abbia inserito una mail valida.
Magari tramite l’utilizzo di questo modulo, che mi pare sia al momento il migliore per yii
http://www.yiiframew…sion/phpmailer/