Come creare la prima mini applicazione con yii

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:

  1. creare la tabella user con relativa classe, dove memorizzeremo gli utenti. (vedi gli step 1 e 2 di yii rights tutorial)

  2. tra le rules della classe User aggiungere:





			array('username, password, email', 'unique'),



per evitare di inserire doppioni

  1. 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();

	}

	

}



  1. 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',

			),

	);

	}

	*/

}



  1. 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/

grazie!

sicuramente a un principiante come me questo è manna dal cielo!!

l’applicazione gira, mi sono registrato con un account, ma non capisco perché se mi registro e poi mi voglio loggare, mi da errore…

poi aspettati altre domande sul codice :)

Grazie del contributo ;D

Allora speriamo di ricevere tanti + dai principianti

Potresti essere più specifico sull’errore che ti da?

non avevo visto che mi avevi risposto, ormai sto leggendo tutto :)

io ti confesso che per ora ho messo il tuo codice e piano piano risalgo la china com "miei sperimenti"

la mia domanda è:

se io vado nella pagina register e mi registro, vedo che tutto va a buon fine e vedo anche che scrive nel DB, se poi vado nella pagina login, cerco di loggarmi con il nuovo utente appena creato ma mi restituisce l’errore “pass o usr errati”

Questa la so!

Me l’aveva spiegata il mitico sensorario.

Per risolvere il problema, devi chiederti ma come funziona l’autenticazione su yii?

Grazie!

Dopo ci dò un occhiata, adesso sono alle prese con le query del Database

;D

ciao dopo un po’ di studio sto facendo delle prove anche io.

ho aggiunto ( e mi sembra che il tuo script non lo faccia, o se lo fa non capisco dove :D ) la query a una nostra tabella utenti per verificare il login solo a determinati utenti (solo registrati ad esempio)

dopo aver aggiunto l’autenticazione e le regole come hai fatto te,

mi sono spostato in UserIdentity.php

e ho scritto:


public function authenticate()

	{

		$command = Yii::app() -> db -> createCommand() 

		-> select('username,password') 

		-> from('tbl_utenti') 

		-> queryRow();

		

		$users=array(

			// username => password

			$command["username"] => $command["password"],

		);

si presume di avere una tabella tbl_utenti con colonne username e password

se te lo fai già, scusami ma non lo vedevo :)

E’ comunque corretto fare una query dentro UserIdentity.php

ciao!

ho visto dopo questa interessante guida

http://www.yiiframework.com/doc/guide/1.1/it/topics.auth

:)

+1 ;) grazie

A proposito di +1. Ti faccio notare che in basso a destra ad ogni nostro post c’è un + verde. Quando qualcuno ti da una risposta buona, dovresti “premiarlo” con quel più uno.

Eh si d’accordo che sono un principiante lo ammetto … ma che non veda l’opzione in basso a destra !!! :lol:

La realtà è che in quel giorno avevo esaurito le munizioni, questo forum dà con il contagocce le possibilità di fare reply e di addare (+1) ;)

Non dimenticherò mai il primo giorno qui, 3 posts potevo fare !! :(

mi aggancio a questa discussione, anche se non è stata usare nelle rules. ma molti lo usano e io non ho chiaro il significato anche dopo aver visto in giro ( si ragazzi sono de coccio! )

nelle rules spesso viene usate

array(‘username’, ‘safe’),

safe a cos a serve? avevo capito che serviva per fare massive assignement, ma credo di essermi sbagliato ( o almeno non funziona con proprietà pubbliche e non lette da DB)

grazie :unsure:

Ciao, come hai definito lo scenario "Login" ?

Seguita la guida , mi loggo come admin/admin

Error 403

You are not authorized to perform this action.