Basic
I modelli
Quando si crea un modello, si dichiara (anzitutto) il namespace a cui dovrà appartenere e poi si richiede
l’utilizzo delle classi Yii e Model, di cui Model è nel namespace yii\base\
<?php
namespace app\models;
use Yii;
use yii\base\Model;
Per convenzione i nomi delle classi dei modelli usano una sintassi di differenziazione, tra maiuscolo e
minuscolo, particolare in cui i termini iniziano tutti con le maiuscole e, quando sono termini composti di più
termini accorpati, ogni termine inizia con una maiuscola, per indicare che si tratta di un termine in a parte
componente il nome (es: ClientiLocali, PagamentiFornitori, ecc ).
Il nome della classe e del file php che la contiene devono essere identici.
In sintesi, quindi:
<?
namespace app\models;
use Yii;
use yii\base\model;
class NomeClasse extends Model
{
?>
Oltre ad estendere Model, un modello può estendere anche una classe modello, che a sua volta deriva da
Model. Per esempio, il modello standard (preconfezionato) User di Yii estende ActiveRecord.
Una volta definito così, poi si potrà usare (in un file in cui possa servire) in un modo tipo:
<?
namespace app\models;
use Yii;
use yii\base\model;
class Operators extends Model
{
}
?>
E, poi quando dovesse servire, potremmo scrivere:
$model = new app\models\Operators;
od anche chiamando la variabile oggetto in altro modo, come per esempio:
$operator = new app\models\Operators;
Dentro la classe si possono definire diverse variabili, funzioni ed oggetti.
<?
namespace app\models;
use Yii;
use yii\base\model;
class NomeClasse extends Model
{
public $var1;
public $var2;
public $var3;
}
?>
$var1, $var2 e $var3 sarebbero attributi e sarebbero raggiungibili poi, una volta dichiarata una variabile
$model ed instanziato con essa l’oggetto NomeClasse, con i percorsi:
Gli attributi così definiti sono anche reperibili come celle dell’array attributes dell’oggetto
$nome_modulo->attrubutes
Per esempio, riprendendo l’esempio del modello Operators, aggiungiamo le variabili $name, $surname ed
$email:
<?
namespace app\models;
use Yii;
use yii\base\model;
class Operators extends Model
{
public $name;
public $surname;
public $email;
}
?>
Una volta creato un file Operators.php nella apposita cartellina \models\, con all’interno il codice suddetto,
poi in una qualunque views (od altri file che necessitino di usare questo modello) potremmo scrivere:
$model = new app\models\Operators;
$model->name = 'Mauri';
echo $model->name;
Od anche
$model = new app\models\Operators;
$model->name = 'Mauri';
echo $model['name'];
Creare un oggetto, nella variabile $model, che sia di tipo app\models\Operators, gli conferisce le tre
variabili pubbliche suddette ($name, $surname, $email ) come, appunto, proprietà (od attributi).
Si verranno a definire anche le celle dell’array $model->attrubutes; dato che ci sono 3 attributi l’array sarà
di 3 celle, come potremo verificare con la funzione count:
echo count($model->attrubutes) darà come risultato, in questo caso, 3.
Possiamo accedere agli attributi di un oggetto con la freccetta -> seguita dal nome dell’attributo oppure
usando l’oggetto come fosse un array in cui il nome dell’attributo è da usarsi come la chiave.
Ovviamente, se l’attributo è stato definito nella classe allora ESISTE e, se viene utilizzato l’utilizzo in se non
causerà errori. Se invece si dovesse tentare di utilizzare attributi che non esistono, perché non sono stati
definiti nella classe, verrà restituito un errore all’apertura della view:
Comparirà un errore del tipo “Unknown Property” riferito alla classe usata, con una scritta del tipo
“Getting unknown property: app\models\NOME CLASSE::NOME PROPRIETA’ SCRITTO MALE”.
Per esempio, se provassimo ad usare in un oggetto della classe app\models\Operators una proprietà
inesistente, per esempio scrivessimo
$model = new app\models\Operators;
$model->name = 'Mauri';
echo $model['namexx'];
non esistendo una proprietà chiamata “namexx”, verrà restituito un errore di questo tipo:
in realtà, più sotto compariranno anche altri errori (circa 12, ma sostanzialmente dipendono tutti da questo,
almeno in riferimento a questo errore dovrebbero esserci).
Le etichette delle proprietà/attributi
Ogni campo di un modello ha per default una etichetta assegnata (che poi la si utilizzi o meno è
indifferente). Queste etichette, in generale, sono i nomi dei campi così come possono (normalmente)
essere mostrati all’utente all’interno di certe view.
Non è detto che sia necessario o si debba o si possa mostrare sempre il nome di un campo all’utente in una
view, alcuni campi non sono neanche resi noti agli utenti, talvolta.
Generalmente, vengono utilizzate poi nei moduli di inserimento/modifica/registrazione/login/ecc..
In tali moduli, infatti, questi campi possono essere elencati di fianco ai box ove andranno inseriti.
Per impostazione di default, è automaticamente generata (però) una etichetta per ogni campo.
Di default l’etichetta corrisponde al nome del campo, però con la prima lettera maiuscola; tuttavia, ogni
etichetta può essere modificata.
In ogni istante si può accedere al nome di una etichetta di un campo attraverso il metodo getAttributeLabel
della classe Model, quindi presente in ogni modello.
Es:
$model = new app\models\Operators;
echo $model->getAttributeLabel('name');
Se non preventivamente modificato, questo produrrà come output la stringa “Name”.
Se ridefiniamo la funzione attributeLabels() possiamo creare le associazioni tra i nomi dei campi e le
etichette
public function attributeLabels()
{
return [
'name' => 'Nome',
'surname' => 'Cognome',
'email' => 'E-Mail'
];
}
Per le app multipilingua si può usare la funzione statica di traduzione \Yii::t() per le etichette degli
attrubuti/proprietà (nello specifico \Yii::t($section, $text) traduce sostituisce il testo $text riferito alla
sezione $section – una categorizzazione per settori – , in quello della lingua selezionata ) e potremmo
scrivere
<?
namespace app\models;
use Yii;
use yii\base\model;
class Operators extends Model
{
public $name;
public $surname;
public $email;
public function attributeLabels()
{
return [
'name' => \Yii::t('app', 'Nome'),
'surname' => \Yii::t('app', 'Cognome'),
'email' => \Yii::t('app', 'E-Mail')
];
}
}
?>
Scenari:
uno scenario è sostanzialmente uno stato di un modello in cui il comportamento, alcune regole, in alcune
cose, possono cambiare. In ogni modello è presente una proprietà, di tipo stringa, che si chiama scenarios e
che, di norma, contiene la stringa “default”.
Se dichiariamo, in una vista qualsiasi, un modello e stampiamo con echo il suo scenario, salvo
comportamenti inconsueti, dovrebbe stampare la scritta “default”.
Default
Come vedremo, si possono definire dei controlli e dei comportamenti (lo vedremo poco più avanti) e questi
possono essere differenziati per scenario.
Quando si definisce un modello si possono definire delle costanti, che rappresenteranno i vari scenari.
Per esempio in un modello Users (che si riferisce alla gestione degli utenti) lo scenario “Registrazione” usa
regole diverse di gestione dei campi, rispetto allo scenario “Login”, perché magari i campi RICHIESTI,
NECESSARI, sono diversi: lo scenario Login, magari, richiederà solo username e password, mentre la
registrazione richiederà anche nome, cognome, magari anche email od indirizzo.
<?
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
}
?>
Adesso creiamo in User due costanti, che indicheranno ben due scenari:
SCENARIO_LOGIN che porremo pari a “login” e SCENARIO_REGISTER che porremo pari a “register”.
In pratica la proprietà scenarios di User potrà essere, quindi:
“Default”, “login” o “Register”.
A questo punto, negli scenari, i campi interessati potrebbero essere diversi: in uno scenario potrebbero
essere utili alcuni campi ed alcune regole ed un un altro altre, come per esempio lo scenario “register” e
“login”.
Definiamo il metodo scenarios() che restituisce un array in cui le chiavi sono i vari scenari possibili ed alle
cui chiavi corrispondono come valori degli array, a loro volta, in cui ci sono i nomi dei campi che interessano
lo scenario
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password'],
];
}
Quindi, in definitiva, una struttura buona potrebbe essere la seguente:
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password'],
];
}
}
Ossia:
abbiamo creato due costanti, SCENARIO_LOGIN e SCENARIO_REGISTER, con stringhe che indicano degli
scenari (“login” e “register”). Ovviamente, non c’è una limitazione su in che modo scrivere l’algoritmo che
restituisce l’array degli scenari; si può fare in diversi modi. E’ perfettamente valido identicamente a quella
precedente, appena vista, anche questa:
<?
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];
$scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];
return $scenarios;
}
}
?>
REGOLE DI CONVALIDA
Un modello ha un metodo di validazione dei dati inseriti/ricevuti, che controlla che i dati inseriti siano tutti
quelli richiesti e siano inseriti correttamente: il metodo validate();
Questo metodo funziona, basansosi sulle regole, che poi sono definite in un altro metodo: il metodo rules()
(regole).
Una volta che i campi sono stati caricati nel modulo si può fare una chiamata al metodo validate()
<?php
if ($model->validate())
{
// all inputs are valid
}
else
{
// validation failed: $errors is an array containing error messages
$errors = $model->errors;
}
?>
Validate() restituisce true o false, a seconda che ritenga i campi correttamente inseriti.
Le valutazioni di validate dipendono dal metodo rules() che noi overraddiamo.
Il metodo rules definisce quali campi sono necessari e come valutare il contenuto di uno o più campi.
Per definire le regole di validazione, rules, restituisce un array multidimensionale, con una struttura ben
definita: in pratica si tratta di un array in cui in ogni cella è inserita una regola ben precisa: campi
obbligatori, regola di validazione di uno specifico campo, ecc.
REGOLA 1
REGOLA 2
REGOLA 3
REGOLA 4
Una singola regola è così strutturata: un array di 2 celle in cui in una cella c’è un altro array con l’elenco dei
nomi dei campi (quindi stringhe) e nella seconda cella c’è una stringa che indica il nome dell’algoritmo di
verifica
Campo 1
Campo 3
Quindi possiamo immaginare così strutturato l’array delle regole
Regola1
Campo 1
Campo 3
Regola2
Regola3
Campo 1
NOME ALGORITMO DI VERIFICA
Campo 2
public function rules()
{
return [
// the name, email, subject and body attributes are required
[['name', 'email', 'subject', 'body'], 'required'],
// the email attribute should be a valid email address
['email', 'email'],
];
}
Qui sono restituite 2 regole, ciascuno indicato dalle rispettive celle dell’array più “esterno”;
Ogni regola è indicata da un array bidimensionale.
In queste regole, che sono 2 nell’esempio, la prima indica i campi “required”, quelli cioè obbligatori e la
seconda indica i campi che devono rispettare il formato indicato dall’algoritmo di verifica “email”.
Nel caso della prima l’array bidimensionale è composto nella prima cella da un array con le stringhe dei
nomi dei campi che sono richiesti obbligatoriamente e la seconda cella reca la stringa “required”, che indica
che i campi presenti nell’array della prima cella sono OBBLIGATORI!
La reconda regola ha solo una stringa al suo interno, che va “letta” come un array di un solo campo e tale
campo è il campo email; l’algoritmo indicato è quello che si chiama “email” ed indica che i campi indicati (il
solo campo “email” nel nostro caso) dovranno essere verificati rispettare certe regole indicate
dall’algoritmo "email”, che sostanzialmente verifica che il campo abbia un formato stringa attendibile come
e-mail.
E’ possibile, anche, indicare delle regole diverse a seconda dello scenario attivo:
<?
public function rules()
{
return [
// username, email and password sono obbligatori nello scenario "register"
[['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER
],
ASSEGNAZIONE MASSIVA
L’assegnazione massiva è una tecnica che consente di recuperare tutti i dati inviati ad una pagina e
trasmetterli direttamente a tutti i corrispettivi campi del modello – a patto che abbiano lo stesso nome –
Usando l’oggetto \Yii::$app si può accedere al suo metodo request che restituisce un oggetto contente a
sua volta altri metodi, come post(); passando al metodo post, tra parentesi, il nome del modulo da cui
derivano i dati
\Yii::$app->request->post(‘ContactForm’);
Così,
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
in un solo comando,
possiamo recuperare i dati eventualmente inviati dalla view ContactForm.
In alternativa, dopo aver creato il modello con $model = new \app\models\ContactForm;Avremmo dovuto
recuperare l’oggetto dei dati inviati dal ContactForm
$data = \Yii::$app->request->post('ContactForm', []);
ed infine avremmo dovuto recuperare i dati uno ad uno, così:
$model->name = $data['name'];
$model->email = $data['email'];
$model->subject = $data['subject'];
$model->body = $data['body'];
tuttavia, se i dati non sono stati inviati correttamente (o se ne manca anche solo qualcuno) questa
operazione può dare luogo a degli errori. Quindi si dovrebbe fare una cosa tipo:
$model->name = NULL;
if (isset($data['name'])) $model->name=$data['name'];
e ripetere questo per tutti gli altri..
oppure, in maniera (comunque) più stilizzata, scrivere
Che per ciascuna riga significa: se il dato inviato $data[NOMECAMPO] è settato metti nel campo $model-
>NOMECAMPO il dato inviato $data[NOMECAMPO], altrimenti mettici NULL!
In definitiva, quindi,
$model = new \app\models\ContactForm;
$data = \Yii::$app->request->post('ContactForm', []);
$model->name = isset($data['name']) ? $data['name'] : null;
$model->email = isset($data['email']) ? $data['email'] : null;
$model->subject = isset($data['subject']) ? $data['subject'] : null;
$model->body = isset($data['body']) ? $data['body'] : null;
Che, per quanto stilistico e carino è pur sempre più voluminoso ed impegnativo di
$model = new \app\models\ContactForm;
$model->attributes = \Yii::$app->request->post('ContactForm');
ATTRIBUTI SICURI E NON SICURI
In un modello, un attributo è sicuro se, in un certo scenario, è ritenuto OBBLIGATORIO, come negli – negli
esempi precedenti – erano username e password nello scenario “login”.
Supponiamo di avere un modello – per esempio User (come già visto prima), appunto, 2 scenari: “login” e
“register” (come negli esempi precedenti)
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password', 'indirizzo'],
];
}
}
Abbiamo detto quali campi sono da considerarsi “interessati” negli scenari “login” e “register”.
Se non specificato diversamente, sono considerati sicuri.
Per indicare che un campo, ad esempio il campo indirizzo, non sia da considerarsi come sicuro, è necessario
indicarlo anteponendo un segno ! (NOT) innanzi al nome, quindi una cosa tipo:
Andremo a scrivere:
public function rules()
{
return [
[
['username', 'password'], 'required',
'on' => self::SCENARIO_LOGIN ],
['username', 'email', 'password','!indirizzo'], 'required',
'on' => self::SCENARIO_REGISTER ]
];
}
In questo modo,
nello scenario “login” (self::SCENARIO_LOGIN)
lo scenario impone che i campi “interessati” sono username e password
e
la regola impone che i campi username e password sono obbligatori
mentre
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
const SCENARIO_LOGIN = 'login';
const SCENARIO_REGISTER = 'register';
public function scenarios()
{
return [
self::SCENARIO_LOGIN => ['username', 'password'],
self::SCENARIO_REGISTER => ['username', 'email', 'password', 'indirizzo'],
];
}
public function rules()
{
return [
[
['username', 'password'], 'required',
'on' => self::SCENARIO_LOGIN ],
['username', 'email', 'password','!indirizzo'], 'required',
'on' => self::SCENARIO_REGISTER ]
];
}
}
I controller
I controller sono classi che si estendono da \yii\base\Controller e si definiscono per LE AZIONI, cioè delle
operazioni, generalmente assolvono a delle richieste dell’utente, operate mediante un particolare tipo di
URL, che funge da ordine di esecuzione e che chiamiamo itinerario ed, eventualmente, visualizzare delle
schermate, che chiamiamo viste o view.
Dentro i controller (o controllori) sono definiti una serie di metodi che, per l’appunto, sono le azioni.
Quando creiamo un controller inizamo indicando il namespace (tipicamente app\controllers )
Poi indichiamo le classi che intendiamo usare con use, generalmente queste (salvo particolari necessità)
<?
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class NomeController extends Controller
{
public function actionNomeAzione1($params)
{
...
}
public function actionNomeAzione2($params)
{
...
}
}
?>
Poi definiamo il controller, il nome lo scriviamo con la prima lettera maiuscola e, se composto da più
termini che normalmente andrebbero indicati separati scriviamo tutto attaccato con le lettere di ogni
parola in maiuscolo (ad indicare l’immaginaria separazione); IMPORTANTE terminiamo con il termine
Controller in coda al nome. Quindi se volessimo creare un controller chiamato post, chiameremo la classe
PostController.
All’interno possiamo, come in ogni classe, inserire dei metodi; tra i metodi possiamo inserire le cosiddette
azioni, che generalmente fanno qualcosa e di solito visualizzano una vista. Il nome sarà composto dalla
parola action seguita (tutta in minuscolo) dal nome della azione, che se prevede la visualizzazione di una
vista spesso sarà proprio la vista che è richiesta. Un controller può contenere una o più azioni. Una azione
può accettare dei parametri, ma anche può non avere parametri richiesti.
Quando Yii riceve questa richiesta passa la palla al controller nomecontroller e chiama una azione, ossia un
metodo del controller nomecontroller chiamato nomeazioneController.
Facciamo un esempio di controller ed azioni:
<?
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
return $this->render('view', [
'model' => $model,
]);
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
?>
L’azione create (actionCreate) istanzia un modello di tipo Post, poi tenta di popolarlo con i dati che,
eventualmente, sono stati inviati dall’utente ed infine tenta di eseguire la funzione save() (che dovrebbe
effettuare il salvataggio dei dati). Se la cosa riesce procede a renderizzare, visualizzare, la view “view”, che
servirà a visualizzare i dati del modello, passandogli l’ID del modello salvato (che ora sarà contenuto in
$model->id).. se non è riuscito nel suo intento, invece, (else) allora renderizzerà la view create, in cui sarà
reso possibile agli utenti di inserire i dati ed eventualmente lanciare il salvataggio, che appunto farà partire
nuovamente l’azione di creazione.
Itinerari
Approfondiamo la questione delle richieste dell’utente.
Un utente (od anche il software stesso) può eseguire delle richieste, che porteranno all’esecuzione di una
azione di un controller lanciando dei percorsi particolari, detti itinerari.
Se per caso, come vedremo più in avanti, l’applicazione è fatta di più moduli non applicativi (un concetto
che vedremo più in avanti), questo modulo avrà un suo ID (una stringa identificarice) MODULO_ID.
http://hostname/
E vogliamo chiamare la azione create del controller post, (presente nel file PostController), scriviamo
http://myii/index.php?r=post/create/
questo eseguirà il metodo create della classe PostController, presente nella cartella dei controller e se
questo metodo contiene un comando render che visualizza una vista, questa verrà visualizzata.
ControllerID/ActionID
Se esiste un modulo specifico di id e si vuole chiamare l’azione ActionID del controller ControllerID del
modulo ModuleID, allora il percorso sarà
ModuleID/ControllerID/ActionID