月度归档:2013年12月

Javascript 数组操作

1、数组的创建

var arrayObj = new Array(); //创建一个数组
var arrayObj = new Array([size]); //创建一个数组并指定长度,注意不是上限,是长度
var arrayObj = new Array([element0[, element1[, ...[, elementN]]]]); //创建一个数组并赋值

2、数组的元素的访问

var testGetArrValue=arrayObj[1]; //获取数组的元素值
arrayObj[1]= "这是新值"; //给数组元素赋予新的值

3、数组元素的添加

arrayObj. push([item1 [item2 [. . . [itemN ]]]]);// 将一个或多个新元素添加到数组结尾,并返回数组新长度
arrayObj.unshift([item1 [item2 [. . . [itemN ]]]]);// 将一个或多个新元素添加到数组开始,数组中的元素自动后移,返回数组新长度
arrayObj.splice(insertPos,0,[item1[, item2[, . . . [,itemN]]]]);//将一个或多个新元素插入到数组的指定位置,插入位置的元素自动后移,返回""。

push是比较常用的函数了。

4、数组元素的删除

arrayObj.pop(); //移除最后一个元素并返回该元素值
arrayObj.shift(); //移除最前一个元素并返回该元素值,数组中元素自动前移
arrayObj.splice(deletePos,deleteCount); //删除从指定位置deletePos开始的指定数量deleteCount的元素,数组形式返回所移除的元素

注意splice函数,它是往数组中间插入和删除元素的有效方法。

5、数组的截取和合并

arrayObj.slice(start, [end]); //以数组的形式返回数组的一部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素
arrayObj.concat([item1[, item2[, . . . [,itemN]]]]); //将多个数组(也可以是字符串,或者是数组和字符串的混合)连接为一个数组,返回连接好的新的数组

6、数组的拷贝

arrayObj.slice(0); //返回数组的拷贝数组,注意是一个新的数组,不是指向
arrayObj.concat(); //返回数组的拷贝数组,注意是一个新的数组,不是指向

7、数组元素的排序

arrayObj.reverse(); //反转元素(最前的排到最后、最后的排到最前),返回数组地址
arrayObj.sort(); //对数组元素排序,返回数组地址

8、数组元素的字符串化

arrayObj.join(separator); //返回字符串,这个字符串将数组的每一个元素值连接在一起,中间用 separator 隔开。
toLocaleString 、toString 、valueOf:可以看作是join的特殊用法,不常用

数组对象的2个属性
1 length属性
指明数组的长度,可变,如果改小这个值,对应数据将丢失。如果改大,访问没有初始化的数据将得到undefined。length属性可能被隐式修改,比如数组长度是10,但是进行了arr[19]=60这个操作,这时length属性值为20,这个特征很奇特(比如Java中length属性是只读的)。

利用这个特征,如果要清空一个数组,可以把length赋值为0即可。另外,如果要清空一个数组,还可以进行arr = []操作,这样就空了。

2 constructor 属性
表示创建对象的函数。数组得到的就是Array。这个实际是构造函数的引用,不是字符串。

永久链接:http://blog.ifeeline.com/1030.html

PHP中的条形码

关于条形码的一些知识可以参考:http://baike.baidu.com/view/13740.htm

条形码

条形码

PHP中要生成这个条形码,有一个非常有用的插件,可以到http://www.barcodephp.com/en/download下载:

条形码下载

条形码下载


注意这里是免费下载的,但是如果是商业使用必须获得授权,就是交银子,当然不交银子也可以使用。这里下载的文件有1D后缀,就是一维的,也有2D的即二维,目前应用十分广泛的二维码就是这种。

下载后得到一个压缩包,把其解压放入到PHP运行环境中,然后运行:
生成条形码接口

在右上角,提供了条形码编码方案:
条形码编码

我这里选择了Code 128,然后再配置页面是可用的配置选项,然后查看输出图片的源代码:
条形码输出源代码
这里可以看到,实际就是调用html文件夹下的image.php文件,后面的参数对应着输入,从这里可以看到,我们只需要更换对应参数就可以获得对应的输出。这个比较有用,我们不需要细致研究它的工作原理就可以开始上手了。

永久链接:http://blog.ifeeline.com/1022.html
原创文件,转载务必保留出处。

使用Zend Studio开发工具

修改字符编码
zsutf8

设置字体和字体大小
zsfont

设置一个Tab置换为4个空格
窗口->首选项->PHP->code style->formatter->Edit->Indentation->General settings Tab size: 4.
tabtofourspace

Zend Studio优化
1 关闭启动时要激活的插件
Window – Preferences
优化Zend Studio
取消所有勾选。

2 取消自动更新
Window – Preferences
优化Zend Studio

3 关闭拼写检查
Window – Preferences
Zend Studio优化

4 关闭语义分析
Window – Preferences
Zend Studio优化

5 提高代码提示速度
Window – Preferences
Zend Studio优化

6 关闭自动构建
Project – Build Automatically


Zend Studio 设置模板

Zend Studio设置模板
这里的模板分Comments(注释)和code(代码),Zend Studio生成内容时会调用对应的模板。比如新建一个PHP文件时:
Zend Studio优化
在这里可以选中一个模板,一般是选择Simple php file模板,可以在模板里面写入公共信息。实际上,其它的文件类型也有模板的概念,比如Javascript。

除了这个,还有所谓的编辑器模板,这个类似于某个代码块的快捷方式:
Zend Studio设置模板
要使用这种代码块,在编辑器中输入名称开始的一个或多个字符(开始代码自动提示),然后按下Alt+/将把所有符合的条件的编辑器模板调出来让你选择:
Zend Studio代码提示
在这个地方可以定义代码段模板,比如类,函数等。

Zend Studio中使用xdebug
1 安装使用Xdebug

##下载安装
wget http://xdebug.org/files/xdebug-2.2.5.tgz
tar zxvf xdebug-2.2.5.tgz
cd xdebug-2.2.5
/usr/local/php-5.5.15/bin/phpize
./configure --enable-xdebug --with-php-config=/usr/local/php-5.5.15/bin/php-config

##配置
vi /usr/local/php-5.5.15/etc/php.ini
##添加如下内容
zend_extension=/usr/local/php-5.5.15/lib/php/extensions/no-debug-zts-20121212/xdebug.so
;xdebug.auto_trace="On"
xdebug.collect_params="On"
xdebug.collect_return="On"
xdebug.trace_output_dir="/usr/local/php-5.5.15/xdebug"
xdebug.profiler_enable="true"
xdebug.profiler_output_dir="/usr/local/php-5.5.15/xdebug"
xdebug.profiler_append=1
xdebug.profiler_enable_trigger=1
xdebug.profiler_output_name="profiler.out.%t-%s"
xdebug.remote_enable="On"
xdebug.remote_autostart="On"
xdebug.remote_host=192.168.1.102
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
##确认
/usr/local/php-5.5.15/bin/php -m | grep xdebug
xdebug

2 配置Zend Studio使用xdebug
Window – Preferences
Zend Studio使用XDebug
这里设置运行服务器,Zend Studio的项目代码要运行必须要有运行环境(xdebug),可以是本地的也可以是远程的,这个没有什么关系,这里使用远程的运行环境。

然后配置Debugger:
Zend Studio使用XDebug
在Installed Degugers中默认只有Zend Debugger和XDebug,这里选择XDebug,然后设置端口,这个端口要和运行环境中的PHP的xdebug设置的远程端口必须一致(实际是废话,要不然怎么通信)。

默认Zend Studio的Debug Mode是Zend Debugger,所以这里要切换成XDebug:
Zend Studio使用XDebug
要开始Debug首先需要建立一个远程项目(这里使用远程服务器作为运行环境而非本地),这个步骤跳过。

Debug配置:
Zend Studio Debug
点击Debug Configurations…
Zend Studio Debug新配置
点击添加新配置:
Zend Studio新配置
输入名字,选择服务器,选择开始的文件(本地项目的要调试的文件),运行的URL。

注:从弹出界面可以看到,可以把项目以CLI或Web或PHPUnit方法来进行调试或运行,这里添加的新配置是要告诉Zend Studio如何调试应用,比如哪台服务器,进入的URL等。

点击Debug开始调试:
Zend Studio Debug

在Zend Studio中配置使用GIT
Github是一个提供代码仓库的地方,可以参考:http://blog.ifeeline.com/999.html设置仓库,并且了解如何设置公钥,这个对于Push代码是非常方便。

下面是对Zend Studio 10设置Git支持的内容。

首先安装EGIT,EGIT是一个插件,首先要先安装它,不在这之前首先要设置安装源,就是如何获取EGIT:
Window – Preferences
Install/Update – Available Software Sites
点击Add,在Name输入Egit(名字任意),在Location输入http://download.eclipse.org/egit/updates,然后点击OK。这样Egit的安装源已经设置好。注意,默认的源可能也可以找到Egit,安装源可能包含很多插件。

然后开始安装:
Help – Install New Software
在Work with中选择刚才设置的安装源,然后会出现可用的软件(插件)列表,勾选Eclipse Git Team Provider,然后点击next完成安装(Zend Studio重启)。
注意,如果要卸载软件,可以到Help – About Zend Studio,点击Installation Details就看到有卸载的按钮。

为了能使用SSH推拉Github上的文件,需要在Zend Studio中设置秘钥:
Window – Preferences
General – Network Connections – SSH2
在这里可以生成一对全新的RSA秘钥并把公钥传递到自己的Gibhub账户中,也可以在General中点击Add Private Key…添加一个已经存在的秘钥(这部分内容这里省略)。

然后到
Window – Open Perspective – Other…
选中Git,然后就会切换到Git视图,会有三个选项:
Add an existing local Git repository
Clone a Git repository
Create a new local Git repository
可以创建一个新的本地Git仓库,之后再设置远程推送地址进行推送,也可以从远程克隆一个Git仓库,修改后进行代码推送,这里点击Clone a Git repository,在弹出的窗口的URI中粘贴Github的仓库地址,比如git@github.com:vfeelit/test.git,对应的Host和Repository Path会被自动添加,在Protocol这里选择ssh,这样就会使用之前设置好的SSH和Github进行通信,否则你要填写账户密码,点击next,然后选择分支,点击next完成代码克隆。

接下来这个步骤有点费解,虽然建立了一个Git仓库(新建 或 克隆),但是它只是个仓库而已,要想往仓库中添加新内容,你还要把这个仓库导入到一个工程中,这样Zend Studio才能管理它。在仓库上右击,选择Import Projects,然后有三个选项:
Import existing projects
Use the New Project wizard
Import as general project
这就是如何把仓库导入到工程,第一个是导入到一个存在的工程,第二个是使用一个向导设置一个工程,第三个是直接把仓库当做一般的工程,一般选择第三个即可,点击next,输入Project name(本地标识),然后点击finish完成导入。切换到PHP视图,就会看到一个新工程,可以在这里面添加修改,然后可以右击选项Team进行提交等操作(也可以切换到Git视图右击选择提交等操作),注意这里能推送成功与否取决于SSH秘钥是否设置正确。

以上这两个步骤也可以通过这样操作:
File – Import…
然后选中Git(Projects from Git),然后有两个选项:
Existing local repository
Clone URI
Existing local repository就是把一个本地存在的仓库导入到项目中,Clone URI就是克隆一个仓库后把它导入到项目中。

//模拟推送一个存在的项目
[root@vfeelit ~]# mkdir FirstGit
[root@vfeelit ~]# cd FirstGit/
[root@vfeelit FirstGit]# vi index.php			#模拟项目已经存在
[root@vfeelit FirstGit]# git init				#初始化
Initialized empty Git repository in /root/FirstGit/.git/
[root@vfeelit FirstGit]# git add .
[root@vfeelit FirstGit]# git commit -m "init"		#提交到本地
[root@vfeelit FirstGit]#git remote add origin git@github.com:vfeelit/ifeeline.git #添加远端
[root@vfeelit FirstGit]#git push -u origin master	#把本地master分支推送到远端origin仓库中

//克隆一个项目,添加文件,推送到远端
[root@vfeelit ~]#git clone git@github.com:vfeelit/test.git
[root@vfeelit test]#cd test
[root@vfeelit test]#touch index.php
[root@vfeelit test]#git add index.php
[root@vfeelit test]#git commit -m “add index.php”
[root@vfeelit test]#git remote add origin git@github.com:vfeelit/test.git #可能不用设置
[root@vfeelit test]#git push -u origin master

以上的操作跟Zend Studio中的设置基本一一对应。

Zend Studio本地开发架构
Zend Studio
在Windows下虚拟一台Linux,它用来跑LANMP环境,Windows上安装的Zend Studio建立远程项目链接到虚拟机,在Windows的Zend Studio中做开发任务,任务完成到虚拟机中推送到服务器代码库,服务器中的代码库做主从备份,还可以定时备份到云端。

也可以简单点,跳过服务端,直接从云端进行推拉,比如在Github上建立仓库,在虚拟机中把仓库拉过来,在Zend Studio中做开发,之后推送到Github。

这里要虚拟一台Linux出来主要原因首先使开发环境和生产环境类似,其次是软件在Linux下运行更加高效并且安装配置更加便利(比如要编译安装PHP扩展)。

为了让Zend Studio链接上Linux,首先:
Project – Properties,点击Automatic Upload,勾选Enable automatic upload,选中Remote Connection,点击Manage添加链接信息:
Zend Studio 设置服务器

点击Finish完成远程链接设置回到上一个界面,在Remote Connection选中刚才设置的链接,在Application Directory输入针对刚才设置的链接的相对目录(可以直接使用斜杠)。

然后建立远程项目:
File – New – PHP Project from Remote Server
Zend Studio 远程项目

填写Project Name(本地标识),Location(本地项目保存地址),Remote Connectin,Application Directory(相对远程链接初始化的目录),Project URL。

在建立的本地项目中,你随时可以选中一个目录或者一个文件,然后右击选择Remote Servers(Upload to server Download from server),一般在本地修改文件应该是自动同步的,有时候链接断开可能导致同步失败,就需要手动Upload,如果在服务器添加或修改了文件,就需要手动Download,应该把服务器上的版本作为最新版本,为了避免覆盖可以先Download一次。

PHP框架Phalcon 之 模型 事务处理

Transactions 事务
When a process performs multiple database operations, it is often that each step is completed successfully so that data integrity can be maintained. Transactions offer the ability to ensure that all database operations have been executed successfully before the data are committed to the database.

Transactions in Phalcon allow you to commit all operations if they have been executed successfully or rollback all operations if something went wrong.

–Manual Transactions 手动事务
If an application only uses one connection and the transactions aren’t very complex, a transaction can be created by just moving the current connection to transaction mode, doing a rollback or commit if the operation is successfully or not:
如果应用只使用一个链接,事务不会非常复杂,通过移动当前链接的事务模式就可以创建一个事务,接下来就是提交或回滚:

<?php

class RobotsController extends Phalcon\Mvc\Controller
{
    public function saveAction()
    {
        $this->db->begin();

        $robot = new Robots();

        $robot->name = "WALL·E";
        $robot->created_at = date("Y-m-d");
        if ($robot->save() == false) {
            $this->db->rollback();
            return;
        }

        $robotPart = new RobotParts();
        $robotPart->robots_id = $robot->id;
        $robotPart->type = "head";
        if ($robotPart->save() == false) {
            $this->db->rollback();
            return;
        }

        $this->db->commit();
    }
}

–Implicit Transactions 隐式事务
Existing relationships can be used to store records and their related instances, this kind of operation implicitly creates a transaction to ensure that data are correctly stored:

<?php

$robotPart = new RobotParts();
$robotPart->type = "head";

$robot = new Robots();
$robot->name = "WALL·E";
$robot->created_at = date("Y-m-d");
$robot->robotPart = $robotPart;

$robot->save(); //Creates an implicit transaction to store both records

(涉及到多张表,默认悄悄开启事务)

–Isolated Transactions 原子事务
Isolated transactions are executed in a new connection ensuring that all the generated SQL, virtual foreign key checks and business rules are isolated from the main connection. This kind of transaction requires a transaction manager that globally manages each transaction created ensuring that they are correctly rolled back/committed before ending the request:
原子事务在一个新链接中执行用来确保所有产生的SQL,虚拟外键检测和业务逻辑是原子性的。这种类型的事务要求一个事务管理器来全局管理每个事务的创建以确保它们在请求结束(这个请求是只请求一个事务吧?)前正确回滚和提交:

<?php

use Phalcon\Mvc\Model\Transaction\Manager as TxManager,
    Phalcon\Mvc\Model\Transaction\Failed as TxFailed;

try {

    //Create a transaction manager
    $manager = new TxManager();

    // Request a transaction
    $transaction = $manager->get();

    $robot = new Robots();
    $robot->setTransaction($transaction);
    $robot->name = "WALL·E";
    $robot->created_at = date("Y-m-d");
    if ($robot->save() == false) {
        $transaction->rollback("Cannot save robot");
    }

    $robotPart = new RobotParts();
    $robotPart->setTransaction($transaction);
    $robotPart->robots_id = $robot->id;
    $robotPart->type = "head";
    if ($robotPart->save() == false) {
        $transaction->rollback("Cannot save robot part");
    }

    //Everything goes fine, let's commit the transaction
    $transaction->commit();

} catch(TxFailed $e) {
    echo "Failed, reason: ", $e->getMessage();
}

(这里引入了事务管理器,开启事务通过调用事务管理器的get方法,按照之前的说明,它是构建一个新链接,然后在它之上操作)

Transactions can be used to delete many records in a consistent way:

<?php

use Phalcon\Mvc\Model\Transaction\Manager as TxManager,
    Phalcon\Mvc\Model\Transaction\Failed as TxFailed;

try {

    //Create a transaction manager
    $manager = new TxManager();

    //Request a transaction
    $transaction = $manager->get();

    //Get the robots will be deleted
    foreach (Robots::find("type = 'mechanical'") as $robot) {
        $robot->setTransaction($transaction);
        if ($robot->delete() == false) {
            //Something goes wrong, we should to rollback the transaction
            foreach ($robot->getMessages() as $message) {
                $transaction->rollback($message->getMessage());
            }
        }
    }

    //Everything goes fine, let's commit the transaction
    $transaction->commit();

    echo "Robots were deleted successfully!";

} catch(TxFailed $e) {
    echo "Failed, reason: ", $e->getMessage();
}

Transactions are reused no matter where the transaction object is retrieved. A new transaction is generated only when a commit() or rollback() is performed. You can use the service container to create the global transaction manager for the entire application:
事务是重用的。一个新的事务仅在commit() or rollback()被执行时产生。你可以使用服务容器创建一个全局事务管理器。

<?php

$di->setShared('transactions', function(){
    return new \Phalcon\Mvc\Model\Transaction\Manager();
});

Then access it from a controller or view:

<?php

class ProductsController extends \Phalcon\Mvc\Controller
{

    public function saveAction()
    {

        //Obtain the TransactionsManager from the services container
        $manager = $this->di->getTransactions();

        //Or
        $manager = $this->transactions;

        //Request a transaction
        $transaction = $manager->get();

        //...
    }

}

While a transaction is active, the transaction manager will always return the same transaction across the application.
但是一个事务是激活的,事务管理在整个应用中将总是返回相同事务。
(Phalcon\DI\FactoryDefault是Phalcon MVC默认实现的DI,它会初始化一个事务管理器,因为可能产生多个事务,所以才需要管理)

PHP框架Phalcon 之 模型 关系定义

Relationships between Models 模型关系
There are four types of relationships: one-on-one, one-to-many, many-to-one and many-to-many. The relationship may be unidirectional or bidirectional, and each can be simple (a one to one model) or more complex (a combination of models). The model manager manages foreign key constraints for these relationships, the definition of these helps referential integrity as well as easy and fast access of related records to a model. Through the implementation of relations, it is easy to access data in related models from each record in a uniform way.
有四种关系类型:one-on-one, one-to-many, many-to-one and many-to-many。这些关系可以是单向或双向,可以简单或复杂。模型管理器为这些关系管理外键约束,这些定义 引用完整性 更简单和快速访问相关记录到模型。通过这些关系的实现,可以使用统一的方式在相关模型中容易地获取来自每条记录的数据。

Unidirectional relationships 单向关系
Unidirectional relations are those that are generated in relation to one another but not vice versa反过来不行.

Bidirectional relations 双向关系
The bidirectional relations build relationships in both models and each model defines the inverse relationship of the other.

Defining relationships 定义关系
In Phalcon, relationships must be defined in the initialize() method of a model. 必须在模型的 initialize()方法中定义关系。The methods belongsTo(), hasOne(), hasMany() and hasManyToMany() define the relationship between one or more fields from the current model to fields in another model. Each of these methods requires 3 parameters: local fields, referenced model, referenced fields.(三个参数,本地字段,引用模型,引用字段)

Method Description
hasMany Defines a 1-n relationship
hasOne Defines a 1-1 relationship
belongsTo Defines a n-1 relationship
hasManyToMany Defines a n-n relationship

(如果某个模型被其它模型引用,就需要定义hasMany,就是所谓的一对多,同理,如果模型引用到另外模型,那么它就需要定义belongsTo,如果通过一个中间表引用到另外的模型,就需要定义hasManyToMany)

The following schema shows 3 tables whose relations will serve us as an example regarding relationships:

CREATE TABLE `robots` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(70) NOT NULL,
    `type` varchar(32) NOT NULL,
    `year` int(11) NOT NULL,
    PRIMARY KEY (`id`)
);

CREATE TABLE `robots_parts` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `robots_id` int(10) NOT NULL,
    `parts_id` int(10) NOT NULL,
    `created_at` DATE NOT NULL,
    PRIMARY KEY (`id`),
    KEY `robots_id` (`robots_id`),
    KEY `parts_id` (`parts_id`)
);

CREATE TABLE `parts` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(70) NOT NULL,
    PRIMARY KEY (`id`)
);

这三张表就是数据库里面常见的多对多关系,在数据库中只要维护中间表的引用即可。不过在这里就要复杂一些。
The model “Robots” has many “RobotsParts”.
The model “Parts” has many “RobotsParts”.
The model “RobotsParts” belongs to both “Robots” and “Parts” models as a many-to-one relation.
The model “Robots” has a relation many-to-many to “Parts” through “RobotsParts”
(被引用的是老子,它有多个仔,老子自然就是有多个仔,仔自然就属于老子,但是多到多就不好理解了,实际它是指要用中间表建立多对多关系,这些模型管理器才知道这些关系)

The models with their relations could be implemented as follows:

<?php

class Robots extends \Phalcon\Mvc\Model
{
    public $id;

    public $name;

    public function initialize()
    {
        $this->hasMany("id", "RobotsParts", "robots_id");
    }

}
<?php

class Parts extends \Phalcon\Mvc\Model
{

    public $id;

    public $name;

    public function initialize()
    {
        $this->hasMany("id", "RobotsParts", "parts_id");
    }

}
<?php

class RobotsParts extends \Phalcon\Mvc\Model
{

    public $id;

    public $robots_id;

    public $parts_id;

    public function initialize()
    {
        $this->belongsTo("robots_id", "Robots", "id");
        $this->belongsTo("parts_id", "Parts", "id");
    }

}

The first parameter indicates the field of the local model used in the relationship; the second indicates the name of the referenced model and the third the field name in the referenced model. You could also use arrays to define multiple fields in the relationship.
Many to many relationships require 3 models and define the attributes involved in the relationship:

<?php

class Robots extends \Phalcon\Mvc\Model
{
    public $id;

    public $name;

    public function initialize()
    {
        $this->hasManyToMany(
            "id",
            "RobotsParts",
            "robots_id", "parts_id",
            "Parts",
            "id"
        );
    }

}
//如果在Parts中定义这个多到多,看起来应该是可以的
<?php

class Parts extends \Phalcon\Mvc\Model
{

    public $id;

    public $name;

    public function initialize()
    {
	$this->hasManyToMany(
            "id",
            "RobotsParts",
            "parts_id", "robots_id ",
            "Robots",
            "id"
        );
	}
}

Taking advantage of relationships
When explicitly defining the relationships between models, it is easy to find related records for a particular record.

<?php

$robot = Robots::findFirst(30);
foreach ($robot->robotsParts as $robotPart) {
    echo $robotPart->parts->name, "\n";
}

优点先不说了,先看看产生的SQL吧

Wed, 03 Sep 14 09:06:05 +0800][INFO] SELECT `robots`.`id`, `robots`.`name`, `robots`.`type`, `robots`.`year` FROM `robots` WHERE `robots`.`id` = 30 LIMIT 1
[Wed, 03 Sep 14 09:06:05 +0800][INFO] SELECT `robots_parts`.`id`, `robots_parts`.`robots_id`, `robots_parts`.`parts_id`, `robots_parts`.`created_at` FROM `robots_parts` WHERE `robots_parts`.`robots_id` = :0
[Wed, 03 Sep 14 09:06:05 +0800][INFO] SELECT `parts`.`id`, `parts`.`name` FROM `parts` WHERE `parts`.`id` = :0 LIMIT 1
[Wed, 03 Sep 14 09:06:05 +0800][INFO] SELECT `parts`.`id`, `parts`.`name` FROM `parts` WHERE `parts`.`id` = :0 LIMIT 1
[Wed, 03 Sep 14 09:06:05 +0800][INFO] SELECT `parts`.`id`, `parts`.`name` FROM `parts` WHERE `parts`.`id` = :0 LIMIT 1

预计会产生联合查询,实际结果让人失望。代码是方便了,实际效率很差。从robot获取robot_part,在从robot_part获取part。

Phalcon uses the magic methods __set/__get/__call to store or retrieve related data using relationships.
By accessing an attribute with the same name as the relationship will retrieve all its related record(s).
Also, you can use a magic getter:

<?php

$robot = Robots::findFirst();
$robotsParts = $robot->getRobotsParts(); // all the related records in RobotsParts
$robotsParts = $robot->getRobotsParts(array('limit' => 5)); // passing parameters

If the called method has a “get” prefix Phalcon\Mvc\Model will return a findFirst()/find() result. The following example compares retrieving related results with using magic methods and without:

<?php

$robot = Robots::findFirst(2);

// Robots model has a 1-n (hasMany)
// relationship to RobotsParts then
$robotsParts = $robot->robotsParts;

// Only parts that match conditions
$robotsParts = $robot->getRobotsParts("created_at = '2012-03-15'");

// Or using bound parameters
$robotsParts = $robot->getRobotsParts(array(
    "created_at = :date:",
    "bind" => array("date" => "2012-03-15")
));

$robotPart = RobotsParts::findFirst(1);

// RobotsParts model has a n-1 (belongsTo)
// relationship to RobotsParts then
$robot = $robotPart->robots;

##########
##Getting related records manually:
<?php

$robot = Robots::findFirst(2);

// Robots model has a 1-n (hasMany)
// relationship to RobotsParts, then
$robotsParts = RobotsParts::find("robots_id = '" . $robot->id . "'");

// Only parts that match conditions
$robotsParts = RobotsParts::find(
    "robots_id = '" . $robot->id . "' AND created_at = '2012-03-15'"
);

$robotPart = RobotsParts::findFirst(1);

// RobotsParts model has a n-1 (belongsTo)
// relationship to RobotsParts then
$robot = Robots::findFirst("id = '" . $robotPart->robots_id . "'");

The prefix “get” is used to find()/findFirst() related records. Depending on the type of relation it will use ‘find’ or ‘findFirst’:

Type Description Implicit Method
Belongs-To Returns a model instance of the related record directly findFirst
Has-One Returns a model instance of the related record directly findFirst
Has-Many Returns a collection of model instances of the referenced model find
Has-Many-to-Many Returns a collection of model instances of the referenced model, it implicitly does ‘inner joins’ with the involved models (complex query)

You can also use “count” prefix to return an integer denoting the count of the related records:

<?php

$robot = Robots::findFirst(2);
echo "The robot has ", $robot->countRobotsParts(), " parts\n";

Aliasing Relationships 关系别名
To explain better how aliases work, let’s check the following example:
Table “robots_similar” has the function to define what robots are similar to others:

mysql> desc robots_similar;
+-------------------+------------------+------+-----+---------+----------------+
| Field             | Type             | Null | Key | Default | Extra          |
+-------------------+------------------+------+-----+---------+----------------+
| id                | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| robots_id         | int(10) unsigned | NO   | MUL | NULL    |                |
| similar_robots_id | int(10) unsigned | NO   |     | NULL    |                |
+-------------------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

A model that maps this table and its relationships is the following:

<?php

class RobotsSimilar extends Phalcon\Mvc\Model
{

    public function initialize()
    {
        $this->belongsTo('robots_id', 'Robots', 'id');
        $this->belongsTo('similar_robots_id', 'Robots', 'id');
    }

}

Since both relations point to the same model (Robots), obtain the records related to the relationship could not be clear:

<?php

$robotsSimilar = RobotsSimilar::findFirst();

//Returns the related record based on the column (robots_id)
//Also as is a belongsTo it's only returning one record
//but the name 'getRobots' seems to imply that return more than one
$robot = $robotsSimilar->getRobots();

//but, how to get the related record based on the column (similar_robots_id)
//if both relationships have the same name?

The aliases allow us to rename both relationships to solve these problems:

<?php

class RobotsSimilar extends Phalcon\Mvc\Model
{

    public function initialize()
    {
        $this->belongsTo('robots_id', 'Robots', 'id', array(
            'alias' => 'Robot'
        ));
        $this->belongsTo('similar_robots_id', 'Robots', 'id', array(
            'alias' => 'SimilarRobot'
        ));
    }

}

With the aliasing we can get the related records easily:

<?php

$robotsSimilar = RobotsSimilar::findFirst();

//Returns the related record based on the column (robots_id)
$robot = $robotsSimilar->getRobot();
$robot = $robotsSimilar->robot;

//Returns the related record based on the column (similar_robots_id)
$similarRobot = $robotsSimilar->getSimilarRobot();
$similarRobot = $robotsSimilar->similarRobot;

Magic Getters vs. Explicit methods
Most IDEs and editors with auto-completion capabilities can not infer提示 the correct types when using magic getters, instead of use the magic getters you can optionally define those methods explicitly明确的 with the corresponding相应的 docblocks(DOC块) helping the IDE to produce a better auto-completion:

<?php

class Robots extends \Phalcon\Mvc\Model
{

    public $id;

    public $name;

    public function initialize()
    {
        $this->hasMany("id", "RobotsParts", "robots_id");
    }

    /**
     * Return the related "robots parts"
     *
     * @return \RobotsParts[]
     */
    public function getRobotsParts($parameters=null)
    {
        return $this->getRelated('RobotsParts', $parameters);
    }

}

Virtual Foreign Keys
By default, relationships do not act like database foreign keys, that is, if you try to insert/update a value without having a valid value in the referenced model, Phalcon will not produce a validation message. You can modify this behavior by adding a fourth parameter when defining a relationship.
默认定义的关系没有扮演类似数据库中的外键,如果插入或更新一个在引用模型中没有验证的值,Phalcon不会产生验证消息。你可以在定义关系时添加第四个参数修改这个行为。
The RobotsPart model can be changed to demonstrate this feature:

<?php

class RobotsParts extends \Phalcon\Mvc\Model
{

    public $id;

    public $robots_id;

    public $parts_id;

    public function initialize()
    {
        $this->belongsTo("robots_id", "Robots", "id", array(
            "foreignKey" => true
        ));

        $this->belongsTo("parts_id", "Parts", "id", array(
            "foreignKey" => array(
                "message" => "The part_id does not exist on the Parts model"
            )
        ));
    }

}

If you alter a belongsTo() relationship to act as foreign key, it will validate that the values inserted/updated on those fields have a valid value on the referenced model. 如果在belongsTo()中定义了外键,那么在插入或更新值时将在引用模型中验证值。 Similarly, if a hasMany()/hasOne() is altered it will validate that the records cannot be deleted if that record is used on a referenced model. 类似,如果hasMany()/hasOne()定义了外键,记录被删除时将检查记录是否被引用。
(belongsTo()/hasMany()/hasOne()之间是逻辑上有外键约束的,但是hasManyToMany()两个实体间被没有这个逻辑关系)

<?php

class Parts extends \Phalcon\Mvc\Model
{

    public function initialize()
    {
        $this->hasMany("id", "RobotsParts", "parts_id", array(
            "foreignKey" => array(
                "message" => "The part cannot be deleted because other robots are using it"
            )
        ));
    }

}

(这个被引用的表设置关联外键之后(严格来说对它不是外键约束,是外键关联),当记录被删除时将被阻止)
————————————————————————
为了更好理解这个阻止操作,使用如下代码测试:

class PushController extends \Phalcon\Mvc\Controller{
public function deletePartAction(){
$part = Parts::findFirst(7);
       	$ros = '';
      	foreach($part->robots as $robot){
        	$ros .= $robot->id."($robot->name) ";
      	}       
     	echo "The robots --> ".$ros." has the same part($part->id $part->name)\n\n";

       	if($part->delete() == false){ 
       		foreach($part->getMessages() as $m){ 
          		echo "Message: ", $m->getMessage(), "\n";
               	echo "Field:   ", $m->getField(), "\n";
             	echo "Type:    ", $m->getType(), "\n";
           	}       
      	}       
    	$this->view->disable()
	}
}

#########
##输出
The robots --> 31(new robot) 36(new robot)  has the same part(7 part_6)

Message: The part cannot be deleted because other robots are using it
Field:   id
Type:    ConstraintViolation

首先使用part_6的机器人有31 和 36,当删除part_6时,这个操作被阻止了,因为它被引用。下面看看产生的SQL:

[Wed, 03 Sep 14 14:15:40 +0800][INFO] SELECT `parts`.`id`, `parts`.`name` FROM `parts` WHERE `parts`.`id` = 7 LIMIT 1
[Wed, 03 Sep 14 14:15:40 +0800][INFO] SELECT `robots`.`id`, `robots`.`similar_robots_id`, `robots`.`name`, `robots`.`type`, `robots`.`year` FROM `robots` INNER JOIN `robots_parts` ON `robots_parts`.`robots_id` = `robots`.`id`  WHERE `robots_parts`.`parts_id` = :0
[Wed, 03 Sep 14 14:15:40 +0800][INFO] SELECT COUNT(*) AS `rowcount` FROM `robots_parts` WHERE `robots_parts`.`parts_id` = :0

先定位具体的part,然后通过part找出使用了它的robot(inner join操作),在删除part前到表robots_parts中检查对应的part是否被引用,如果被引用删除就被阻止了。

从这里可以看到,所谓的外键,是程序维护的,不是数据库意义上的外键,所有这里叫虚拟外键。所谓外键引用完整性就是这里的避免被引用的part被删除。

逻辑上,part属于robot一部分,part被引用时它不应该被删除,robot有多个part组成,robot删除时,它的引用信息也应该被删除(不是删除part哦)。
————————————————————————
Cascade/Restrict actions
Relationships that act as virtual foreign keys by default restrict the creation/update/deletion of records to maintain the integrity of data:

<?php

namespace Store\Models;

use Phalcon\Mvc\Model,
    Phalcon\Mvc\Model\Relation;

class Robots extends Model
{

    public $id;

    public $name;

    public function initialize()
    {
        $this->hasMany('id', 'Store\\Models\Parts', 'robots_id', array(
            'foreignKey' => array(
                'action' => Relation::ACTION_CASCADE
            )
        ));
    }

}

The above code set up to delete all the referenced records (parts) if the master record (robot) is deleted.
同样的,这个级联删除也是程序维护的。实际上,外键约束 和 级联删除这些,现代数据库系统都支持这些特征,可以把这些交给数据库系统来做,如果程序来维护开销是很大的,比如插入一条记录,要验证引用ID的有效性,需要针对被引用表来发起一次查询。

————————————————————————
注意到以上代码通过part找到使用了这个part的robots,实际过程产生了一条inner join查询,如果Parts只设置了hasMany()和RobotsParts产生对应,但是它是如果直接可以访问robots($part->robots)的呢?实际是不可以的,需要添加它跟robots的多对多关系才可以:

        $this->hasManyToMany(
            "id",
            "RobotsParts",
            "parts_id", "robots_id",
            "Robots",
            "id"
        );

这个就高速模型管理器,Parts和Robots的多对多关系通过RobotsParts实现,同理,要在Robot中获取Part,也需要在Robots中定义多对多关系,它们是产生JOIN的依据。

另外,我们在控制器代码中通过$part->robots获取使用这个part的robots,这个逻辑应该写入到自己的模型中:

    public function getRobots ($parameters=null)
    {
        return $this->getRelated('Robots', $parameters);
    }

这里的内容,就是Phalcon提供的ORM方案的核心部分。实际上,数据库查询操作还有更加复杂的内容,这些大概就是ORM无法封装的了,这个时候就要使用原生SQL了。

最后,这里提到了一对一的关系,但是没有具体内容。查看hasOne()方法有如下例子:

<?php

class Robots extends \Phalcon\Mvc\Model
{

   public function initialize()
   {
       $this->hasOne('id', 'RobotsDescription', 'robots_id');
   }

}

这里的意思就是Robots有一个对应的RobotsDescription。正如文档开头描述那样,hasOne应该是一个双向关系,hasManyToMany也是双向关系,而hasMany和belongsTo是单向的,所以在RobotsDescription中也应该定义hasOne。