Facebook Twitter LinkedIn E-mail
magnify
Home Posts tagged "Yii"

CodeSmith 使用教程(15) 为Yii Framework 创建生成ActiveRecord的代码模板

CodeSmith 使用教程(3): 自动生成Yii Framework ActiveRecord 我们通过SchemaExploer为Yii Framework从数据库生成简单的ActiveRecord类,没有考虑到表和表之间的关系。本例我们使用CodeSmith为Yii Framework创建一个通用的代码模板,可以使用上例介绍的SchemaExploer ,不过在查看CodeSmith自带的例子中有个生成Hibernate的例子,这个模板的使用可以参见CodeSmith 使用教程(1): 概述 ,CodeSmith提供了这个模板的源码,使用到了CodeSmith.SchemaHelper (CodeSmith没有提供相应的文档),不过可以通过阅读NHiberante的模板了解其一般用法。

为生成Yii Framework ActiveRecord类之间的relation ,先要了解一下表和表之间的关系:

两个 AR 类之间的关系直接通过 AR 类所代表的数据表之间的关系相关联。 从数据库的角度来说,表 A 和 B 之间有三种关系:一对多(one-to-many,例如 tbl_user 和 tbl_post),一对一( one-to-one 例如 tbl_user 和tbl_profile)和 多对多(many-to-many 例如 tbl_category 和 tbl_post)。 在 AR 中,有四种关系:

  • BELONGS_TO(属于): 如果表 A 和 B 之间的关系是一对多,则 表 B 属于 表 A (例如 Post 属于 User);
  • HAS_MANY(有多个): 如果表 A 和 B 之间的关系是一对多,则 A 有多个 B (例如 User 有多个 Post);
  • HAS_ONE(有一个): 这是 HAS_MANY 的一个特例,A 最多有一个 B (例如 User 最多有一个 Profile);
  • MANY_MANY: 这个对应于数据库中的 多对多 关系。 由于多数 DBMS 不直接支持 多对多 关系,因此需要有一个关联表将 多对多 关系分割为 一对多 关系。 在我们的示例数据结构中,tbl_post_category 就是用于此目的的。在 AR 术语中,我们可以解释 MANY_MANY 为 BELONGS_TO 和 HAS_MANY 的组合。 例如,Post 属于多个(belongs to many) Category ,Category 有多个(has many) Post.

本例还是使用Chinook数据库,修改Yii Framework 开发教程(27) 数据库-关联Active Record示例。数据表之间的关系如下:

20130107001

CodeSmith 中PLINQO-NH代码位置:

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

20130111001

CodeSmith.SchemaHelper定义的主要类有:

20130111002

几个主要的类为

  • EntityManager 管理所有的Entity(对应于整个数据库)
  • Entity实体类(对应到单个表,视图)
  • IAssoication 关系(定义表和表之间的关系)
  • AssoicationType 关系的类型 (见下表)

根据AssociationType ,数据库之间的关系以及Yii AR支持的几种关系,可以定义下表:

20130111003

整个模板也是采用主-从模板的方式 ,主模板枚举EntityManager中的每个Entity,然后调用子模板为每个表生成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);
}

子模板则根据每个Entity的Assoications(关系属性)为AR 生成relations函数,

<?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 就可以为数据库的表生成对应的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)'),
		);
	}
}

如果实在看不懂本例也无所谓,可以直接使用该模板,只要设置数据源 ,如果数据库的表有前缀,比如Wordpress的表有wp_ 可以设置表前缀(不是必须的)

20130111004

本例下载 ,如果需要使用本例的模板,直接把项目中protected下的codesmith 目录拷贝到你自己的项目中,然后为codesmith.csp 配置数据源(或者还有表前缀),然后生成代码即可。

20130111005

本例下载

 

Yii Framework 开发教程(49) 总结

到目前为止,基本介绍了开发Yii 应用的基本知识,其它更进一步的如扩展Yii框架,调用第三方库(如Zend库),错误处理,安全,性能调试可以参考Yii文档(建议参考英文版,中文版翻译得有点怪怪的,很像机器翻译的),以后有时间在写进阶开发指南。

为方便起见,给出所有开发指南链接如下:

  1. Yii Framework 开发教程(1) 第一个应用Hello World
  2. Yii Framework 开发教程(2) Yii Web应用基础
  3. Yii Framework 开发教程(3) 为应用添加日志
  4. Yii Framework 开发教程(4) Hangman 猜单词游戏实例
  5. Yii Framework 开发教程(5) URL管理
  6. Yii Framework 开发教程(6) CComponent 组件
  7. Yii Framework 开发教程(7) 使用CHtml 创建Form
  8. Yii Framework 开发教程(8) 使用FormModel
  9. Yii Framework 开发教程(9) UI 组件 Widget 概述
  10. Yii Framework 开发教程(10) UI 组件 自定义组件
  11. Yii Framework 开发教程(11) UI 组件 ActiveForm示例
  12. Yii Framework 开发教程(12) UI 组件 ClipWidget示例
  13. Yii Framework 开发教程(13) UI 组件 ContentDecorator示例
  14. Yii Framework 开发教程(14) UI 组件 MaskedTextField示例
  15. Yii Framework 开发教程(15) UI 组件 MultiFileUpload示例
  16. Yii Framework 开发教程(16) UI 组件 StarRating示例
  17. Yii Framework 开发教程(17) UI 组件 TabView示例
  18. Yii Framework 开发教程(18) UI 组件 TextHighlighter示例
  19. Yii Framework 开发教程(19) UI 组件 TreeView示例
  20. Yii Framework 开发教程(20) UI 组件 Captcha示例
  21. Yii Framework 开发教程(21) UI 组件 自定义Captcha示例
  22. Yii Framework 开发教程(22) UI 组件 Zii组件简介
  23. Yii Framework 开发教程(23) 数据库-概述
  24. Yii Framework 开发教程(24) 数据库-DAO 示例
  25. Yii Framework 开发教程(25) 数据库-Query Builder示例
  26. Yii Framework 开发教程(26) 数据库-Active Record示例
  27. Yii Framework 开发教程(27) 数据库-关联Active Record示例
  28. Yii Framework 开发教程(28) Data Provider 简介
  29. Yii Framework 开发教程(29) Zii组件-Menu 示例
  30. Yii Framework 开发教程(30) Zii组件-ListView 示例
  31. Yii Framework 开发教程(31) Zii组件-DetailView 示例
  32. Yii Framework 开发教程(32) Zii组件-GridView示例
  33. Yii Framework 开发教程(33) Zii组件-Accordion示例
  34. Yii Framework 开发教程(34) Zii组件-AutoComplete示例
  35. Yii Framework 开发教程(35) Zii组件-Button示例
  36. Yii Framework 开发教程(36) Zii组件-DatePicker示例
  37. Yii Framework 开发教程(37) Zii组件-Dialog示例
  38. Yii Framework 开发教程(38) Zii组件-ProgressBar示例
  39. Yii Framework 开发教程(39) Zii组件-Slider示例
  40. Yii Framework 开发教程(40) Zii组件-SliderInput示例
  41. Yii Framework 开发教程(41) Zii组件-Tabs示例
  42. Yii Framework 开发教程(42) Zii组件-Draggable示例
  43. Yii Framework 开发教程(43) Zii组件-Droppable示例
  44. Yii Framework 开发教程(44) Zii组件-Resizable示例
  45. Yii Framework 开发教程(45) Zii组件-Selectable示例
  46. Yii Framework 开发教程(46) Zii组件-Sortable示例
  47. Yii Framework 开发教程(47) 主题 Theme 示例
  48. Yii Framework 开发教程(48) 多国语言示例
 

Yii Framework 开发教程(48) 多国语言示例

本例通过Yii Framework 开发教程(11) UI 组件 ActiveForm示例添加中文支持简要说明一下多国语言支持。详细文档可可以参考Yii文档

信息翻译是通过调用 Yii::t() 实现的。此方法会将信息从 源语言 翻译为 目标语言

总体来说,要实现信息翻译,需要执行如下几步:

  1. 在合适的位置调用 Yii::t() ;
  2. 以 protected/messages/LocaleID/CategoryName.php 的格式创建 PHP 翻译文件。 每个文件简单的返回一个信息翻译数组。 注意,这是假设你使用默认的 CPhpMessageSource 存储翻译信息。
  3. 配置 CApplication::sourceLanguage 和 CApplication::language

因此第一步为Application添加合适的配置:

<?php

// This is the main Web application configuration. Any writable
// CWebApplication properties can be configured here.
return array(
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
'sourceLanguage'=>'en',
'language'=>'zh_cn',
'name'=>'i18nDemo',

...
// application components
'components'=>array(
'coreMessages'=>array(
		'basePath'=>'protected/messages',
		),),
);

源语言为en,目录语言为中文(zh_cn) ,翻译信息的目录为protected/messages.

然后再protected/messages 创建zh_cn 目录,如果还需要支持其它语言,可以创建相应的目录,然后在zh_cn创建一个yii.php ,其中yii 做为翻译时的分类名(你可以选择你自己喜欢的名字)。
检查代码中需要翻译的地方,然后在yii.php 文件中定义对应的翻译:

return array (

	'Application Name' => '应用程序名称',
	'Greetings from Santa'=>'来自圣诞老人的问候',
	'firstName'=>'名',
	'lastName'=>'姓',
	'Choose your Christmas Gift'=>'选择你喜欢的圣诞礼物',
	'iPad'=>'iPad',
	'Remote control helicopter'=>'遥控直升飞机',
	'60 inch 3D LED TV'=>'60寸3D LED电视',
	'Holy Bible'=>'圣经',
	'Choose your Christmas dinner'=>'选择你圣诞节晚餐',
	'Egg'=>'鸡蛋',
	'Ham'=>'火腿',
	'Chicken'=>'鸡',
	'Pork'=>'猪肉',
	'Beer'=>'啤酒',
	'Coke'=>'可乐',
	'Wine'=>'白酒',
	'Submit'=>'提交',
	'Merry Christmas'=>'圣诞快乐',
	'On Christmas Day,You will be given'=>'圣诞节那天你将获得',
	'And you will have'=>'你可以有',
	'for Christmas dinner'=>'作为圣诞晚餐',
	'Start Again'=>'重新选择'
);


然后将原先使用英文字符串的地方换成yii::t('yii',xxx);比如DataModel.php

public function getGifts()
{
	return array(
		'1'=>Yii::t('yii','iPad'),
		'2'=>Yii::t('yii','Remote control helicopter'),
		'3'=>Yii::t('yii','60 inch 3D LED TV'),
		'4'=>Yii::t('yii','Holy Bible'),
		);
}

public function getMeals()
{
	return array(
		'1'=>Yii::t('yii','Egg'),
		'2'=>Yii::t('yii','Ham'),
		'3'=>Yii::t('yii','Chicken'),
		'4'=>Yii::t('yii','Pork'),
		'5'=>Yii::t('yii','Beer'),
		'6'=>Yii::t('yii','Coke'),
		'7'=>Yii::t('yii','Wine'),
	);
}

201212129020本例下载

 

Yii Framework 开发教程(47) 主题 Theme 示例

Theming是一个在Web应用程序里定制网页外观的系统方式。通过采用一个新的主题,可以非常方便的改变应用的外观。

在Yii,每个主题由一个目录代表,包含view文件,layout文件和相关的资源文件,如图片, CSS文件, JavaScript文件等。主题的名字就是他的目录名字。全部主题都放在在同一目录WebRoot/themes下 。在任何时候,只有一个主题可以被激活。

提示:默认的主题根目录WebRoot/themes可被配置成其他的。只需要配置themeManager应用部件的属性basePathbaseUrl为你所要的值。

要激活一个主题,设置Web应用程序的属性theme为你所要的名字。可以在application configuration中配置或者在执行过程中在控制器的动作里面修改。

注:主题名称是区分大小写的。如果您尝试启动一个不存在的主题, yii: :app()->theme将返回null 。

主题目录里面内容的组织方式和application base path目录下的组织方式一样。例如,所有的view文件必须位于views下 ,布局view文件在views/layouts下 ,和系统view文件在views/system下。例如,如果我们要替换PostControllercreate view文件为classic主题下,我们将保存新的view文件为WebRoot/themes/classic/views/post/create.php

对于在module里面的控制器view文件,相应主题view文件将被放在views目录下。例如,如果上述的PostController是在一个命名为forum的模块里 ,我们应该保存create view 文件为WebRoot/themes/classic/views/forum/post/create.php 。如果 forum模块嵌套在另一个名为support模块里 ,那么view文件应为WebRoot/themes/classic/views/support/forum/post/create.php 。

本例通过修改Yii Framework 开发教程(11) UI 组件 ActiveForm示例 ,为它添加两个主提,为简单起见,两个主题名字分别为cyan,grey, 以其背景颜色命名。

首先在应用目录结构下创建themes 目录,然后创建cyan,grey 两个子目录,然后将缺省的protected/views分别复制到两个子目录下,作为Theme的初始版本。

201212129016

并使用Yii缺省的Css定义,也分别拷贝到两个主题子目录下,作为CSS的初始文件。

下面分别对两个主题下的CSS文件稍微做些修改,以示区别,修改css/main.css 修改body的背景色

body
{
	margin: 0;
	padding: 0;
	color: #555;
	font: normal 10pt Arial,Helvetica,sans-serif;
	background: #00FFFF;
}

分别该为Cyan和Grey的颜色。

然后修改layout/main.php 引用主题下的CSS文件,在一个主题的视图,我们经常需要链接其他主题资源文件。例如,我们可能要显示一个在主题下images目录里的图像文件。使用当前激活主题的baseurl属性,我们就可以为此图像文件生成如下url

yii: :app()->theme->baseUrl . '/images/FileName.gif'

修改后的布局文件如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta name="language" content="en" />
	<!-- blueprint CSS framework -->
	<link rel="stylesheet" type="text/css"
		href="<?php echo Yii::app()->theme->baseUrl; ?>/css/screen.css"
		media="screen, projection" />
	<link rel="stylesheet" type="text/css"
		href="<?php echo Yii::app()->theme->baseUrl; ?>/css/print.css"
		media="print" />
	<!--[if lt IE 8]>
	<link rel="stylesheet" type="text/css"
		href="<?php echo Yii::app()->theme->baseUrl; ?>/css/ie.css"
		media="screen, projection" />
	<![endif]-->

	<link rel="stylesheet" type="text/css"
		href="<?php echo Yii::app()->theme->baseUrl; ?>/css/main.css" />
	<link rel="stylesheet" type="text/css"
		href="<?php echo Yii::app()->theme->baseUrl; ?>/css/form.css" />
	<title><?php echo  Yii::app()->name . ' ' . Yii::app()->theme->name;    ?></title>
</head>

<body>

<h1>Theme Name:<?php echo Yii::app()->theme->name;?></h1>

<?php echo $content; ?>

</body>
</html>

定义好主题后,就可以通过配置Application的theme属性来设置当前主题:

return array(
    'theme'=>'cyan',
    ......
);

两个主题显示如下:

201212129017

201212129018

 

如果指定的主题不存在,相当于没有主题,将使用缺省的显示方式:

201212129019

本例下载

 

Yii Framework 开发教程(46) Zii组件-Sortable示例

CJuiSortable显示一个列表,列表中的列表项可以通过拖放重新调整顺序,它封装了 JUI Sortable插件。

其基本用法如下:

<?php
Yii::app()->clientScript->registerCss('sortable', "
#sortable {list-style-type: none; margin: 0; padding: 0; width: 60%;}
#sortable li {margin: 2px; padding: 4px;
border: 1px solid #e3e3e3; background: #f7f7f7}
", 'screen', CClientScript::POS_HEAD);
?>

<h2><?php echo Yii::t('ui','Sortable');?></h2>

<?php $this->widget('zii.widgets.jui.CJuiSortable', array(
	'id'=>'sortable',
	'items'=>array(
		'id1'=>'Item 1',
		'id2'=>'Item 2',
		'id3'=>'Item 3',
	),
	'options'=>array(
		'cursor'=>'n-resize',
	),
));
?>

201212129015本例下载

 

Yii Framework 开发教程(45) Zii组件-Selectable示例

CJuiSelectable可以显示一个列表,列表的每个项支持Select事件,它封装了 JUI Selectable插件,其基本用法如下:

<?php
Yii::app()->clientScript->registerCss('selectable',"
#selectable {list-style-type: none; margin: 0; padding: 0; width: 60%;}
#selectable li {margin: 2px; padding: 4px; border: 1px solid #e3e3e3; background: #f7f7f7}
#selectable .ui-selecting { border: 1px solid #fad42e; }
#selectable .ui-selected { border: 1px solid #fad42e; background: #fcefa1;}
#select-result {margin: 0 0 10px 2px; }
", 'screen', CClientScript::POS_HEAD);
?>

<h2><?php echo 'Selectable'; ?></h2>

<div id="select-result">none</div>

<?php
$this->widget('zii.widgets.jui.CJuiSelectable',array(
	'id'=>'selectable',
	'items'=>array(
				'id1'=>'Item 1',
				'id2'=>'Item 2',
				'id3'=>'Item 3'
				),
			'options'=>array(
				'stop'=>'js: function(event,ui){
			var result = $("#select-result").empty();
			$(".ui-selected", this).each(function(){
				var index = $("#selectable li").index(this);
				result.append(" #" + (index + 1));
			});
		}'
				)
			));
?>

使用Javascipts 来响应选择事件。

201212129014

本例下载