Hur man programmerar med Yii2 Skyllande beteenden

Vad du ska skapa

Om du frågar, "Vad är Yii?" kolla in min tidigare handledning: Introduktion till Yii Framework, som granskar fördelarna med Yii och innehåller en översikt över vad som är nytt i Yii 2.0, släppt i oktober 2014.

I denna programmering med Yii2-serien guidar jag läsare som använder den nyuppgraderade Yii2 Framework for PHP. I denna handledning guidar jag dig genom en annan av Yii2s intressanta beteenden: hjälper till att automatisera den gemensamma webbutvecklingsuppgiften att tilldela skapad av och uppdateras av user_ids över modellerna i din webbapp med DRY-kodning och Yii2 BlameableBehavior. Vi skapar också en logg som registrerar vem som uppdaterade statustabellen för varje förändring som gjorts.

För dessa exempel fortsätter vi att föreställa oss att vi bygger ett ramverk för att skicka enkla statusuppdateringar, t.ex. vår egen mini-Twitter.

Bara en påminnelse, jag deltar i kommentera trådarna nedan. Jag är särskilt intresserad om du har olika tillvägagångssätt, ytterligare idéer, eller vill föreslå ämnen för framtida handledning.

Vad är ett beteende?

Yii2 Behavior är i huvudsak mixins. Wikipedia beskriver mixins som "en klass som innehåller en kombination av metoder från andra klasser. Hur en sådan kombination görs beror på språket, men det är inte av arv."

Yii beskriver dem så här:

Att anbringa ett beteende på en komponent "injicerar" beteendets metoder och egenskaper i komponenten, vilket gör de metoderna och egenskaperna tillgängliga som om de definierades i komponentklassen själv.

Yii2 erbjuder flera inbyggda beteenden, varav vi dokumenterar, inklusive sluggable (se Programmering med Yii2: Sluggable Behavior), skylleröst och tidstämpel (kommande, kolla seriersidan). Beteenden är ett enkelt sätt att återanvända gemensam kod på många av dina datamodeller utan att behöva upprepa koden på många ställen. Injicera ett beteende i en modell kan ofta göras med bara två rader av kod. När antalet modeller i din ansökan ökar blir beteenden mer och mer användbara.

Vad är skyllerbeteende?

Blameable gör det enkelt för oss att genomföra den ofta nödvändiga uppgiften att tilldela den nuvarande inloggade användaren till inlägg och uppdateringar i en ActiveRecord-modell, vilket automatiskt ställer in egenskaperna för skapad av och updated_by.

I programmering med Yii2: Authorization Med Access Control Filter genomförde vi vårt eget skyllande beteende i två delar. Först skapade vi en migrering för att lägga till en skapad av fältet till vår status tabell:

db-> drivrutinName === 'mysql') $ tableOptions = 'CHARACTER SET 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'); 

För det andra tilldelade vi skapad av fält till aktuellt användar ID i StatusControllers skapande åtgärd:

public function actionCreate () $ model = ny status (); om ($ model-> load (Yii :: $ app-> request-> post ())) $ model-> created_by = Yii :: $ app-> user-> getId ();

Genomförandet av Skyllbart beteende kommer att göra det automatiskt för oss och kan enkelt läggas till i alla ActiveRecord-modellerna i ett webbprogram.

Genomföra skyllerbeteende i statusmodellen

Utvidga status tabellen

Först måste vi förlänga statustabellen med en migrering en gång till för att stödja en updated_by fält.

Jeff $ ./yii migrera / skapa extend_status_table_for_updated_by Yii Migration Tool (baserat på Yii v2.0.2) Skapa ny migrering '/Users/Jeff/Sites/hello/migrations/m150209_200619_extend_status_table_for_updated_by.php'? (ja | nej) [nej]: ja Ny migrering skapades framgångsrikt.

Här är migreringskoden vi ska använda:

db-> drivrutinName === 'mysql') $ tableOptions = 'CHARACTER SET 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');  offentlig funktion ner () $ this-> dropForeignKey ('fk_status_updated_by', '% status'); $ This-> dropColumn ( '% status', 'updated_by');  

Om du försöker köra den här migreringen med befintliga data i din databas får du ett fel när du försöker skapa det främmande nyckelindexet, eftersom updated_by är 0 och existerar inte i användartabellen. 

hej Jeff $ ./yii migrera / upp Yii Migrationsverktyg (baserat på Yii v2.0.2) Totalt 1 ny migration som ska tillämpas: m150209_200619_extend_status_table_for_updated_by Använd ovanstående migrering? (ja | nej) nej: ja *** tillämpning m150209_200619_extend_status_table_for_updated_by> lägg till kolumn updated_by heltal NOT NULL till tabell % status ... done (tid: 0.042s)> lägg till främmande nyckel fk_status_updated_by: % status (uppdaterad_by) referenser % user (id) ... Undantag 'yii \ db \ IntegrityException' med meddelande 'SQLSTATE [23000]: Integritetsbegränsningsbrott: 1452 Kan inte lägga till eller uppdatera en raden barn: en främmande nyckelbegränsning misslyckas hej "." # sql-22f_1d0 ", CONSTRAINT" fk_status_updated_by "FRÅN KEY ('updated_by') REFERENSER 'användare' ('id') OM DELETE CASCADE PÅ UPPDATERING AV CASCADE) SQL som kördes var: ALTER TABLE 'status' ADD CONSTRAINT 'fk_status_updated_by' FRÅN KEY ('updated_by') REFERENSER 'användare' ('id') OM DELETE CASCADE ON UPDATE CASCADE 'i /Users/Jeff/Sites/hello/vendor/yiisoft/yii2/db/Schema.php:532 Fel Info: Array ([0] => 23000 [1] => 1452 [2] => Det går inte att lägga till eller uppdatera en raden för barn: En främmande nyckelbegränsning misslyckas ("hej". "# Sql-22f_1d0", CONSTRAINT 'fk_status_updated_by' FRÄMRE IGN KEY ('updated_by') REFERENSER 'användare' ('id') OM DELETE CASCADE ON UPDATE CASCADE)) 

Vi kunde arbeta runt detta genom att uppdatera data manuellt i migreringen och sedan lägga till en utländsk nyckel. Eftersom det här är en testplattform är det dock enklast att migrera ner tre steg - tappa statustabellen och dess testdata - och sedan migrera upp igen:

hej Jeff $ ./yii migrera / ner 3 Yii Migrationsverktyg (baserat på Yii v2.0.2) Totalt 3 migreringar som ska återställas: m150128_233458_extend_status_table_for_slugs m150128_003709_extend_status_table_for_created_by m141201_013120_create_status_table Återställ ovanstående migreringar? (ja | nej) nej: ja *** återgår m150128_233458_extend_status_table_for_slugs> släpp kolumnslak från bordet % status ... gjort (tid: 0.009s) *** återgår m150128_233458_extend_status_table_for_slugs (tid: 0.013s) *** återgår m150128_003709_extend_status_table_for_created_by> släpp utländsk nyckel fk_status_created_by från bordet % status ... gjort (tid: 0.010s)> släpp kolumn skapad_by från bordet % status ... gjort (tid: 0.008s) *** återgår m150128_003709_extend_status_table_for_created_by (tid: 0,019s) *** återgår m141201_013120_create_status_table> droppbord % status ... klar (tid: 0.001s) *** återgår m141201_013120_create_status_table (tid: 0.002s) Migrerad nere framgångsrikt. hej Jeff $ ./yii migrera / upp 4 Yii Migration Tool (baserat på Yii v2.0.2) Totalt 4 nya migreringar som ska tillämpas: 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 Använd ovanstående migreringar? (ja | nej) nej: ja *** tillämpning m141201_013120_create_status_table> skapa tabell % status ... gjort (tid: 0.007s) *** tillämpas m141201_013120_create_status_table (tid: 0.010s) *** ansöker m150128_003709_extend_status_table_for_created_by> lägg till kolumn created_by heltal NOT NULL till tabell % status ... gjort (tid: 0.007s)> lägg till främmande nyckel fk_status_created_by: % status (created_by) referenser % user (id) ... gjort : 0.008s) *** applied m150128_003709_extend_status_table_for_created_by (tid: 0.016s) *** ansöker m150128_233458_extend_status_table_for_slugs> lägg till kolumnslangsträng INTE NULL till tabell % status ... gjort (tid: 0.007s) *** applicerat m150128_233458_extend_status_table_for_slugs : 0.008s) *** tillämpa m150209_200619_extend_status_table_for_updated_by> lägg till kolumn updated_by heltal NOT NULL till tabell % status ... gjort (tid: 0.007s)> lägg till främmande nyckel fk_status_updated_by: % status (updated_by) referenser  % användare (id) ... gjort (tid: 0.007s) *** tillämpas m 150209_200619_extend_status_table_for_updated_by (tid: 0.015s) Migrerades framgångsrikt.

Lägga till BlameableBehavior till statusmodellen

Därefter bifogar vi BlameableBehavior till vår Status-modell. I modeller / Status.php lägger vi till BlameableBehavior efter Sluggable:

klassstatus utökar \ yii \ db \ ActiveRecord const PERMISSIONS_PRIVATE = 10; const PERMISSIONS_PUBLIC = 20; public function behaviors () return [['class' => SluggableBehavior :: className (), 'attribute' => 'meddelande', 'immutable' => true, 'secureUnique' => true,], => BlameableBehavior :: className (), 'createdByAttribute' => 'created_by', 'updatedByAttribute' => 'updated_by',],]; 

Vi måste också inkludera Skyllbart beteende längst upp i vår modell:

Sedan tar vi bort den nödvändiga regeln för skapad av i modellreglerna:

allmänna funktionsregler () return [[['message', 'created_at', 'updated_at', 'created_by'], 'required'],

Så här:

allmänna funktionsregler () return [[[['message', 'created_at', 'updated_at'], 'required'],

Detta gör det möjligt för valideringen att lyckas och fortsätta till beteenden.

Vi kan också kommentera eller ta bort StatusController skapad av uppgift i skapandet:

public function actionCreate () $ model = ny status (); om ($ model-> load (Yii :: $ app-> request-> post ())) // $ model-> created_by = Yii :: $ app-> user-> getId ();

När alla dessa ändringar är klara, kan vi skriva en ny statuspost:

Och vi kan kika in i tabellvyn med PHPMyAdmin och se de skapade_by och uppdaterade_by inställningarna:

Loggar uppdateringar till status tabellen

När en statuspost skapas vet vi alltid vem som skapade den första posten. Men med skyllande beteende vet vi bara vem som senast uppdaterade posten.

Låt oss gå igenom en enkel loggimplementering för att spela in ID för den person som gör varje uppdatering. Då kan du enkelt se en uppdateringshistorik eller utvidga den till att vara en fullständig revisionslogg.

Skapa tabellen för StatusLog

Först måste vi skapa en migrering för StatusLog:

hej Jeff $ ./yii migrera / skapa create_status_log_table Yii Migrationsverktyg (baserat på Yii v2.0.2) Skapa ny migration '/Users/Jeff/Sites/hello/migrations/m150209_204852_create_status_log_table.php'? (ja | nej) [nej]: ja Ny migrering skapades framgångsrikt.

Därefter kodar vi migreringen för att inkludera relationsfält för Status-tabell-id och Användare updated_by fält:

db-> drivrutinName === 'mysql') $ tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB';  $ this-> createTable ('status 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 status_log", "status_id", "status", "id", "CASCADE", "CASCADE"); $ this-> addForeignKey ('fk_status_log_updated_by', 'status status_log', 'updated_by', '% user', 'id', 'CASCADE', 'CASCADE');  offentlig funktion ner () $ this-> dropForeignKey ('fk_status_updated_by', 'status status_log'); $ This-> dropForeignKey ( 'fk_status_id', '% status_log'); $ This-> dropColumn ( '% status_log', 'updated_by');  

Därefter kör vi migreringen:

hej Jeff $ ./yii migrera / upp Yii Migrationsverktyg (baserat på Yii v2.0.2) Totalt 1 ny migration som ska tillämpas: m150209_204852_create_status_log_table Använd ovanstående migrering? (ja | nej) nej: ja *** tillämpa m150209_204852_create_status_log_table> skapa tabell % status_log ... gjort (tid: 0.008s)> lägg till främmande nyckel fk_status_log_id: % status_log (status_id) referenser  % status (id) ... gjort (tid: 0.008s)> lägg till främmande nyckel fk_status_log_updated_by: % status_log (uppdaterad_by) referenser % user (id) ... gjort (tid: 0.008s) ** * tillämpas m150209_204852_create_status_log_table (tid: 0.028s) Migrerad framgångsrikt.

Det snabbaste sättet att skapa en modell för StatusLog (och CRUD-filer så att vi enkelt kan bläddra i tabellen) är med Yii2s kodgenerator, Gii. Du har sett mig använda den i tidigare tutorials.

Besök http: // localhost: 8888 / hej / gii och skapa modellen med dessa inställningar:

Här är CRUD-inställningarna:

Därefter förlänger vi AfterSave-händelsen i Status-modellen:

offentlig funktion afterSave ($ insert, $ changedAttributes) förälder :: afterSave ($ insert, $ changedAttributes); // när du lägger in felaktigt, har posten uppdaterats om (! $ insert) // lägg till StatusLog entry $ status_log = new StatusLog; $ status_log-> status_id = $ this-> id; $ status_log-> updated_by = $ this-> updated_by; $ status_log-> created_at = time (); $ Status_log-> Spara (); 

Denna metod kallar standardfunktionen för moderen för afterSave men skapar sedan en ny StatusLog-post när det finns en uppdatering till en Statusrad:

Teoretiskt kan vi också förlänga BlameableBehavior, men eftersom du måste se till att det finns en loggmodell för varje ActiveRecord-modell du använder den med, verkade det lättare att bygga den här funktionaliteten i Status.

Om du uppdaterar ett par Status-poster kan du sedan bläddra i StatusLog med Giis CRUD. Bilden nedan visar två ändringar som gjorts av Status.id 1.

Om du vill gå vidare bör det vara relativt enkelt att utvidga detta till en översynstabell komplett med tidigare och ny statustext för att stödja återställningsfunktionalitet.

Vad kommer härnäst?

Jag hoppas att du har haft lärt dig om Yii2 Behaviors och Blameable. Därefter utforskar vi Timestamp Behaviors, vilket minskar mängden kod du behöver skriva med varje ny modell för den gemensamma funktionen att skapa tidsstämplar för inlägg och uppdateringar.

Titta på kommande handledningar i min programmering med Yii2-serien när jag fortsätter att dyka in i olika aspekter av ramen. Du kanske också vill kolla in min Building Your Startup With PHP-serie som använder Yii2s avancerade mall när jag bygger en verklig världsapplikation.

Jag välkomnar funktion och ämnesförfrågningar. Du kan skicka in dem i kommentarerna nedan eller maila mig på min Lookahead Consulting webbplats.

Om du vill veta när nästa Yii2 handledning kommer, följ mig @ reifman på Twitter eller kolla min instruktörssida. Min instruktörssida kommer att innehålla alla artiklar från denna serie så snart de publiceras. 

relaterade länkar

  • Den Yii2 Definitive Guide: Behavior
  • Yii2 Dokumentation: Skyllbart beteende
  • Yii2 Developer Exchange, min egen Yii2 resurs webbplats