はじめに
開発でYiiフレームワークを使用する人は誰でも、データベースへのアクセスとして、ほとんどの場合、組み込みORMコンポーネントActiveRecordを使用することを知っています。 しかし、ある時点で、複数のリモートサーバーに物理的に配置されたデータを操作する必要があるという事実に直面しました。 これは、私が働いている会社の分散ネットワークでFTPおよびRadiusユーザーを管理するための集中型システムの開発であり、支店と中央オフィスを組み合わせていました。
実際、さまざまなネットワーク上のサーバーにあるデータを操作する必要がある状況は多くあります。 ちょっとした考えから、HTTPプロトコルとそれに基づく
RESTアプローチを使用することになりました。 最初と主な2つの理由がありました。RESTを使用してサーバーとクライアントの両方のパーツを開発する方法を学ぶためです。 2つ目は、HTTPプロトコルを使用することの利便性です。私の場合、ほとんどのファイアウォールで開かれており、プロキシサーバーも使用できます。
ソースコードの一部を記事の本文に挿入する必要があったため、かなりの量になりました。
降りる
そのため、決定が下されました。 一見すると、奇妙な束ができます。 通常、REST APIはサイトではなくモバイルアプリケーションで使用されます。 ユーザーが私のアカウント管理ページにHTTPリクエストを行い、ページを提供するWebサーバーが、アカウントが直接配置されているサーバーにさらに別のHTTPリクエストを行うことがわかりました。 また、それらを管理するためのREST APIも実装しました。
サーバー側
既製のソリューションの1つ、たとえば
restfullyiiを使用することは可能でしたが、私は研究し、それがどのように機能するか、または内部から機能する必要があるかを理解したいと思っていました。 したがって、私たちは子供の天才の発明に従事します。
サーバー側を自分で作成する方法
は、公式プロジェクトwikiで非常に詳しく説明さ
れています 。 この決定は基礎として採用されました。
Yii RESTアプリケーションのメインマジックは、
protected / config / main.phpの urlManager設定で発生します。
'urlManager' => array( 'urlFormat' => 'path', 'showScriptName' => false, 'rules' => array(
次の形式のリクエストを送信するルールは次のとおりです。
POST api.domain.ru/api/users
で
POST api.domain.ru/api/create?model=users
この例で気に入らなかったのは、実際にモデルがスイッチブロックに読み込まれるときのアプローチです。 これは、プロジェクトに新しいモデルを追加し、コントローラーを変更する場合、より普遍的なソリューションを作成したかったことを意味します。 その結果、動作中のモデルを作成するために、フォームの構築が使用されました。
if (isset($_GET['model'])) $_model = CActiveRecord::model(ucfirst($_GET['model']));
次に、私のケースで判明したコントローラーの完全なリストを提供します(クラスのヘルパーメソッドの実装を意図的に削除しました。これは、変更なしで上記のリンクから例から取られました。さらに、章の最後にYiiアプリケーションの完全なソースへのリンクがあります):
<?php class ApiController extends Controller { Const APPLICATION_ID = 'ASCCPE'; private $format = 'json'; public function filters() { return array(); } public function actionList() { if (isset($_GET['model'])) $_model = CActiveRecord::model(ucfirst($_GET['model'])); if (isset($_model)) { $_data = $_model->summary($_GET)->findAll(); if (empty($_data)) $this->_sendResponse(200, sprintf('No items were found for model <b>%s</b>', $_GET['model'])); else { $_rows = array(); foreach ($_data as $_d) $_rows[] = $_d->attributes; $this->_sendResponse(200, CJSON::encode($_rows)); } } else { $this->_sendResponse(501, sprintf( 'Error: Mode <b>list</b> is not implemented for model <b>%s</b>', $_GET['model'])); Yii::app()->end(); } } public function actionView() { if (isset($_GET['model'])) $_model = CActiveRecord::model(ucfirst($_GET['model'])); if (isset($_model)) { $_data = $_model->findByPk($_GET['id']); if (empty($_data)) $this->_sendResponse(200, sprintf('No items were found for model <b>%s</b>', $_GET['model'])); else $this->_sendResponse(200, CJSON::encode($_data)); } else { $this->_sendResponse(501, sprintf( 'Error: Mode <b>list</b> is not implemented for model <b>%s</b>', $_GET['model'])); Yii::app()->end(); } } public function actionCreate() { $post = Yii::app()->request->rawBody; if (isset($_GET['model'])) { $_modelName = ucfirst($_GET['model']); $_model = new $_modelName; } if (isset($_model)) { if (!empty($post)) { $_data = CJSON::decode($post, true); foreach($_data as $var => $value) $_model->$var = $value; if($_model->save()) $this->_sendResponse(200, CJSON::encode($_model)); else {
このアプローチでは、適切なモデルの準備が必要です。 たとえば、
ActiveRecordに組み込まれた配列変数
属性は、データベースのテーブル構造のみに基づいて形成されます。 選択に関連テーブルのフィールドまたは計算可能なフィールドを含める必要がある
場合 、
getAttributesメソッドと、必要に
応じて、モデルのhasAttributeをオーバーロードする必要
があります 。 例として、
getAttributesの実装:
public function getAttributes($names = true) { $_attrs = parent::getAttributes($names); $_attrs['quota_limit'] = $this->limit['bytes_in_avail']; $_attrs['quota_used'] = $this->tally['bytes_in_used']; return $_attrs; }
また、ページネーションとソートが正しく機能するように、モデルに名前付きスコープ
サマリーを作成する必要があります。
public function summary($_getvars = null) { $_criteria = new CDbCriteria(); if (isset($_getvars['count'])) { $_criteria->limit = $_getvars['count']; if (isset($_getvars['page'])) $_criteria->offset = ($_getvars['page']) * $_getvars['count']; } if (isset($_getvars['sort'])) $_criteria->order = str_replace('.', ' ', $_getvars['sort']); $this->getDbCriteria()->mergeWith($_criteria); return $this; }
モデルの全文:
<?php class Ftpuser extends CActiveRecord {
完全な幸福のために欠けているのは、フォーム
/ users / 156 / recordsのネストされたリクエストの処理を処理する方法がないことです。しかし、これはフレームワークであり、CMSではありません。 私の場合は簡単です、それは必要ありませんでした。
サーバー部分が完了したら、クライアントに移動します。 興味がある人のために、Yiiサーバーサイドアプリケーションの完全なソースコードを
ここに投稿し
ます 。 リンクをより確実に配置するためのより実用的な提案がある場合、リンクがどのくらいの期間存続するかわかりません-コメントに注意してください。
クライアント部
私の自転車を書かないために、小さな検索が実行され、優れた
ActiveResource拡張機能が見つかりました。 著者によると、インスピレーションの源は、Ruby on Railsでの
ActiveResourceの実装でした。 このページには、インストール方法と使用方法の簡単な説明があります。
しかし、ほとんどすぐに
ActiveRecordと互換性のあるコンポーネントであることが
わかりましたが 、Yiiウィジェット
GridViewまたは
ListViewで使用するには、
ActiveDataProviderと互換性のあるコンポーネント
が必要でした。
EActiveResourceDataProviderと
EActiveResourceQueryCriteriaを含む
別のブランチで行われた改善、および拡張機能の作成者が参加した
フォーラムスレッドでのそれらの議論に簡単な検索をもたらしました。
ESortおよび
EActiveResourceDataProviderの改訂版
も公開され
ました 。
ソリューションのすべての優雅さにもかかわらず、ファイルは完全ではありませんでした。 問題は、グリッド内のページネーションコンポーネントの誤動作でした。 ソースの簡単な調査により、拡張機能で使用されるオフセットはレコード数で表される実際のオフセットであり、
GridViewのページネーションではページ番号が使用されることが示されました。 ページごとに10個のレコードを設定すると、ページ2に移動すると、ページ20にスローされたことが判明しました。コードを調べて編集します。 これを行うには、
buildQueryStringメソッドの本体の
保護されたファイル
/ extensions / EActiveResource / EActiveResourceQueryCriteria.phpで、次の編集を行います。
if($this->offset>0)
その後、
EActiveResourcePaginationから
getOffsetメソッドのオーバーロードを不要にする必要があります。
したがって、RESTデータソースを使用してアプリケーションを作成する場合、必要なモデルを手動で作成する必要があり、残りはGIIを介して問題なく作成されます。
また、いくつかのサーバーでの動作に注目したいと思います。 最初は、リモートREST APIへの接続は構成に記述されているため、デフォルトではサイトで1つの接続のみを使用できます。 接続情報をデータベーステーブルに保存し、
ActiveResourceコンポーネントで使用するには、そこからオーバーロードされた
getConnectionメソッドを使用して子孫を作成する必要がありました(FTPユーザーの場合、サーバーデータはFTPServersモデルで記述されたテーブルに保存されます):
abstract class EActiveResourceSelect extends EActiveResource { public function getConnection() { $_server_id = Yii::app()->session['ftp_server_id']; $_db_params = array(); if (isset($_server_id)) { $_srv = FTPServers::model()->findByPk($_server_id); if (isset($_srv)) $_db_params = $_srv->attributes; else Yii::log('info', "No FTP server with ID: $_server_id were found"); } else { $_srv = FTPServers::model()->find(); if (isset($_srv)) { $_db_params = $_srv->attributes; Yii::app()->session['ftp_server_id'] = $_srv->id; } else Yii::log("No FTP servers were found", CLogger::LEVEL_INFO); } self::$_connection = new EActiveResourceConnection(); self::$_connection->init(); self::$_connection->site = $_db_params['site']; self::$_connection->acceptType = $_db_params['acceptType']; self::$_connection->contentType = $_db_params['contentType']; if (isset($_db_params['username']) && isset($_db_params['password'])) { self::$_connection->auth = array( 'type' => 'basic', 'username' => $_db_params['username'], 'password' => $_db_params['password'], ); } return self::$_connection; } }
クライアント部分のさらなる開発は、通常の
ActiveRecordを使用した開発と大きな違いはありませんでした。これは、私の意見では、
ActiveResource拡張の主な魅力です。
この記事がお役に立てば幸いです。