Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
649 views
in Technique[技术] by (71.8m points)

api - Yii2 REST query

Hy. I have a ProductController which extends the yii estActiveController. Question is that how can i make querys via HTTP GET request.

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

And the return object will contains all products with name iphone.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

UPDATE: Apr 29 2016

This is one more approach simpler than the one I introduced in the previous update. It is always about involving the Search class generated by gii. I like using it to define and maintain all the search related logic in a single place like using custom scenarios, handle validations, or involve related models on the filtering process (like in this example). So I'm going back to my first answer :

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

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

Then be sure that your search class is using load($params,'') instead of load($params) or alternatively add this to the model class:

class Product extends yiidbActiveRecord
{
    public function formName()
    {
        return '';
    }

That should be enough to have your requests looking like:

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


UPDATE: Sep 23 2015

This is the same approach but by implementing a complete & cleaner solution :

namespace appapimodulesv1controllers;

use yii
estActiveController;
use yiihelpersArrayHelper;
use yiiwebBadRequestHttpException;

class ProductController extends ActiveController
{
    public $modelClass = 'appmodelsProduct';
    // 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 yiiaseModel::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 appmodelsProductSearch();    
        return $searchModel->search($searchByAttr);     
    }
}

Now your GET request will look like :

/products?name=iphone

Or even like :

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

  • Note:

    If instead of /products?name=iphone you are looking for a specific action to handle searching or filtering requests like :

    /products/search?name=iphone

    Then, in the code above you'll need to remove the actions function with all its content :

    public function actions() { ... }

    rename :indexDataProvider() to actionSearch()

    & finally add 'extraPatterns' => ['GET search' => 'search'] to your yiiwebUrlManager::rules as described in @KedvesHunor's answer.


Original answer: May 31 2015

There is a short way to do this, if when using Gii to generate CRUD for your model, you defined a Search Model Class, then you can use it to filter results, all you have to do is to override the prepareDataProvider function of indexAction to force it return the ActiveDataProvider instance returned by the search function of your model search class rather than creating a custom new one.

To resume if your model is Product.php & you generated a ProductSearch.php as a search class to it, then in your Controller you just need to add this :

public function actions() {

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

    return $actions;
}

public function prepareDataProvider() {

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

Then to filter results, your url may look like this :

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

or even like this :

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...