Inheritance Yii中带有ActiveRecord的派生类

Inheritance Yii中带有ActiveRecord的派生类,inheritance,activerecord,yii,Inheritance,Activerecord,Yii,我正在从事以下数据库设计,并希望了解如何在Yii中最好地解决它 +---------------+----------------+---------------------+ | Table | Column name | Column Type | +---------------+----------------+---------------------+ | question | id | int

我正在从事以下数据库设计,并希望了解如何在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、文本、网格、真/假等。它们的公共数据可以在“问题”表中捕获,而细节可以在相应的表中捕获。在简单的编程概念中,很容易将此结构映射到“问题”类,该类是所有其他类(如“MCQ_问题”、“文本_问题”)的基类。此类问题的类型将来可能会增加,因此需要灵活性

通过上述方法,我可以将“question_id”用于与其他表的任何外键关系,而不是处理每个表,这将使代码更加复杂

从我阅读一些文章的理解来看,这种方法不适用于Yii:

有没有办法用Yii来做?如果没有,那么对于这个问题,有哪些变通方法可以使代码保持简单呢

问候,,
Kapil

您提到的链接不适用于您的问题。您只需创建派生类并重写
tableName()
-函数,以便从正确的表中加载它们

首先,在这种情况下,我不会对派生类型使用不同的ID,只使用相同的ID。一个问题ID对于所有人来说都更容易维护(并存储答案)。只需从派生表id字段中删除自动增量,并删除问题id。假设这样做:

MCQ_问题的简化示例:

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

   function tableName()
   {
      return 'mcq_question';
   }
}
现在只剩下两个问题: 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
中执行此操作

嗨,布莱兹,谢谢你的精彩解释。你的方法很合乎逻辑,我认为应该奏效。正如您所说的,使用“traits”还可以帮助我减少冗余代码。唯一让我稍感困扰的是,如果在“基类”中修改/添加了任何列,那么所有派生类也需要更新,但我想这是没有出路的。再次感谢您的指导!你好,卡皮尔,不客气。您可以通过自己创建一个新的问题实例,保存它,然后将其作为一个属性传递给派生类来解决更改的列问题。beforeSave只需复制$id即可。只是个主意。毕竟,这并不是说您将在代码中的大量位置上创建问题。