当我在yii2中使用findbysql查询时排序和搜索列

时间:2016-05-05 04:02:49

标签: yii2

我正在搜索四个表并加入它们并获得我想要的输出。但无法对输出进行排序或过滤。请告诉我如何按地区或销售范围或收集范围进行搜索。 缔约方搜索模型是 -

<?php

namespace frontend\modules\districtreport\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\districtreport\models\Parties;
use frontend\modules\districtreport\models\Bills;
use frontend\modules\districtreport\models\Payment;
use yii\db\Query;
use yii\db\Command;
$query = \Yii::$app->db;
/**
 * PartiesSearch represents the model behind the search form about `frontend\modules\districtreport\models\Parties`.
 */
class PartiesSearch extends Parties
{
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['party_id'], 'integer'],
            [['parties_partyname', 'address', 'parties_district', 'name_manager', 'transport', 'dlno', 'instruction', 'con', 'district','sale','sell','collection'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {

        $sql = 'select 
        tsell.district as district,
        tsell.totalsale as sell,
        coalesce(tcollection.collection,0) as collection 
        from 
            (SELECT 
                district, 
                coalesce(sell.sale,0) as totalsale 
            FROM `districts` 
            left join 
                (SELECT 
                    parties_district, 
                    billdate,
                    sum(billamount) as sale 
                FROM `bills` 
                left join parties on bills.bills_partyname = parties.parties_partyname 
                group by parties_district) as sell 
            on sell.parties_district = districts.district) as tsell 
        left join 
            (SELECT 
                parties_district,
                payment_date,
                COALESCE(sum(payment_amount),0) as collection 
            FROM `payment` 
            left join parties on payment.payment_partyname = parties.parties_partyname 
            group by parties_district) as tcollection 
               on tsell.district = tcollection.parties_district';
        $query = Parties::findBySql($sql);

        // add conditions that should always apply here

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            //'sort'=> ['defaultOrder' => ['district'=>SORT_DESC]]
        ]);

        $dataProvider->setSort([
            'attributes' => [
                'sell' => [
                   'asc' => ['sell' => SORT_ASC],
                    'desc' => ['sell' => SORT_DESC],
                    'label' => 'Sell'
                ],
                'collection' => [
                    'asc' => ['collection' => SORT_ASC],
                    'desc' => ['collection' => SORT_DESC],      
                    'label' => 'Collection'
                ],      
                'district' => [
                    'asc' => ['tsell.district' => SORT_ASC],
                    'desc' => ['tsell.district' => SORT_DESC],
                    'label' => 'District'
                ]                               
            ]
        ]);



        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        // grid filtering conditions
        $query->andFilterWhere([
            'party_id' => $this->party_id,
        ]);

        $query->andFilterWhere(['like', 'parties_partyname', $this->parties_partyname])
            ->andFilterWhere(['like', 'address', $this->address])
            ->andFilterWhere(['like', 'parties_district', $this->parties_district])
            ->andFilterWhere(['like', 'name_manager', $this->name_manager])
            ->andFilterWhere(['like', 'transport', $this->transport])
            ->andFilterWhere(['like', 'dlno', $this->dlno])
            ->andFilterWhere(['like', 'instruction', $this->instruction])
            ->andFilterWhere(['like', 'con', $this->con])
            ->andFilterWhere(['like', 'sell', $this->sell])
            ->andFilterWhere(['like', 'collection', $this->collection])
            ->andFilterWhere(['like', 'district', $this->district]);

        return $dataProvider;
    }
}

缔约方模式

<?php

namespace frontend\modules\districtreport\models;

use Yii;

/**
 * This is the model class for table "parties".
 *
 * @property integer $party_id
 * @property string $parties_partyname
 * @property string $address
 * @property string $parties_district
 * @property string $name_manager
 * @property string $transport
 * @property string $dlno
 * @property string $instruction
 * @property string $con
 */
class Parties extends \yii\db\ActiveRecord
{
    public $sale;
    public $district;
    public $sell;
    public $collection;
    public $bills;
    public $partyname;
    public $billdate;

    //public $sale;
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'parties';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['parties_partyname', 'parties_district', 'name_manager'], 'required'],
            [['parties_partyname'], 'string', 'max' => 60],
            [['address', 'instruction'], 'string', 'max' => 100],
            [['parties_district'], 'string', 'max' => 20],
            [['name_manager', 'transport', 'dlno'], 'string', 'max' => 30],
            [['con'], 'string', 'max' => 10],
            [['parties_partyname'], 'unique'],
            [['name_manager'], 'exist', 'skipOnError' => true, 'targetClass' => Managers::className(), 'targetAttribute' => ['name_manager' => 'manager_managername']],
            [['con'], 'exist', 'skipOnError' => true, 'targetClass' => Console::className(), 'targetAttribute' => ['con' => 'console']],
            [['parties_district'], 'exist', 'skipOnError' => true, 'targetClass' => Districts::className(), 'targetAttribute' => ['parties_district' => 'district']],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'party_id' => 'Party ID',
            'parties_partyname' => 'Parties Partyname',
            'address' => 'Address',
            'parties_district' => 'Parties District',
            'name_manager' => 'Name Manager',
            'transport' => 'Transport',
            'dlno' => 'Dlno',
            'instruction' => 'Instruction',
            'con' => 'Con',
        ];
    }
    public function getDistricts()
    {
        return $this->hasOne(Districts::className(), ['district' => 'parties_district']);
    }
    public function getBills()
    {
        return $this->hasMany(Bills::className(), ['bills_partyname' => 'parties_partyname']);
    }
    public function getPayment()
    {
        return $this->hasMany(Payment::className(), ['payment_partyname' => 'parties_partyname']);
    }
}

的index.php

<?php

use yii\helpers\Html;
use kartik\grid\GridView;
//use kartik\widgets\DatePicker;
use kartik\daterange\DateRangePicker;
use kartik\form\ActiveForm;
use dosamigos\datepicker\DatePicker;
use frontend\modules\districtreport\models\ExpartiesSearch;

/* @var $this yii\web\View */
/* @var $searchModel frontend\modules\districtreport\models\PartiesSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = 'Parties';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="parties-index">

    <h1><?= Html::encode($this->title) ?></h1>
    <?php // echo $this->render('_search', ['model' => $searchModel]); ?>

<!--     <p>
        <?= Html::a('Create Parties', ['create'], ['class' => 'btn btn-success']) ?>
    </p> -->
    <!-- <div class="custom-filter">

    Date range:
     <input name="start" />
     <input name="end" />

    </div> -->


        <?= GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'export' => false,
        'columns' => [
        [
            //['class' => 'yii\grid\SerialColumn'],
            'class' => 'kartik\grid\ExpandRowColumn',
            'value' => function($model, $key, $index, $column){
                return GridView::ROW_COLLAPSED;
            },
            'detail' => function($model, $key, $index, $column){
                $searchModel = new ExpartiesSearch();
                $searchModel-> district = $model->district;
                $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

                return Yii::$app->controller->renderPartial('_exparties', [
                    'searchModel' => $searchModel,
                    'dataProvider' => $dataProvider,
                    ]);                   

                },
            ],
            //['class' => 'yii\grid\SerialColumn'],


            'district',
            // [
            // 'attribute' => 'date',
            // 'value' => 'tsell.date',
            // 'filter' => \yii\jui\DatePicker::widget(['language' => 'ru', 'dateFormat' => 'dd-MM-yyyy']),
            // 'format' => 'html',
            // ],           
            'sell',
            'collection',


            //['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>
</div>

enter image description here 在这张图片中,我们可以看到尽管销售排序存在,但实际上并没有对数据进行排序。 enter image description here 在图2中,我们可以看到虽然数据assam被传递到kartik expandrow的下一级,但它并没有过滤。

4 个答案:

答案 0 :(得分:0)

根据yii2's docs您使用内置ActiveDataProvider的{​​{1}}。这是the example docs page的案例。

我认为您要做的是ActiveRecord,而不仅仅是return $dataProvider->getModels()。这可能会返回所有行,因为我没有看到分页机制(可能有默认的分页计数)。

对于排序,您可以尝试对return $dataProvider而不是$query进行排序。尝试类似:

$dataProvider

答案 1 :(得分:0)

使用数据提供者和过滤器模型设置网格视图。

在您看来:

<?= GridView::widget([
    'dataProvider'=> $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        'parties_partyname', 
        'address', 
        'parties_district', 
        'name_manager', 
        'transport', 
        'dlno', 
        'instruction', 
        'con', 
        'district',
        'sale',
        'sell',
        'collection'
    ]
]); ?>

在您的控制器中(将index替换为您的视图文件的名称):

$searchModel = new \frontend\modules\districtreport\models\PartiesSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);

return $this->render('index', [
    'searchModel' => $searchModel,
    'dataProvider' => $dataProvider,
]);

尝试从搜索模型中删除$dataProvider->setSort()

此外,在您的gridview详细信息中,您对搜索模型和数据提供者使用相同的变量名称。它们是两个独立的模型,因此变量不应该相同。

<?php

namespace frontend\modules\districtreport\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\districtreport\models\Parties;
use frontend\modules\districtreport\models\Bills;
use frontend\modules\districtreport\models\Payment;
use yii\db\Query;
use yii\db\Command;

/**
 * PartiesSearch represents the model behind the search form about `frontend\modules\districtreport\models\Parties`.
 */
class PartiesSearch extends Parties
{
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['party_id'], 'integer'],
            [['parties_partyname', 'address', 'parties_district', 'name_manager', 'transport', 'dlno', 'instruction', 'con', 'district','sale','sell','collection'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {
        $sql = 'select
        tsell.district as district,
        tsell.totalsale as sell,
        coalesce(tcollection.collection,0) as collection
        from
            (SELECT
                district,
                coalesce(sell.sale,0) as totalsale
            FROM `districts`
            left join
                (SELECT
                    parties_district,
                    billdate,
                    sum(billamount) as sale
                FROM `bills`
                left join parties on bills.bills_partyname = parties.parties_partyname
                group by parties_district) as sell
            on sell.parties_district = districts.district) as tsell
        left join
            (SELECT
                parties_district,
                payment_date,
                COALESCE(sum(payment_amount),0) as collection
            FROM `payment`
            left join parties on payment.payment_partyname = parties.parties_partyname
            group by parties_district) as tcollection
               on tsell.district = tcollection.parties_district';
        $query = Parties::findBySql($sql);

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        $query->andFilterWhere([
            'party_id' => $this->party_id,
            'parties_partyname' => $this->parties_partyname,
            'address' => $this->address,
            'parties_district' => $this->parties_district,
            'name_manager' => $this->name_manager,
            'transport' => $this->transport,
            'dlno' => $this->dlno,
            'instruction' => $this->instruction,
            'con' => $this->con,
            'sell' => $this->sell,
            'collection' => $this->collection,
            'district' => $this->district,
        ]);

        return $dataProvider;
    }
}

适用于我的简化搜索模型:

<?php

namespace common\models\event;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\Model;

class ModelSearch extends Model
{
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id', 'name'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {
        $query = Model::find()->indexBy('id');

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => false
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        $query->andFilterWhere([
            'id' => $this->id,
            'name' => $this->name,
        ]);

        return $dataProvider;
    }
}

答案 2 :(得分:0)

根据http://www.yiiframework.com/doc-2.0/yii-db-activerecord.html#findBySql%28代码,findbysql无法排序或过滤。 但是,可以通过在页面后面构建查询来感受它 - http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html 查询将类似于 -

$subQuery1 = (new Query())->select(['parties_district','billdate','sum(billamount) as sale'])->from ('parties')->join('LEFT JOIN','bills','bills.bills_partyname = parties.parties_partyname')->groupby('parties_district')->where('billdate != "NULL"');        
        $subQuery2 = (new Query())->select(['district','coalesce(sell.sale,0) as totalsale'])->from('districts')->leftJoin(['sell' => $subQuery1],'sell.parties_district = districts.district'); 
        $subQuery3 = (new Query())->select(['parties_district','payment_date','COALESCE(sum(payment_amount),0) as collection'])->from('payment')->join('LEFT JOIN','parties','payment.payment_partyname = parties.parties_partyname')->groupby('parties_district');
        $query = (new Query())->select(['tsell.district as district','tsell.totalsale as sell','coalesce(tcollection.collection,0) as collection'])->from(['tsell'=> $subQuery2])->leftJoin(['tcollection' => $subQuery3],'tcollection.parties_district = tsell.district');

答案 3 :(得分:0)

这些年来,任何人都应该遇到这个问题:如果您将它们传递给findBySql方法,则该查询将可以可排序(分页也可以,不适用于ActiveDataProvider + findBySql)。到ArrayDataProvider而不是ActiveDataProvider,如下所示:

$query = MyModel::findBySql("SELECT * FROM ...");
$dataProvider = new ArrayDataProvider([
    'allModels' => $query->all(),
    'sort' => [
        'attributes' => [
            'model_id',
            'model_name'
        ],
        'defaultOrder' => [
            'model_name' => SORT_ASC
        ]
    ],
    'pagination' => [
        'pageSize' => 30
    ]
]);

传统的过滤方法不起作用。来自findBySql() docs

请注意,由于已经指定了SQL语句,因此在创建的yii \ db \ ActiveQuery实例上调用其他查询修改方法(例如where(),order())将无效。

我想出了一种解决方法,它比使用接口方法需要更多的工作,但是对于复杂的查询(我认为这是您尝试使用findBySql的原因),它可以工作:

public function search($params)
{
    $this->load($params);

    $filters = [];

    if ($this->model_id) $filters[] = ' "table"."model_id" = '.$this->model_id.' ';
    if ($this->model_name) $filters[] = ' "table"."model_name" ILIKE \'%'.$this->model_name.'%\' ';

    // ugly ifs can be put into a foreach if you don't need to customize each condition too much

    $filterCondition = count($filters) > 0 ? ' AND ' . implode(' AND ', $filters) : ' ';

    $query = MyModel::findBySql(
        // your query here
        .$filterCondition.
        // group by, order by etc.
    );

    // assemble the ArrayDataProvider (see above)

    return $dataProvider;
}

Yii版本:2.0.34,PHP版本:7.3.18,PostgreSQL版本:12.2