【问题标题】:Yii saving many MANY_MANY relationshipsYii 保存了许多 MANY_MANY 关系
【发布时间】:2013-02-05 14:40:00
【问题描述】:

信息

我在保存具有许多 MANY_MANY 关系的模型时遇到了一些问题。我有一个页面,您可以在其中添加产品属性以及产品属性级别。我现在要做的是在产品的更新页面上添加对此的支持。因此,当我进入更新页面时,我会看到所有产品属性,并且对于每个产品属性,都会有一个下拉列表,其中包含该特定产品属性的相关产品属性级别。

数据库

产品

  • 身份证

产品属性

  • 身份证

ProductAttributeLevel

  • 身份证
  • product_attribute_id ## FK

ProductProductAttributeLevel -- 这是数据透视表

  • product_id ## FK PK
  • product_attribute_level_id ## FK PK

活动记录

产品:

class Product extends S360ActiveRecord {

    public function behaviors() {
        return array('CAdvancedArBehavior' => array(
            'class' => 'application.extensions.CAdvancedArBehavior')
        );
    }

    public function rules() {
        return array(
            array('attributeLevels', 'safe'),
        );
    }

    public function relations() {
        return array(
            'attributeLevels' => array(self::MANY_MANY,
                'ProductAttributeLevel',
                'product_product_attribute_level(product_id,product_attribute_level_id)'
            ),
        );
    }

}

产品属性:

class ProductAttribute extends S360ActiveRecord {

    public function relations() {
        return array(
            'levels' => array(self::HAS_MANY, 'ProductAttributeLevel', 'product_attribute_id'),
        );
    }

}

产品属性级别:

class ProductAttributeLevel extends S360ActiveRecord {

    public function relations() {
        return array(
            'productAttribute' => array(self::BELONGS_TO, 'ProductAttribute', 'product_attribute_id'),
            'products' => array(self::MANY_MANY, 'Product', 'product_product_attribute_level(product_attribute_level_id,product_id)'),
        );
    }
}

ProductProductAttributeLevel:

class ProductProductAttributeLevel extends S360ActiveRecord {

    public function relations()
    {
        return array(
            'productAttributeLevel' => array(self::BELONGS_TO, 'ProductAttributeLevel', 'product_attribute_level_id'),
            'product' => array(self::BELONGS_TO, 'Product', 'product_id'),
        );
    }

}

我的 ProductController 更新产品的方法如下所示:

public function actionUpdate($id) {

    $model = $this->loadModel($id);
    $this->performAjaxValidation($model);

    if (isset($_POST['Product'])) {
        $model->attributes = $_POST['Product'];

        if ($model->save()) {
            $this->redirect(array('index'));
        }
    }

  $this->render('update', array('model' => $model));

}

我的表单视图中的相关部分:

<?php 
    $form=$this->beginWidget('S360ActiveForm', array(
        'id' => 'product-form',
        'enableAjaxValidation' => true,
    )); 
?>

<?php $attributes = ProductAttribute::model()->findAllByAttributes(array('survey_id' => $model->parent_id)); if ($attributes): ?>
<div class="span6">
    <?php foreach ($attributes as $attribute) {
        echo $form->dropDownListRow($model, 'attributeLevels',
            CMap::mergeArray(
                array('0' => Yii::t('backend','No attribute level')),
                CHtml::listData($attribute->levels, 'id', 'label')
            ),
            array('class' => 'span5')
        );
    }?>
</div>
<?php endif; ?>

问题

我得到了这个 CDBException:

CDbCommand 未能执行 SQL 语句:SQLSTATE[23000]:完整性约束违规:1452 无法添加或更新子行:外键约束失败(product_product_attribute_level, CONSTRAINT product_product_attribute_level_ibfk_2 FOREIGN KEY (product_attribute_level_id)参考product_attribute_level (id) DELE)。执行的SQL语句是:insert into product_product_attribute_level(product_id, product_attribute_level_id) values ('5', '0')

问题是,虽然 ID 为“0”的 product_attribute_level 不存在,但 ID 从“1”开始。我将如何更改它以插入正确的 ID 号?

我想要的示例

假设我有 2 个产品属性;属性 1 和属性 2。 Attribute1 具有产品属性级别 Attribute1_Level1 和 Attribute1_Level2。 Attribute2有产品属性级别Attribute2_Level1、Attribute2_Level2和Attribute2_Level3。

当我进入我的产品编辑/更新页面时,我想看到这个: Attributes http://img201.imageshack.us/img201/9252/screenshot20130207at103.png Attribute2 Dropdown http://img405.imageshack.us/img405/9252/screenshot20130207at103.png

产品属于调查。产品属性也属于调查,因此获取产品可以拥有的所有产品属性很容易:

$attributes = ProductAttribute::model()->findAllByAttributes(array('survey_id' => $product->survey_id));

之后我需要获取属于每个属性的所有产品属性级别,这也很容易:

foreach ($attributes as $attribute) {

    echo $form->dropDownList($attribute, 'label',
        CHtml::listData($attribute->levels, 'id', 'label'),
        $htmlOptions
    );

}

问题是如何将它与产品连接并根据我从不同下拉列表中选择的内容相应地更新其“$product->attributeLevels”关系。 $product->attributeLevels 应该是 ProductAttributeLevel 的列表,并且应该通过表“product_product_attribute_level”存储。

【问题讨论】:

    标签: php forms activerecord yii many-to-many


    【解决方案1】:

    当然,您是从下拉列表中选择的?因为如果不是,您确实在发送“0”

    <?php foreach ($attributes as $attribute) {
            echo $form->dropDownListRow($model, 'attributeLevels',
                CMap::mergeArray(
                    // **HERE**
                    array('0' => Yii::t('backend','No attribute level')),
                    CHtml::listData($attribute->levels, 'id', 'label')
                ),
                array('class' => 'span5')
            );
        }?>
    

    如果您想要将不代表记录的内容作为第一个选项,则有两个选项,使用来自文档的promptdropDownListempty 属性:

    • prompt:字符串,指定显示为第一个列表选项的提示文本。它的值为空。注意,提示文本不会 HTML 编码。

    • empty:字符串,指定空选对应的文本。它的值为空。 'empty' 选项也可以是一个数组 值-标签对。每对将用于呈现一个列表选项 一开始。请注意,文本标签不会被 HTML 编码。

    现在,您需要一个属性级别的下拉列表,但您希望将它们保存在产品上。因此迭代属性,获取其级别,但将它们保存在产品上,如下所示:

    <?php foreach ($attributes as $i => $attribute) {
            echo $form->dropDownListRow($product, "[$i]attributeLevels",
                CHtml::listData($attribute->levels, 'id', 'label'),
                array('class' => 'span5', 'prompt' => 'No attribute level')
            );
        }?>
    

    现在要将它们保存在您的产品上,请在您的控制器中执行此操作:

    public function actionUpdate($id) {
    
      $model = $this->loadModel($id);
      $this->performAjaxValidation($model);
    
      if (isset($_POST['Product'])) {
        $attrLevels = $_POST['Product']['attributeLevels'];
        unset($_POST['Product']['attributeLevels']);
        $model->attributes = $_POST['Product'];
    
        if( $model->save() ) {
          $valid=true;
          foreach($attrLevels as $i=>$attrLevel)
          {
            $pivot = new ProductProductAttributeLevel;
            $pivot->product_id = $model->id;
            $pivot->product_attribute_level_id = $attrLevel;
            $valid=$item->save() && $valid;
          }
          if($valid){
            $this->redirect(array('index'));
          }
        }
      }
    
      $this->render('update', array('model' => $model));
    
    }
    

    免责声明:复制/粘贴可能不起作用,但你明白了

    【讨论】:

    • 起初我就像“他在说什么”,因为我当然从下拉列表中选择了一些东西。但不是全部(我目前有 3 个测试属性,并且只在其中一个属性上选择了一个级别)。产品属性不是强制性的,因此用户应该能够选择“0”(例如“无属性级别”)。我将如何用表格解决这个问题?基本上我想要做的只是运行属性更新,如果它不是 0。
    • 感谢您的回复,不幸的是它不起作用,因为我需要“分组”属于产品 PER 属性的属性级别。例如,如果属性“品牌”具有级别“芬达”和“百事可乐”,属性“尺寸”具有“33cl”和“50cl”,我想要 2 个下拉列表,1 个用于“品牌”,1 个用于“尺寸”,每个将归属级别显示为选项的列表。最重要的是,我需要能够从列表中选择一个选项(或将其留空)并让它更新/插入/删除连接“产品”与“产品属性级别”的数据透视表中的连接。
    • 要么你没有把自己说清楚,要么你没有尝试示例中的代码,也没有阅读文档。
    • 您很可能不理解我的意思,因为我既尝试了您的代码又阅读了文档。如果我使用您的代码,所有属性的下拉列表(包含它们的级别)都会获得相同的 HTML 元素 ID。我通过手动处理表单数据通过一个丑陋的黑客解决了它,它现在必须做。不过感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多