首页 > 解决方案 > 为 foreach() 提供的参数无效 - 尝试在 SearchModel 上进行查询时 - Yii2

问题描述

我试图为迷你地理定位系统制作搜索模型。每当我尝试通过直接调用 GeoData 模型对数据进行排序时,除非我们尝试排序,否则它会起作用。

但是当我们尝试使用 CustomMade SearchModel 时,它会发送:为 foreach() 提供的参数无效

这是搜索模型:

<?php namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\GeoData;
/**
 * This is the ActiveQuery class for [[GeoData]].
 *
 * @see GeoData
 */
class GeoDataSearch extends GeoData
{
    const TODOS = 1;
    const FECHA = 2;
    const ENVIADO_POR = 3;

    public function rules()
    {
         return [
            [['latitude', 'longitude'], 'required'],
            [['latitude', 'longitude', 'accuracy', 'speed', 'betterlocation'], 'number'],
            [['device_id', 'active', 'sended', 'mobildate', 'created_at', 'updated_at', 'created_by'], 'integer'],
            [['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['created_by' => 'id']],
        ];
    }
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    public function scenarios()
    {
        return Model::scenarios();
    }
    public function search($params)
    {
        $query = GeoData::find()->where(['created_by' => $params])->all();

            $dataProvider = new ActiveDataProvider([
                'query' => $query,
                'sort' => [
                    'defaultOrder' => [
                        'created_at' => SORT_DESC,
                    ]
                ]
                
            ]);
        
        $dataProvider->sort->attributes['created_at'] = [
            'asc' => ['created_at' => SORT_ASC],
            'desc' => ['created_at' => SORT_DESC]
        ];
        $dataProvider->sort->attributes['created_by'] = [
            'asc' => ['created_by' => SORT_ASC],
            'desc' => ['created_by' => SORT_DESC]
        ];
        $dataProvider->sort->attributes['geo_id'] = [
            'asc' => ['geo_id' => SORT_ASC],
            'desc' => ['geo_id' => SORT_DESC]
        ];

        $this->load($params);
   
        $query->andFilterWhere([
            'geo_id' => $this->geo_id,
            'latitude' => $this->latitude,
            'longitude' => $this->longitude,
            'accuracy' => $this->accuracy,
            'speed' => $this->speed,
            'device_id' => $this->device_id,
            'betterlocation' => $this->betterlocation,
            'active' => $this->active,
            'mobiledate' => $this->mobildate,
            'sended' => $this->sended,
            'updated_at' => $this->updated_at,
            'created_at' => $this->created_at,
            'created_by' => $this->created_by,
        ]);

        return $dataProvider;

    }

}

控制器(注释的代码有效,但不允许我使用 GridView 也不允许过滤或排序:

public function actionView($id)
    {
        $title = "Ver Historial";
        $id = $_GET['id'];
        $searchModel = new GeoDataSearch;
        
        $dataProvider = $searchModel->search($id);
        return $this->render(
            'view',
            [
                'title' => $title,
                'searchModel' => $searchModel,
                'dataProvider' => $dataProvider
        ]);
/*
        if (Yii::$app->user->can(AuthItem::ROLE_ADMINISTRATOR))
        {
        $id = $_GET['id'];
        $title = "Ver Historial";
        $model = new GeoData;
        $dataProvider = $model::findAll(['created_by'=> $id]); 
        return $this->render(
            'view',
            [
                'title' => $title,
                'dataProvider' => $dataProvider,
            ]
            );
        } else {
            throw new ForbiddenHttpException('Access denied for user '.Yii::$app->user->identity->id);
        }
   */
    }

任何建议将不胜感激!

标签: phpmodelyii2yii2-modelcactivedataprovider

解决方案


First your GeoDataSearch search() function should look like this:

public function search($params)
{
    $query = GeoData::find();

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

    $this->load($params);

    if (!$this->validate())
        return $dataProvider;

    $query->andFilterWhere([
        'geo_id' => $this->geo_id,
        'latitude' => $this->latitude,
        'longitude' => $this->longitude,
        'accuracy' => $this->accuracy,
        'speed' => $this->speed,
        'device_id' => $this->device_id,
        'betterlocation' => $this->betterlocation,
        'active' => $this->active,
        'mobiledate' => $this->mobildate,
        'sended' => $this->sended,
        'updated_at' => $this->updated_at,
        'created_at' => $this->created_at,
        'created_by' => $this->created_by,
    ]);

    return $dataProvider;
}

Add a second function to GeoDataSearch model:

/**
 * @param int $creator_id ID of the creator
 * @param mixed $params
 * @return ActiveDataProvider
 */
public function searchByCreator($creator_id, $params)
{
    // This will verify if the $params['GeoDataSearch'] index exists
    if ( isset( $params['GeoDataSearch'] ) ) {
        $params['GeoDataSearch']['created_by'] = $creator_id;
    } else {
        // Create the index if it doesn't exist
        $params['GeoDataSearch'] = ['created_by' => $creator_id];
    }

    return $this->search($params);
}

Update your actionView:

public function actionView($id)
{
    $title = "Ver Historial";
    $searchModel = new GeoDataSearch;
    // The $id param is already receiving the $_GET['id']
    $dataProvider = $searchModel->searchByCreator($id, Yii::$app->request->queryParams);

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

Don't setup user validation in the actionView, use AccessControl instead.


Explaining

DataProvider are for easy data pagination and sorting (View docs).

Querys filters the data for the DataProvider manipulation.

The search method is a convention for attaching query filters to a DataProvider in a single place for better reuse. So you should be looking to have a generic search method that can be reused on multiple scenarios.

In this example, the GeoDataSearch->search() method is used for searching multiple GeoData models by passing any parameter you want, so you will pass an array to it, most likely with the data from front-end.

If you are using Yii2 ActiveForm inputs (with GET method set) in your front-end, on GeoDataSearch fields. For example:

<?= $form->field($searchModel, 'speed')->textInput(); ?>

This will allow to filter the results by the speed attribute. When a user submit this form, your back-end will get the following content from Yii::$app->request->queryParams:

[
    'GeoDataSearch' => [
        'speed' => '10.00'
    ]
]

You should use this on the search method, that will filter all GeoData by their speed.

Important to notice that the speed attribute will be a index on GeoDataSearch. This is the way Yii2 builds params with their core form methods and affects the models load() method, and the reason that the implementation on searchByCreator seems a little convoluted.

Back to your case,

You need to filter by the creator. Since this needs to always be attached to the search query params, I recommend you to create a new method (searchByCreator), which consumes the already existing search method, force the created_by attribute, all while keeping the other filters alive.


推荐阅读