基于关系的 CGridView 列显示不正确的数据

CGridView column based on relation displays incorrect data

我目前正在开发一个 Web 应用程序(用 PHP 编写,基于 Yii),除其他外,它可以让您连接产品的不同价格值。每个产品可以有多个价格,但系统的构建方式使我可以轻松(正确)确定价格字段的类型 - 因此虽然每个产品可以有多个价格字段,但每种类型只能有一个价格字段。

我卡住的部分是当我必须在列表中显示这些字段的存储值,并按它们对列表视图进行排序时 - 我可以正确显示它们并按一列对它们进行排序,但一旦我尝试使列表可以按所有列排序(当然不是同时),行开始显示错误的值。

相关代码如下:

在模型中,在关系处:

'productPriceConnects' => array(Product::HAS_MANY, 'ProductPriceConnect', 'product_id'),
'price1' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
    'joinType' => 'LEFT JOIN',
    'on' => 'priceBeszerzesi.product_price_field_id=:product_price_field_id AND priceBeszerzesi.active = 1',
    'params' => array(':product_price_field_id' =>ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 6))->id),
    'alias' => 'priceBeszerzesi',
),
'price2' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
    'joinType' => 'LEFT JOIN',
    'on' => 'priceEladasi.product_price_field_id=:product_price_field_id AND priceEladasi.active = 1',
    'params' => array(':product_price_field_id' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 1))->id),
    'alias' => 'priceEladasi'
),
'price3' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
    'joinType' => 'LEFT JOIN',
    'on' => 'priceAkcios.product_price_field_id=:product_price_field_id AND priceAkcios.active = 1',
    'params' => array(':product_price_field_id' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 5))->id),
    'alias' => 'priceAkcios'
),

在模型中,在搜索中:

...
$criteria->with('price1', 'price2', 'price3);

...

$criteria->compare('price1.price', $this->beszerzesi, true);
$criteria->compare('price2.price', $this->eladasi, true);
$criteria->compare('price3.price', $this->akcios, true);

...
$sort->attributes = array(
    'price1' =>array(
        'asc' => 'priceBeszerzesi.price ASC',
        'desc' => 'priceBeszerzesi.price DESC',
    ),
    'price2' =>array(
        'asc' => 'priceEladasi.price ASC',
        'desc' => 'priceEladasi.price DESC',
    ),
    'priceAkcios' =>array(
        'asc' => 'price3.price ASC',
        'desc' => 'price3.price DESC',
    ),
    '*'
);

...

return new CActiveDataProvider($this, array(
        'criteria' => $criteria,
        'sort' => $sort,
        'pagination' => array(
            'pageSize' => 10,
        ),
    )
);

gridview 中列的数据:

'eladasi' => array(
    'name' => 'price2',
    'header' => 'Eladási ár',
    'type' => 'raw',
    'headerHtmlOptions' => array('class' => 'auto-width text-center'),
    'value' => '!empty($data->price2->price) ? $data->price2->price == "" ? "-" : $data->price2->price : "-"',
    'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
'akcios' => array(
    'name' => 'priceAkcios',
    'header' => 'Akciós',
    'value' => '!empty($data->price3->price) ? $data->price3->price == "" ? "-" : $data->price3->price : "-"',
    'headerHtmlOptions' => array('class' => 'auto-width text-center'),
    'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),
    'beszerzesi' => array(
    'name' => 'price1',
    'header' => 'Beszerzési ár',
    'type' => 'raw',
    'value' => '!empty($data->price1->price) ? $data->price1->price == "" ? "-" : $data->price1->price : "-"',
    'headerHtmlOptions' => array('class' => 'auto-width text-center'),
    'htmlOptions' => array('class' => 'border-right auto-width text-center'),
),

此代码可以按所有三个依赖于关系的列对列表进行排序,但每一列都显示相同的值 - with 数组中最后一个关系的值。如果数组中的最后一个元素是 price3,则列显示 price3 关系的值。 当我从 with 数组中删除所有关系名称时,我可以按该列而不是其他列对列表进行排序。

我的问题是: 有什么办法 1)肯定会向模型添加任意数量的关系,连接到相同的数据库字段,但取决于条件, 2) 并显示这些值,同时启用基于它们的排序?

在下面找到解决方案:

我在我的系统上创建了这些表并使用了您的关系和网格视图代码。我对该代码进行了一些更改,现在在下面的代码中搜索和排序工作正常。

我在模型 class 中定义了三个变量,即

public $beszerzesi;
public $eladasi;
public $akcios;

然后我更改了与左连接一起使用的关系数组中的参数名称。这是您代码中的主要问题。您为参数使用了相同的名称,即 :product_price_field_id 我为每个参数分配了不同的名称。当 yii 准备 sql 查询时,它将替换分配给查询的参数。在您的情况下,它正在为所有三个参数替换相同的值。

我还对 CActiveDataProvider 中传递的属性进行了排序和比较。您可以在下面的模型文件中找到所有更改。

Product.php

<?php

/**
 * This is the model class for table "product".
 *
 * The followings are the available columns in table 'product':
 * @property integer $id
 * @property string $name
 */
class Product extends CActiveRecord
{
    public $beszerzesi;
    public $eladasi;
    public $akcios;

    /**
     * @return string the associated database table name
     */
    public function tableName()
    {
        return 'product';
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules()
    {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            array('name', 'required'),
            array('name', 'length', 'max' => 100),
            // The following rule is used by search().
            // @todo Please remove those attributes that should not be searched.
            array('id, name, beszerzesi, eladasi,akcios', 'safe', 'on' => 'search'),
        );
    }

    /**
     * @return array relational rules.
     */
    public function relations()
    {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
            'productPriceConnects' => array(Product::HAS_MANY, 'ProductPriceConnect', 'product_id'),
            'price1' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
                'joinType' => 'LEFT JOIN',
                'on' => 'priceBeszerzesi.product_price_field_id=:product_price_field_id1 AND priceBeszerzesi.active = 1',
                'params' => array(':product_price_field_id1' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 6))->id),
                'alias' => 'priceBeszerzesi',
            ),
            'price2' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
                'joinType' => 'LEFT JOIN',
                'on' => 'priceEladasi.product_price_field_id=:product_price_field_id2 AND priceEladasi.active = 1',
                'params' => array(':product_price_field_id2' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 1))->id),
                'alias' => 'priceEladasi'
            ),
            'price3' => array(Product::HAS_ONE, 'ProductPriceConnect', 'product_id',
                'joinType' => 'LEFT JOIN',
                'on' => 'priceAkcios.product_price_field_id=:product_price_field_id3 AND priceAkcios.active = 1',
                'params' => array(':product_price_field_id3' => ProductPriceField::model()->findByAttributes(array('active' => 1, 'type' => 1, 'category' => 5))->id),
                'alias' => 'priceAkcios'
            ),
        );
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels()
    {
        return array(
            'id' => 'ID',
            'name' => 'Name',
        );
    }

    /**
     * Retrieves a list of models based on the current search/filter conditions.
     *
     * Typical usecase:
     * - Initialize the model fields with values from filter form.
     * - Execute this method to get CActiveDataProvider instance which will filter
     * models according to data in model fields.
     * - Pass data provider to CGridView, CListView or any similar widget.
     *
     * @return CActiveDataProvider the data provider that can return the models
     * based on the search/filter conditions.
     */
    public function search()
    {
        // @todo Please modify the following code to remove attributes that should not be searched.

        $criteria = new CDbCriteria;

        $criteria->with = array('price1', 'price2', 'price3');

        $criteria->compare('id', $this->id);
        $criteria->compare('name', $this->name, true);

        $criteria->compare('priceBeszerzesi.price', $this->beszerzesi, true);
        $criteria->compare('priceEladasi.price', $this->eladasi, true);
        $criteria->compare('priceAkcios.price', $this->akcios, true);

//        $criteria->attributes = ;

        return new CActiveDataProvider($this, array(
                'criteria' => $criteria,
                'sort' => array(
                    'attributes' => array(
                        'beszerzesi' => array(
                            'asc' => 'priceBeszerzesi.price',
                            'desc' => 'priceBeszerzesi.price DESC',
                        ),
                        'eladasi' => array(
                            'asc' => 'priceEladasi.price',
                            'desc' => 'priceEladasi.price DESC',
                        ),
                        'akcios' => array(
                            'asc' => 'priceAkcios.price',
                            'desc' => 'priceAkcios.price DESC',
                        ),
                        '*'
                    )
                ),
                'pagination' => array(
                    'pageSize' => 10,
                ),
            )
        );
    }

    /**
     * Returns the static model of the specified AR class.
     * Please note that you should have this exact method in all your CActiveRecord descendants!
     * @param string $className active record class name.
     * @return Product the static model class
     */
    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }
}

网格视图代码

<?php $this->widget('zii.widgets.grid.CGridView', array(
    'id' => 'product-grid',
    'dataProvider' => $model->search(),
    'filter' => $model,
    'columns' => array(
        'id',
        'name',
        'eladasi' => array(
            'name' => 'eladasi',
            'header' => 'Eladási ár',
            'type' => 'raw',
            'headerHtmlOptions' => array('class' => 'auto-width text-center'),
            'value' => '!empty($data->price2->price) ? $data->price2->price == "" ? "-" : $data->price2->price : "-"',
            'htmlOptions' => array('class' => 'border-right auto-width text-center'),
        ),
        'akcios' => array(
            'name' => 'akcios',
            'header' => 'Akciós',
            'value' => '!empty($data->price3->price) ? $data->price3->price == "" ? "-" : $data->price3->price : "-"',
            'headerHtmlOptions' => array('class' => 'auto-width text-center'),
            'htmlOptions' => array('class' => 'border-right auto-width text-center'),
        ),
        'beszerzesi' => array(
            'name' => 'beszerzesi',
            'header' => 'Beszerzési ár',
            'type' => 'raw',
            'value' => '!empty($data->price1->price) ? $data->price1->price == "" ? "-" : $data->price1->price : "-"',
            'headerHtmlOptions' => array('class' => 'auto-width text-center'),
            'htmlOptions' => array('class' => 'border-right auto-width text-center'),
        ),
        array(
            'class' => 'CButtonColumn',
        ),
    ),
)); ?>

您可以在 Searching and sorting by related model in CGridView | Wiki | Yii PHP Framework

找到搜索和排序关系数据的分步指南