标签:yii

浏览模式: 标准 | 列表 | 图文
Yii数据库迁移工具

Yii数据库迁移工具

其实早就想写这样的文章来介绍这个玩意,不过一来项目中没有用到这个东西,二来官方的文档也是英文的,自己也没有仔细看就没有介绍。
官方的文档其实写的很详细了,不过因为真没用到它,也就没有发言权。
不过这两天在闲逛 ,倒是看到了一些文章:

  1. 小试Yii框架下的数据迁移功能
  2. yiichina的中文版介绍 :http://www.yiichina.com/api/CDbMigration
  3. 官方的手册:http://www.yiiframework.com/doc/guide/1.1/en/database.migration

这些文章还是可以看看的,关键问题是这些配置改了之后,难道会自动更新model文件吗?哎。

Yii框架中使用PDO时强制将表字段设为小写的BUG

Yii框架的使用者越来越多,虽然大多数情况下都能够被使用的很正常,但偶尔还是有一些小问题,比如mysql对库名和表名区分大小写,但字段名不区分;这个问题就给Yii框架使用者带来很大的烦恼,特别是这个表还不是自己设计的,只是你拿来使用的,这个郁闷程度啊。
于是有人提醒我,应该这么用,在components中的db数组里加入:attributes=>array(PDO::ATTR_CASE,PDO::CASE_LOWER),以达到强制小写的目的,嗯,想法是好的,但是gii没法用了,gii直接报错,排查了半天,发现可能是CMysqlSchema这个类中的createColumn方法导致的错误,因为当你设置为列名小写后,这个方法中的一些判断却还是用的大写,于是很多变量就找不到了,没有办法继续下一步操作,于是我改了一下这个函数:

/**
* Creates a table column.
* @param array $column column metadata
* @return CDbColumnSchema normalized column metadata
*/
protected function createColumn($column) {
$column = array_change_key_case($column, CASE_LOWER);
$c = new CMysqlColumnSchema;
$c->name = $this->getDbConnection()->getColumnCase() == PDO::CASE_LOWER
? strtolower($column['field'])
: ($this->getDbConnection()->getColumnCase() == PDO::CASE_UPPER ? strtoupper($column['field'])
: $column['field']);
$c->rawName = $this->quoteColumnName($c->name);
$c->allowNull = $column['null'] === 'YES';
$c->isPrimaryKey = strpos($column['key'], 'PRI') !== false;
$c->isForeignKey = false;
$c->init($column['type'], $column['default']);
$c->autoIncrement = strpos(strtolower($column['extra']), 'auto_increment') !== false;
return $c;
}


当然上面的代码很粗燥,我也只是先临时解决这个问题,这么写之后,马上gii就恢复正常了。黑黑

顺便再说一个技巧:以前用gii的时候,对于生成model类都是一个个的输入一个个的生成,总想着应该可以批量生成,但一直不知道如何操作,直到昨天在解决上面的问题时,发现原来在gii里有这样一段代码:

if($this->tableName[strlen($this->tableName)-1]==='*')

我晶啊,原来那个表名可以写成*啊,这样就是自动生成整个数据库的model了,哎,以前居然一直没在意。

Yii Framework DB 中的一些参数

Yii Framework DB 中的一些参数

Yii的AR一直在被使用,当然,偶尔也只是看看其中的参数,都是直接把main.php中的数据注释掉就完事了
但事实上我们都忽略了一点点现实,那就是默认参数是没有什么性能提升的。

1、'schemaCachingDuration'=>3600,
这个在默认参数中是不存在的,如果你的表结构不太变动,完全可以不需要每次都show columns,show create table之类的。加上这个参数,可以将表结构缓存一小时(嗯,前提是,你配置了cache组件)

2、emulatePrepare=true ,这个是默认里面就带有的,它有什么用呢?
官方是这样说的:whether to turn on prepare emulation. Defaults to false, meaning PDO will use the native prepare support if available. For some databases (such as MySQL), this may need to be set true so that PDO can emulate the prepare support to bypass the buggy native prepare support. Note, this property is only effective for PHP 5.1.3 or above.
好吧,为了以防万一,还是打开算了

3、'enableProfiling'=>true
如果你想优化代码的话,可以尝试设置为true看看。官方在guide里说:By setting CDbConnection::enableProfiling to be true in the application configuration, every SQL statement being executed will be profiled. The results can be readily displayed using the aforementioned CProfileLogRoute, which can show us how much time is spent in executing what SQL statement. We can also call CDbConnection::getStats() to retrieve the total number SQL statements executed and their total execution time.

4、 'enableParamLogging'=>true,
这个就相对比较简单了,如果你设置为True,你在log中,就可以看到你的每次参数的参数是什么了,而不是:y01:y02这样的顺序变量。

5、tablePrefix = "xxx",这没什么好解释的。。。

6、charset="xxx" ,也没有什么好解释的,utf-8的话,中间的减号不要。。


这些都是常用的。。所以列出来

烂桔回忆录:如何给字段默认赋值

烂桔回忆录:如何给字段默认赋值

以下内容都是烂桔童鞋的一些回忆录,嗯,针对Yii开发的回忆录,烂桔童鞋用yii应该也有2年多了吧。所以,有些小技巧啥的还是值得看看的:
好,现在又到了不定期yii小技巧分享的时间了,今天来说说如何给字段默认赋值
一般的表里都会有类似 created的字段,记录一个数据产生的时间戳,传统的做法都是在插入数据库的时候给这个字段赋值。
在yii框架呢?方法就很多了,比如在beforeValidae()里用 $this->created = time() 或者写个behavoir。
但我要说,这种做法弱爆了,其实有更简单的办法。
yii默认提供了一个叫 default的验证器,意思就是给字段默认赋值,那么现在重点来了,怎么是使用呢?以created字段举例:
array('created', 'default', 'value' => time(), 'setOnEmpty' => false, 'on' => 'insert'),

在model的rules()方法里,加上上面那行即可,这样,created字段就会在插入新数据的时候自动被赋值一个时间戳了。

同理也可以用在updated字段,修改 'on' => 'update' 即可

当然,这个不仅仅可以用来记录时间戳,我们操作后台的时间,往往要记录一些最后操作某条记录的某个人,同样也可以直接在这里做,非常方便,不是吗?
------------------
正在征集烂桔更多的回忆录。

yii 镜像

Yii Framework有的时候会很容易就被墙。所以,自己给他做了一个反向代理 .
每个月5G流量。访问地址为: http://yii.neatcn.com(跳转)

这样的功能,当然不是我做的。我没有那么多的精力来做这个,我是用了一个现成的网站(Speedy Mirror)处理的。

很方便,这里有一篇中文教程:http://www.ml934.com/archives/204.html

我们知道Speedy Mirror是 一家免费CDN加速服务网站,服务器节点分布在英美,感觉用它给国内的网站做加速意义不大。所以这里只谈用SpeedMirror为GAE做“反向代 理”,准确的说就是做了一个“镜像网站”。我们知道由于GAE被墙,appspot.com是不能在国内访问的,这就导致以GAE做为平台的各种应用无法 正常使用和访问,比如在GAE上搭建的博客,相册,网盘等等。这里我们就正好可以利用其CDN的特性,为你的GAE 站点做个镜像,当我们访问xxxx.appspot.com的时候,就会从Speedy Mirror的服务器上获取缓存内容。Speedy Mirror支持https访问,使用简单,注册后只需填写你要推送的网址,完成设置立刻生效。

先添加一个你喜欢的名称,再填写你要推送的网址,点击Detect and save确定就可以了。然后就会给你一个xxxx.speedymirror.com二级域名(下图,可点击放大查看)

Speedy Mirror默认的缓存更新是7天,你要是更新频繁,你就点击xxxx.speedymirror.com把缓存更新改为1即可。点击Add new mirror,再新添加一个镜像就可以了哦,怎么样?够简单吧,那就时间起来吧,呵呵。

PS:GAE虽然可以通过APPs企业套件绑定域名的方法实现墙内访问,但对于新手来说,步骤繁琐,且反向代理也不好找,远不如 Speedy Mirror简单。唯一的缺憾就是speedymirror.com这个域名太不好记了,简直有点坑爹。老外的东西,没办法了,就凑活着用 吧!

----------

原文中没有说明,其实现在可以cname指向到你申请的那个二级域名的页面的。所以我才变成了yii.neatcn.com

yii 与 namespace

yii 与 namespace

Yii在import方面已经做的很好了,很多人都说Yii无法导入命名空间,事实上官方很早就给出过意见,如这里:http://www.yiiframework.com/doc/guide/1.1/zh_cn/basics.namespace,当然,这是中文版的

4. Namespace

不要将路径别名和名字空间混淆了,名字空间是指对一些类名的一个逻辑组合,这样它们就可以相互区分开,即使有相同的名字。 而路径别名是用于指向一个类文件或目录。路径别名与名字空间并不冲突。

提示: 由于 5.3.0 版本之前的 PHP 本质上不支持名字空间,你无法创建两个具有相同名字但不同定义的类的实例。 鉴于此,所有的 Yii 框架类都以字母 'C'(意为 'Class') 作前缀,这样它们可以区分于用户定义的类。我们建议前缀 'C' 只保留给 Yii 框架使用,用户定义的类则使用其他的字母作前缀。

5. 使用命名空间的类

使用命名空间的类是指一个在非全局命名空间下声明的类。比如说,类application\components\GoogleMap 在命名空间application\components下的类。使用命名空间需要 PHP 5.3.0 或者以上版本。

从1.1.5开始,可以无需显式引入而使用一个包含命名空间的类。比如说,我们可以创建一个application\components\GoogleMap 的实例而无需去处理引入的路径,这样就增强了Yii的自动导入机制。

若要自动导入使用命名空间的类,命名空间的格式必须和路径别名相似。比如说,类application\components\GoogleMap 所对应的路径必须和别名application.components.GoogleMap一致。

----------
如果仅看中文版,或许你还是看不懂,但你应该看英文版的,下面还有评论,你就知道怎么用了
评论是这个样子的,LOOK,http://www.yiiframework.com/doc/guide/1.1/en/basics.namespace:

Namespaces in a nutshell

Namespaces in php are like directory paths in console (bash, dos etc)
When you use namespace php keyword like this

namespace a\random\namespace;   class Foo {     static function bar(){}    }

is like executing cd a\specific\directory except that the namespace is created if not exists.
Now everything follows is belonging to that namespace. This means that if you want to instantiate, extend or call a static method from eg foo class on another namespace you have to

//The leading backslash '\' here denotes the global namespace that //is the root folder or C:\ counterpart from console environment  $baz= new \a\random\namespace\Foo;  class Fighter extends \a\random\namespace\Foo {} \a\random\namespace\Foo::bar();

Yii imports a very intuitive convention here that the namespace structure (if implemented) should be reflected on the physical directory structure and additionally makes its Path Alias convenience available for that purpose.
Please be my guest to follow these steps:
1. Create a new web app 2. Go to protected\components and create a folder foo 3. Move Controller.php in foo folder and open it with an editor 4. At line 6, at Controller class declaration import this:

namespace application\components\foo; class Controller extends \CController //Note the leading backslash here
  1. Now open protected\controllers\SiteController.php for editing
  2. Replace the SiteController class declaration with this
class SiteController extends \application\components\foo\Controller

As you will see, your new web app still working fine and application path alias will point properly at protected folder.
You can find more about php namespaces here
Enjoy coding :>

--------
果然,这年头,英文版的资料比中文版多多了,手册上也是,不过PHP手册是一定要带评论,对于我们这些初学者来说,那是相当的有用啊

YII中的CComponent,CEvent与Behavior及CActiveRecordBehavior

YII中的CComponent,CEvent与Behavior及CActiveRecordBehavior

上一篇文章我介绍的是官方的event和behavior的文章,这一篇是来自个人博客。
嗯,本来只是放在read it later做备份的,不料今天打开的时候发现,这篇文章已经不见了。该博客重新创建,原来的数据都没有了。所幸,我曾用read it later处理过纯文本模式,因此,我将它备份下来,以防哪天read it later也崩了。
ok,上原文链接。靠,原文打不开了,还是上这家网站的地址吧。。。http://www.trailroom.com/
内容其实这样的:

YII中的CComponent,CEvent与Behavior及CActiveRecordBehavior个人理解
这一块教程少,今天个人理解了下,写了个小例子,有助于理解

完成如下功能,一个JTool类,继承CComponent,当其长度改变时,调用事件,输出”change me”.
JTool.php在protected/components 下:

<?php
class JTool extends CComponent{
private $_width;
public function getWidth(){
return $this->_width ? $this->_width : 1;
}

public function setWidth($width){
if($this->hasEventHandler(‘onChange’)){
$this->onChange(new CEvent());
}
$this->_width = $width;
}

public function onChange($event){
$this->raiseEvent(‘onChange’, $event);
}

OK,功能已经实现了,找个控制器,执行

现在我们想给JTool添加一个功能,返回长度的100倍,我们可以继承JTool.php写一个方法

[。。。TEXT后,这一段内容消失了]
OK,功能实现了,这个执行就简单了new JToolSub调用方法即可

上边的这两种办法,就是仅完成功能,下边演示Behavior及events来实现

如何用Behavior来实现上边的增加一个方法,返回长度的100倍的功能呢?
写类JBe
JBe.php在protected/behavior 下:

class JBe extends CBehavior{

public function get100width(){
return $this->Owner->width*100;
}
}

OK,功能已经实现了,找个控制器,执行

如何用Behavior实现JTool中的长度改变时,调用一个事件的功能呢?
写类JBe:

class JBe extends CBehavior{
public function events(){
return array_merge(parent::events(),array(
‘onChange’=>’change’,
));
}

public function change(){
echo ‘changed’;
}

public function get100width(){
return $this->Owner->width*100;
}
}

OK,功能实现随便找个控制器,执行

这里的要点是events方法
返回的数组array(‘onChange’=>’change’)定义了事件(event)和对应的事件处理方法(event hander)

事件是是Compents(JTool中)定义的,即JTool中的onChange
处理方法同由Behavior(JBe中)类定义的,即JBe中的change

这样子再看CActiveRecordBehavior,其是绑定给CActiveRecord 这个组件的,绑定方法重写behaviors()
CActiveRecordBehavior中的events() 方法返回事件及事处理函数的对应,如:
‘onBeforeSave’=>’beforeSave’

即组件CActiveRecord中的onBeforeSave这个事件对应的处理函数是
CActiveRecordBehavior中的beforeSave方法

这样子CActiveRecord在调用save()时,触发事件onBeforeSave,调用CActiveRecordBehavior对应的处理函数beforeSave
我们只要写一个CActiveRecordBehavior的子类,重写其中的beforeSave,执行一些操作,然后给CActiveRecord绑定即可

我还有个疑问,在继承CBehavior时,是不是一定要让方法events()反回那个对应关系的数组,如果这里为空,没有默认的对应关系?

---------

虽然TEXT保留了大部分内容,但还是有一小部分不见了。真可惜

Yii 官方关于event和behaviors的文章

Yii 官方关于event和behaviors的文章

本文内容来自官方的wiki,一来是做备份,二来自己查询起来也会快一点,毕竟yiiframework官网经常抽风,当然它的抽风不是因为自己,而是因为GFW,说来也可怜,好象很多技术网站都被墙了。
不过想想也正常,如果一个搞技术的,连翻墙也不会,还搞个毛技术啊。
OK,上正菜:http://www.yiiframework.com/wiki/44/behaviors-events/#hh1

These features provide endless possibilities and unbelievable flexibility, but as current documentation does not give more than a few examples, it might be difficult to fully understand their internals and requirements.

It should be noted that they do mostly the same thing. You can attach behaviors and event handlers to components to modify the components' behavior.

Events

It is useful when you want to interrupt the normal application flow without extending base classes.

For example, enabling gzip compression on the output could be done via extending CWebApplication. But because there are entry points for event handlers, one can do this:

Yii::app()->onbeginRequest = create_function('$event', 'return ob_start("ob_gzhandler");'),
Yii::app()->onendRequest = create_function('$event', 'return ob_end_flush();'),

You can create an event handler -- which is simply a method in some class with a specific signature -- and attach it to the event of an object. You can add as many event handlers as you wish, from as many objects as you wish. If the event handler is, effectively static, then you can create the object as you assign it:

$test_comp->onSomethingGoesOn = array(new SomeClass, 'eventHandler1');
$test_comp->onSomethingGoesOn = array(new SomeOtherClass, 'eventHandler2');
$test_comp->onSomethingGoesOn = array(new YetAnotherClass, 'eventHandler3');

As long as you have a handle on the object, then you can add an event handler to it.

At some point, you can then raise the event with something like one of these:

$test_comp->onSomethingGoesOn(new CEvent($this));
$test_comp->onSomethingGoesOn(new CEvent());

So, basically, it allows you to build a list of function calls that can later be executed, in the order they were added. It can save you passing around a lot of object refs and building conditional code, since you can still raise the event, even if it doesn't do anything.

Behaviors

Behaviors are simply a way of adding methods to an object.

Take this scenario: You have 2 classes: MySuperClass1, MySuperClass2. There might be lots of methods from MySuperClass1 & 2 that you want in some new class, say MyBoringClass. Unfortunately, php does not allow for this:

class MyBoringClass extends MySuperClass1, MySuperClass2 {
}

This is where behaviors come in. Instead, you can go:

class MyBoringClass extends MySuperClass1 {
}

$classInstance = new MyBoringClass();
$classInstance->attachbehavior('uniqueName', new MySuperClass2);

Now $classInstance has all the methods from MySuperClass1 and MySuperClass2. Since MySuperClass2 is being used as a behavior, it has to extend CBehavior. The only caveat to this is an attached behavior cannot override any class methods of the component it is being attached to. If a method already exists, if it be from the original class or already added by a previously attached behavior, it will not be overwritten.

In an OO language like Ruby, it's quite possible to start with a completely empty object and simply build its behavior as you go along. Yii provides this behavior with a little magic. The key is that the class you wish to add the behavior from must extend Cbehavior.

class SomeClass extends CBehavior
{
public function add($x, $y) { return $x + $y; }
}

Then use with:

$test_comp = new TestComponent();
$test_comp->attachbehavior('blah', new SomeClass);
$test_comp->add(2, 5);

So, in this case, you are extending the functionality of an object with functionality of another object.

After studying this cookbook page you are encouraged to reread the corresponding guide page as it contains advanced information (for example, if you are familiar with interfaces, you might find it enough to implement IBehavior before extending CBehavior).

-------------

behavior和Event在model里用的会相对较多一点,不过,其他地方也可以一用。如果你真的不明白这一块的逻辑,那还是用其他方式替代吧。

Yii for SAE 提交到google code

Yii for SAE 提交到google code

Yii for sae终于提交到了google code了。

yii framework for sina sae platform. 初步实现将yii framework 移植到 sina SAE云平台。 assets目录生成到storage中,runtime则是用memcached来实现, 也因此需要激活storage和memcached。 几乎不需要改原来的代码,如果需要设置主从数据库,请另外加一个components节点:sdb,配置信息与db一样,但需增加一个 'isSlave'=>true,即OK。不过在这种情况下,所有的model都请继承SaeActiveRecord?。

配置信息中,在params里增加 'params'=>array(

'sae'=>array(
'storage' => array(
'assets' => 'assets', 'runtime'=>'runtime',
), 'db'=>array(
'slave'=>'sdb', 'master'=>'db',
)
),

);
----
配置起来是不是很简单?
当前目前还是有一点问题的

  1. 比如对于文件的LOG暂时无法实现,准备使用memcache存储部分,等到了一定大小后,扔到storage中存储。
  2. GII暂时还不能在线上使用,这个有点纠结,因为GII是需要生成文件 ,而sae平台不支持生成文件。如果扔到storage中,效率又不能保存。如果扔到memcache中。include之类的又不知道是否能够完美实现?纠结中。。(生成到storage?然后下载?扔到models目录中?这多没意思,与其这样,还不如本地建好model,然后SVN上传呢)
  3. 好象cdbcache是用了sqlite,原本看代码里是能够自动生成的,但在线上,怎么搞呢?存到storage中又没有效率?哎。。。

不过,暂时基本功能都实现了,嗯项目地址是:http://code.google.com/p/yiiforsae/

Yii for SAE

Yii for SAE

这两天在实现Yii for SAE,之所以想实现这样的功能,主要还是YII相对比较深入开发人员的人心吧?
这一定是我的个人偏见,因此不要打口水仗。
开发的时候,遇到的问题大多是runtime目录下的存储,以及主从数据库的问题,因此大部分的精力都是在解决这些。
所幸,还是可以利用yii的一些特性来搞定它。
嗯。70%已经完成了。如果差不多了,到时候会扔到google code上,这样想用的人就直接可以使用啦。

目前还在测试中,可以访问 http://ixyz.sinaapp.com进行测试。