Come programmare con Yii2 comportamenti biasimevoli

Cosa starai creando

Se stai chiedendo "Cos'è Yii?" guarda il mio tutorial precedente: Introduzione al framework Yii, che rivede i vantaggi di Yii e include una panoramica delle novità in Yii 2.0, rilasciata nell'ottobre 2014.

In questa serie di programmazione con Yii2, guido i lettori all'uso del nuovo Yii2 Framework per PHP. In questo tutorial, ti guiderò attraverso un altro dei comportamenti interessanti di Yii2: aiutare ad automatizzare l'attività di sviluppo Web comune dell'assegnazione creata e aggiornata da user_ids tra i modelli nella tua app Web utilizzando la codifica DRY e Yii2 BlameableBehavior. Creeremo anche un log che registra chi ha aggiornato la tabella dello stato per ogni modifica apportata.

Per questi esempi, continueremo a immaginare che stiamo costruendo un framework per la pubblicazione di semplici aggiornamenti di stato, ad es. il nostro mini-Twitter.

Solo un promemoria, parteciperò alle discussioni dei commenti qui sotto. Sono particolarmente interessato se hai approcci diversi, idee aggiuntive o vuoi suggerire argomenti per futuri tutorial.

Che cosa è un comportamento?

I comportamenti di Yii2 sono essenzialmente mixin. Wikipedia descrive i mixins come "una classe che contiene una combinazione di metodi di altre classi, e come tale combinazione dipenda dal linguaggio, ma non dall'ereditarietà".

Yii li descrive in questo modo:

Il collegamento di un comportamento a un componente "inietta" i metodi e le proprietà del comportamento nel componente, rendendo accessibili tali metodi e proprietà come se fossero definiti nella classe del componente stessa.

Yii2 offre diversi comportamenti incorporati, la maggior parte dei quali verranno documentati, incluso il collegamento (vedi Programmazione con Yii2: Comportamento Sluggable), biasimevole e timestamp (in arrivo, controlla la pagina della serie). I comportamenti sono un modo semplice per riutilizzare il codice comune su molti modelli di dati senza dover ripetere il codice in molti punti. L'iniezione di un comportamento in un modello può spesso essere eseguita con due sole righe di codice. Man mano che aumenta il numero di modelli nella tua applicazione, i comportamenti diventano sempre più utili.

Qual è il comportamento biasimevole?

Blameable semplifica l'implementazione dell'attività frequente di assegnazione dell'utente corrente connesso a inserimenti e aggiornamenti in un modello ActiveRecord, impostando automaticamente le proprietà per creato da e Aggiornato da.

Nella programmazione con Yii2: Autorizzazione con il filtro di controllo dell'accesso, abbiamo implementato il nostro comportamento biasimevole in due parti. Innanzitutto, abbiamo creato una migrazione per aggiungere a creato da campo alla nostra tabella di stato:

db-> driverName === 'mysql') $ tableOptions = 'SET CARATTERE utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB';  $ this-> addColumn ('% status', 'created_by', Schema :: TYPE_INTEGER. 'NOT NULL'); $ this-> addForeignKey ('fk_status_created_by', '% status', 'created_by', '% user', 'id', 'CASCADE', 'CASCADE'); 

Secondo, abbiamo assegnato il creato da campo alla corrente ID utente nell'azione di creazione di StatusController:

funzione pubblica actionCreate () $ model = new Status (); if ($ model-> load (Yii :: $ app-> request-> post ())) $ model-> created_by = Yii :: $ app-> user-> getId ();

L'implementazione del comportamento di Blameable lo farà automaticamente per noi e può essere facilmente aggiunto a tutti i modelli di ActiveRecord in un'applicazione web.

Implementazione del comportamento biasimevole nel modello di stato

Estensione della tabella di stato

Innanzitutto, è necessario estendere la tabella di stato con una migrazione ancora una volta per supportare un Aggiornato da campo.

Jeff $ ./yii migrate / create extend_status_table_for_updated_by Yii Migration Tool (basato su Yii v2.0.2) Crea nuova migrazione '/Users/Jeff/Sites/hello/migrations/m150209_200619_extend_status_table_for_updated_by.php'? (sì | no) [no]: sì Nuova migrazione creata con successo.

Ecco il codice di migrazione che useremo:

db-> driverName === 'mysql') $ tableOptions = 'SET CARATTERE utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB';  $ this-> addColumn ('% status', 'updated_by', Schema :: TYPE_INTEGER. 'NOT NULL'); $ this-> addForeignKey ('fk_status_updated_by', '% status', 'updated_by', '% user', 'id', 'CASCADE', 'CASCADE');  public function down () $ this-> dropForeignKey ('fk_status_updated_by', '% status'); $ This-> dropColumn ( 'status%', 'updated_by');  

Se si tenta di eseguire questa migrazione con dati esistenti nel database, si verificherà un errore quando si tenta di creare l'indice della chiave esterna, poiché Aggiornato da è 0 e non esiste nella tabella utente. 

ciao Jeff $ ./yii migrate / up Yii Migration Tool (basato su Yii v2.0.2) Totale 1 nuova migrazione da applicare: m150209_200619_extend_status_table_for_updated_by Applicare la migrazione sopra? (sì | no) [no]: sì *** applicando m150209_200619_extend_status_table_for_updated_by> aggiungi colonna updated_by intero NOT NULL alla tabella % status ... done (time: 0.042s)> aggiungi chiave esterna fk_status_updated_by: % status (updated_by) riferimenti % user (id) ... Eccezione 'yii \ db \ IntegrityException' con messaggio 'SQLSTATE [23000]: Violazione del vincolo di integrità: 1452 Impossibile aggiungere o aggiornare una riga secondaria: un vincolo di chiave esterna non riesce (' ciao '.' # sql-22f_1d0 ', CONSTRAINT' fk_status_updated_by 'FOREIGN KEY (' updated_by ') REFERENCES' user '(' id ') ON DELETE CASCADE ON UPDATE CASCADE) L'SQL in esecuzione è stato: ALTER TABLE' status 'ADD CONSTRAINT 'fk_status_updated_by' FOREIGN KEY ('updated_by') REFERENCES 'utente' ('id') ON DELETE CASCADE ON UPDATE CASCADE 'in /Users/Jeff/Sites/hello/vendor/yiisoft/yii2/db/Schema.php:532 Errore Info: Array ([0] => 23000 [1] => 1452 [2] => Impossibile aggiungere o aggiornare una riga secondaria: un vincolo di chiave esterna non riesce ('ciao'. '# Sql-22f_1d0', CONSTRAINT 'fk_status_updated_by' PRUA, TESTA TASTO IGN ('updated_by') REFERENCES 'utente' ('id') ON DELETE CASCADE ON UPDATE CASCADE)) 

Potremmo aggirare questo aggiornando i dati manualmente nella migrazione e aggiungendo una chiave esterna. Tuttavia, poiché si tratta di una piattaforma di test, è più semplice migrare tre passaggi, eliminando la tabella di stato e i suoi dati di test, quindi eseguire nuovamente la migrazione:

ciao Jeff $ ./yii migrate / down 3 Yii Migration Tool (basato su Yii v2.0.2) Totale 3 migrazioni da ripristinare: m150128_233458_extend_status_table_for_slugs m150128_003709_extend_status_table_for_created_by m141201_013120_create_status_table Annulla le migrazioni di cui sopra? (sì | no) [no]: sì *** ripristino di m150128_233458_extend_status_table_for_slugs> slug della colonna di rilascio dalla tabella % status ... completato (tempo: 0.009s) *** ripristinato m150128_233458_extend_status_table_for_slugs (tempo: 0.013s) *** ripristino m150128_003709_extend_status_table_for_created_by> rilascia la chiave esterna fk_status_created_by dalla tabella % status ... done (time: 0.010s)> drop column created_by dalla tabella % status ... done (time: 0.008s) *** ripristinato m150128_003709_extend_status_table_for_created_by (time: 0,019s) *** ripristino di m141201_013120_create_status_table> drop table % status ... done (time: 0.001s) *** ripristinato m141201_013120_create_status_table (time: 0.002s) Migrato correttamente. ciao Jeff $ ./yii migrate / up 4 Yii Migration Tool (basato su Yii v2.0.2) Totale 4 nuove migrazioni da applicare: m141201_013120_create_status_table m150128_003709_extend_status_table_for_created_by m150128_233458_extend_status_table_for_slugs m150209_200619_extend_status_table_for_updated_by Applicare le migrazioni di cui sopra? (sì | no) [no]: sì *** applicando m141201_013120_create_status_table> crea tabella % status ... done (time: 0.007s) *** applicato m141201_013120_create_status_table (time: 0.010s) *** applicando m150128_003709_extend_status_table_for_created_by> add column created_by intero NOT NOT NULL alla tabella % status ... done (time: 0.007s)> aggiungi chiave esterna fk_status_created_by: % status (created_by) reference % user (id) ... done (time : 0.008s) *** applicato m150128_003709_extend_status_table_for_created_by (time: 0.016s) *** applicando m150128_233458_extend_status_table_for_slugs> aggiungi colonna slug stringa NOT NULL alla tabella % status ... done (time: 0.007s) *** applicato m150128_233458_extend_status_table_for_slugs (time : 0.008s) *** applicando m150209_200619_extend_status_table_for_updated_by> aggiungi colonna updated_by intero NOT NOT NULL alla tabella % status ... done (time: 0.007s)> aggiungi chiave esterna fk_status_updated_by: % status (updated_by) reference  % utente (id) ... fatto (tempo: 0.007s) *** applicato m 150209_200619_extend_status_table_for_updated_by (time: 0.015s) Migrato con successo.

Aggiunta del comportamento Blameable al modello di stato

Successivamente, allegheremo il BlameableBehavior al nostro modello Status. In models / Status.php aggiungiamo il BlameableBehavior dopo Sluggable:

lo stato della classe si estende \ yii \ db \ ActiveRecord const PERMISSIONS_PRIVATE = 10; const PERMISSIONS_PUBLIC = 20; public function behaviors () return [['class' => SluggableBehavior :: className (), 'attribute' => 'message', 'immutable' => true, 'ensureUnique' => true,], ['class' => BlameableBehavior :: className (), 'createdByAttribute' => 'created_by', 'updatedByAttribute' => 'updated_by',],]; 

Dobbiamo anche includere il comportamento di Blameable nella parte superiore del nostro modello:

Quindi, rimuoviamo la regola richiesta per creato da nelle regole del modello:

public function rules () return [[['message', 'created_at', 'updated_at', 'created_by'], 'required'],

Come questo:

public function rules () return [[['message', 'created_at', 'updated_at'], 'required'],

Ciò consente alla convalida di avere successo e di continuare i comportamenti.

Possiamo anche commentare o eliminare StatusController creato da assegnazione nell'azione di creazione:

funzione pubblica actionCreate () $ model = new Status (); if ($ model-> load (Yii :: $ app-> request-> post ())) // $ model-> created_by = Yii :: $ app-> user-> getId ();

Una volta completate tutte queste modifiche, possiamo scrivere un nuovo post di stato:

E possiamo dare un'occhiata alla vista tabella con PHPMyAdmin e vedere le impostazioni create_by e updated_by:

Registrazione degli aggiornamenti nella tabella di stato

Quando viene creato un post di stato, sapremo sempre chi ha creato la prima voce. Ma, con i Behavior Blameable, sapremo solo chi ha aggiornato il record.

Passiamo attraverso una semplice implementazione del registro per registrare l'id della persona che esegue ogni aggiornamento. Quindi è possibile visualizzare facilmente una cronologia degli updaters o estenderla per essere un registro di revisione completo.

Creazione della tabella per StatusLog

Innanzitutto, dobbiamo creare una migrazione per il StatusLog:

ciao Jeff $ ./yii migrate / create create_status_log_table Strumento di migrazione Yii (basato su Yii v2.0.2) Crea nuova migrazione '/Users/Jeff/Sites/hello/migrations/m150209_204852_create_status_log_table.php'? (sì | no) [no]: sì Nuova migrazione creata con successo.

Quindi, codifichiamo la migrazione per includere i campi relazionali per l'id della tabella di stato e per l'utente Aggiornato da campi:

db-> driverName === 'mysql') $ tableOptions = 'SET CARATTERE utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB';  $ this-> createTable ('% status_log', ['id' => Schema :: TYPE_PK, 'status_id' => Schema :: TYPE_INTEGER. 'NOT NULL', 'updated_by' => Schema :: TYPE_INTEGER. 'NOT NULL', 'created_at' => Schema :: TYPE_INTEGER. 'NOT NULL',], $ tableOptions); $ this-> addForeignKey ('fk_status_log_id', '% status_log', 'status_id', '% status', 'id', 'CASCADE', 'CASCADE'); $ this-> addForeignKey ('fk_status_log_updated_by', '% status_log', 'updated_by', '% user', 'id', 'CASCADE', 'CASCADE');  public function down () $ this-> dropForeignKey ('fk_status_updated_by', '% status_log'); $ This-> dropForeignKey ( 'fk_status_id', '% status_log'); $ This-> dropColumn ( '% status_log', 'updated_by');  

Successivamente, eseguiamo la migrazione:

ciao Jeff $ ./yii migrate / up Yii Migration Tool (basato su Yii v2.0.2) Totale 1 nuova migrazione da applicare: m150209_204852_create_status_log_table Applicare la suddetta migrazione? (sì | no) [no]: sì *** applicando m150209_204852_create_status_log_table> crea tabella % status_log ... fatto (tempo: 0.008s)> aggiungi chiave esterna fk_status_log_id: % status_log (status_id) riferimenti  % status (id) ... done (time: 0.008s)> aggiungi chiave esterna fk_status_log_updated_by: % status_log (updated_by) riferimenti % user (id) ... done (time: 0.008s) ** * applicato m150209_204852_create_status_log_table (time: 0.028s) Migrato con successo.

Il modo più veloce per creare un modello per StatusLog (e file CRUD in modo da poter sfogliare facilmente la tabella) è con il generatore di codice di Yii2, Gii. Mi hai visto usarlo nei tutorial precedenti.

Visita http: // localhost: 8888 / ciao / gii e crea il modello con queste impostazioni:

Ecco le impostazioni CRUD:

Quindi, estendiamo l'evento AfterSave nel modello Status:

funzione pubblica afterSave ($ insert, $ changedAttributes) parent :: afterSave ($ insert, $ changedAttributes); // quando inserisci false, quindi il record è stato aggiornato se (! $ insert) // aggiungi la voce StatusLog $ status_log = new StatusLog; $ status_log-> status_id = $ this-> id; $ status_log-> updated_by = $ this-> updated_by; $ status_log-> created_at = time (); $ Status_log-> save (); 

Questo metodo chiama la funzionalità genitore predefinita per AfterSave ma poi crea una nuova voce StatusLog ogni volta che c'è un aggiornamento a una riga Status:

In teoria, potremmo anche estendere BlameableBehavior, ma dal momento che devi assicurarti che ci sia un modello di log per ogni modello di ActiveRecord con cui lo stai usando, è sembrato più semplice creare questa funzionalità in Status.

Se aggiorni un paio di record di stato, puoi sfogliare lo StatusLog utilizzando il CRUD di Gii. L'immagine sotto mostra due modifiche apportate da Status.id 1.

Se vuoi andare oltre, dovrebbe essere relativamente semplice estenderlo a una tabella di revisione completa di testo di stato precedente e nuovo per supportare la funzionalità di rollback.

Qual'è il prossimo?

Spero ti sia divertito a conoscere i Behaviour Behavior e i Blameable. Successivamente, esploreremo i comportamenti di Timestamp, che riducono la quantità di codice che è necessario scrivere con ogni nuovo modello per l'operazione comune di creazione di data / ora per inserti e aggiornamenti.

Guarda le prossime esercitazioni nella mia serie Programming with Yii2 mentre continuo a immergermi nei diversi aspetti del framework. Potresti anche voler controllare la mia creazione della tua startup con la serie PHP che usa il template avanzato di Yii2 mentre costruisco un'applicazione del mondo reale.

Accolgo richieste di argomenti e argomenti. Puoi postarli nei commenti qui sotto o mandarmi una e-mail sul mio sito Web di Lookahead Consulting.

Se vuoi sapere quando arriverà il prossimo tutorial di Yii2, seguimi @reifman su Twitter o controlla la mia pagina di istruttore. La mia pagina di istruttore includerà tutti gli articoli di questa serie non appena saranno pubblicati. 

Link correlati

  • La Guida Definitiva Yii2: Comportamenti
  • Documentazione Yii2: comportamento da biasimevole
  • Yii2 Developer Exchange, il mio sito di risorse Yii2