Yii2 REST 查询

问题描述 投票:0回答:7

嘿。 我有一个 ProductController ,它扩展了 yii est\ActiveController。 问题是我如何通过 HTTP GET 请求进行查询。

喜欢:http://api.test.loc/v1/products/search?name=iphone

返回对象将包含所有名称为 iphone 的产品。

php api rest yii2
7个回答
47
投票

更新:2016 年 4 月 29 日

这是比我在上一次更新中介绍的方法更简单的一种方法。它总是涉及由 gii 生成的 Search 类。我喜欢使用它在一个地方定义和维护所有与搜索相关的逻辑,例如使用自定义场景、处理验证或在过滤过程中涉及相关模型(如本示例所示)。所以我回到我的第一个答案:

public function actions() 
{ 
    $actions = parent::actions();
    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];
    return $actions;
}

public function prepareDataProvider() 
{
    $searchModel = new \app\models\ProductSearch();    
    return $searchModel->search(\Yii::$app->request->queryParams);
}

然后确保您的搜索类使用

load($params,'')
而不是
load($params)
或者将其添加到 model 类中:

class Product extends \yii\db\ActiveRecord
{
    public function formName()
    {
        return '';
    }

这应该足以让您的请求看起来像这样:

/产品?name=iphone&status=available&sort=name,-price


更新:2015 年 9 月 23 日

这是相同的方法,但通过实施完整且更清洁的解决方案:

namespace app\api\modules\v1\controllers;

use yii\rest\ActiveController;
use yii\helpers\ArrayHelper;
use yii\web\BadRequestHttpException;

class ProductController extends ActiveController
{
    public $modelClass = 'app\models\Product';
    // Some reserved attributes like maybe 'q' for searching all fields at once 
    // or 'sort' which is already supported by Yii RESTful API
    public $reservedParams = ['sort','q'];

    public function actions() {
        $actions = parent::actions();
        // 'prepareDataProvider' is the only function that need to be overridden here
        $actions['index']['prepareDataProvider'] = [$this, 'indexDataProvider'];
        return $actions;
    }

    public function indexDataProvider() {
        $params = \Yii::$app->request->queryParams;

        $model = new $this->modelClass;
        // I'm using yii\base\Model::getAttributes() here
        // In a real app I'd rather properly assign 
        // $model->scenario then use $model->safeAttributes() instead
        $modelAttr = $model->attributes;

        // this will hold filtering attrs pairs ( 'name' => 'value' )
        $search = [];

        if (!empty($params)) {
            foreach ($params as $key => $value) {
                // In case if you don't want to allow wired requests
                // holding 'objects', 'arrays' or 'resources'
                if(!is_scalar($key) or !is_scalar($value)) {
                    throw new BadRequestHttpException('Bad Request');
                }
                // if the attr name is not a reserved Keyword like 'q' or 'sort' and 
                // is matching one of models attributes then we need it to filter results
                if (!in_array(strtolower($key), $this->reservedParams) 
                    && ArrayHelper::keyExists($key, $modelAttr, false)) {
                    $search[$key] = $value;
                }
            }
        }

        // you may implement and return your 'ActiveDataProvider' instance here.
        // in my case I prefer using the built in Search Class generated by Gii which is already 
        // performing validation and using 'like' whenever the attr is expecting a 'string' value.
        $searchByAttr['ProductSearch'] = $search;
        $searchModel = new \app\models\ProductSearch();    
        return $searchModel->search($searchByAttr);     
    }
}

现在您的 GET 请求将如下所示:

/产品?名称=iphone

或者甚至喜欢:

/产品?name=iphone&status=available&sort=name,-price

  • 注意:

    如果您正在寻找特定的而不是

    /products?name=iphone
    处理搜索或过滤请求的操作,例如:

    /产品/搜索?名称=iphone

    然后,在上面的代码中,您需要remove actions 函数及其所有内容:

    public function actions() { ... }

    重命名

    indexDataProvider()
    actionSearch()

    & 最后 add

    'extraPatterns' => ['GET search' => 'search']
    到您的 yii\web\UrlManager::rules,如所述 在@KedvesHunor 的回答中。


原答案:2015年5月31日

有一个简短的方法可以做到这一点,如果在使用 Gii 为模型生成 CRUD 时,您定义了一个 Search Model Class,那么您可以使用它来过滤结果,您所要做的就是覆盖

prepareDataProvider
indexAction
函数强制返回模型
ActiveDataProvider
函数返回的
search
实例 搜索类,而不是创建一个自定义的新实例。

要恢复,如果您的模型是 Product.php 并且您生成了 ProductSearch.php 作为其 搜索类,那么在您的 Controller 中,您只需添加以下内容:

public function actions() {

    $actions = parent::actions();
    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];

    return $actions;
}

public function prepareDataProvider() {

    $searchModel = new \app\models\ProductSearch();    
    return $searchModel->search(\Yii::$app->request->queryParams);
}

然后要过滤结果,您的网址可能如下所示:

api.test.loc/v1/products?ProductSearch[name]=iphone

甚至像这样:

api.test.loc/v1/products?ProductSearch[available]=1&ProductSearch[name]=iphone

27
投票

好吧,我想通了,只需将其放入控制器中并修改配置中的 URL 路由器即可。

public function actionSearch()
{
    if (!empty($_GET)) {
        $model = new $this->modelClass;
        foreach ($_GET as $key => $value) {
            if (!$model->hasAttribute($key)) {
                throw new \yii\web\HttpException(404, 'Invalid attribute:' . $key);
            }
        }
        try {
            $provider = new ActiveDataProvider([
                'query' => $model->find()->where($_GET),
                'pagination' => false
            ]);
        } catch (Exception $ex) {
            throw new \yii\web\HttpException(500, 'Internal server error');
        }

        if ($provider->getCount() <= 0) {
            throw new \yii\web\HttpException(404, 'No entries found with this query string');
        } else {
            return $provider;
        }
    } else {
        throw new \yii\web\HttpException(400, 'There are no query string');
    }
}

以及 URL 规则(编辑)

'urlManager' => [
        'enablePrettyUrl' => true,
        'enableStrictParsing' => true,
        'showScriptName' => false,
        'rules' => [
            ['class' => 'yii\rest\UrlRule', 'controller' => ['v1/product'], 'extraPatterns' => ['GET search' => 'search']],
        ],
    ],

8
投票

我不建议直接使用超级全局$_GET。相反,您可以使用

Yii::$app->request->get()

以下是如何创建通用搜索操作并在控制器中使用它的示例。

控制器端

public function actions() {

$actions = [
    'search' => [
        'class'       => 'app\[YOUR NAMESPACE]\SearchAction',
        'modelClass'  => $this->modelClass,
        'checkAccess' => [$this, 'checkAccess'],
        'params'      => \Yii::$app->request->get()
    ],
];

return array_merge(parent::actions(), $actions);
}

public function verbs() {

    $verbs = [
        'search'   => ['GET']
    ];
    return array_merge(parent::verbs(), $verbs);
}

自定义搜索操作

<?php

namespace app\[YOUR NAMESPACE];

use Yii;
use yii\data\ActiveDataProvider;
use yii\rest\Action;


class SearchAction extends Action {

    /**
     * @var callable a PHP callable that will be called to prepare a data provider that
     * should return a collection of the models. If not set, [[prepareDataProvider()]] will be used instead.
     * The signature of the callable should be:
     *
     * ```php
     * function ($action) {
     *     // $action is the action object currently running
     * }
     * ```
     *
     * The callable should return an instance of [[ActiveDataProvider]].
     */
    public $prepareDataProvider;
    public $params;

    /**
     * @return ActiveDataProvider
     */
    public function run() {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }

        return $this->prepareDataProvider();
    }

    /**
     * Prepares the data provider that should return the requested collection of the models.
     * @return ActiveDataProvider
     */
    protected function prepareDataProvider() {
        if ($this->prepareDataProvider !== null) {
            return call_user_func($this->prepareDataProvider, $this);
        }

        /**
         * @var \yii\db\BaseActiveRecord $modelClass
         */
        $modelClass = $this->modelClass;

        $model = new $this->modelClass([
        ]);

        $safeAttributes = $model->safeAttributes();
        $params = array();

        foreach($this->params as $key => $value){
            if(in_array($key, $safeAttributes)){
               $params[$key] = $value;                
            }
        }

        $query = $modelClass::find();

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

        if (empty($params)) {
            return $dataProvider;
        }


        foreach ($params as $param => $value) {
            $query->andFilterWhere([
                $param => $value,
            ]);
        }

        return $dataProvider;
    }

}

3
投票

从 yii 2.0.13 开始,

yii\rest\IndexAction
有了新属性 -
dataFilter
,简化了过滤过程。默认情况下,ActiveController 使用
yii\rest\IndexAction
来执行
index
操作:

    class ActiveController extends Controller {
        public function actions()
        {
            return [
                'index' => [
                    'class' => 'yii\rest\IndexAction',
                    'modelClass' => $this->modelClass,
                    'checkAccess' => [$this, 'checkAccess'],
                ]
        }
}

ProductController
控制器中执行以下操作:

class ProductController extends ActiveController
{
    public function actions()
    {
        $actions = parent::actions();

        $actions['index']['dataFilter'] = [
            'class' => 'yii\data\ActiveDataFilter',
            'searchModel' => 'app\models\ProductSearch'
        ];

        return $actions;
    }
}

假设

app\models\ProductSearch
是产品过滤器型号。


1
投票

在 Config/web.php -> 添加 'extraPatterns' => ['GET search' => 'search']

'urlManager' => [
        'enablePrettyUrl' => true,
        'showScriptName' => false,
        'rules' => [['class' => 'yii\rest\UrlRule', 'controller' => 'v1/basicinfo', 'pluralize'=>false,'extraPatterns' => ['GET search' => 'search']]]]

**在 Rest Api 控制器中:- 模块/v1/控制器/ **

basicinfo :- 是您的控制器名称,名称和年龄是您的字段名称。您可以添加表中存在的所有参数。

搜索网址如下:- basicinfo/search?name=yogi&age=12-23

包括使用 yii\data\ActiveDataProvider;

 public function actionSearch()
{
    if (!empty($_GET)) {
        $model = new $this->modelClass;
        foreach ($_GET as $key => $value) {
            if (!$model->hasAttribute($key)) {
                throw new \yii\web\HttpException(404, 'Invalid attribute:' . $key);
            }
        }
        try {

           $query = $model->find();
            foreach ($_GET as $key => $value) {
                 if ($key != 'age') {
                    $query->andWhere(['like', $key, $value]);
                  }
                  if ($key == 'age') {
                  $agevalue = explode('-',$value);
                  $query->andWhere(['between', $key,$agevalue[0],$agevalue[1]]);

             }

            }

            $provider = new ActiveDataProvider([
                'query' => $query,
                'sort' => [
                    'defaultOrder' => [
                        'updated_by'=> SORT_DESC
                    ]
                ],
                  'pagination' => [
                'defaultPageSize' => 20,
            ],
            ]);
        } catch (Exception $ex) {
            throw new \yii\web\HttpException(500, 'Internal server error');
        }

        if ($provider->getCount() <= 0) {
            throw new \yii\web\HttpException(404, 'No entries found with this query string');
        } else {
            return $provider;
        }
    } else {
        throw new \yii\web\HttpException(400, 'There are no query string');
    }
}

0
投票

如果您需要访问您的 api,例如:

api/product/index?name=fashion
更短的过滤方法是:
-取消设置动作,在我的例子中是
index
动作。

public function actions()
{
    $actions = parent::actions(); 
    unset($actions['index']);
    return $actions;
}
  • 执行自定义查询,如下所示。

    公共函数actionIndex(){

    $query = Product::find();
    $dataProvider =  new ActiveDataProvider([
        'query' => $query,
        'pagination' => [
                'pageSize' => 1,
            ],
    ]);
    
    if (isset($_GET['name']) && !empty($_GET['name'])) {
        $searchWord = strtolower(trim($_GET['name']));
        $query->andFilterWhere(['like', 'name', $searchWord]);
    }
    
    return $dataProvider;
    

    }


0
投票

如果您想按唯一字段搜索,这里有一个较短的版本,用于仅获取一条记录:

    public function actionSearch()
    {
        if (!empty($_GET)) {
            $model = new $this->modelClass;
            return $model::findOne($_GET);
        } else {
            throw new HttpException(400, 'There are no query string');
        }
    }

用途:

http://example.com/api/search?slug=abs

© www.soinside.com 2019 - 2024. All rights reserved.