yii添删改查实例

网络编程 2025-04-04 18:36www.168986.cn编程入门

Yii框架的增删改查实例:从DAO到关联查询的深入

一、初探数据访问对象(DAO)

Yii框架中的DAO基于PHP Data Objects (PDO)构建,为各种流行的数据库管理系统(DBMS)提供了统一的数据访问方式。这些DBMS包括MySQL、PostgreSQL等。要使用Yii DAO,需要安装PDO扩展以及特定的PDO数据库驱动,如PDO_MYSQL。

Yii DAO主要包含四个核心类:

1. CDbConnection:代表一个数据库连接。

2. CDbCommand:代表一条通过数据库执行的SQL语句。

3. CDbDataReader:代表一个只向前移动的、来自查询结果集的行的流。

4. CDbTransaction:代表一个数据库事务。

二、建立数据库连接

建立数据库连接是第一步。创建一个CDbConnection实例并激活它。连接数据库需要一个数据源名(DSN)来指定连接信息,可能还需要用户名和密码。如果连接过程中发生错误(例如,错误的DSN或无效的用户名/密码),将抛出一个异常。

示例代码:

```php

$connection = new CDbConnection($dsn, $username, $password);

// 尝试建立连接

try {

$connection->active = true;

// 执行数据库操作...

$connection->active = false; // 关闭连接

} catch (Exception $e) {

// 处理异常

}

```

DSN的格式取决于使用的PDO数据库驱动。不同的数据库,DSN的格式也不同。例如,SQLite的DSN为"sqlite:/path/to/dbfile",MySQL的为"mysql:host=localhost;dbname=testdb"。

三、将数据库连接作为应用组件使用

CDbConnection继承自CApplicationComponent,也可以作为应用组件使用。这样可以在应用配置中配置一个名为'db'的应用组件,然后通过Yii::app()->db访问数据库连接。这种方式使得数据库连接可以在代码中的多个地方共享。

四、执行SQL语句

数据库连接建立后,就可以通过CDbCommand执行SQL语句了。使用指定的SQL语句作为参数调用CDbConnection::createCommand()可以创建一个CDbCommand实例。接下来,就可以使用这个实例来执行各种增删改查的数据库操作。

1. 数据库连接与命令创建

在 Yii 应用中,数据库连接是首要的步骤。假设你已经建立了一个名为 "db" 的连接,否则你需要显式地创建一个。通过 `Yii::app()->db` 获取连接后,你可以使用 `$connection->createCommand($sql)` 来创建一个 CDbCommand 实例,执行你的 SQL 语句。如果需要对 SQL 语句进行修改,你可以通过 `$mand->text=$newSQL;` 进行调整。

2. CDbCommand 的执行与结果获取

执行 SQL 语句有两种主要方式:`execute()` 和 `query()`. `execute()` 用于执行无查询(non-query)的 SQL,如 INSERT, UPDATE 和 DELETE,返回受影响的行数。而 `query()` 则用于执行返回多行数据的 SQL 语句,如 SELECT。如果执行过程中发生错误,会抛出一个异常。

3. 查询结果的获取与处理

当使用 `query()` 方法获取一个 CDbDataReader 实例后,你可以通过反复调用 `read()` 方法获取结果中的行,或者使用 foreach 循环遍历数据。你还可以使用 `readAll()` 一次性提取所有行到一个数组中。值得注意的是,不同于 `query()`, 所有 `queryXXX()` 方法会直接返回数据。例如,`queryRow()` 会返回代表查询结果第一行的数组。

4. 事务处理

事务在 Yii 中通过 CDbTransaction 实例来处理。开始事务后,你可以一个个执行查询。任何对数据库的更新在事务提交前对外界都是不可见的。如果所有查询都成功,事务提交,更新变为可见;但如果其中一条查询失败,整个事务会回滚。这一流程可以通过以下代码实现:

```php

$transaction = $connection->beginTransaction();

try {

$connection->createCommand($sql1)->execute();

$connection->createCommand($sql2)->execute();

// ... other SQL executions

$transaction->commit();

} catch (Exception $e) {

// 如果有一条查询失败,则会抛出异常

$transaction->rollback();

}

```

5. 参数绑定

为了防范 SQL 注入攻击并提升重复执行的 SQL 语句的效率,你可以使用参数绑定。通过 "准备" 一条含有占位符的 SQL 语句,然后在参数绑定时替换这些占位符为实际参数,这样可以避免潜在的安全风险并提高执行效率。

在数据库操作中,参数占位符的使用至关重要。它们可以是命名的,如 `:username` 和 `:email`,也可以是未命名的,表现为问号。为了替换这些占位符,我们可以使用 `CDbCommand::bindParam()` 或 `CDbCommand::bindValue()` 方法。这些参数不需要额外加引号,底层的数据库驱动会自动处理。在 SQL 语句执行前,参数绑定必须完成。

以下是一个使用命名占位符的示例:

假设我们有一个 SQL 语句,其中包含两个占位符 `:username` 和 `:email`。

```php

$sql = "INSERT INTO tbl_user (username, email) VALUES(:username,:email)";

$command = $connection->createCommand($sql);

// 使用实际的用户名替换占位符 ":username"

$command->bindParam(":username", $username, PDO::PARAM_STR);

// 使用实际的替换占位符 ":email"

$command->bindParam(":email", $email, PDO::PARAM_STR);

$command->execute();

```

bindParam() 和 bindValue() 方法非常相似,主要区别在于前者使用 PHP 变量绑定参数,而后者直接使用值。对于内存中的大数据块参数,出于性能考虑,推荐使用前者。

当我们获取查询结果时,也可以使用 PHP 变量绑定列。这样,每次获取查询结果中的一行时,都会自动使用的值填充。例如:

```php

$sql = "SELECT username, email FROM tbl_user";

$dataReader = $connection->createCommand($sql)->query();

// 使用 $username 变量绑定第一列 (username)

$dataReader->bindColumn(1, $username);

// 使用 $email 变量绑定第二列 (email)

$dataReader->bindColumn(2, $email);

while ($dataReader->read() !== false) {

// 此时 $username 和 $email 包含了当前行中的 username 和 email

}

```

二、关于表前缀的使用

为了使用表前缀,我们需要配置 `CDbConnection::tablePrefix` 属性为我们想要的表前缀。之后,在 SQL 语句中,我们可以使用 `{{TableName}}` 来代表不带前缀的表名。例如,如果数据库中有一个名为 `tbl_user` 的表,并且 `tbl_` 是配置好的表前缀,那么我们可以这样执行用户相关的查询:

```php

$sql = 'SELECT FROM {{user}}';

$users = $connection->createCommand($sql)->queryAll();

```

三、关于Active Record的使用

尽管Yii DAO可以处理几乎所有的数据库任务,但我们大部分时间可能都在编写执行CRUD(创建、读取、更新、删除)操作的SQL语句。当代码中有大量混杂的SQL语句时,代码会变得难以维护。为了解决这个问题,我们可以使用Active Record(AR)。

Yii框架中的Active Record:数据库交互的新方式

在Yii框架中,Active Record(AR)是一种强大的数据库交互方式,它允许我们以面向对象的方式与数据库进行交互。让我们深入了解AR的工作原理以及如何在实际项目中使用它。

让我们从一个简单的例子开始。假设我们有一个名为"tbl_post"的数据表,我们想创建一个与之对应的AR类。在Yii中,我们可以轻松地通过继承CActiveRecord类来做到这一点。AR类代表数据库中的一个表,而AR实例则代表表中的一行。

我们需要建立数据库连接。AR依赖于一个数据库连接来执行所有数据库操作。在Yii中,我们可以通过配置db应用组件来提供数据库连接。例如,我们可以配置CDbConnection来连接到SQLite数据库。

一旦我们建立了数据库连接,我们就可以开始定义AR类了。对于每个数据表,我们都需要一个对应的AR类。这些类代表数据库中的表,并允许我们以面向对象的方式访问数据。

在AR类中,我们可以通过覆盖一些方法来定义表的结构。例如,我们可以通过实现tableName()方法来指定表名。我们还可以使用primaryKey()方法来指定表的主键。这些方法帮助我们定义AR类与数据库表之间的映射关系。

一旦我们定义了AR类,我们就可以开始使用它来访问数据库了。我们可以创建AR实例,并通过其属性来访问数据表中的列。这些属性是动态生成的,对应于数据表中的列。我们可以像操作普通PHP对象一样来操作这些属性,这使得数据库交互变得非常简单和直观。

除了简单的数据访问外,AR还提供了许多其他功能,如数据验证、关联查询等。我们可以通过AR类来实现复杂的业务逻辑,并与数据库进行高效交互。

为了提高性能,我们还可以使用表结构缓存。由于AR依赖于表的元数据来确定列的信息,读取元数据并它需要一些时间。如果我们数据库的表结构很少改变,我们可以通过配置CDbConnection的schemaCachingDuration属性来开启表结构缓存,从而提高性能。

3、主键与记录创建

要设定数据表的主键,我们通常在AR类中定义一个名为`primaryKey`的函数,它返回代表主键的字符串或字符串数组。例如:

```php

public function primaryKey()

{

return 'id';

}

```

对于复合主键,返回类似如下的数组:

```php

return array('pk1', 'pk2');

```

```php

$post = new Post;

$post->title = 'sample post';

$post->content = 'content for the sample post';

$post->create_time = time();

$post->save();

```

如果表结构中的列使用了静态默认值(如字符串或数字),则AR实例中相应的属性会在创建时自动采用这些默认值。要改变此默认值,可以在AR类中显式定义该属性。例如:

```php

class Post extends CActiveRecord

{

public $title = 'please enter a title';

// ...其他代码...

}

```

接着创建Post实例并访问其属性:

```php

$post = new Post;

echo $post->title; // 这里将显示: please enter a title

```

在记录保存到数据库之前,可以将其属性设置为`CDbExpression`类型。例如,为了保存由MySQL的`NOW()`函数返回的时间戳,可以这样做:

```php

$post = new Post;

$post->create_time = new CDbExpression('NOW()'); // CDbExpression类用于计算数据库表达式值

$post->save(); // 注意:'$post->create_time='NOW()';' 不会起作用,因为 'NOW()' 会被当作字符串处理。

```

提示:

由于AR允许我们执行数据库操作而无需编写大量SQL语句,我们经常会想知道AR在背后到底执行了什么SQL语句。这可以通过开启Yii的日志功能来实现。例如,在应用配置中开启`CWebLogRoute`,我们将会在每个网页上看到执行过的SQL语句。通过设置`CDbConnection::enableParamLogging`为true,还可以记录绑定在SQL语句中的参数值。

4、读取记录

要读取数据表中的数据,可以调用AR类的`find`系列方法中的一种。例如:

查找满足指定条件的结果中的第一行:

```php

$post = Post::model()->find($condition, $params);

```

查找具有指定主键值的那一行:

```php

$post = Post::model()->findByPk($postID, $condition, $params);

```

通过指定的SQL语句查找结果中的第一行:

```php

$post = Post::model()->findBySql($sql, $params);

```

当你想要寻找特定的记录时,如 postID 为 10 的那一行,你可以这样操作:

```php

$post = Post::model()->findRow(['postID' => ':postID'], ['postID' => 10]);

```

请注意,在特定的数据库管理系统中,可能需要转义列名。例如,PostgreSQL 默认对列名的大小写不敏感,因此可能需要使用 "postID"=:postID 的形式。

对于更复杂的查询条件,我们可以使用 CDbCriteria 或直接通过数组指定。例如:

```php

$criteria = new CDbCriteria();

$criteria->select = 'title'; // 只选择 'title' 列

$criteria->condition = 'postID=:postID';

$criteria->params = ['postID' => 10];

$post = Post::model()->find($criteria);

$post = Post::model()->find([

'select' => 'title',

'condition' => 'postID=:postID',

'params' => ['postID' => 10],

]);

```

当需要匹配多个列的值时,可以使用 findByAttributes() 方法。而一些框架提供了类似 findByNameAndTitle 的方法,尽管看起来方便,但可能引起混淆和大小写敏感问题。

当需要获取满足特定条件的多行数据时,可以使用 findAll 方法。还有其他一些方法如 findAllByPk、findAllByAttributes 和 findAllBySql 等,分别用于查找带有指定主键、指定属性值的所有行以及通过指定 SQL 语句查找所有行。

如果没有记录符合查询条件,findAll 方法将返回一个空数组。而对于 find 方法,如果没有找到匹配的记录,它将返回 null。

除了基本的查询方法,还有 count 和 exists 等方法用于获取满足条件的记录数量以及检查是否至少有一行符合指定条件。

至于更新记录,一旦 AR 实例填充了列的值,我们可以修改这些值并将其保存回数据表。这通常涉及到一个简单的 update 方法调用,将新的数据值和任何必要的条件传递给该方法。这样,我们就可以轻松地在数据库中更新记录。

当我们深入数据库操作时,会发现一种强大的工具——AR(Active Record)。它让我们能够以更直观、更便捷的方式与数据库进行交互。想象一下,我们有一个名为“Post”的模型,我们可以轻松地对它进行各种操作。

假设我们要更新一个已经存在的帖子标题。我们可以这样操作:

通过 `Post::model()->findByPk(10)` 找到ID为10的帖子实例。然后,我们可以直接修改其属性,如 `title`,为 `'new post title'`。调用 `save()` 方法将更改保存到数据库。这就是AR的魅力所在,我们无需复杂的SQL语句,只需简单的对象操作。

AR为我们提供了方便的类级别方法,让我们能够直接更新数据表中的行,无需先加载它们。例如,我们可以使用 `Post::model()->updateAll()` 方法来更新符合特定条件的所有行。如果我们知道要更新的行的主键,还可以使用 `updateByPk()` 方法。我们还可以使用 `updateCounters()` 方法来更新满足特定条件的行的计数列。

删除记录同样简单。假设我们有一个被数据填充的AR实例,代表一个帖子,我们只需调用 `delete()` 方法即可从数据表中删除该行。我们还可以使用类级别方法 `deleteAll()` 和 `deleteByPk()` 来删除符合特定条件的行。

我们可以使用AR来对比两个AR实例。只需比较它们的主键值或使用 `CActiveRecord::equals()` 方法即可。

AR还支持复合主键,即由多个字段组成的主键。在Yii的AR中,主键值表现为一个数组。这使得处理具有复合主键的表同样便捷。

在 CActiveRecord 中,为了增加灵活性并允许用户自定义工作流程,提供了几个占位符方法可以在子类中覆盖。这些方法在特定的时间点被调用,允许开发者定制自己的业务逻辑。比如,beforeValidate 和 afterValidate 方法分别在验证数据有效性之前和之后被调用,我们可以在这里添加自定义的验证逻辑。类似地,beforeSave 和 afterSave 方法在保存 AR 实例之前和之后被调用,我们可以在这里添加保存前的数据处理或保存后的操作。同样,beforeDelete 和 afterDelete 方法允许我们在删除 AR 实例前后执行特定操作。afterConstruct 方法在每个使用 new 操作符创建 AR 实例后被调用,可以用于初始化实例的某些属性。这些方法为我们提供了强大的工具来定制我们的数据模型行为。

除了基本的 CRUD 操作外,AR 还支持事务处理。每个 AR 实例都有一个名为 dbConnection 的属性,它是一个 CDbConnection 实例。这意味着我们可以利用 Yii DAO 提供的事务功能来确保数据的完整性和一致性。通过使用事务,我们可以确保一系列的操作(如查找和更新)在一个操作失败时能够全部撤销,保证数据的完整性不受影响。这在处理复杂的数据操作时特别有用。

AR 还引入了命名范围(named scope)的概念。命名范围是一组预定义的查询规则,可以像过滤器一样应用于 AR 查询。它们提供了一种组织和管理复杂查询的方式,使得代码更加简洁清晰。通过声明命名范围,我们可以在 Post 模型类中定义不同的查询规则(如 published 和 recently),然后在 find 方法调用中使用这些命名范围来过滤结果集。这使得我们可以轻松地执行复杂的查询操作,而无需编写冗长的查询语句。命名范围也可以用于 update 和 delete 方法,提供了一种方便的方式来批量处理数据。

关于自定义命名范围,例如我们想要限制最近发布的帖子的数量。我们并不需要在CActiveRecord::scopes方法中声明命名范围,而是可以通过定义一个方法来实现这一目的。这个方法的名字可以和我们想要的命名范围相同。例如:

```php

public function recentPosts($limit = 5) {

$this->getDbCriteria()->mergeWith([

'order' => 'create_time DESC',

'limit' => $limit,

]);

return $this;

}

```

使用上述方法,我们可以轻松地获取特定数量的最近发布的帖子。如,要获取3条最近发布的帖子,只需调用`$posts = Post::model()->published()->recentPosts(3)->findAll();`。如果不提供参数,默认将返回5条最近发布的帖子。

接下来,我们谈谈默认的命名范围。在某些情况下,我们可能希望某些查询默认应用特定的范围。例如,一个多语言网站可能只想显示当前用户指定语言的内容。这时,我们可以通过定义一个默认命名范围来解决这个问题。这可以通过覆盖CActiveRecord类中的defaultScope方法实现:

```php

class Content extends CActiveRecord {

public function defaultScope() {

return [

'condition' => "language='".Yii::app()->language."'",

];

}

}

```

现在,每次执行Content模型的查询时,都会自动应用这个默认的范围。这意味着,无论我们如何查询Content模型的数据,都会只返回当前用户指定语言的内容。

让我们一下关联查询。我们已经知道如何从单个数据表中获取数据,但如何使用AR从关联的数据表中获取数据呢?我们需要在数据库中建立关联的数据表之间的主键-外键关联。然后,AR会根据数据库中的元信息来决定如何连接数据。在使用关联AR之前,我们需要告诉AR各个AR类之间的关联。这些关联反映了数据库中数据表之间的关联。我们可以根据数据表之间的关系选择适当的关联类型:BELONGS_TO、HAS_MANY、HAS_ONE和MANY_MANY。要声明这些关联,我们可以通过覆盖CActiveRecord中的relations()方法来实现。这样,我们就可以轻松地使用AR进行关联查询,获取我们需要的数据。

Active Record提供了一种简洁、直观的方式来与数据库进行交互。通过自定义命名范围、默认命名范围和关联查询,我们可以更灵活地获取我们需要的数据,提高开发效率和代码的可读性。深入理解关系数据库中的关联定义与查询:以狼蚁网站SEO优化为例

在关系数据库中,实体间的关系是数据模型的重要组成部分。为了更好地管理和查询这些关系,许多框架和库都提供了方便的API来定义和查询这些关联。在狼蚁网站的SEO优化代码中,这种关系定义和查询被广泛应用。接下来,我们将深入如何定义这些关系以及如何进行关联查询。

一、关系定义

在关系数据库中,实体间的关系可以有多种类型,如:BELONGS_TO(属于)、HAS_ONE(有一个)、HAS_MANY(有多个)和MANY_MANY(多对多)。为了使用这些关系,我们需要定义它们并指定相关的参数。在狼蚁网站的代码中,Post和User类就定义了这样的关系。

例如,在Post类中,我们定义了与User的BELONGS_TO关系和与Category的MANY_MANY关系。这些关系的定义包含了关系的名称(VarName)、关系的类型(RelationType)、关联到的类的名称(ClassName)以及外键(ForeignKey)等信息。对于多对多的关系,我们还需要指定关联表,如PostCategory(postID, categoryID)。

二、关联查询

关联查询是关系数据库中的一项重要功能。在狼蚁网站的代码中,关联查询主要通过访问关联属性来完成。这种方式被称为延迟加载,也就是说,只有在访问到某个属性时,才会去数据库中查询相关的数据。

例如,我们先获取了ID为10的Post实例,然后通过$post->author访问其关联的User实例。如果没有查询到结果,对于BELONGS_TO和HAS_ONE关联,将返回null;对于HAS_MANY和MANY_MANY关联,将返回空数组。

延迟加载在某些情况下并不高效。例如,如果我们想获取多个Post的作者信息,每次访问author属性都会触发一次数据库查询。这时,我们可以使用急切加载方法,即预先加载所有的关联数据。在狼蚁网站的代码中,使用with()方法可以实现急切加载。

例如,使用Post::model()->with('author')->findAll()可以获取所有的Post实例,并且每个Post实例的author属性都已经被关联的User实例填充。

关系数据库中的关联定义和查询是数据操作的重要部分。通过狼蚁网站的代码示例,我们深入理解了如何定义和查询这些关联。在实际开发中,我们可以根据需求选择合适的方式来进行关联查询,以提高数据操作的效率和准确性。在数据库操作中,我们并不需要对每个帖子执行单独的连接查询。相反,我们可以利用急切加载策略,在一个单独的查询中取出所有的帖子及其相关的作者信息。

当我们谈论狼蚁网站的SEO优化时,我们采用的是一种高效的数据库操作方式。使用模型的with()方法,我们可以轻松地指定多个关联名。例如,通过以下代码,我们可以一次性获取所有的帖子以及它们的作者和分类信息:

```php

$posts = Post::model()->with('author', 'categories')->findAll();

```

我们还可以使用嵌套的急切加载策略。不必为每个关联指定单独的列表,我们可以将关联名称以层次结构的方式传递给with()方法。例如:

```php

$posts = Post::model()->with(

'author.profile',

'author.posts',

'categories'

)->findAll();

```

上述代码不仅检索了帖子及其作者和分类信息,还进一步获取了每个作者的profile以及他们发表的所有帖子。这种方法的优点是减少了数据库查询的数量,提高了数据检索的效率。

除了使用with()方法外,我们还可以指定CDbCriteria的with属性来实现急切加载。例如:

```php

$criteria = new CDbCriteria;

$criteria->with = array(

'author.profile',

'author.posts',

'categories',

);

$posts = Post::model()->findAll($criteria);

```

或者采用数组形式:

```php

$posts = Post::model()->findAll(array(

'with' => array(

'author.profile',

'author.posts',

'categories',

)

));

```

关联查询的选项非常丰富,允许我们定制查询的各个方面。我们可以指定select来选择特定的字段,使用condition来添加WHERE子句,params来绑定参数,on来添加额外的连接条件,order来排序结果等等。这些选项使我们能够精确地控制从数据库检索数据的方式。对于狼蚁网站的SEO优化而言,一些特定的关联选项在延迟加载时也是可用的,比如group、having、limit和offset等。这些选项为我们提供了灵活的数据库查询工具,帮助我们高效地从数据库中提取所需的数据。狼蚁网站的SEO优化之旅:优化User模型中的posts关联声明

在狼蚁网站的开发过程中,我们决定对User模型中的posts关联声明进行优化。为了更好地满足用户需求并提升用户体验,我们决定采用一些高级的编程技术来实现这一优化。

让我们来看一下这段经典的代码片段:

class User extends CActiveRecord

{

public function relations()

{

return array(

'posts' => array(self::HAS_MANY, 'Post', 'author_id', 'order' => 'posts.create_time DESC', 'with' => 'categories'), // 这一行定义了用户与其发布的帖子之间的关系,我们称之为一对多的关系。这里,我们通过设定排序方式为帖子创建时间的降序排列,并且每个帖子都加载了其所属的分类信息。

'profile' => array(self::HAS_ONE, 'Profile', 'owner_id'), // 这一行定义了用户与其个人资料之间的关系,我们称之为一对一的关系。个人资料是由用户ID来标识的。

);

}

}

经过我们的优化,现在,当用户访问 `$author->posts` 时,他们将看到用户的帖子列表,这个列表按照发布时间降序排列。这意味着的帖子将首先显示在页面上。每个帖子实例都包含了其分类信息。这样的设计不仅提升了用户体验,也使得网站的数据结构更加清晰和合理。我们相信这将有助于狼蚁网站的SEO优化工作,帮助网站在搜索引擎中获得更好的排名。我们利用Cambrian模板引擎的`render('body')`方法呈现给用户一个精彩纷呈的内容主体。通过这种方式,我们保证了用户在访问狼蚁网站时能够得到生动、丰富且有用的信息体验。

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by