鍍金池/ 教程/ C++/ 為 Yii Framework 創(chuàng)建生成 ActiveRecord 的代碼模板
使用主從代碼模板
CodeTemplateInfo 對象
引用其它文件或 .Net 類庫
編寫第一個代碼模板
Progress 對象
基本語法-使用注釋
基本語法-轉(zhuǎn)義Asp.Net標(biāo)記
調(diào)試
為 Yii Framework 創(chuàng)建生成 ActiveRecord 的代碼模板
基本語法-聲明和使用屬性
自動生成Yii Framework ActiveRecord類簡單模板
Merge 策略
使用 XMLProperty
CodeTemplate 對象
基本語法-CodeTemplate 指令
概述
使用 SchemaExplorer 來獲取數(shù)據(jù)庫定義

為 Yii Framework 創(chuàng)建生成 ActiveRecord 的代碼模板

CodeSmith 使用教程(3): 自動生成 Yii Framework ActiveRecord 我們通過 SchemaExploer 為 Yii Framework 從數(shù)據(jù)庫生成簡單的 ActiveRecord 類,沒有考慮到表和表之間的關(guān)系。本例我們使用 CodeSmith 為 Yii Framework 創(chuàng)建一個通用的代碼模板,可以使用上例介紹的SchemaExploer ,不過在查看 CodeSmith 自帶的例子中有個生成 Hibernate 的例子,這個模板的使用可以參見 CodeSmith 使用教程(1): 概述 ,CodeSmith 提供了這個模板的源碼,使用到了 CodeSmith.SchemaHelper (CodeSmith 沒有提供相應(yīng)的文檔),不過可以通過閱讀 NHiberante 的模板了解其一般用法。

為生成 Yii Framework ActiveRecord 類之間的 relation ,先要了解一下表和表之間的關(guān)系:

兩個 AR 類之間的關(guān)系直接通過 AR 類所代表的數(shù)據(jù)表之間的關(guān)系相關(guān)聯(lián)。 從數(shù)據(jù)庫的角度來說,表 A 和 B 之間有三種關(guān)系:一對多(one-to-many,例如 tbl_user 和 tbl_post),一對一( one-to-one 例如 tbl_user 和tbl_profile)和 多對多(many-to-many 例如 tbl_category 和 tbl_post)。 在 AR 中,有四種關(guān)系:

  • BELONGS_TO(屬于): 如果表 A 和 B 之間的關(guān)系是一對多,則 表 B 屬于 表 A (例如 Post 屬于 User);

  • HAS_MANY(有多個): 如果表 A 和 B 之間的關(guān)系是一對多,則 A 有多個 B (例如 User 有多個 Post);

  • HAS_ONE(有一個): 這是 HAS_MANY 的一個特例,A 最多有一個 B (例如 User 最多有一個 Profile);

  • MANY_MANY: 這個對應(yīng)于數(shù)據(jù)庫中的 多對多 關(guān)系。 由于多數(shù) DBMS 不直接支持 多對多 關(guān)系,因此需要有一個關(guān)聯(lián)表將 多對多 關(guān)系分割為 一對多 關(guān)系。 在我們的示例數(shù)據(jù)結(jié)構(gòu)中,tbl_post_category 就是用于此目的的。在 AR 術(shù)語中,我們可以解釋 MANY_MANY 為 BELONGS_TO 和 HAS_MANY 的組合。 例如,Post 屬于多個(belongs to many) Category ,Category 有多個(has many) Post.

本例還是使用 Chinook 數(shù)據(jù)庫,修改 Yii Framework 開發(fā)教程(27) 數(shù)據(jù)庫-關(guān)聯(lián) Active Record 示例。數(shù)據(jù)表之間的關(guān)系如下:

http://wiki.jikexueyuan.com/project/codesmith/images/26.png" alt="第26張" />

CodeSmith 中 PLINQO-NH 代碼位置:

缺省目錄為 C:\Program Files (x86)\CodeSmith\v6.5\Samples\Templates\Frameworks\PLINQO-NH

http://wiki.jikexueyuan.com/project/codesmith/images/27.png" alt="第27張" />

CodeSmith.SchemaHelper 定義的主要類有:

http://wiki.jikexueyuan.com/project/codesmith/images/28.png" alt="第28張" />

幾個主要的類為

  • EntityManager 管理所有的 Entity(對應(yīng)于整個數(shù)據(jù)庫)
  • Entity 實(shí)體類(對應(yīng)到單個表,視圖)
  • IAssoication 關(guān)系(定義表和表之間的關(guān)系)
  • AssoicationType 關(guān)系的類型 (見下表)

根據(jù) AssociationType ,數(shù)據(jù)庫之間的關(guān)系以及 Yii AR 支持的幾種關(guān)系,可以定義下表:

http://wiki.jikexueyuan.com/project/codesmith/images/29.png" alt="第29張" />

整個模板也是采用主-從模板的方式,主模板枚舉 EntityManager 中的每個 Entity,然后調(diào)用子模板為每個表 生成 AR 類:

public void Generate()
{
   EntityManager entityManager = CreateEntityManager();
   foreach(IEntity entity in entityManager.Entities)
    {
        if (!(entity is CommandEntity)) {
            RenderEntity(entity);
        }
    }
}

...

private void RenderEntity(IEntity entity)
{

    string folder=@"../models/";
    EntityTemplate entityTemplate = this.Create<EntityTemplate>();
    entityTemplate.SourceEntity = entity;
    entityTemplate.RenderToFile(folder+entity.Name+".php", true);
}

子模板則根據(jù)每個 Entity 的 Assoications(關(guān)系屬性)為 AR 生成 relations 函數(shù),

<?php

class <%= SourceEntity.Name %> extends CActiveRecord
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return '<%= SourceEntity.GetSafeName() %>';
    }

    <%if (SourceEntity.Associations.Count>0){ %>
    public function relations()
    {
        return array(
         <% IEnumerable<IAssociation> associations = SourceEntity.Associations; %>
         <% foreach(IAssociation association in associations) { %>
         <% if(association.Entity.Name!=association.ForeignEntity.Name) {%>
            <% if (association.AssociationType == AssociationType.ManyToOne
                || association.AssociationType==AssociationType.ManyToZeroOrOne) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::BELONGS_TO,
            '<%= association.ForeignEntity.Name %>',
            <%=GetBelongToKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.OneToMany
                || association.AssociationType==AssociationType.ZeroOrOneToMany) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::HAS_MANY,
            '<%= association.ForeignEntity.Name %>',
            <%=GetKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.OneToOne
                || association.AssociationType==AssociationType.OneToZeroOrOne) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::HAS_ONE,
            '<%= association.ForeignEntity.Name %>',
            <%=GetKey(association) %>
            <% } %>
            <% if (association.AssociationType == AssociationType.ManyToMany) { %>
            '<%= ToCameral(association.Name) %>'=>array(self::MANY_MANY,
            '<%= association.IntermediaryAssociation.Entity.Name %>',
            <%=GetManyToManyKey(association) %>
            <% } %>
         <% } %>
     <% } %>
        );
    }
    <% } %>
}

?>

<script runat="template">

public string ToCameral(string name)
{
    return StringUtil.ToCamelCase(name);
 }

public string GetKey(IAssociation association)
{
    string retString=string.Empty;

    if(association.Properties.Count>1)
    {
        retString="array(";
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.ForeignProperty.GetSafeName()+"',";
        }
        retString+="),";
    }else{
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.ForeignProperty.GetSafeName()+"'),";
        }

    }
    return retString;
}

public string GetBelongToKey(IAssociation association)
{
    string retString=string.Empty;

    if(association.Properties.Count>1)
    {
        retString="array(";
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.Property.GetSafeName()+"',";
        }
        retString+="),";
    }else{
        foreach (AssociationProperty associationProperty in association.Properties)
        {
            retString+="'"+associationProperty.Property.GetSafeName()+"'),";
        }

    }
    return retString;
}

public string GetManyToManyKey(IAssociation association)
{

    string retString="'"+association.ForeignEntity.GetSafeName()+"(";

    foreach (AssociationProperty associationProperty in association.Properties)
    {
        retString+=associationProperty.ForeignProperty.GetSafeName()+",";
    }
    IAssociation intermidateAssociation=association.IntermediaryAssociation;
    if(intermidateAssociation!=null)
    {
           foreach (AssociationProperty associationProperty in intermidateAssociation.Properties)
        {
            retString+=associationProperty.ForeignProperty.GetSafeName()+",";
        }
    }

    retString=retString.Substring(0,retString.Length-1);
    retString+=")'),";
    return retString;
}
</script>

然后 generated output 就可以為數(shù)據(jù)庫的表生成對應(yīng)的 AR 類,比如生成的 Track 類

class Track extends CActiveRecord
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return 'track';
    }

    public function relations()
    {
        return array(
            'album'=>array(self::BELONGS_TO,'Album','AlbumId'),
            'genre'=>array(self::BELONGS_TO,'Genre','GenreId'),
            'mediatype'=>array(self::BELONGS_TO,'Mediatype','MediaTypeId'),
            'invoicelines'=>array(self::HAS_MANY,'Invoiceline','TrackId'),
            'playlists'=>array(self::MANY_MANY,'Playlist','playlisttrack(TrackId,PlaylistId)'),
        );
    }
}

如果實(shí)在看不懂本例也無所謂,可以直接使用該模板,只要設(shè)置數(shù)據(jù)源 ,如果數(shù)據(jù)庫的表有前綴,比如Wordpress 的表有 wp_ 可以設(shè)置表前綴(不是必須的)

http://wiki.jikexueyuan.com/project/codesmith/images/30.png" alt="第30張" />

本例下載 ,如果需要使用本例的模板,直接把項(xiàng)目中 protected 下的codesmith 目錄拷貝到你自己的項(xiàng)目中,然后為 codesmith.csp 配置數(shù)據(jù)源(或者還有表前綴),然后生成代碼即可。

http://wiki.jikexueyuan.com/project/codesmith/images/31.png" alt="第31張" />

本例下載