0 follower

Relationell Active Record

Vi har redan sett hur man kan anvÀnda Active Record (AR) till att selektera data frÄn en enstaka databastabell. I det hÀr avsnittet, beskrivs hur man anvÀnder AR till att sammanfoga (join) ett antal relaterade databastabeller och lÀmna den resulterande datamÀngden i retur.

NÀr relationell AR anvÀnds, rekommenderas att sambandsrestriktioner (constraints) avseende primÀrnyckel och referensattribut deklareras för de tabeller som kommer att sammanfogas (join). Restriktionerna hjÀlper till att upprÀtthÄlla korrekthet och integritet för relationellt data.

För enkelhets skull kommer databasschemat som visas i följande entity- relationshipdiagram (ER-diagram) att anvÀndas för att illustrera exempel i detta avsnitt.

ER-diagram

ER-diagram

Info: Stödet för referensattributrestriktioner varierar mellan olika databashanterare. SQLite < 3.6.19 stöder inte referensattributrestriktioner, men det gÄr ÀndÄ att deklarera restriktionerna nÀr tabeller skapas. MySQL:s MyISAM-motor saknar stöd för referensattributrestriktioner.

1. Deklarera tabellsamband ¶

Innan AR kan anvÀndas till att genomföra relationella frÄgor, mÄste AR fÄ veta hur en AR-klass relaterar till en annan.

Samband mellan tvÄ AR-klasser Àr direkt förknippat med sambandet mellan databastabellerna som AR-klasserna representerar. FrÄn databasens synvinkel kan sambandet mellan tvÄ tabeller A and B ha tre typer: en-till-mÄnga (t.ex. tbl_user och tbl_post), en-till-en (t.ex. tbl_user och tbl_profile) samt mÄnga-till-mÄnga (t.ex. tbl_category och tbl_post). Inom AR finns det fyra sorters samband:

  • BELONGS_TO: om sambandet mellan tabellerna A och B Ă€r en-till-mĂ„nga, sĂ„ Ă€r B tillhörig A (t.ex. Post tillhör User);

  • HAS_MANY: om sambandet mellan tabellerna A och B Ă€r en-till-mĂ„nga, sĂ„ har A mĂ„nga B (t.ex. User har mĂ„nga Post);

  • HAS_ONE: detta Ă€r ett specialfall av HAS_MANY dĂ€r A har som mest en B (t.ex. User har som mest en Profile);

  • MANY_MANY: detta motsvarar mĂ„nga-till-mĂ„ngasambandet i databasen. En assisterande tabell erfordras för att bryta upp ett mĂ„nga-till-mĂ„ngasamband i ett-till-mĂ„ngasamband, eftersom de flesta databashanterare saknar direkt stöd för mĂ„nga-till-mĂ„ngasamband. I vĂ„rt exempelschema, tjĂ€nar tbl_post_category detta syfte. Med AR terminology kan MANY_MANY förklaras som kombinationen av BELONGS_TO och HAS_MANY. Till exempel, Post tilhör mĂ„nga Category och Category har mĂ„nga Post.

Tabellsamband deklareras i AR genom att metoden relations() i CActiveRecord ÄsidosÀtts. Denna metod returnerar en array med sambandskonfigurationer. Varje element i denna array representerar ett enstaka samband, pÄ följande format:

'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)

dÀr VarName Àr sambandets namn; RelationType specificerar sambandets typ, som kan vara en av de fyra konstanterna: self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY samt self::MANY_MANY; ClassName Àr namnet pÄ den AR-klass som har samband med denna AR-klass; ForeignKey specificerar det eller de referensattribut som Àr involverade i sambandet. Ytterligare alternativ kan specificeras i slutet av varje sambandsdeklaration (beskrivs lÀngre fram).

Följande kod visar hur sambandet mellan klasserna User och Post deklareras.

class Post extends CActiveRecord
{
    ......
 
    public function relations()
    {
        return array(
            'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
            'categories'=>array(self::MANY_MANY, 'Category',
                'tbl_post_category(post_id, category_id)'),
        );
    }
}
 
class User extends CActiveRecord
{
    ......
 
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
            'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
        );
    }
}

Info: Ett referensattribut kan vara sammansatt och bestÄ av tvÄ eller flera kolumner. I det fallet skall namnen pÄ kolumner som ingÄr i referensattributet skrivas efter varandra, separerade av kommatecken alternativt som en vektor sÄ som array('key1','key2'). I fallet att anpassade PK->FK associationer behöver specificeras kan formatet array('fk'=>'pk') anvÀndas. För sammansatta nycklar blir det array('fk_c1'=>'pk_c1','fk_c2'=>'pk_c2'). För samband av typen MANY_MANY mÄste namnet pÄ den assisterande tabellen ocksÄ specificeras i referensattributet. Till exempel, sambandet categories i Post Àr specificerat med referensattributet tbl_post_category(post_id, category_id). Deklarationen av samband i en AR-klass lÀgger underförstÄtt till en property i klassen för varje samband. NÀr en relationell frÄga har utförts kommer den motsvarande propertyn att innehÄlla den relaterade AR-instansen(-erna). Till exempel, om $author representerar en AR-instans User, kan $author->posts anvÀndas för tillgÄng till dess relaterade Post-instans.

2. Utföra relationell frÄga ¶

Det enklaste sÀttet att utföra en relationell frÄga Àr genom att lÀsa en relationell property i en AR-instans. Om denna property inte har lÀsts tidigare kommer en relationell frÄga att initieras, som slÄr samman de tvÄ relaterade tabellerna och filtrerar med primÀrnyckeln i aktuell AR-instans. FrÄgeresultatet kommer att sparas i propertyn som en eller flera instanser av den relaterade AR- klassen. Detta förfarande Àr kÀnt som lazy loading, dvs den relationella frÄgan utförs först nÀr relaterade objekt refereras till första gÄngen. Exemplet nedan visar hur man anvÀnder detta tillvÀgagÄngssÀtt:

// retrieve the post whose ID is 10
$post=Post::model()->findByPk(10);
// retrieve the post's author: a relational query will be performed here
$author=$post->author;

Info: Om det saknas en relaterad instans i ett samband kan den motsvarande propertyn anta vÀrdet null eller en tom array. För sambanden BELONGS_TO och HAS_ONE , Àr resultatet null; för HAS_MANY och MANY_MANY, Àr det en tom array. MÀrk att sambandstyperna HAS_MANY och MANY_MANY returnerar arrayer av objekt, dÀrför behöver man iterera över resultatet för att komma Ät propertyn. Om man inte gör detta erhÄlls felet "Trying to get property of non-object".

TillvÀgagÄngssÀttet med lazy loading Àr mycket bekvÀmt att anvÀnda, men har lÀgre prestanda i vissa scenarier. Till exempel, om vi vill fÄ tillgÄng till information om författare för N postningar, kommer tillvÀgagÄngssÀttet lazy att omfatta körning av N join-frÄgor. Under dessa omstÀndigheter bör det alternativa tillvÀgagÄngssÀttet, kallat eager loading, anvÀndas.

TillvÀgagÄngssÀttet eager loading hÀmtar in relaterade AR-instanser tillsammans med huvudinstansen (-instanserna). Detta Ästadkommes genom anvÀndning av metoden with() tillsammans med en av find- eller findAll-metoderna i AR. Till exempel,

$posts=Post::model()->with('author')->findAll();

OvanstÄende kod returnerar en array bestÄende av Post-intanser. Till skillnad frÄn tillvÀgagÄngssÀttet lazy, Àr propertyn author i varje instans av Post redan laddad med den relaterade User-instansen redan innan vi refererar till propertyn. I stÀllet för att exekvera en join-frÄga för varje postning, hÀmtar tillvÀgagÄngssÀttet eager loading in samtliga postningar tillsammans med deras respektive författare, alltsammans i en enda join-frÄga!

Man kan specificera flera sambandsnamn till metoden with() och tillvÀgagÄngssÀttet eager loading kommer att hÀmta in dem alla i ett moment. Till exempel, följande kod hÀmtar in postningar tillsammans med deras repektive författare och kategorier:

$posts=Post::model()->with('author','categories')->findAll();

Det gÄr att anvÀnda nÀstlad eager loading. I stÀllet för en lista med sambandsnamn, lÀmnar vi med en hierarkisk representation av sambandsnamnen till metoden with(), som i följande exempel,

$posts=Post::model()->with(
    'author.profile',
    'author.posts',
    'categories')->findAll();

OvanstÄende exempel hÀmtar in alla postningar tillsammans med deras respektive författare och kategorier. Det hÀmtar Àven in varje författares profil samt postningar.

Eager loading kan Àven exekveras genom att man specificerar propertyn CDbCriteria::with, som i följande exempel:

$criteria=new CDbCriteria;
$criteria->with=array(
    'author.profile',
    'author.posts',
    'categories',
);
$posts=Post::model()->findAll($criteria);

eller

$posts=Post::model()->findAll(array(
    'with'=>array(
        'author.profile',
        'author.posts',
        'categories',
    )
));

3. Genomföra relationell frÄga utan att hÀmta relaterade modeller ¶

Ibland vill vi utföra frÄgor som anvÀnder samband, utan att hÀmta in relaterade modeller. Antag att vi har User-poster som skapat mÄnga Post-poster. Post kan ha status publicerad men kan Àven ha utkaststatus. Detta bestÀms av fÀltet published i modellen Post. Ny vill vi hÀmta alla anvÀndare som har publicerade postningar utan att inkludera sjÀlva postningarna. Detta kan upppnÄs pÄ följande sÀtt:

$users=User::model()->with(array(
    'posts'=>array(
        // vi vill inte selektera fÀlt frÄn postningar
        'select'=>false,
        // men vi vill bara ha anvÀndare med publicerade postningar
        'joinType'=>'INNER JOIN',
        'condition'=>'posts.published=1',
    ),
))->findAll();

4. Alternativ för relationella frÄgor ¶

Som nÀmnts kan ytterligare alternativ anges i sambandsdeklarationer. Dessa alternativ, specificerade i form av namn-vÀrdepar, anvÀnds för att anpassa den relationella frÄgan. De sammanfattas nedan.

  • select: en lista med med kolumner som skall selekteras till den relaterade AR-klassen. Den har standardvĂ€rdet '*', vilket innebĂ€r alla kolumner. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga.

  • condition: motsvarar WHERE-ledet. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga.

  • params: parametrarna som skall kopplas ihop med den genererade SQL-satsen. Dessa skall ges som en array bestĂ„ende av namn-vĂ€rdepar.

  • on: motsvarar ON-ledet. Villkoret som specificeras hĂ€r kommer att lĂ€ggas till sammanslagningsvillkoret med hjĂ€lp av AND-operatorn. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga. Detta alternativ Ă€r inte relevant vid MANY_MANY-samband.

  • order: motsvarar ORDER BY-ledet. Det Ă€r som standard tomt. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga.

  • with: en lista med underordnade relaterade objekt som skall laddas tillsammans med detta objekt. Var uppmĂ€rksam pĂ„ att om detta alternativ anvĂ€nds olĂ€mpligt, kan det leda till en Ă€ndlös slinga av relationer.

  • joinType: typ av sammanslagning för detta samband. Den Ă€r som standard LEFT OUTER JOIN.

  • alias: aliasnamn för tabellen som förknippas med detta samband. StandardvĂ€rde Ă€r null, vilket innebĂ€r att tabellalias genereras automatiskt. Detta skiljer sig frĂ„n aliasToken pĂ„ sĂ„ sĂ€tt att den senare bara Ă€r en platshĂ„llare och ersĂ€tts med faktiskt tabellalias.

  • together: huruvida tabellen associerad med detta samband skall tvingas till en ovillkorlig sammanfogning (join) med den primĂ€ra tabellen och andra tabeller. Detta alternativ Ă€r endast relevant för samband av typerna HAS_MANY och MANY_MANY. Om alternativet sĂ€tts till false, kommer tabellen som Ă€r associerad med HAS_MANY- eller MANY_MANY-sambandet att sammanfogas med den primĂ€ra tabellen i en separat SQL-frĂ„ga, nĂ„got som kan öka total frĂ„geprestanda eftersom mindre mĂ€ngd redundant data returneras. Om detta alternativ sĂ€tts till true, kommer den associerade tabellen alltid att sammanfogas med den primĂ€ra tabellen i en och samma SQL-frĂ„ga, Ă€ven om den primĂ€ra tabellen sidindelas. Om detta alternativ inte sĂ€tts, kommer den associerade tabellen att sammanfogas med den primĂ€ra tabellen i en och samma SQL-frĂ„ga, endast nĂ€r den primĂ€ra tabellen inte sidindelas. För fler detaljer se avsnittet "Relational Query Performance".

  • join: ett tillkommande JOIN-led. Det Ă€r som standard tomt. Detta alternativ har varit tillgĂ€ngligt sedan version 1.1.3.

  • group: motsvarar GROUP BY-ledet. Det Ă€r som standard tomt. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga.

  • having: motsvarar HAVING-ledet. Det Ă€r som standard tomt. Kolumnnamn som refereras till i detta alternativ mĂ„ste göras otvetydiga.

  • index: namnet pĂ„ kolumnen vars vĂ€rden skall anvĂ€ndas som nycklar i den array som lagrar relaterade objekt. Om detta alternativ inte sĂ€tts kommer en relaterad objektarray att anvĂ€nda ett nollbaserat heltalsindex. Detta alternativ kan endast sĂ€ttas för sambandstyperna HAS_MANY och MANY_MANY.

  • scopes: omfĂ„ng som skall tillĂ€mpas. Ett enstaka omfĂ„ng kan anges som 'scopes'=>'scopeName', för flerfaldiga omfĂ„ng anvĂ€nds formatet 'scopes'=>array('scopeName1','scopeName2'). Detta alternativ blev tillgĂ€ngligt i version 1.1.9.

Dessutom Àr följande alternativ tillgÀngliga för vissa samband nÀr lazy loading anvÀnds:

  • limit: begrĂ€nsar antalet rader som kan selekteras. Detta alternativ Ă€r INTE tillĂ€mpligt pĂ„ BELONGS_TO-samband.

  • offset: offset till rader som skall selekteras. Detta alternativ Ă€r INTE tillĂ€mpligt pĂ„ BELONGS_TO-samband.

  • through: namnet pĂ„ en modells samband, vilket skall anvĂ€ndas som brygga vid hĂ€mtning av relaterad data. Kan tillĂ€mpas enbart pĂ„ HAS_ONE- och HAS_MANY-samband. Detta alternativ har varit tillgĂ€ngligt sedan version 1.1.7.

Nedan har deklarationen av sambandet posts i User varierats genom inkludering av nÄgra av ovanstÄende alternativ:

class User extends CActiveRecord
{
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id',
                            'order'=>'posts.create_time DESC',
                            'with'=>'categories'),
            'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
        );
    }
}

Om vi nu refererar till $author->posts, kommer vi att erhÄlla författarens postningar sorterade i fallande ordning efter tid de skapats. Varje instans av postning har ocksÄ fÄtt sina kategorier laddade.

Info: NÀr ett kolumnnamn upptrÀder i tvÄ eller fler tabeller som slÄs samman (join), behöver det göras otvetydigt. Detta Ästadkommes genom att föregÄ kolumnnamnet med dess tabellnamn. Till exempel, id blir Team.id. I AR:s relationella frÄgor dÀremot, saknas denna frihet eftersom SQL-satserna genereras automatiskt av AR, vilket systematiskt ger varje tabell ett alias. Av denna anledning anvÀnds, för att undvika konflikter mellan kolumnnamn, en platshÄllare för att indikera förekomsten av en kolumn som behöver göras otvetydig. AR ersÀtter platshÄllaren med ett passande tabellalias och gör kolumnen otvetydig.

5. Göra kolumnnamn otvetydiga ¶

NÀr ett kolumnnamn förekommer i tvÄ eller fler tabeller som sammanfogas, behöver det göras otvetydigt. Detta gör man genom att skjuta in tabellens aliasnamn före kolumnnamnet.

I en relationell AR-frÄga Àr aliasnamnet för den primÀra tabellen alltid t, medan aliasnamnet för en relaterad tabell som standard Àr samma som det motsvarande sambandets namn. Till exempel sÄ har Post och Comment i följande exempel, aliasnamnen t respektive comments:

$posts=Post::model()->with('comments')->findAll();

Antag nu att bÄde Post och Comment har en kolumn create_time som indikerar tiden en postning respektive kommentar skapats och att vi vill hÀmta postningar tillsammans med tillhörande kommentarer samt sortera dessa efter först postningarnas dÀrefter kommentarernas skapandetid. Vi behöver dÄ göra kolumnen create_time otvetydig pÄ följande sÀtt:

$posts=Post::model()->with('comments')->findAll(array(
    'order'=>'t.create_time, comments.create_time'
));

6. Alternativ för dynamisk relationell frÄga ¶

Det gÄr att anvÀnda alternativ för dynamisk relationell frÄga bÄde med metoden with() och med with-alternativet. De dynamiska alternativen skriver över existerande alternativ som specificerats i metoden relations(). Till exempel, för att, med ovanstÄende User-modell, anvÀnda tillvÀgagÄngssÀttet eager loading till att hÀmta in postningar tillhörande en författare i stigande ordningsföljd (order-alternativet i sambandet specificerar fallande ordningsföljd), kan man göra följande:

User::model()->with(array(
    'posts'=>array('order'=>'posts.create_time ASC'),
    'profile',
))->findAll();

Dynamiska frÄgealternativ kan Àven anvÀndas med relationella frÄgor som anvÀnder tillvÀgagÄngssÀttet lazy loading. För att göra sÄ, anropa en metod vars namn Àr lika sambandsnamnet och lÀmna med de dynamiska frÄgealternativen som metodparameter. Till exempel returnerar följande kod de av en anvÀndares postningar vars status` Àr lika med 1:

$user=User::model()->findByPk(1);
$posts=$user->posts(array('condition'=>'status=1'));

7. Prestanda för relationell frÄga ¶

Som beskrivits ovan anvÀnds tillvÀgagÄngssÀttet eager loading huvudsakligen i ett scenario dÀr vi behöver accessa mÄnga relaterade objekt. Det genererar en stor komplex SQL-sats genom att utföra join med alla tabeller som behövs. En stor SQL-sats Àr att föredra i mÄnga fall eftersom den förenklar filtrering baserad pÄ en kolumn i en relaterad tabell. Dock kan den vara mindre effektiv i vissa fall.

TÀnk ett exempel dÀr vi behöver hitta de senaste postningarna tillsammans med tillhörande kommentarer. Med antagandet att varje postning har 10 kommentarer och en enda stor SQL-sats, kommer en stor mÀngd redundant data om postningar att returneras eftersom varje postning kommer att repeteras för varje tillhörande kommentar. TÀnk ett annat tillvÀgagÄngssÀtt: först en frÄga om senaste postningarna, dÀrefter en frÄga om tillhörande kommentarer. Vid detta nya tillvÀgagÄngssÀtt, behöver tvÄ SQL-satser exekveras. Vinsten Àr att det inte kommer med reduntant data i frÄgeresultatet.

SĂ„ vilket tillvĂ€gagĂ„ngssĂ€tt Ă€r mer effektivt? Det finns inget absolut svar. Att exekvera en enstaka stor SQL-sats kan vara mer effektivt eftersom det leder till mindre overhead i DBMS:en för parsning och exekvering av SQL-satser. Å andra sidan, vid anvĂ€ndning av en enstaka SQL-sats, kommer det att gĂ„ Ă„t mer tid till att lĂ€sa och bearbeta den större mĂ€ngden redundant data.

Av detta skÀl, tillhandahÄller Yii frÄgealternativet together sÄ att vi kan vÀlja mellan de tvÄ tillvÀgagÄngssÀtten. Som standard anvÀnder sig Yii av eager loading, dvs, att skapa en enda SQL-sats, utom nÀr LIMIT Àr Äsatt den primÀra modellen. Genom att sÀtta alternativet together till true i sambandsdeklarationen kan en enstaka SQL-sats tvingas fram, Àven nÀr LIMIT anvÀnds. Om alternativet together sÀtts till false sker join mellan
vissa tabeller i separata SQL-satser. Till exempel, för att anvÀnda det andra tillvÀgagÄngssÀttet i samband med
en frÄga om de senaste postningarna med tillhörande kommentarer, kan vi deklarera sambandet comments i Post-klassen pÄ följande sÀtt,

public function relations()
{
    return array(
        'comments' => array(self::HAS_MANY, 'Comment', 'post_id', 'together'=>false),
    );
}

Vi kan Àven sÀtta detta alternativ dynamiskt nÀr vi genomför eager loading:

$posts = Post::model()->with(array('comments'=>array('together'=>false)))->findAll();

8. StatistikfrÄga ¶

Utöver relationella frÄgor som beskrivits ovan, stöder Yii ocksÄ sÄ kallade statistikfrÄgor (eller aggregationsfrÄgor). Detta refererar till inhÀmtning av aggregeringsinformation om relaterade objekt, sÄsom antalet kommentarer till varje postning, den genomsnittliga poÀngsÀttningen för varje produkt, etc. StatistikfrÄgor kan endast utföras mot objekt som har sambandstyperna HAS_MANY (t.ex. en postning har mÄnga kommentarer) eller MANY_MANY (t.ex. en postning tillhör mÄnga kategorier och en kategori har mÄnga postningar).

Att genomföra en statistikfrÄga Àr mycket snarlikt till att utföra en relationell frÄga, som tidigare besrivits. Först deklareras en statistikfrÄga i metoden relations() i CActiveRecord precis som vid en relationell frÄga.

class Post extends CActiveRecord
{
    public function relations()
    {
        return array(
            'commentCount'=>array(self::STAT, 'Comment', 'post_id'),
            'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'),
        );
    }
}

Ovan deklareras tvÄ statistikfrÄgor: commentCount berÀknar antalet kommentarer som tillhör en postning och categoryCount berÀknar antalet kategorier en postning tillhör. MÀrk att sambandstypen mellan between Post och Comment Àr HAS_MANY, medan sambandstypen mellan Post och Category Àr MANY_MANY (med hjÀlp av mellantabellen post_category). Som tydligt framgÄr Àr deklarationen mycket snarlik de sambandsdeklarationer som beskrivits i tidigare delavsnitt. Den enda skillnaden Àr att sambandstypen STAT anvÀnds hÀr.

Med ovanstÄende deklaration kan vi hÀmta antalet kommentarer till en postning med hjÀlp av uttrycket $post->commentCount. NÀr vi anvÀnder denna property första gÄngen, kommer en SQL-sats att exekveras implicit för att hÀmta in det önskade resultatet. Som bekant Àr detta den sÄ kallade lazy loading-metoden. Vi kan Àven anvÀnda eager loading-metoden om vi behöver avgöra antalet kommentarer för ett flertal postningar:

$posts=Post::model()->with('commentCount', 'categoryCount')->findAll();

OvanstÄende programsats exekverar tre SQL-satser för att leverera alla postningar tillsammans med deras respektive kommentarantal och antal kategorier. Om lazy loading-metoden anvÀnds blir resultatet att 2*N+1 SQL-frÄgor exekveras givet N postningar.

Som standard kalkylerar en statistikfrÄga COUNT-uttrycket (och dÀrmed kommentarantalet och antalet kategorier i ovanstÄende exempel). Detta kan vi anpassa genom att ange ytterligare alternativ nÀr vi deklarerar relations(). De tillgÀngliga alternativen summeras nedan.

  • select: statistikfrĂ„gan. Som standard COUNT(*), innebĂ€rande antalet underordnade objekt.

  • defaultValue: vĂ€rde som skall tilldelas de poster som inte erhĂ„ller ett resultat frĂ„n statistikfrĂ„gan. Till exempel, om en postning inte har nĂ„gra kommentarer, kommer dess commentCount att Ă„sĂ€ttas detta vĂ€rde. StandardvĂ€rde för detta alternativ Ă€r 0.

  • condition: WHERE-ledet. Som standard tomt.

  • params: parametrarna som skall kopplas till den genererade SQL-satsen. De skall anges som en array av namn-vĂ€rdepar.

  • order: ORDER BY-ledet. Som standard tomt.

  • group: GROUP BY-ledet. Som standard tomt.

  • having: HAVING-ledet. Som standard tomt.

9. Relationell frÄga med namngivna omfÄng ¶

Relationella frÄgor kan Àven utföras i kombination med namngivna omfÄng. Detta kan ske i tvÄ former. I den första formen appliceras namngivna omfÄng pÄ huvudmodellen. I den andra formen appliceras namngivna omfÄng pÄ relaterade modeller.

Följande kod visar hur namngivna omfÄng appliceras pÄ huvudmodellen.

$posts=Post::model()->published()->recently()->with('comments')->findAll();

Detta Àr mycket snarlikt icke-relationella frÄgor. Den enda skillnaden Àr anropet av with() efter kedjan av namngivna omfÄng. OvanstÄende frÄga skulle hÀmta nyligen publicerade postningar tillsammans med dess kommentarer.

FÀljande kod visar hur namngivna omfÄng appliceras pÄ relaterade modeller.

$posts=Post::model()->with('comments:recently:approved')->findAll();
// eller sedan 1.1.7
$posts=Post::model()->with(array(
    'comments'=>array(
        'scopes'=>array('recently','approved')
    ),
))->findAll();
// eller sedan 1.1.7
$posts=Post::model()->findAll(array(
    'with'=>array(
        'comments'=>array(
            'scopes'=>array('recently','approved')
        ),
    ),
));

OvanstÄende frÄga skulle hÀmta alla postningar tillsammans med deras för publicering godkÀnda kommentarer. MÀrk att comments refererar till sambandsnamnet, medan recently och approved refererar till tvÄ namngivna omfÄng som deklarerats i modellklassen Comment. Sambandsnamnet och de namngivna omfÄngen skall separeras med kolon.

Namngivna omfÄng kan Àven specificeras med alternativet with i sambandsdeklarationen i CActiveRecord::relations(). I följande exempel kommer - om vi accessar $user->posts - alla postningarnas godkÀnda (för publicering) kommentarer att hÀmtas.

class User extends CActiveRecord
{
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id',
                'with'=>'comments:approved'),
        );
    }
}
 
// eller sedan 1.1.7
class User extends CActiveRecord
{
    public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id',
                'with'=>array(
                    'comments'=>array(
                        'scopes'=>'approved'
                    ),
                ),
            ),
        );
    }
}

MÀrk: Före 1.1.7 gÀllde att namngivna omfÄng som appliceras pÄ relaterade modeller mÄste specificeras i CActiveRecord::scopes. Detta medför att de inte kan parametriseras.

Sedan version 1.1.7 Àr det möjligt att lÀmna med parametrar för relationella namngivna omfÄng. Till exempel, ett omfÄng med namnet rated i modellen Post som accepterar minsta önskade ratingvÀrde för en postning, kan anvÀndas pÄ följande sÀtt frÄn modellen User:

$users=User::model()->findAll(array(
    'with'=>array(
        'posts'=>array(
            'scopes'=>array(
                'rated'=>5,
            ),
        ),
    ),
));

10. Relationell frÄga med 'through' ¶

Vid anvÀndning av through skall sambandsdefinitionen se ut som följer:

'comments'=>array(self::HAS_MANY,'Comment',array('key1'=>'key2'),'through'=>'posts'),

I ovanstÄende array('key1'=>'key2'):

  • key1 Ă€r en nyckel definierad i sambandet som specificeras i through (i detta fall posts).
  • key2 Ă€r en nyckel definierad i modellen detta samband (comments) refererar till (i detta fall Comment).

through kan anvÀndas med HAS_ONE- och HAS_MANY-samband.

HAS_MANY through

HAS_MANY through ER

HAS_MANY through ER

Ett exempel pÄ HAS_MANY med through Àr att hÀmta anvÀndare tillhörande en specifik grupp nÀr anvÀndare Àr tilldelade grupp via roller.

Ett mer komplext exempel Àr att hÀmta samtliga kommentarer för alla anvÀndare tillhörande en specifik grupp . I detta fall behöver vi anvÀnda flera samband med through, i en enda modell:

class Group extends CActiveRecord
{
   ...
   public function relations()
   {
       return array(
           'roles'=>array(self::HAS_MANY,'Role','group_id'),
           'users'=>array(self::HAS_MANY,'User',array('user_id'=>'id'),'through'=>'roles'),
           'comments'=>array(self::HAS_MANY,'Comment',array('id'=>'user_id'),'through'=>'users'),
       );
   }
}

AnvÀndningsexempel

// hÀmta alla grupper samt alla tillhörande anvÀndare
$groups=Group::model()->with('users')->findAll();
 
// hÀmta alla grupper med alla tillhörande anvÀndare och roller
$groups=Group::model()->with('roles','users')->findAll();
 
// hÀmta alla anvÀndare och roller för vilka grupp-ID Àr 1
$group=Group::model()->findByPk(1);
$users=$group->users;
$roles=$group->roles;
 
// hÀmta alla kommentarer för vilka grupp-ID Àr 1
$group=Group::model()->findByPk(1);
$comments=$group->comments;

HAS_ONE through

HAS_ONE through ER

HAS_ONE through ER

Ett exempel pÄ anvÀndning av HAS_ONE med through Àr att hÀmta en anvÀndares adress nÀr anvÀndaren Àr kopplad till addressen genom en profil. Alla dessa entiteter (user, profile, och address) har motsvarande modeller:

class User extends CActiveRecord
{
   ...
   public function relations()
   {
       return array(
           'profile'=>array(self::HAS_ONE,'Profile','user_id'),
           'address'=>array(self::HAS_ONE,'Address',array('id'=>'profile_id'),'through'=>'profile'),
       );
   }
}

AnvÀndningsexempel

// hÀmta adressen för en anvÀndare vars ID Àr 1
$user=User::model()->findByPk(1);
$address=$user->address;

Egensamband med through

through kan anvÀndas för en modell som relaterar till sig sjÀlv via en bryggmodell. I exemplet handlar det om en anvÀndare som Àr mentor för andra anvÀndare:

through self ER

through self ER

I detta fall definierar vi sambandet sÄ hÀr:

class User extends CActiveRecord
{
   ...
   public function relations()
   {
       return array(
           'mentorships'=>array(self::HAS_MANY,'Mentorship','teacher_id','joinType'=>'INNER JOIN'),
           'students'=>array(self::HAS_MANY,'User',array('student_id'=>'id'),'through'=>'mentorships','joinType'=>'INNER JOIN'),
       );
   }
}

AnvÀndningsexempel

// hÀmta alla studenter som undervisas av lÀraren vars ID Àr 1
$teacher=User::model()->findByPk(1);
$students=$teacher->students;

Found a typo, or you think this page needs improvement?
Edit it on GitHub !