これは「Build Your Startup with PHP」シリーズの第 2 部であり、私のスタートアップ Meeting Planner のコンセプトから立ち上げまでの開発プロセス全体を説明します。このセクションでは、機能的な目標と要件の概要を説明し、その後、初期のデータベース設計と移行について説明します。
多くの機能がすぐに有効になるわけではないため、このエピソードは少し無味乾燥に見えるかもしれませんが、今後のすべての基礎を築くことが重要です。 Active Record データベースの移行を設計したことがなく、Yii のスキャフォールディング アプリケーションである Gii を使用したことがない場合は、おそらく多くのことを学ぶことになるでしょう。
Meeting Planner データベースの詳細を説明する前に、Meeting Planner が提供する高度な機能について説明します。
上記は機能の完全なリストではありませんが、データベース スキーマがサポートする必要があるものを明確に理解できます。
Meeting Planner の開発環境のセットアップを開始するには、私のガイド「Yii2 によるプログラミング: 入門」を使用し、手順に従って Composer をインストールします。
すべてのミーティング プランナーのチュートリアルは、無料のオープンソース Github リポジトリにタグ付けされます。したがって、チュートリアル シリーズのこの部分では、ここから基本的なミーティング プランナー フレームワークをインストールできます。
Meeting Planner の場合、Yii2 の高度なアプリケーション テンプレートをインストールしました。これは、フロントエンド (エンドユーザー) とバックエンド (管理) アクセス用のさまざまなアプリケーションなど、複雑なアプリケーション向けにもう少し強力なアーキテクチャを提供します。
コードの使用を開始するには、リポジトリのクローンを作成し、チュートリアルのこの部分のタグ付きバージョンを表示し、初期化を実行して、Composer にファイルを更新するように依頼する必要があります。 リーリー
私はローカル開発環境で MAMP を使用しています。したがって、優先フロントエンド ローカルホスト URL を~/Sites/mp/frontend/web:
に指定する必要があります。
リーリー
次に、MySQL にデータベースを作成し、
\environment\dev\common\main-local.php:
に配置する必要があります。
リーリー
データベース アーキテクチャの設計
コード構築の初期段階にあるため、データベースのレイアウトを徹底的に行うよう努めていますが、作業が進むにつれて設計の変更や進化が必要になる場合があります。
Yii のアクティブ レコード移行により、ローカル環境や運用環境などのさまざまな環境にデータベースをプログラムで作成し、時間の経過とともに進化させることが比較的簡単になります。 Yii Active Record について詳しくは、こちらをご覧ください。
第一次迁移构建了用户表,它包含在 Yii 的高级应用程序模板中 - 请参阅 /mp/console/migrations/m130524_201442_init.php
。
此迁移告诉 Yii 创建一个新的 SQL 表,其中包含用户表所需的字段,如下所示:
<?php use yii\db\Schema; use yii\db\Migration; class m130524_201442_init extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%user}}', [ 'id' => Schema::TYPE_BIGPK, 'friendly_name' => Schema::TYPE_STRING . ' NOT NULL', 'username' => Schema::TYPE_STRING . ' NOT NULL', 'auth_key' => Schema::TYPE_STRING . '(32) NOT NULL', 'password_hash' => Schema::TYPE_STRING . ' NOT NULL', 'password_reset_token' => Schema::TYPE_STRING, 'email' => Schema::TYPE_STRING . ' NOT NULL', 'role' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); } public function down() { $this->dropTable('{{%user}}'); } }
您可以运行第一次迁移,如下所示:
cd ~/Sites/mp ./yii migrate/up 1
您应该看到类似这样的内容:
Jeffs-Mac-mini:mp Jeff$ ./yii migrate/up 1 Yii Migration Tool (based on Yii v2.0.0) Creating migration history table "migration"...done. Total 1 out of 15 new migrations to be applied: m130524_201442_init Apply the above migration? (yes|no) [no]:yes *** applying m130524_201442_init > create table {{%user}} ... done (time: 0.068s) *** applied m130524_201442_init (time: 0.071s) Migrated up successfully.
Yii 为注册、登录、注销等常见操作提供内置 Web 支持。此功能和此表将为我们的初始身份验证功能提供支持基础。我们稍后可能会以各种方式对其进行扩展,例如支持 Twitter 或 Google OAuth 进行身份验证。
通过 Active Record 迁移,您还可以向后迁移。这在开发过程中特别有帮助。例如向下迁移会删除User表:
Jeffs-Mac-mini:mp Jeff$ ./yii migrate/down 1 Yii Migration Tool (based on Yii v2.0.0) Total 1 migration to be reverted: m130524_201442_init Revert the above migration? (yes|no) [no]:yes *** reverting m130524_201442_init > drop table {{%user}} ... done (time: 0.001s) *** reverted m130524_201442_init (time: 0.070s) Migrated down successfully.
如果您需要调整表格设计,可以先进行调整,然后再迁移回来。
会议架构以及与会议相关的所有表格对于我们应用程序的功能非常重要。
这是会议的基本架构:
$this->createTable('{{%meeting}}', [ 'id' => Schema::TYPE_PK, 'owner_id' => Schema::TYPE_BIGINT.' NOT NULL', 'meeting_type' => Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0', 'message' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions);
会议的基础由所有者、会议指示符类型、邀请消息、状态字段以及标准创建和更新时间字段组成。
通过 Active Record,Yii 可以帮助我们自动创建表之间的关系。在会议表中,我们将创建一个关系,即每个会议在用户表中都有一个所有者。我们在迁移中通过创建一个外键将会议 -> Owner_ID 连接到用户 -> ID 来完成此操作。
$this->addForeignKey('fk_meeting_owner', '{{%meeting}}', 'owner_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
我们还需要在向下迁移中删除外键:
public function down() { $this->dropForeignKey('fk_meeting_owner', '{{%meeting}}'); $this->dropTable('{{%meeting}}'); }
在我们进入 Yii 的自动化脚手架系统 Gii 之前,请耐心等待我概述更多架构。
您可以在 /mp/console/migrations 文件夹
中看到所有迁移:
我们将在下面回顾其中的大部分内容。
地点也是 Meeting Planner 中的一个重要组成部分,因为它们是每个人都会见面的地点。它们按地理位置编入索引,并在 Google 地方信息中引用。
这是地点的架构:
$tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%place}}', [ 'id' => Schema::TYPE_PK, 'name' => Schema::TYPE_STRING.' NOT NULL', 'place_type' => Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'google_place_id' => Schema::TYPE_STRING.' NOT NULL', // e.g. google places id 'created_by' => Schema::TYPE_BIGINT.' NOT NULL', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_place_created_by', '{{%place}}', 'created_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
地点由 name
、place_type
、status
、created_at
和 更新_at
。但它们还包含 google_place_id
将它们与 Google Places 目录关联起来。
请注意,没有任何与此表中的地点关联的地理位置。这是因为 MySQL InnoDB 引擎不支持空间索引。因此,我使用 MyISAM 表创建了一个辅助表来存储地点的地理位置坐标。这是 Place_GPS 表:
class m141025_213611_create_place_gps_table extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=MyISAM'; } $this->createTable('{{%place_gps}}', [ 'id' => Schema::TYPE_PK, 'place_id' => Schema::TYPE_INTEGER.' NOT NULL', 'gps'=>'POINT NOT NULL', ], $tableOptions); $this->execute('create spatial index place_gps_gps on '.'{{%place_gps}}(gps);'); $this->addForeignKey('fk_place_gps','{{%place_gps}}' , 'place_id', '{{%place}}', 'id', 'CASCADE', 'CASCADE'); }
请注意,它通过 place_id
与 Place 表相关。地点的位置只是 GPS 坐标,或 MySQL POINT。
会议参与者存储在名为“参与者”的连接表中。他们通过 meeting_id
加入会议表,并通过 participant_id
加入用户表。如果我们希望每次会议有多个会议参与者,此表将在将来实现这一点。
class m141025_215701_create_participant_table extends Migration { public function up() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable('{{%participant}}', [ 'id' => Schema::TYPE_PK, 'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', 'participant_id' => Schema::TYPE_BIGINT.' NOT NULL', 'invited_by' => Schema::TYPE_BIGINT.' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_participant_meeting', '{{%participant}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_participant_participant', '{{%participant}}', 'participant_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_participant_invited_by', '{{%participant}}', 'invited_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE'); }
还有其他几个表格可以帮助定义我们的会议计划选项。
此表包含按开始时间(时间戳)列出的所有建议会议时间(和日期)。 Suggested_by
显示谁建议的时间。而status
决定是否选择会议时间。
$this->createTable('{{%meeting_time}}', [ 'id' => Schema::TYPE_PK, 'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', 'start' => Schema::TYPE_INTEGER.' NOT NULL', 'suggested_by' => Schema::TYPE_BIGINT.' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_meeting_time_meeting', '{{%meeting_time}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_participant_suggested_by', '{{%meeting_time}}', 'suggested_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
此表显示了为会议建议的地点:
$this->createTable('{{%meeting_place}}', [ 'id' => Schema::TYPE_PK, 'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', 'place_id' => Schema::TYPE_INTEGER.' NOT NULL', 'suggested_by' => Schema::TYPE_BIGINT.' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_meeting_place_meeting', '{{%meeting_place}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_meeting_place_place', '{{%meeting_place}}', 'place_id', '{{%place}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_meeting_suggested_by', '{{%meeting_place}}', 'suggested_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
此表记录特定会议的所有添加和更改的历史记录。会议安排期间采取的每个操作都会被记录下来,以提供与会议相关的事件的时间历史记录。它将帮助用户查看一段时间内会议的所有更改的记录,并且还可能帮助我们进行开发调试。
$this->createTable('{{%meeting_log}}', [ 'id' => Schema::TYPE_PK, 'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', 'action' => Schema::TYPE_INTEGER.' NOT NULL', 'actor_id' => Schema::TYPE_BIGINT.' NOT NULL', 'item_id' => Schema::TYPE_INTEGER.' NOT NULL', 'extra_id' => Schema::TYPE_INTEGER.' NOT NULL', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_meeting_log_meeting', '{{%meeting_log}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_meeting_log_actor', '{{%meeting_log}}', 'actor_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
用户在对会议进行更改时可以来回发送简短的注释。该表记录了这些注释。
$this->createTable('{{%meeting_note}}', [ 'id' => Schema::TYPE_PK, 'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', 'posted_by' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0', 'note' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_meeting_note_meeting', '{{%meeting_note}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_meeting_note_posted_by', '{{%meeting_note}}', 'posted_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
有几个表可以扩展用户定义。
这是一个索引表,列出了每个用户的好友。它还跟踪他们是否是最喜欢的朋友以及他们举行的会议数量。这可能有助于简化调度体验,例如首先显示最喜欢的或经常的朋友。
$this->createTable('{{%friend}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_BIGINT.' NOT NULL', 'friend_id' => Schema::TYPE_BIGINT.' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'number_meetings' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0', 'is_favorite' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_friend_user_id', '{{%friend}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_friend_friend_id', '{{%friend}}', 'friend_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
这是用户喜欢见面或过去见过的地点的索引表。我们将在此处跟踪该用户最喜欢的地点和举行的会议数量。 is_special
字段将指示某个地点是用户自己的家、办公室或会议地点。
$this->createTable('{{%user_place}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_BIGINT.' NOT NULL', 'place_id' => Schema::TYPE_INTEGER.' NOT NULL', 'is_favorite' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'number_meetings' => Schema::TYPE_INTEGER . ' NOT NULL DEFAULT 0', 'is_special' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'note' => Schema::TYPE_STRING . ' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_user_place_user', '{{%user_place}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE'); $this->addForeignKey('fk_user_place_place', '{{%user_place}}', 'place_id', '{{%place}}', 'id', 'CASCADE', 'CASCADE');
此表提供特定用户的联系信息,例如电话号码、Skype 地址以及与在这些地方联系用户相关的任何注释。
$this->createTable('{{%user_contact}}', [ 'id' => Schema::TYPE_PK, 'user_id' => Schema::TYPE_BIGINT.' NOT NULL', 'contact_type' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'info' => Schema::TYPE_STRING . ' NOT NULL', 'details' => Schema::TYPE_TEXT . ' NOT NULL', 'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', 'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', 'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', ], $tableOptions); $this->addForeignKey('fk_user_contact_user', '{{%user_contact}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
为了简单起见,我现在将跳过会议模板架构。而且我还没有设计与收入相关的功能。这样做的主要原因是,我目前有很多工作要做,以运行核心功能集并完成本教程系列的前几集。然而,这是一个值得教育的时刻。这是一个很好的例子,说明企业家的资源有限,专注于核心功能,但没有“意识到”创收也是一个核心功能。因为我相信我最初可以在没有收入的情况下启动 Meeting Planner,所以这是我目前能够做出的妥协。
既然您对我们的数据库架构和 Active Record 迁移有了更多的背景知识,那么让我们运行其余部分:
cd ~/Sites/mp ./yii migrate/up all
您应该看到类似这样的内容:
Yii Migration Tool (based on Yii v2.0.0) Total 14 new migrations to be applied: m141025_212656_create_meeting_table m141025_213610_create_place_table m141025_213611_create_place_gps_table m141025_215701_create_participant_table m141025_215833_create_meeting_time_table m141025_220016_create_meeting_place_table m141025_220133_create_meeting_log_table m141025_220524_create_friend_table m141025_220923_create_user_place_table m141025_221627_create_meeting_note_table m141025_221902_create_user_contact_table m141025_222213_create_template_table m141025_222431_create_template_time_table m141025_222531_create_template_place_table Apply the above migrations? (yes|no) [no]:yes *** applying m141025_212656_create_meeting_table > create table {{%meeting}} ... done (time: 0.124s) > add foreign key fk_meeting_owner: {{%meeting}} (owner_id) references {{%user}} (id) ... done (time: 0.307s) *** applied m141025_212656_create_meeting_table (time: 0.434s) *** applying m141025_213610_create_place_table > create table {{%place}} ... done (time: 0.091s) > add foreign key fk_place_created_by: {{%place}} (created_by) references {{%user}} (id) ... done (time: 0.114s) *** applied m141025_213610_create_place_table (time: 0.206s) *** applying m141025_213611_create_place_gps_table > create table {{%place_gps}} ... done (time: 0.120s) > execute SQL: create spatial index place_gps_gps on {{%place_gps}}(gps); ... done (time: 0.114s) > add foreign key fk_place_gps: {{%place_gps}} (place_id) references {{%place}} (id) ... done (time: 0.112s) *** applied m141025_213611_create_place_gps_table (time: 0.347s) *** applying m141025_215701_create_participant_table > create table {{%participant}} ... done (time: 0.100s) > add foreign key fk_participant_meeting: {{%participant}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.138s) > add foreign key fk_participant_participant: {{%participant}} (participant_id) references {{%user}} (id) ... done (time: 0.112s) > add foreign key fk_participant_invited_by: {{%participant}} (invited_by) references {{%user}} (id) ... done (time: 0.149s) *** applied m141025_215701_create_participant_table (time: 0.500s) *** applying m141025_215833_create_meeting_time_table > create table {{%meeting_time}} ... done (time: 0.142s) > add foreign key fk_meeting_time_meeting: {{%meeting_time}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.148s) > add foreign key fk_participant_suggested_by: {{%meeting_time}} (suggested_by) references {{%user}} (id) ... done (time: 0.122s) *** applied m141025_215833_create_meeting_time_table (time: 0.413s) *** applying m141025_220016_create_meeting_place_table > create table {{%meeting_place}} ... done (time: 0.120s) > add foreign key fk_meeting_place_meeting: {{%meeting_place}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.125s) > add foreign key fk_meeting_place_place: {{%meeting_place}} (place_id) references {{%place}} (id) ... done (time: 0.135s) > add foreign key fk_meeting_suggested_by: {{%meeting_place}} (suggested_by) references {{%user}} (id) ... done (time: 0.137s) *** applied m141025_220016_create_meeting_place_table (time: 0.518s) *** applying m141025_220133_create_meeting_log_table > create table {{%meeting_log}} ... done (time: 0.109s) > add foreign key fk_meeting_log_meeting: {{%meeting_log}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.126s) > add foreign key fk_meeting_log_actor: {{%meeting_log}} (actor_id) references {{%user}} (id) ... done (time: 0.113s) *** applied m141025_220133_create_meeting_log_table (time: 0.348s) *** applying m141025_220524_create_friend_table > create table {{%friend}} ... done (time: 0.109s) > add foreign key fk_friend_user_id: {{%friend}} (user_id) references {{%user}} (id) ... done (time: 0.125s) > add foreign key fk_friend_friend_id: {{%friend}} (friend_id) references {{%user}} (id) ... done (time: 0.102s) *** applied m141025_220524_create_friend_table (time: 0.337s) *** applying m141025_220923_create_user_place_table > create table {{%user_place}} ... done (time: 0.109s) > add foreign key fk_user_place_user: {{%user_place}} (user_id) references {{%user}} (id) ... done (time: 0.137s) > add foreign key fk_user_place_place: {{%user_place}} (place_id) references {{%place}} (id) ... done (time: 0.114s) *** applied m141025_220923_create_user_place_table (time: 0.360s) *** applying m141025_221627_create_meeting_note_table > create table {{%meeting_note}} ... done (time: 0.109s) > add foreign key fk_meeting_note_meeting: {{%meeting_note}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.125s) > add foreign key fk_meeting_note_posted_by: {{%meeting_note}} (posted_by) references {{%user}} (id) ... done (time: 0.101s) *** applied m141025_221627_create_meeting_note_table (time: 0.337s) *** applying m141025_221902_create_user_contact_table > create table {{%user_contact}} ... done (time: 0.098s) > add foreign key fk_user_contact_user: {{%user_contact}} (user_id) references {{%user}} (id) ... done (time: 0.125s) *** applied m141025_221902_create_user_contact_table (time: 0.225s) *** applying m141025_222213_create_template_table > create table {{%template}} ... done (time: 0.108s) > add foreign key fk_template_owner: {{%template}} (owner_id) references {{%user}} (id) ... done (time: 0.171s) *** applied m141025_222213_create_template_table (time: 0.281s) *** applying m141025_222431_create_template_time_table > create table {{%template_time}} ... done (time: 0.111s) > add foreign key fk_template_time_template: {{%template_time}} (template_id) references {{%template}} (id) ... done (time: 0.114s) *** applied m141025_222431_create_template_time_table (time: 0.226s) *** applying m141025_222531_create_template_place_table > create table {{%template_place}} ... done (time: 0.099s) > add foreign key fk_template_place_template: {{%template_place}} (template_id) references {{%template}} (id) ... done (time: 0.103s) > add foreign key fk_template_place_place: {{%template_place}} (place_id) references {{%place}} (id) ... done (time: 0.101s) *** applied m141025_222531_create_template_place_table (time: 0.304s) Migrated up successfully.
同样,当我们在生产中安装 Meeting Planner 时,我们也将使用迁移来构建初始数据库。无需导出和导入 SQL 文件,这些文件可能会因我们跨环境使用的版本不同而损坏。
在我们继续之前,您需要将自己注册为管理用户。单击工具栏中的注册链接,只需注册该应用程序即可。
如果成功,当您返回主页时,您将看到工具栏指示您的登录状态.
这些表单和应用逻辑都包含在 Yii 的高级应用模板中。
现在我们可以构建脚手架来支持常见的创建、读取、更新和删除操作 (CRUD) 的模型视图控制器代码。
我们将使用 Gii(Yii 令人惊叹的自动代码生成器)来构建许多基本框架代码。这个名字可能很愚蠢,但它非常强大并且是 Yii 开发的核心。我们将从会议和地点开始。
将浏览器指向 http://localhost:8888/mp/gii。你应该看到这个:
使用 Gii 构建时,通常从每个表的模型生成器开始。在使用模型生成器之前,您必须先运行迁移以在数据库中创建表,就像我们上面所做的那样。 Gii 使用 SQL 表定义为您的模型生成代码。
让我们使用模型生成器为会议桌生成模型代码。代码已在您的 Github 存储库中生成,但您可以随意再次运行这些练习。 Gii 将为您预览并可选择覆盖代码。
按如下所示为会议模型填写模型生成器:
然后,生成 Place 模型:
Gii 非常令人惊奇——基于我们的表定义,它生成了大量的逻辑。
在 /mp/frontend/models/Meeting.php
模型中,您将看到自动生成的属性标签:
public function attributeLabels() { return [ 'id' => 'ID', 'owner_id' => 'Owner ID', 'meeting_type' => 'Meeting Type', 'message' => 'Message', 'status' => 'Status', 'created_at' => 'Created At', 'updated_at' => 'Updated At', ]; }
它为表单生成字段验证规则:
public function rules() { return [ [['owner_id', 'message', 'created_at', 'updated_at'], 'required'], [['owner_id', 'meeting_type', 'status', 'created_at', 'updated_at'], 'integer'], [['message'], 'string'] ]; }
它会生成数据库关系 - 以下是一些示例:
/* @property User $owner * @property MeetingLog[] $meetingLogs * @property MeetingNote[] $meetingNotes * @property MeetingPlace[] $meetingPlaces * @property MeetingTime[] $meetingTimes * @property Participant[] $participants */ /** * @return \yii\db\ActiveQuery */ public function getMeetingLogs() { return $this->hasMany(MeetingLog::className(), ['meeting_id' => 'id']); } /** * @return \yii\db\ActiveQuery */ public function getMeetingNotes() { return $this->hasMany(MeetingNote::className(), ['meeting_id' => 'id']); } /** * @return \yii\db\ActiveQuery */ public function getMeetingPlaces() { return $this->hasMany(MeetingPlace::className(), ['meeting_id' => 'id']); }
现在,我们可以使用 CRUD 生成器来构建用于创建、读取、更新和删除操作的代码。
访问 CRUD 生成器并为会议创建它。请注意,前端是用户将看到的 Yii 应用程序。
当您点击预览时,您应该会看到如下内容:
当您点击生成时,您应该会看到以下结果:
接下来,对地点重复上述过程。
これで、Web サイト上のセッションと場所を実際に参照して、実際に生成されたコードを確認できるようになりました。ブラウザで http://localhost:8888/mp/meeting を参照します。次のようになります:
アカウントにサインアップした場合は、ミーティングを作成できるはずです。 Gii は、コードが管理する必要があるフィールドとユーザーが提供するフィールドの違いを知らないことに注意してください。次のチュートリアルでこれをクリーンアップします。現在、owner_id
(1 を使用 - これが最初にログインしたユーザーです)、meeting_type
、status
、created_at# に整数を入力する必要があります。 # # および
updated_at:
p>
Gii と Yii の機能を組み合わせることで、他の方法よりも速く Web アプリケーションを構築できます。驚くべきことに、データベース テーブル構造と移行コードの一部だけで、Bootstrap でレスポンシブに構築された動作するコントローラーとフォームを簡単に使用できます。###次は何ですか?
データベースと Gii のウォークスルーに興味を持っていただければ幸いです。このシリーズの次の記事では、場所を中心としたフィーチャの構築に焦点を当てます。 Google プレイス、Google マップ、HTML5 地理位置情報を使用して、Meeting Planner に必要な機能を構築する方法を説明します。これらのトピックについて有利にスタートしたい場合は、「HTML5 地理位置情報で Zillow Neighborhood Maps を使用する方法」というチュートリアルを書きました。
以下にお気軽に質問やコメントを追加してください。私も通常はディスカッションに参加します。 Twitter @reifman で私にご連絡いただくか、直接メールでご連絡いただくこともできます。
###関連リンク###Yii2 によるプログラミング: はじめに
以上がPHP を使用してスタートアップのデータベースを設計し、機能要件の概要を説明するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。