在Yii中使用ActiveRecord派生类

时间:2014-05-19 06:22:58

标签: inheritance activerecord yii

我正在使用以下数据库设计,并希望了解我们如何在Yii中最好地解决它。

+---------------+----------------+---------------------+
|     Table     |  Column name   |     Column Type     |
+---------------+----------------+---------------------+
| question      | id             | int                 |
|               | description    | text                |
|               | type           | ENUM('mcq', 'text') |
|               |                |                     |
|               |                |                     |
| mcq_question  | id             | int                 |
|               | question_id    | int                 |
|               | option1        | text                |
|               | option2        | text                |
|               | option3        | text                |
|               | option4        | text                |
|               | correct_answer | option1             |
|               |                |                     |
|               |                |                     |
| text_question | id             | int                 |
|               | question_id    | int                 |
|               | answer         | text                |
+---------------+----------------+---------------------+

这个想法是有不同类型的问题,如mcq,text,grid,true / false等。他们的常见数据可以在"问题"表,而具体可以在相应的表中捕获。在简单的编程概念中,很容易将这个结构映射到问题" class,它是所有其他类的基类,如" MCQ_Question"," Text_Question"这些问题的类型将来可能会增加,因此需要灵活性。

通过上述方法,我可以使用" question_id"对于与其他表的任何外键关系,而不是使用每个单独的表,这将使代码更复杂。

从我通过阅读一些文章的理解,这种方法不适用于Yii: http://www.yiiframework.com/forum/index.php/topic/23405-cactiverecord-inheritance-and-dynamic-attributes/

Yii没办法做到吗?如果没有,那么这个问题的一些解决方法可能会使代码变得简单。

此致 卡皮尔

1 个答案:

答案 0 :(得分:1)

您引用的链接不适用于您的问题。您可以创建派生类并覆盖tableName() - 函数,以便它们加载出正确的表。

首先,在这种情况下,我不会为派生类型使用不同的ID,只需使用相同的ID。一个问题_所有人都更容易维护(并存储答案)。只需从派生表id字段中删除auto_increment,然后删除question_id即可。假设你这样做:

MCQ_Question的最小化示例:

class MCQ_Question extends Question
{
   public $description = '';
   public $type = 'mcq';

   function tableName()
   {
      return 'mcq_question';
   }
}

现在只剩下2个问题了: 1)基类中用于加载特定问题的泛型函数 2)如果保存派生类,它还应该更新/创建问题实例

1)这很简单,但您可能需要添加一个数组来转换"转换"枚举和类名之间:

class Question extends CActiveRecord
{
   public function findById($questionId)
   {
      $types = array('mcq' => 'MCQ_Question');
      $question = $this->findByPk($questionId);
      if ($question && array_key_exists($question->type, $types)
      {
         $class = $types[$question->type];
         return $class::model()->findByPk($question->id);
      }
   }
}

带链接的数组不是最优雅的解决方案,但它可以解决问题。您也可以将其添加为静态,以便更清楚它必须更新,等等。

2)您可以通过覆盖派生类中的beforeSave() - 函数来完成此任务。

  public function beforeSave()
  {
     if ($this->isNewRecord)
     {
        $question = new Question();
        $question->type = $this->type;
        $question->description = $this->description;
        $question->save();
        $this->id = $question->id;
     }
     // else check if perhaps the description was changed and update it
    ...

这显然不是你需要的所有代码,但是再次,你的工作不是我的工作;)

它应该让你开始。 beforeSave可能在各处几乎都是一样的,所以如果你的PHP足够新,你可以在trait中完成。