Maison > php教程 > php手册 > Yii框架模型类的实现以及PHP5动态语言特性的应用

Yii框架模型类的实现以及PHP5动态语言特性的应用

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
Libérer: 2016-06-06 19:51:06
original
1543 Les gens l'ont consulté

Yii框架提供一个代码生成器gii, 我们一般用它来生成模型类代码。模型类是对数据(表)操作进行封装 不过在模型类中你看不到get/set属性的方法,甚至看不到和表字段关联的属性成员变量,但并不影响我们直接操作其属性,仿佛这些属性就在那里一样。 其具体实现

Yii框架提供一个代码生成器gii, 我们一般用它来生成模型类代码。模型类是对数据(表)操作进行封装

不过在模型类中你看不到get/set属性的方法,甚至看不到和表字段关联的属性成员变量,但并不影响我们直接操作其属性,仿佛这些属性就在那里一样。

其具体实现方式,正是一些设计模式和PHP5动态语言特性的一个很好的应用案例。

举个例子,如下一个用户模型类,对应的数据表为users

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<?php class User extends CActiveRecord

{

    /**

     * The followings are the available columns in table 'users':

     * @var double $Id

     * @var string $Username

     * @var string $Password

     * @var string $Email

     */

 

    /**

     * Returns the static model of the specified AR class.

     * @return CActiveRecord the static model class

     */

    public static function model($className='User')

    {

        return parent::model($className);

    }

 

    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'users';

    }

}

Copier après la connexion

现在我们想读取一条user记录,首先当然得构造一个User对象

$user = new User();

可以执行一下var_dump($user),你会发现这个$user有个私有的_md属性(模型的元数据),该属性类型为CActiveRecordMetaData

在这个_md变量中包含了数据表结构定义(Schema)。

究竟执行了什么代码,会构造出这样一个对象,并且读取了数据表结构定义,下面我们来跟踪一下:

1、和其他面向对象语言一样,在调用new创建一个对象时,首先会调用类的构造函数,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

    /**

     * Constructor.

     * @param string $scenario scenario name. See {@link CModel::scenario} for more details about this parameter.

     */

    public function __construct($scenario='insert')

    {

        if($scenario===null) // internally used by populateRecord() and model()

            return;

 

        $this->setScenario($scenario);

        $this->setIsNewRecord(true);

        $this->_attributes=$this->getMetaData()->attributeDefaults;

 

        $this->init();

                ......

    }

Copier après la connexion
可以看到模型的构造函数调用了getMetaData方法,并且还给模型对象的属性成员变量($this->_attributes)赋予了缺省值。

看起来模型对象元数据的构造以及数据表shema的读取和这个函数有关,继续

1

2

3

4

5

6

7

8

9

10

11

/**

 * Returns the meta-data for this AR

 * @return CActiveRecordMetaData the meta for this AR class.

 */

public function getMetaData()

{

    if($this->_md!==null)

        return $this->_md;

    else

        return $this->_md=self::model(get_class($this))->_md;

}

Copier après la connexion
getMetaData函数调用了self::model方法,这个函数我们很熟悉,是一个静态方法,根据类名返回模型类的静态实例。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/**

 * Returns the static model of the specified AR class.

 * The model returned is a static instance of the AR class.

 */

public static function model($className=__CLASS__)

{

    if(isset(self::$_models[$className]))

        return self::$_models[$className];

    else

    {

        $model=self::$_models[$className]=new $className(null);

        $model->_md=new CActiveRecordMetaData($model);

        $model->attachBehaviors($model->behaviors());

        return $model;

    }

}

Copier après la connexion

注意self::$_models是一个单例模式静态变量,你的应用所加载过的模型都被放在该对象数组中统一管理,你可以把它看作集中的模型对象管理器。看来,即使模型不再被实际使用,已经建立的模型对象也不会被释放。上述代码中创建了一个CActiveRecordMetaData对象,即前述的数据表Schema,注意User模型本身被作为其构造函数的参数被传递了进去,这里类似于应用了一种委托的模式,即模型类把获取数据表Schema的任务委托给CActiveRecordMetaData类。继续看下去,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

    /**

     * Constructor.

     * @param CActiveRecord $model the model instance

     */

    public function __construct($model)

    {

        $this->_model=$model;

 

        $tableName=$model->tableName();

        if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null)

            throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.',

                array('{class}'=>get_class($model),'{table}'=>$tableName)));

 

        ......

    }

Copier après la connexion

上述代码中getTable($tableName),这里应该是getTable('User')函数调用了CMysqlSchema的loadTable方法,最终通过SQL语句

1

SHOW FULL COLUMNS FROM ...

Copier après la connexion
获取到数据表的结构定义并赋值给了模型对象。


现在我们才刚刚了解到User对象中的元数据和缺省属性成员变量值是怎么来的。

接下来,才是动态语言特性相关部分,我们看看如何通过user对象,来所谓“动态”的操作数据属性的。

数据库中的users表中有Email字段,那么我们现在想给新创建的user对象的Email属性赋值,如下:

$user->Email = 'iefreer@hotmail.com';

如果是传统面向对象语言如c++/java,这里会报编译错误,因为User类没有定义Email成员变量。

而对于PHP5而言,由于语言对动态特性(魔法函数)的支持,这样的调用没有任何问题。我们看看它内部是怎么实现的。

如我之前的PHP语言动态特性文章中所言,设置对象的一个不存在的属性,会触发该对象的__set魔法函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

/**

 * PHP setter magic method.

 * This method is overridden so that AR attributes can be accessed like properties.

 * @param string $name property name

 * @param mixed $value property value

 */

public function __set($name,$value)

{

    if($this->setAttribute($name,$value)===false)

    {

        if(isset($this->getMetaData()->relations[$name]))

            $this->_related[$name]=$value;

        else

            parent::__set($name,$value);

    }

}

Copier après la connexion

上述代码中的setAttribute函数会把Email添加到$_attributes这个数组类型的成员变量中,也就是$_attributes充当了模型所对应的数据表属性动态管理器的功能。

再看读取user对象属性的语句:

$email = $user->Email;

类似的,该语句将触发CActiveRecord类的__get魔法函数,会返回$_attributes数组中相应属性的值。


by iefreer

Étiquettes associées:
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Recommandations populaires
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal