広告 1 と 2: データ モデルは問題ありません。ここでは、外部キーを使用することが重要です。もう 1 つ気を付けなければならないことは、データベースが POST ごとに TOPIC レコードがあることを確認する必要があるということです。これは、POST.topic_id NOT NULL属性を設定することによって行われます。これは DB 側で十分な安全メカニズムであり、TOPIC なしで POST が残されることはありません。POST で何をするにしても、TOPIC を提供する義務があります。
広告 3: TOPIC テーブルに追加のデータ (IsSticky、IsLocked など) があるため、ここではストアド プロシージャを使用したトリガーはお勧めしません。これは、TOPIC レコードの作成時に提供する必要がある場合があります。また、そのようなトリガーが適用される場合、データベース設計は非正規化の対象になります。
広告 4: ビジネス ロジック側では、topic_id を指定せずに新しい POST レコードが作成されるたびに TOPIC レコードを作成する自動メカニズムを作成することで、自分自身を助けることができます。これには ORM を使用するか、任意の MVC フレームワークで利用可能なデータ モデルを利用することをお勧めします。このようなモデルの設計図は次のようになります。
abstract class AModel // this class should be provided by ORM or framework
{
/**
* @var PDO
*/
protected $_db_driver;
public function getLastInsertId()
{
$stmt = $this->_db_driver->prepare('SELECT LAST_INSERT_ID() AS id');
$stmt->execute();
return $stmt->fetch(PDO::FETCH_OBJ)->id;
}
public abstract function getFieldList();
}
class ForumTopicModel extends AModel
{
public function insert(array $data)
{
$sql = 'INSERT INTO topic VALUES (:id, :forum_id, :person_id, :is_locked, ...)';
$stmt = $this->_db_driver->prepare($sql);
return $stmt->execute($data);
}
public function getFieldList()
{
return array('id', 'forum_id', 'person_id', 'is_locked', /*...*/);
}
// ...
}
class ForumPostModel extends AModel
{
public function insert(array $data)
{
$sql = 'INSERT INTO post VALUES (:id, :topic_id, :person_id, :subject, ...)';
$stmt = $this->_db_driver->prepare($sql);
return $stmt->execute($data);
}
public function getFieldList()
{
return array('id', 'topic_id', 'person_id', 'subject', /*...*/);
}
public function insertInitialTopicPost(array $form_data)
{
$this->_db_driver->beginTransaction();
$result = true;
if ( empty($form_data['topic_id']) ) {
// no topic_id provided, so create new one:
$topic = new ForumTopicModel();
$topic_data = array_intersect_key(
$form_data, array_flip($topic->getFieldList())
);
$result = $topic->insert($topic_data);
$form_data['topic_id'] = $topic->getLastInsertId();
}
if ( $result ) {
$forum_post_data = array_intersect_key(
$form_data, array_flip($this->getFieldList())
);
$result = $this->insert($forum_post_data);
}
if ( $result ) {
$this->_db_driver->commit();
}
else {
$this->_db_driver->rollBack();
}
return $result;
}
// ...
}
注: 優れた MVC プラクティスとして、これらのモデルは、テーブルの行を直接操作する唯一の場所である必要があります。そうしないと、SQL エラーが発生することになります (ただし、データ モデルは一貫したままであるため、何かが壊れる心配はありません)。
最後に、コントローラーレイヤーでモデルを利用します。
class ForumPostController extends AController
{
public function createInitialTopicPostAction()
{
$form_data = $this->getRequest()->getPost(); /* wrapper for getting
the $_POST array */
// (...) validate and filter $form_data here
$forumPost = new ForumPostModel();
$result = $forumPost->insertInitialTopicPost($form_data);
if ( $result ) {
// display success message
}
else {
// display failure message
}
}
}