[Risolto Relazione 1 A N Tra Tabella Lingua E Tabella Utente: Come Ottenere La Combo ?

Tabella lingua

id name

1 italian

2 english

Tabella utenti

id name languageId

1 pippo 1

2 luca 1

3 duffy 2

Ho la necessità nella _form dell’utente di avere una combo con le lingue possibili.

Attualmente ho fatto così in view/user/_form:




<?php echo $form->dropDownList($model,'languageId', $this->getLanguageOptions()); ?>

Nello UserController per ora, barando ho fatto così:


public function getLanguageOptions() {

	    $usersArray = array("1"=>"ITaliano","2"=>"ENglish");

	    return $usersArray;

}

In models/Uuser.php ho impostato, manualmente (a db NON ci sono e non ci possono essere relazioni tra chiavi esterne definite) la relazione così (e non so se ha senso):


public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

		    'language' => array(self::BELONGS_TO, 'Language', 'language_id'),

		);

	}

Come faccio a implementare la getLanguageOptions in modo che legga tutti gli id e i nomi delle lingue ?

Cosi’:


<?php echo $form->dropDownList($model,'languageId', CHtml::listData(Language::model()->findAll(), 'pk', 'Nome')); ?>

Dai una occhiata alla doc di CHtml::listdata per toglierti ogni dubbio, cmq fa una banale foreach sui model.

dato che servirà in altri posti, l’ho lasciata nel controller sotto questa forma:




public function getLanguageOptions() {

	    $usersArray = CHtml::listData(Language::model()->findAll(), 'id', 'name');

	    return $usersArray;

	}



cosi funziona , grazie !

ma … la relazione che ho impostato in User ha senso, o funziona per caso ?

La relazione è sempre Utenti N <—> 1 Language dove in user c’è una colonna language Id




'language' => array(self::BELONGS_TO, 'Language', 'language_id'),



Lo chiedo perchè in altri esempi ho visto usare HAS_ONE e non ne percepisco ancora il significato

Edit: Sto leggendo questo: http://www.yiiframework.com/wiki/181/relations-belongs_to-versus-has_one/

Non serve fare la funzione nel controller, mettila pure nella view, e’ perfettamente in standard.

Se proprio ci tieni a fare una funzione per questa cosa, meglio metterla nel model in modo che la puoi usare anche da altre parte.

La belongs to significa che la chiave si trova nel modello in cui scrivi la relation, has one/has many significa che la chiave si trova nel model relazionato.

grazie di entrambe le relazioni effettivamente l’articolo era molto chiaro.

Sai perchè stavo mettendo questa cose nel controller ? perchè l’ho visto fare nel libro che sto leggendo. Queste ‘getOptions’ lui le mette sempre nel controller.

Dal punto di vista MVC… dove dovrebbero stare?

Io non la faccio proprio la funzione, se esiste CHtml::listData e’ proprio per evitarsi la seccatura di scrivere ogni volta una funzione per ottenere un array da passare alla dropdown.

Le funzioni che servono per ottnere dati aggregati o rielaborazioni dei dati del model vanno nel model, per esempio


public function getNomeCompleto()

{

   return $this->nome.' '.$this->cognome;

}

Questa funzione che restitusce un array non ha bisogno di essere chiamata in un model, per cui la puoi mettere dovunque, i miei suggerimenti sono (in ordine di preferenza):

  • da nessuna parte

  • nel model (anche come static function)

  • come metodo statico in una classe qualunque (sulla falsariga di CHtml::listdata, si chiamano helper)

Nel controller non la metterei, perche’ se poi mi serve da un’altra parte, in un altro controller, non la posso riutilizzare.

Puoi leggerti questo articolo sulle best practices, comunque considera che sono soprattuto gusti personali, cmq ottieni il risultato va bene.

in parole povere, se funziona, e’ giusto.

ho accettato il suggerimento, usando direttamente la Chtml::listData direttamente dentro la _form dello User.




<?php echo $form->dropDownList($model,'languageId', CHtml::listData(Language::model()->findAll(), 'id', 'name')  ); ?>



Funziona, quindi è giusto.

Ti chiedo di pazientare ancora un poco e valutare il problema simile che ho invece nella view vera e propria (la tabella con i dati di un singolo record User)




<?php $this->widget('zii.widgets.CDetailView', array(

	'data'=>$model,

	'attributes'=>array(

		'id',

		'username',

		'password',

		'realname',

		'email',

		'companyId',

		'languageId',

	    'languageName',

		'registrationDate',

		'registrationUserId',

		'isActive',

		'isNotified',

		'defaultModulesId',

	),

)); ?>




Come vedi ho aggiunto languageName.

Per farlo andare, per intuito più che altro, ho creato dentro al model di user questo metodo:





	public function GetLanguageName()

	{

	    $language = Language::model()->findByPk($this->languageId);

	    $out = $language->name; 

	    

	    return $out;

	}



Questo è corretto? Ai miei capi è sembrato strano che un metodo di un modello in realtà usi semplicemente i dati di un altro modello… che ne pensi ?

La tua soluzione e’ correata, ma meglio usare la relation qua:


<?php $this->widget('zii.widgets.CDetailView', array(

        'data'=>$model,

        'attributes'=>array(

                'id',

                 [...]

                 'language.name',

                 [...]

                'defaultModulesId',

        ),

)); ?>

Come vedi, anche in questo caso non serve creare funzioni, bastano i metodi che hai gia’.

Vedi la doc di CListView

A, dimenticavo, benvenuto nel forum!

usando language.name e togliendo la getLanguageName, entrando nella index, mi dice

"La proprietà "User"."languageName" non Ú definita."

la relation è rimasta invariata




'language' => array(self::BELONGS_TO, 'Language', 'language_id'),



ed effetivamente la classe model sia chiama Language, dentro il file Language.php, e la proprietà name esiste.

Ma visto l’errore mi cerca languageName dentro a User. Forse devo rinominare language in languageName nella relazione ?

Ci ho provato:




	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

		    'languageName' => array(self::BELONGS_TO, 'Language', 'languageId'),

		);

	}



Ma poi mi dice questo:

"htmlspecialchars() expects parameter 1 to be string, object given"

Allora sono andato nella vista della lista nell’action index è ho modfiicato le’cho togliendo CHtml:Encode ed è diventatata così




<td><?php echo $data->languageName; ?></td>



ma ora mi dice

"Object of class Language could not be converted to string"

HO RISOLTO !!!!!!!!!

model User:



	public function relations()


	{


		// NOTE: you may need to adjust the relation name and the related


		// class name for the relations automatically generated below.


		return array(


		    'languageName' =&gt; array(self::BELONGS_TO, 'Language', 'languageId'),


		);


	}




_vew che rappresenta la singola riga




...

<td><?php echo CHtml::encode($data->languageName->name); ?></td> 

...




Con ogni probabilita’ hai dimenticato di togliere la linea relativa alla vecchia proprieta’, la notazione relations.proprieta funziona sia per la detailView che per la GridView

confermo per la detailView, dove puoi passare l’elenco dei campi. Grazie mille

la mia lista invece è generata da un’altro widget, non da una gridview:

View.Php




<tbody>

				<?php $this->widget('CListViewMWS', array(

					'dataProvider'=>$dataProvider,

					'itemView'=>'_view',

					'sortableAttributes'=>array(),

				)); ?>

			</tbody>



Probabilmente perchè in azienda si è scelto un template particolare …

In questo caso la notazione "$data->languageName->name" nella singola riga (_view.php) funziona benissimo