Yii 2.0 ActiveRecord解释了

The ActiveRecord class in Yii provides an object oriented interface (aka ORM) for accessing database stored data. Similar structures can be found in most modern frameworks like Laravel, CodeIgniter, Smyfony and Ruby. Today, we’ll go over the implementation in Yii 2.0 and I’ll show you some of the more advanced features of it.

Yii中的ActiveRecord类提供了一个面向对象的接口(又名ORM)来访问数据库存储的数据。 在大多数现代框架(例如Laravel,CodeIgniter,Smyfony和Ruby)中都可以找到类似的结构。 今天,我们将介绍Yii 2.0的实现,并向您展示其一些更高级的功能。

Database and networking concept

模特班简介 (Model class intro)

The Yii ActiveRecord is an advanced version of the base yii\base\Modelwhich is the foundation of the Model-View-Controller architecture. I’ll quickly explain the most important functionality that ActiveRecord inherits from the Model class:

Yii ActiveRecord是基础yii\base\Model的高级版本,它是Model-View-Controller体系结构的基础。 我将快速解释ActiveRecord从Model类继承的最重要的功能:

属性 (Attributes)

The business data is held in attributes. These are publicly available properties of the model instance. All the attributes can conveniently be assigned massively by assigning any array to the attributes property of a model. This works because the base Component class (the base of almost everything in Yii 2.0) implements the __set() method which in turn calls the setAttributes() method in the Model class. The same goes for retrieving; all attributes can be retrieved by getting the attributes property. Again, built upon the Component class which implements __get() which calls the getAttributes() in the Model class. Models also supply attribute labels which are used for display purposes which makes using them in forms on pages easier.

业务数据保存在属性中。 这些是模型实例的公共可用属性。 通过将任何数组分配给模型的attributes属性,可以方便地大规模分配所有attributes 。 之所以__set()是因为基础Component类(Yii 2.0中几乎所有内容的基础)都实现了__set()方法,该方法又调用了Model类中的setAttributes()方法。 检索也是如此; 所有属性可以通过获取检索attributes属性。 再次,在实现__get()的Component类的基础上,该类调用Model类中的getAttributes() 。 模型还提供了用于显示目的的属性标签,这使得在页面上的表单中使用它们更加容易。

验证方式 (Validation)

Data passed in the model from user input should be checked to see that they satisfy your business logic. This is done by specifying rules() which would normally hold one or more validators for each attribute. By default, only attributes which are considered ‘safe’, meaning they have a validation rule defined for them, can be assigned massively.

应该检查从用户输入传递到模型中的数据,以确保它们满足您的业务逻辑。 这是通过指定rules()来完成的,通常将为每个属性保留一个或多个验证器。 默认情况下,只能大规模分配被认为是“安全”的属性,这意味着它们具有为它们定义的验证规则。

情境 (Scenarios)

The scenarios allow you to to define different ‘scenarios’ in which you’ll use a model allowing you to change the way it validates and handles its data. The example in the documentation describes how you can use it in a FormModel (which also extends the Model class) by specifying different validation rules for both user registration and login simply by defining two different scenarios in one Model.

这些方案允许您定义不同的“方案”,在这些“方案”中,您将使用一个模型,该模型允许您更改其验证和处理数据的方式。 文档中的示例描述了如何通过在一个模型中定义两种不同的情况而为用户注册和登录指定不同的验证规则,来在FormModel中使用它(该模型还扩展了Model类)。

创建一个ActiveRecord模型 (Creating an ActiveRecord model)

An ActiveRecord instance represents a row in a database table, therefore we need a database. In this article I’ll use the database design in the picture below as as an example. It’s a simple structure for blog articles. Authors can have multiple articles which can have multiple tags. The articles are related through an N:M relation to the tags because we want to be able to show related articles based on the tags. The ‘articles’ table will get our focus because it has the most interesting set of relations.

ActiveRecord实例代表数据库表中的一行,因此我们需要一个数据库。 在本文中,我将使用下图中的数据库设计作为示例。 这是博客文章的简单结构。 作者可以有多篇文章,其中可以有多个标签。 文章通过N:M关系与标签相关联,因为我们希望能够基于标签显示相关文章。 “文章”表将成为我们关注的焦点,因为它具有最有趣的一组关系。

db_structure.PNG

I’ve used the Gii extension to generate the model based on the table and it’s relations. Here’s what is generated for the articles table from the database structure just by clicking a few buttons:

我已经使用Gii扩展来基于表格及其关系生成模型。 只需单击几个按钮,即可从数据库结构为articles表生成以下内容:

namespace app\models;

use Yii;

/**
 * This is the model class for table "articles".
 *
 * @property integer $ID
 * @property integer $AuthorsID
 * @property string $LastEdited
 * @property string $Published
 * @property string $Title
 * @property string $Description
 * @property string $Content
 * @property string $Format
 *
 * @property Authors $authors
 * @property Articlestags[] $articlestags
 */
class Articles extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'articles';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['AuthorsID'], 'integer'],
            [['LastEdited', 'Title', 'Description', 'Content'], 'required'],
            [['LastEdited', 'Published'], 'safe'],
            [['Description', 'Content'], 'string'],
            [['Format'], 'in', 'range' => ['MD', 'HTML']],
            [['Title'], 'string', 'max' => 250],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'ID' => Yii::t('app', 'ID'),
            'AuthorsID' => Yii::t('app', 'Authors ID'),
            'LastEdited' => Yii::t('app', 'Last Edited'),
            'Published' => Yii::t('app', 'Published'),
            'Title' => Yii::t('app', 'Title'),
            'Description' => Yii::t('app', 'Description'),
            'Content' => Yii::t('app', 'Content'),
            'Format' => Yii::t('app', 'Format'),
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getAuthors()
    {
        return $this->hasOne(Authors::className(), ['ID' => 'AuthorsID']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getArticlestags()
    {
        return $this->hasMany(Articlestags::className(), ['ArticlesID' => 'ID']);
    }
}

The properties listed in the comment before the class definition show which attributes are available on every instance. It is good to notice that because of the relation definitions (defined in this class), you also get properties for the related data; one Authors $authors and multiple Articlestags[] $articlestags.

类定义之前的注释中列出的属性显示了每个实例上可用的属性。 值得一提的是,由于关系定义(在此类中定义),因此您还可以获得相关数据的属性。 一个Authors $authors和多个Articlestags[] $articlestags

The tableName() function defines which database table is related to this Model. This allows a decoupling of the class name from the actual table name.

tableName()函数定义哪个数据库表与此模型相关。 这允许将类名与实际表名解耦。

The rules() define the validation rules for the model. There are no scenarios defined so there is only one set of rules. It’s quite readable; showing which fields are required and which require an integer or string. There are quite a few core validators available which suit most people’s needs.

rules()定义模型的验证规则。 没有定义方案,因此只有一组规则。 可读性强; 显示哪些字段是必填字段,哪些字段需要整数或字符串。 有很多核心验证器可以满足大多数人的需求。

The attributeLabels() function supplies labels to be shown for each attribute should it be used in views. I chose to make mine i18n compatible from Gii which added all the calls to Yii::t(). This basically means that translation of the labels, which end up in the rendered pages, will be much easier later on should we need it. Finally, the getAuthors() and getArticlestags() functions define the relation of this table to other tables.

如果在视图中使用每个属性, attributeLabels()函数将提供要显示的标签。 我选择让Gii与我的i18n兼容,后者将所有调用都添加到Yii::t() 。 从根本上讲,这意味着最终在渲染页面中的标签翻译将变得更加容易,如果需要的话。 最后, getAuthors()getArticlestags()函数定义此表与其他表的关系。

Note: I was quite surprised to find the ‘Format’ attribute was missing completely from the properties, validators and labels. Turns out that Gii doesn’t support ENUMs. Besides MySQL (and its forks) only PostgreSQL supports it and therefore it wasn’t implemented. I had to add them manually.

注意 :我很惊讶地发现属性,验证器和标签中完全没有'格式'属性。 原来Gii不支持ENUM。 除MySQL(及其分支)外,只有PostgreSQL支持它,因此未实现。 我必须手动添加它们。

完成模型 (Completing the model)

You can probably see that the generated Articles class only has relations defined for the foreign key tables. The N:M relation from Articles to Tags won’t be generated automatically so we’ll have to define that by hand.

您可能会看到,生成的Articles类仅具有为外键表定义的关系。 从文章到标签的N:M关系不会自动生成,因此我们必须手动进行定义。

The relations are all returned as instances of yii\db\ActiveQuery. To define a relation between Articles and Tags we’ll need to use the ArticlesTags as a junction table. In ActiveQuery, this is done by defining a via table. ActiveQuery has two methods you can use for this:

这些关系都作为yii \ db \ ActiveQuery的实例返回。 要定义文章和标签之间的关系,我们需要使用ArticlesTags作为连接表。 在ActiveQuery中,这是通过定义via table来完成的。 ActiveQuery有两种方法可用于此目的:

  • via() allows you to use an already defined relation in the class to define the relation.

    via()允许您使用类中已定义的关系来定义该关系。

  • viaTable() alternatively allows you to define a table to use for a relation.

    viaTable()允许您定义用于关系的表。

The via() method allows you to use an already defined relation as via table. In our example, however, the ArticlesTags table holds no information that we care for so I’ll use the viaTable() method.

via()方法允许您使用已经定义的关系作为via table 。 但是,在我们的示例中,ArticlesTags表没有保存我们需要关注的信息,因此我将使用viaTable()方法。

/**
 * @return \yii\db\ActiveQuery
 */
public function getTags()
{
    return $this->hasMany(Tags::className(), ['ID' => 'TagsID'])
			    ->viaTable(Articlestags::tableName(), ['ArticlesID' => 'ID']);
}

Now that we’ve defined all the relations, we can start using the model.

现在我们已经定义了所有关系,我们可以开始使用模型了。

使用模型 (Using the model)

I’ll quickly create some database content by hand.

我将快速手动创建一些数据库内容。

$phpTag = new \app\models\Tags();		//Create a new tag 'PHP'
$phpTag->Tag = 'PHP';
$phpTag->save();

$yiiTag = new \app\models\Tags();		//Create a new tag 'Yii'
$yiiTag->Tag = 'Yii';
$yiiTag->save();

$author = new \app\models\Authors();	//Create a new author
$author->Name = 'Arno Slatius';
$author->save();

$article = new \app\models\Articles();	//Create an article and link it to the author
$article->AuthorsID = $author->ID;
$article->Title = 'Yii 2.0 ActiveRecord';
$article->Description = 'Arno Slatius dives into the Yii ActiveRecord class';
$article->Content = '... the article ...';
$article->LastEdited = new \yii\db\Expression('NOW()');
$article->save();

$tagArticle = new \app\models\ArticlesTags();	//Link the 'PHP' tag to the article
$tagArticle->ArticlesID = $article->ID;
$tagArticle->TagsID = $phpTag->ID;
$tagArticle->save();

$tagArticle = new \app\models\ArticlesTags();	//Link the 'Yii' tag to the article
$tagArticle->ArticlesID = $article->ID;
$tagArticle->TagsID = $yiiTag->ID;
$tagArticle->save();

It should be noted that the code above has some assumptions which I’ll explain;

应该注意的是,上面的代码有一些假设,我将解释这些假设。

  • The result of save(), a boolean, isn’t evaluated. This isn’t wise normally, because Yii will actually call validate() on the class before actually saving it in the database. The database INSERT won’t be executed should any of the validation rules fail.

    布尔值save()的结果未评估。 这通常是不明智的,因为Yii实际上会在实际将其保存在数据库中之前在类上调用validate() 。 如果任何验证规则失败,则将不会执行数据库INSERT

  • You might notice that the ID attributes of the various instances are used while they are not set. This can be done safely because the save() call will INSERT the data and get assigned the primary key back from the database and make the ID property value valid.

    您可能会注意到,各种实例的ID属性在未设置的情况下使用。 可以安全地完成此操作,因为save()调用将INSERT数据并从数据库中分配回主键,并使ID属性值有效。

  • The $article->LastEdited is a DateTime value in the database. I want to insert the current datetime by calling the NOW() SQL function on it. You can do this by using the Expression class which allows the usage of various SQL expressions with ActiveRecord instances.

    $article->LastEdited是数据库中的DateTime值。 我想通过在其上调用NOW() SQL函数来插入当前日期时间。 您可以使用Expression类来实现此目的,该类允许在ActiveRecord实例中使用各种SQL表达式。

You can then retrieve the article again from the database;

然后,您可以再次从数据库中检索文章。

//Look up our latest article
$article = \app\models\Articles::findOne(['Title'=>'Yii 2.0 ActiveRecord']);

//Show the title
echo $article->Title;

//The related author, there is none or one because of the hasOne relation
if (isset($article->authors)) {
    echo $article->authors->name
}

//The related tags, always an array because of the hasMany relations
if (isset($article->tags)) {
    foreach($article->tags as $tag) {
        echo $tag->Tag;
    }
}

新的和高级的用法 (New and advanced usages)

The Yii ActiveRecord, as I’ve described it so far, is straight forward. Let’s make it interesting and go into the new and changed functionality in Yii 2.0 a bit more.

正如我到目前为止所描述的,Yii ActiveRecord非常简单。 让我们变得有趣,并进一步了解Yii 2.0中的新功能和更改的功能。

脏属性 (Dirty attributes)

Yii 2.0 introduced the ability to detect changed attributes. For ActiveRecord, these are called dirty attributes because they require a database update. This ability now by default allows you to see which attributes changed in a model and to act on that. When, for example, you’ve massively assigned all the attributes from a form POST you might want to get only the changed attributes:

Yii 2.0引入了检测更改属性的功能。 对于ActiveRecord,它们被称为属性,因为它们需要数据库更新。 现在,默认情况下,此功能现在使您可以查看模型中哪些属性已更改并对此采取行动。 例如,当您从POST表单中大规模分配了所有属性时,您可能只想获取已更改的属性:

//Get a attribute => value array for all changed values
$changedAttributes = $model->getDirtyAttributes();

//Boolean whether the specific attribute changed
$model->isAttributeChanged('someAttribute');

//Force an attribute to be marked as dirty to make sure the record is 
// updated in the database
$model->markAttributeDirty('someAttribute');

//Get on or all old attributes
$model->getOldAttribute('someAttribute');
$model->getOldAttributes();

可排列的 (Arrayable)

The ActiveRecord, being extended from Model, now implements the \yii\base\Arrayable trait with it’s toArray() method. This allows you to convert the model with attributes to an array quickly. It also allows for some nice additions.

Model扩展的ActiveRecord现在通过toArray()方法实现\yii\base\Arrayable特性。 这使您可以将具有属性的模型快速转换为数组。 它还允许一些不错的添加。

Normally a call to toArray() would call the fields() function and convert those to an array. The optional expand parameter of toArray() will additionally call extraFields() which dictates which fields will also be included.

通常,对toArray()调用将调用fields()函数并将其转换为数组。 toArray()的可选expand参数将另外调用extraFields() ,它指示还将包括哪些字段。

These two fields methods are implemented by BaseActiveRecord and you can implement them in your own model to customize the output of the toArray() call.

这两个字段方法由BaseActiveRecord实现,您可以在自己的模型中实现它们,以自定义toArray()调用的输出。

I’d like, in my example, to have the extended array contain all the tags of an article available as a comma separated string in my array output as well;

在我的示例中,我想让扩展数组包含文章的所有标签,以及在数组输出中以逗号分隔的字符串的形式;

public function extraFields()
{
	return [
		'tags'=>function() {
			if (isset($this->tags)) {
				$tags = [];
				foreach($this->tags as $articletag) {
					$tags[] = $articletag->Tag;
				}
				return implode(', ', $tags);
			}
		}
	];
}

And then get an array of all the fields and this extra field from the model;

然后从模型中获取所有字段以及这个额外字段的数组;

//Returns all the attributes and the extra tags field
$article->toArray([], ['tags']);

大事记 (Events)

Yii 1.1 already implemented various events on the CActiveRecord and they’re still there in Yii 2.0. The ActiveRecord life cycle in the Yii 2.0 guide shows very nicely how all these events are fired when using an ActiveRecord. All the events are fired surrounding the normal actions of your ActiveRecord instance. The naming of the events is quite obvious so you should be able to figure out when they are fired; afterFind(), beforeValidate(), afterValidate(), beforeSave(), afterSave(), beforeDelete(), afterDelete().

Yii 1.1已经在CActiveRecord上实现了各种事件,并且它们仍在Yii 2.0中存在。 Yii 2.0指南中的ActiveRecord生命周期很好地展示了使用ActiveRecord时如何触发所有这些事件。 围绕您的ActiveRecord实例的常规操作触发所有事件。 这些事件的命名非常明显,因此您应该能够确定何时触发它们。 afterFind()beforeValidate()afterValidate()beforeSave()afterSave() beforeDelete()afterDelete()afterDelete()

In my example, the LastEdited attribute is a nice way to demonstrate the use of an event. I want to make sure LastEdited always reflects the last time the article was edited. I could set this on two events; beforeSave() and beforeValidate(). My model rules define LastEdited as a required attribute so we need to use the beforeValidate() event to make sure it is also set on new instances of the model;

在我的示例中, LastEdited属性是演示事件使用的一种好方法。 我想确保LastEdited总是反映上次编辑文章的时间。 我可以在两个事件上设置它; beforeSave()beforeValidate() 。 我的模型规则将LastEdited定义为必填属性,因此我们需要使用beforeValidate()事件来确保也将其设置在模型的新实例上。

public function beforeValidate($insert)
{
    if (parent::beforeValidate($insert)) {
		$this->LastEdited = new \yii\db\Expression('NOW()');
        return true;
    }
    return false;
}

Note that with all of these events, you should call the parent event handler. Returning false (or nothing!) from a before event in these functions stops the action from happening.

请注意,对于所有这些事件,应调用父事件处理程序。 在这些函数中,从before事件返回false(或什么都没有!)可阻止该操作发生。

行为 (Behavior)

Behavior can be used to enhance the functionality of an existing component without modifying its code. It can also respond to the events in the component that it was attached to. They behave similar to the traits introduced in PHP 5.4. Yii 2.0 comes with a number of available behaviors;

行为可用于增强现有组件的功能,而无需修改其代码。 它还可以响应其所附加的组件中的事件。 它们的行为类似于PHP 5.4中引入的特征 。 Yii 2.0具有许多可用的行为。

  • yii\behaviors\AttributeBehavior allows you to specify attributes which need to be updated on a specified event. You can, for example, set an attribute to a value based on an unnamed function on a BEFORE_INSERT event.

    yii\behaviors\AttributeBehavior允许您指定需要在指定事件上更新的属性。 例如,您可以在BEFORE_INSERT事件BEFORE_INSERT属性设置为基于未命名函数的值。

  • yii\behaviors\BlameableBehavior does what you’d expect; blame someone. You can set two attributes; a createdByAttribute and updatedByAttribute which will be set to the current user ID when the object is created or updated.

    yii\behaviors\BlameableBehavior您的期望; 怪某人。 您可以设置两个属性; 创建或更新对象时,将createdByAttribute一个updatedByAttributeupdatedByAttribute设置为当前用户ID。

  • yii\behaviors\SluggableBehavior allows you to automatically create a URL slug based on one of the attributes to another attribute in the model.

    yii\behaviors\SluggableBehavior允许您基于模型中另一个属性的一个属性自动创建URL段。

  • yii\behaviors\TimestampBehavior will allow you to automatically create and update the time stamp in a createdAtAttribute and updatedAtAttribute in your model.

    yii\behaviors\TimestampBehavior可以让你自动创建和更新的时间戳createdAtAttributeupdatedAtAttribute在模型中。

You can probably see that these have some practical applications in my example as well. Assuming that the person currently logged in to the application is the actual author of an article, I could use the BlameableBehavior to make them the author and I can also use the TimestampBehaviour to make sure the LastEdited attribute stays up to date. This would replace my previous implementation of the beforeValidate() event in my model. This is how I attached the behaviors to my Articles model:

您可能会在我的示例中看到它们也有一些实际应用。 假设当前登录到该应用程序的人是文章的实际作者,则可以使用BlameableBehavior来使他们成为作者,也可以使用TimestampBehaviour来确保LastEdited属性保持最新。 这将替换模型中我之前对beforeValidate()事件的实现。 这是我将行为附加到我的文章模型的方式:

public function behaviors()
{
    return [
	    [
            'class' => \yii\behaviors\BlameableBehavior::className(),
            'createdByAttribute' => 'AuthorID',
            'updatedByAttribute' => 'AuthorID',
        ],
        [
            'class' => \yii\behaviors\TimestampBehavior::className(),
            'createdAtAttribute' => false,    //or 'LastEdited'
            'updatedAtAttribute' => 'LastEdited',
            'value' => new \yii\db\Expression\Expression('NOW()'),
        ],
    ];
}

I assume here of course that the creator and the editor of the article is the same person. Since I don’t have a created timestamp field, I chose not to use it by setting the createdAtAttribute to false. I could of course also set this to 'LastEdited'.

我在这里当然假设文章的创建者和编辑是同一个人。 由于我没有创建的时间戳字段,因此我通过将createdAtAttribute设置为false选择不使用它。 我当然也可以将其设置为'LastEdited'

交易操作 (Transactional operations)

The last feature I want to touch is the possibility to automatically force the usage of transactions in a model. With the enforcement of foreign keys also comes the possibility for database queries to fail because of that. This can be handled more gracefully by wrapping them in a transaction. Yii allows you to specify operations that should be transactional by implementing a transactions() function in your model that specifies which operations in which scenarios should be enclosed in a transaction. Note that you should return a rule for the SCENARIO_DEFAULT if you want this to be done by default on operations.

我要触摸的最后一个功能是可以自动强制模型中使用事务。 强制执行外键也可能导致数据库查询失败。 通过将它们包装在事务中,可以更优雅地处理此问题。 Yii允许您通过在模型中实现transactions()函数来指定应进行事务处理的操作,该函数指定在事务中应包含哪些业务情景。 请注意,如果您希望默认情况下在操作上完成此操作,则应返回SCENARIO_DEFAULT的规则。

public function transactions()
{
	return [
		//always enclose updates in a transaction
	    \yii\base\Model::SCENARIO_DEFAULT => self::OP_UPDATE,	    
	    //include all operations in a transaction for the 'editor' scenario
	    'editor' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
	];
}

结论 (Conclusion)

The Yii ActiveRecord class already made ORM handling very simple, Yii 2.0 builds upon this great base and extends it even further. The flexibility is huge due to the possibility to define different usage scenarios, attach behaviors and use events.

Yii ActiveRecord类已经使ORM处理变得非常简单,Yii 2.0在此基础上进一步扩展。 由于可以定义不同的使用方案,附加行为和使用事件,因此灵活性非常大。

These are some features in the ActiveRecord that I’ve found most useful over time and most welcome with the arrival of Yii 2.0. Did you miss a feature of ActiveRecord, or perhaps feel that Yii ActiveRecord is missing a great feature from another framework? Please let us know in the comments!

这些是ActiveRecord中的一些功能,随着时间的推移,我发现它们最有用,随着Yii 2.0的到来,这些功能也将受到欢迎。 您是否错过了ActiveRecord的功能,还是觉得Yii ActiveRecord缺少了另一个框架的重要功能? 请在评论中让我们知道!

翻译自: https://www.sitepoint.com/yii-2-0-activerecord-explained/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值