Anúncio 1 e 2:seu modelo de dados está bom. Usar chaves estrangeiras é crucial aqui. Mais uma coisa que você precisa cuidar é que o banco de dados deve garantir que haja um registro TOPIC para cada POST. Isso é feito definindo POST.topic_id NOT NULL atributo. Este é um mecanismo de segurança suficiente no lado do banco de dados, pois garante que nenhum POST fique sem TOPIC. Não importa o que você faça agora com seu POST, você é obrigado a fornecer um TÓPICO.
Anúncio 3:Um gatilho com procedimento armazenado não é recomendado aqui, pois você tem dados adicionais em sua tabela TOPIC (IsSticky, IsLocked etc.), que você pode fornecer na criação do registro TOPIC. Além disso, se tal gatilho fosse aplicável, o design do banco de dados estaria sujeito à desnormalização.
Anúncio 4:No lado da lógica de negócios, agora você pode se ajudar escrevendo um mecanismo automatizado para criar o registro TOPIC toda vez que um novo registro POST for criado sem o topic_id especificado. Recomendo usar algum ORM para isso ou aproveitar os modelos de dados disponíveis em qualquer framework MVC. O modelo para esses modelos ficaria assim:
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;
}
// ...
}
Nota:como uma boa prática de MVC, esses modelos devem ser o único local para operar diretamente nas linhas da tabela. Caso contrário, você acabará recebendo erros de SQL (mas o modelo de dados permanecerá coerente, então você não precisa se preocupar com a quebra de algo).
Por fim, aproveite seus modelos no controlador camada:
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
}
}
}