在 CodeSmith 使用教程(3): 自動(dòng)生成 Yii Framework ActiveRecord 我們通過(guò) SchemaExploer 為 Yii Framework 從數(shù)據(jù)庫(kù)生成簡(jiǎn)單的 ActiveRecord 類,沒(méi)有考慮到表和表之間的關(guān)系。本例我們使用 CodeSmith 為 Yii Framework 創(chuàng)建一個(gè)通用的代碼模板,可以使用上例介紹的SchemaExploer ,不過(guò)在查看 CodeSmith 自帶的例子中有個(gè)生成 Hibernate 的例子,這個(gè)模板的使用可以參見(jiàn) CodeSmith 使用教程(1): 概述 ,CodeSmith 提供了這個(gè)模板的源碼,使用到了 CodeSmith.SchemaHelper (CodeSmith 沒(méi)有提供相應(yīng)的文檔),不過(guò)可以通過(guò)閱讀 NHiberante 的模板了解其一般用法。
為生成 Yii Framework ActiveRecord 類之間的 relation ,先要了解一下表和表之間的關(guān)系:
兩個(gè) AR 類之間的關(guān)系直接通過(guò) AR 類所代表的數(shù)據(jù)表之間的關(guān)系相關(guān)聯(lián)。 從數(shù)據(jù)庫(kù)的角度來(lái)說(shuō),表 A 和 B 之間有三種關(guān)系:一對(duì)多(one-to-many,例如 tbl_user 和 tbl_post),一對(duì)一( one-to-one 例如 tbl_user 和tbl_profile)和 多對(duì)多(many-to-many 例如 tbl_category 和 tbl_post)。 在 AR 中,有四種關(guān)系:
BELONGS_TO(屬于): 如果表 A 和 B 之間的關(guān)系是一對(duì)多,則 表 B 屬于 表 A (例如 Post 屬于 User);
HAS_MANY(有多個(gè)): 如果表 A 和 B 之間的關(guān)系是一對(duì)多,則 A 有多個(gè) B (例如 User 有多個(gè) Post);
HAS_ONE(有一個(gè)): 這是 HAS_MANY 的一個(gè)特例,A 最多有一個(gè) B (例如 User 最多有一個(gè) Profile);
本例還是使用 Chinook 數(shù)據(jù)庫(kù),修改 Yii Framework 開(kāi)發(fā)教程(27) 數(shù)據(jù)庫(kù)-關(guān)聯(lián) Active Record 示例。數(shù)據(jù)表之間的關(guān)系如下:
CodeSmith 中 PLINQO-NH 代碼位置:
缺省目錄為 C:\Program Files (x86)\CodeSmith\v6.5\Samples\Templates\Frameworks\PLINQO-NH
CodeSmith.SchemaHelper 定義的主要類有:
幾個(gè)主要的類為
根據(jù) AssociationType ,數(shù)據(jù)庫(kù)之間的關(guān)系以及 Yii AR 支持的幾種關(guān)系,可以定義下表:
整個(gè)模板也是采用主-從模板的方式,主模板枚舉 EntityManager 中的每個(gè) Entity,然后調(diào)用子模板為每個(gè)表 生成 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ù)每個(gè) 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ù)庫(kù)的表生成對(duì)應(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í)在看不懂本例也無(wú)所謂,可以直接使用該模板,只要設(shè)置數(shù)據(jù)源 ,如果數(shù)據(jù)庫(kù)的表有前綴,比如Wordpress 的表有 wp_ 可以設(shè)置表前綴(不是必須的)
本例下載 ,如果需要使用本例的模板,直接把項(xiàng)目中 protected 下的codesmith 目錄拷貝到你自己的項(xiàng)目中,然后為 codesmith.csp 配置數(shù)據(jù)源(或者還有表前綴),然后生成代碼即可。
本例下載
更多建議: