Ad 1 og 2:Din datamodel er fin. Brug af fremmednøgler er afgørende her. En ting mere, du skal passe på, er, at databasen skal sikre, at der er en TOPIC-post for hver POST. Dette gøres ved at indstille POST.topic_id NOT NULL attribut. Dette er tilstrækkelig sikkerhedsmekanisme på DB-siden, da det sikrer, at ingen POST efterlades uden TOPIC. Lige meget hvad du gør nu med din POST, er du forpligtet til at give et TOPIC.
Ad 3:En trigger med lagret procedure anbefales ikke her, da du har yderligere data i din TOPIC-tabel (IsSticky, IsLocked, osv.), som du måske ønsker at angive ved oprettelse af TOPIC-post. Hvis en sådan trigger også ville være anvendelig, ville databasedesignet være genstand for denormalisering.
Ad 4:På forretningslogiksiden kan du nu hjælpe dig selv ved at skrive en automatiseret mekanisme til at oprette TOPIC-posten, hver gang en ny POST-post oprettes uden specificeret topic_id. Jeg anbefaler at bruge noget ORM til dette eller drage fordel af de tilgængelige datamodeller i enhver MVC-ramme. Planen for sådanne modeller ville se sådan ud:
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;
}
// ...
}
Bemærk:Som en god MVC-praksis bør disse modeller være det eneste sted, hvor man kan operere direkte på tabelrækkerne. Ellers ender du med at få SQL-fejl (men datamodellen forbliver sammenhængende, så du behøver ikke bekymre dig om, at noget går i stykker).
Udnyt endelig dine modeller i controlleren lag:
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
}
}
}