# 创建数据库与模型

本节将通过为 `books` 资源创建 SQLite 数据库表、填充示例数据并定义访问模型来构建数据层。完成后，将获得一个包含示例数据的 `books` 表。

## 创建迁移

迁移通过定义应用与回滚逻辑，实现对数据库架构的版本控制。下面为简单的 `authors` 和 `books` 表创建迁移。

运行 Spark 命令：

    php spark make:migration CreateAuthorsTable
    php spark make:migration CreateBooksTable

这将在 app/Database/Migrations/ 目录下创建新文件。

将 app/Database/Migrations/CreateAuthorsTable.php 修改为：

    <?php

    namespace App\Database\Migrations;

    use CodeIgniter\Database\Migration;

    class CreateAuthorsTable extends Migration
    {
        public function up()
        {
            $this->forge->addField([
                'id' => [
                    'type'           => 'INTEGER',
                    'unsigned'       => true,
                    'auto_increment' => true,
                ],
                'name' => [
                    'type'       => 'VARCHAR',
                    'constraint' => '255',
                    'null'       => false,
                ],
                'created_at' => [
                    'type' => 'DATETIME',
                    'null' => true,
                ],
                'updated_at' => [
                    'type' => 'DATETIME',
                    'null' => true,
                ],
            ]);

            $this->forge->addPrimaryKey('id');
            $this->forge->addUniqueKey('name');
            $this->forge->createTable('authors');
        }

        public function down()
        {
            $this->forge->dropTable('authors');
        }
    }

在本示例中，作者仅需姓名。已将姓名设为唯一键以防止重复。

现在，将 app/Database/Migrations/CreateBooksTable.php 修改为：

    <?php

    namespace App\Database\Migrations;

    use CodeIgniter\Database\Migration;

    class CreateBooksTable extends Migration
    {
        public function up()
        {
            $this->forge->addField([
                'id' => [
                    'type'           => 'INTEGER',
                    'unsigned'       => true,
                    'auto_increment' => true,
                ],
                'title' => [
                    'type'       => 'VARCHAR',
                    'constraint' => '255',
                    'null'       => false,
                ],
                'author_id' => [
                    'type'     => 'INTEGER',
                    'unsigned' => true,
                    'null'     => false,
                ],
                'year' => [
                    'type' => 'INTEGER',
                    'null' => true,
                ],
                'created_at' => [
                    'type' => 'DATETIME',
                    'null' => true,
                ],
                'updated_at' => [
                    'type' => 'DATETIME',
                    'null' => true,
                ],
            ]);

            $this->forge->addPrimaryKey('id');
            $this->forge->addForeignKey('author_id', 'authors', 'id');
            $this->forge->createTable('books');
        }

        public function down()
        {
            $this->forge->dropTable('books');
        }
    }

其中包含指向 `authors` 表的外键引用。由此可将每本书与作者关联，并统一管理作者姓名。

运行迁移：

    php spark migrate

此时，数据库已具备存储图书与作者记录所需的结构。

## 创建数据填充

数据填充用于在开发阶段加载示例数据，以便立即开展工作。此处将添加一些示例图书及其作者。

运行：

    php spark make:seeder BookSeeder

修改 app/Database/Seeds/BookSeeder.php 文件：

    <?php

    namespace App\Database\Seeds;

    use CodeIgniter\Database\Seeder;

    class BookSeeder extends Seeder
    {
        public function run()
        {
            // Define author data and insert
            $authorData = [
                ['name' => 'Frank Herbert'],
                ['name' => 'William Gibson'],
                ['name' => 'Ursula K. Le Guin'],
            ];

            $this->db->table('authors')->insertBatch($authorData);

            // Get all inserted authors, keyed by name for easy lookup
            $authors = $this->db->table('authors')
                ->get()
                ->getResultArray();

            $authorsByName = array_column($authors, 'id', 'name');

            // Define books with author references
            $books = [
                [
                    'title'     => 'Dune',
                    'author_id' => $authorsByName['Frank Herbert'],
                    'year'      => 1965,
                ],
                [
                    'title'     => 'Neuromancer',
                    'author_id' => $authorsByName['William Gibson'],
                    'year'      => 1984,
                ],
                [
                    'title'     => 'The Left Hand of Darkness',
                    'author_id' => $authorsByName['Ursula K. Le Guin'],
                    'year'      => 1969,
                ],
            ];

            $this->db->table('books')->insertBatch($books);
        }
    }

该填充器先向 `authors` 表插入作者信息并获取其 ID，随后使用这些 ID 向 `books` 表插入图书数据。

然后运行数据填充：

    php spark db:seed BookSeeder

此时应看到记录已插入的确认信息。

## 创建 Book 模型

模型为数据表提供了面向对象接口与流畅的查询 API，使数据库访问变得简单且易于复用。接下来为 `authors` 和 `books` 表创建模型。

生成模型：

    php spark make:model AuthorModel
    php spark make:model BookModel

这两个模型都将简单地继承 CodeIgniter 的基础模型类。

修改 app/Models/AuthorModel.php：

    <?php

    namespace App\Models;

    use CodeIgniter\Model;

    class AuthorModel extends Model
    {
        protected $table         = 'authors';
        protected $primaryKey    = 'id';
        protected $allowedFields = ['name'];
        protected $useTimestamps = true;
    }

修改 app/Models/BookModel.php：

    <?php

    namespace App\Models;

    use CodeIgniter\Model;

    class BookModel extends Model
    {
        protected $table         = 'books';
        protected $primaryKey    = 'id';
        protected $allowedFields = ['title', 'author_id', 'year'];
        protected $useTimestamps = true;
    }

这会告知 CodeIgniter 使用哪张表，以及哪些字段允许批量赋值。

下一节将使用这些新模型来构建 RESTful API 控制器。届时将构建 `/api/books` 接口，并了解 CodeIgniter 的 `Api\ResponseTrait` 如何简化 CRUD 操作。