3 フォロワー

データプロバイダー

ページネーションソート のセクションでは、エンドユーザーがデータの特定のページを選択して表示したり、特定の列でソートしたりする方法を説明しました。ページネーションとソートのタスクは非常に一般的なため、Yii はそれをカプセル化するために一連の データプロバイダー クラスを提供しています。

データプロバイダーは yii\data\DataProviderInterface を実装するクラスです。これは主にページネーションされたソート済みデータの取得をサポートします。通常、データウィジェット と連携して使用され、エンドユーザーが対話的にデータをページネーションおよびソートできるようにします。

Yii のリリースには、次のデータプロバイダークラスが含まれています。

これらすべてのデータプロバイダーの使用には、次の共通パターンがあります。

// create the data provider by configuring its pagination and sort properties
$provider = new XyzDataProvider([
    'pagination' => [...],
    'sort' => [...],
]);

// retrieves paginated and sorted data
$models = $provider->getModels();

// get the number of data items in the current page
$count = $provider->getCount();

// get the total number of data items across all pages
$totalCount = $provider->getTotalCount();

データプロバイダーのページネーションとソートの動作は、それぞれ yii\data\Paginationyii\data\Sort の構成に対応する、paginationsort プロパティを構成することによって指定します。ページネーションやソート機能を無効にするために、これらを false に構成することもできます。

yii\grid\GridView などの データウィジェット には、データプロバイダーインスタンスを受け取り、それを提供するデータを表示できる dataProvider という名前のプロパティがあります。例えば、

echo yii\grid\GridView::widget([
    'dataProvider' => $dataProvider,
]);

これらのデータプロバイダーは、主にデータソースの指定方法が異なります。次のサブセクションでは、これらの各データプロバイダーの詳細な使用法について説明します。

アクティブデータプロバイダー

yii\data\ActiveDataProvider を使用するには、query プロパティを構成する必要があります。これには、yii\db\Query または yii\db\ActiveQuery オブジェクトのいずれかを使用できます。前者の場合、返されるデータは配列になります。後者の場合、返されるデータは配列または アクティブレコード インスタンスのいずれかになります。例えば、

use yii\data\ActiveDataProvider;

$query = Post::find()->where(['status' => 1]);

$provider = new ActiveDataProvider([
    'query' => $query,
    'pagination' => [
        'pageSize' => 10,
    ],
    'sort' => [
        'defaultOrder' => [
            'created_at' => SORT_DESC,
            'title' => SORT_ASC, 
        ]
    ],
]);

// returns an array of Post objects
$posts = $provider->getModels();

上記の例で$queryが以下のコードを使って作成された場合、データプロバイダーは生の配列を返します。

use yii\db\Query;

$query = (new Query())->from('post')->where(['status' => 1]); 

注意: クエリがすでにorderBy句を指定している場合、エンドユーザーから(sort設定を通じて)与えられた新しい順序付けの指示は、既存のorderBy句に追加されます。既存のlimitおよびoffset句は、エンドユーザーからのページネーションリクエスト(pagination設定を通じて)によって上書きされます。

デフォルトでは、yii\data\ActiveDataProviderは、データベース接続としてdbアプリケーションコンポーネントを使用します。yii\data\ActiveDataProvider::$dbプロパティを設定することで、別のデータベース接続を使用できます。

SQLデータプロバイダー

yii\data\SqlDataProviderは、必要なデータをフェッチするために使用される生のSQLステートメントを扱います。sortおよびpaginationの指定に基づいて、プロバイダーはSQLステートメントのORDER BY句とLIMIT句を適宜調整し、要求されたページのデータのみを目的の順序でフェッチします。

yii\data\SqlDataProviderを使用するには、sqlプロパティとtotalCountプロパティを指定する必要があります。例えば、

use yii\data\SqlDataProvider;

$count = Yii::$app->db->createCommand('
    SELECT COUNT(*) FROM post WHERE status=:status
', [':status' => 1])->queryScalar();

$provider = new SqlDataProvider([
    'sql' => 'SELECT * FROM post WHERE status=:status',
    'params' => [':status' => 1],
    'totalCount' => $count,
    'pagination' => [
        'pageSize' => 10,
    ],
    'sort' => [
        'attributes' => [
            'title',
            'view_count',
            'created_at',
        ],
    ],
]);

// returns an array of data rows
$models = $provider->getModels();

情報: totalCountプロパティは、データをページネーションする必要がある場合にのみ必要です。これは、sqlを介して指定されたSQLステートメントが、現在要求されているページのデータのみを返すようにプロバイダーによって変更されるためです。プロバイダーは、利用可能なページ数を正しく計算するために、データアイテムの総数を把握する必要があります。

配列データプロバイダー

yii\data\ArrayDataProviderは、大きな配列を扱う場合に最適です。このプロバイダーを使用すると、1つ以上の列でソートされた配列データのページを返すことができます。yii\data\ArrayDataProviderを使用するには、allModelsプロパティを大きな配列として指定する必要があります。大きな配列の要素は、連想配列(例:DAOのクエリ結果)またはオブジェクト(例:Active Recordインスタンス)のいずれかになります。例えば、

use yii\data\ArrayDataProvider;

$data = [
    ['id' => 1, 'name' => 'name 1', ...],
    ['id' => 2, 'name' => 'name 2', ...],
    ...
    ['id' => 100, 'name' => 'name 100', ...],
];

$provider = new ArrayDataProvider([
    'allModels' => $data,
    'pagination' => [
        'pageSize' => 10,
    ],
    'sort' => [
        'attributes' => ['id', 'name'],
    ],
]);

// get the rows in the currently requested page
$rows = $provider->getModels();

注意: アクティブデータプロバイダーおよびSQLデータプロバイダーと比較して、配列データプロバイダーは、すべてのデータをメモリにロードする必要があるため、効率が劣ります。

データキーの扱い

データプロバイダーによって返されたデータアイテムを使用する場合、各データアイテムを一意のキーで識別する必要があることがよくあります。たとえば、データアイテムが顧客情報を表す場合、各顧客データのキーとして顧客IDを使用することができます。データプロバイダーは、yii\data\DataProviderInterface::getModels()によって返されたデータアイテムに対応するキーのリストを返すことができます。例えば、

use yii\data\ActiveDataProvider;

$query = Post::find()->where(['status' => 1]);

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

// returns an array of Post objects
$posts = $provider->getModels();

// returns the primary key values corresponding to $posts
$ids = $provider->getKeys();

上記の例では、yii\data\ActiveDataProvideryii\db\ActiveQueryオブジェクトを提供しているため、主キー値をキーとして返すのに十分なインテリジェンスがあります。また、yii\data\ActiveDataProvider::$keyを列名またはキー値を計算するcallableで構成することにより、キー値をどのように計算するかを明示的に指定することもできます。例えば、

// use "slug" column as key values
$provider = new ActiveDataProvider([
    'query' => Post::find(),
    'key' => 'slug',
]);

// use the result of md5(id) as key values
$provider = new ActiveDataProvider([
    'query' => Post::find(),
    'key' => function ($model) {
        return md5($model->id);
    }
]);

カスタムデータプロバイダーの作成

独自のカスタムデータプロバイダークラスを作成するには、yii\data\DataProviderInterfaceを実装する必要があります。より簡単な方法は、yii\data\BaseDataProviderから拡張することで、コアのデータプロバイダーロジックに集中できます。特に、主に次のメソッドを実装する必要があります。

  • prepareModels(): 現在のページで利用可能になるデータモデルを準備し、それらを配列として返します。
  • prepareKeys(): 現在利用可能なデータモデルの配列を受け取り、それらに関連付けられたキーを返します。
  • prepareTotalCount: データプロバイダー内のデータモデルの総数を示す値を返します。

以下は、CSVデータを効率的に読み込むデータプロバイダーの例です。

<?php
use yii\data\BaseDataProvider;

class CsvDataProvider extends BaseDataProvider
{
    /**
     * @var string name of the CSV file to read
     */
    public $filename;
    
    /**
     * @var string|callable name of the key column or a callable returning it
     */
    public $key;
    
    /**
     * @var SplFileObject
     */
    protected $fileObject; // SplFileObject is very convenient for seeking to particular line in a file
    
 
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        parent::init();
        
        // open file
        $this->fileObject = new SplFileObject($this->filename);
    }
 
    /**
     * {@inheritdoc}
     */
    protected function prepareModels()
    {
        $models = [];
        $pagination = $this->getPagination();
 
        if ($pagination === false) {
            // in case there's no pagination, read all lines
            while (!$this->fileObject->eof()) {
                $models[] = $this->fileObject->fgetcsv();
                $this->fileObject->next();
            }
        } else {
            // in case there's pagination, read only a single page
            $pagination->totalCount = $this->getTotalCount();
            $this->fileObject->seek($pagination->getOffset());
            $limit = $pagination->getLimit();
 
            for ($count = 0; $count < $limit; ++$count) {
                $models[] = $this->fileObject->fgetcsv();
                $this->fileObject->next();
            }
        }
 
        return $models;
    }
 
    /**
     * {@inheritdoc}
     */
    protected function prepareKeys($models)
    {
        if ($this->key !== null) {
            $keys = [];
 
            foreach ($models as $model) {
                if (is_string($this->key)) {
                    $keys[] = $model[$this->key];
                } else {
                    $keys[] = call_user_func($this->key, $model);
                }
            }
 
            return $keys;
        }

        return array_keys($models);
    }
 
    /**
     * {@inheritdoc}
     */
    protected function prepareTotalCount()
    {
        $count = 0;
 
        while (!$this->fileObject->eof()) {
            $this->fileObject->next();
            ++$count;
        }
 
        return $count;
    }
}

データフィルターを使用したデータプロバイダーのフィルタリング

データのフィルタリングおよびデータウィジェットガイドの個別のフィルターフォームセクションで説明されているように、アクティブデータプロバイダーの条件を手動で構築できますが、柔軟なフィルター条件が必要な場合に非常に役立つデータフィルターがYiiにはあります。データフィルターは次のように使用できます。

$filter = new ActiveDataFilter([
    'searchModel' => 'app\models\PostSearch'
]);

$filterCondition = null;

// You may load filters from any source. For example,
// if you prefer JSON in request body,
// use Yii::$app->request->getBodyParams() below:
if ($filter->load(\Yii::$app->request->get())) { 
    $filterCondition = $filter->build();
    if ($filterCondition === false) {
        // Serializer would get errors out of it
        return $filter;
    }
}

$query = Post::find();
if ($filterCondition !== null) {
    $query->andWhere($filterCondition);
}

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

PostSearchモデルは、フィルタリングに許可されるプロパティと値を定義する目的を果たします。

use yii\base\Model;

class PostSearch extends Model 
{
    public $id;
    public $title;
    
    public function rules()
    {
        return [
            ['id', 'integer'],
            ['title', 'string', 'min' => 2, 'max' => 200],            
        ];
    }
}

データフィルターは非常に柔軟です。条件の構築方法や許可される演算子をカスタマイズできます。詳細については、yii\data\DataFilterのAPIドキュメントを確認してください。

タイプミスを見つけたり、このページを改善する必要があると思われる場合は?
githubで編集する !