SymfonyのREST API、FOSRestBundle + GlavwebDatagridBundle

みなさんこんにちは! 前の記事で、 FOSRestBundle + JMSSerializerでのアセンブリを使用したREST APIでの経験について話しました。 今日は、FOSRestBundle + GlavwebDatagridBundleでREST APIを開発するアプローチを共有します。



新しい挑戦。



RESTプロジェクトの数の増加に伴い、APIの要件が増加しました。 多くの場合、このAPIのユーザーは、ソリューションに特定のニーズがあるサードパーティの開発者です。 そのため、たとえば、すべての人がリクエストでオブジェクトのネストの設定を渡すことを好まなかった。 すぐに使用できるフィルタリングオプションもありませんでした。



しかし、API開発アプ​​ローチを改訂する最も重要な理由はパフォーマンスの問題でした。 これらの問題の理由は、SQLが最適に記述されていないか、ドクトリンとシリアライザーで結果を処理するための追加コストでした。



SQLを使用して最適化する方法が明確な場合、ドクトリンとシリアライザのパフォーマンスの低下はさらに困難です。 データベースクエリが完了すると、次のようになります。



    PDO ->     ( , ) ->         jmsserializer ->    json  .
      
      





このパスを次のように短縮することが決定されました。



     PDO ->       ( , ) ->  ,     .
      
      





オブジェクトへの水分補給を放棄したため、JMSSerializerも放棄する必要がありました。 JMSSerializerは、APIに対して多くの有用なことを行いました(これについては、 以前の記事で説明しました)。 さらに、彼は別の重要な作業を実行しました-ネストされたエンティティーが結合で定義されていない場合、それらをロードします



jmsserializerの拒否の結果として生じた機能の「穴」を閉じるために、GlavwebDatagridBundleが開発されました。



GlavwebDatagridBundleの概要



GlavwebDatagridBundleの目的を次のように簡単に決定します。フィルター、制限、オフセットに基づいて、正しい方法でフォーマットされたデータを受信します。 分かりませんか? 知ってるよ。 そして今、まず最初に。



GlavwebDatagridBundleは、次のコンポーネントに基づいています。



Datagridbuilder



DatagridBuilderは、DataSchema、フィルター、および追加のクエリパラメーターを使用してQueryBuilderを形成します。



 $datagridBuilder = $this->get('glavweb_datagrid.doctrine_datagrid_builder'); $datagridBuilder ->setEntityClassName(Entity::class) ->setFirstResult(0) ->setMaxResults(100) ->setOrderings(['id'=>'DESC']) ->setOperators(['field1' => '=']) ->setDataSchema('entity.schema.yml', 'entity/list.yml') ;
      
      





次に、Datagridオブジェクトを返します。



 $datagrid = $datagridBuilder->build($paramFetcher->all());
      
      





フィルター



フィルターを使用すると、リクエストで追加の条件を指定できます。



 // Define filters $datagridBuilder ->addFilter('field1') ->addFilter('field2') ;
      
      





データグリッド



Datagridは、DatagridBuilderからQueryBuilderを取得し、リストとしてデータを返すことができます。

 $datagrid->getList();
      
      





そして一行として:



 $datagrid->getItem();
      
      





Datachema



DataSchemaは、yaml形式のデータセットを定義します。



 schema: class: AppBundle\Entity\Article db_driver: orm properties: id: # integer type: # ArticleType name: # string body: # text publish: # boolean publishAt: # datetime
      
      





フィールドと関係を定義します。



 schema: class: AppBundle\Entity\Movie db_driver: orm properties: ... tags: # AppBundle\Entity\Tag join: left properties: id: # integer
      
      





追加条件:



 schema: class: AppBundle\Entity\Article db_driver: orm conditions: ["{{alias}}.publish = true"] properties: ....
      
      





関係の追加条件を定義することもできます。



 schema: class: AppBundle\Entity\Movie db_driver: orm properties: ... articles: # AppBundle\Entity\Article conditions: ["{{alias}}.publish = true"] join: none properties: ....
      
      





DataSchemaは、結合として構成されていない場合、ネストされたエンティティをロードする機能も実装します。



 schema: class: AppBundle\Entity\Movie db_driver: orm properties: ... articles: # AppBundle\Entity\Article join: none properties: id: # integer
      
      





DataSchemaは、DatagridBuilderでクエリを構築し、データをDatagridに変換するために使用されます。



範囲



スコープを使用すると、DataSchemaで定義されたデータセットを絞り込むことができます。



たとえば、スコープを使用して、レコードのリスト(article \ list.yml)に少量のデータを定義できます。



 scope: id: name:
      
      





1つのレコードを表示するための完全なデータセット(記事\ view.yml):



 scope: id: type: name: body: publish: publishAt: movies: # AppBundle\Entity\Movie id: name:
      
      







言葉からデモまで。



デモ用に特別に作成された架空のapi映画館に注目します。 エンティティのエンティティとフィールドは、APIの可能性を最大限にするような方法で選択されます。



githubに投稿されたデモプロジェクト。



プロジェクトをローカルにデプロイするには、3つの簡単な手順を実行する必要があります。



1.コンポーザーを使用してプロジェクトを作成します。



 composer create-project glavweb/rest-demo-app
      
      





2.移行を実行します。



 php bin/console d:m:m -n
      
      





3.フィクスチャを実行します。



 php bin/console h:d:f:l -n
      
      







エンティティ



次のエンティティが利用可能です。



それらの間には、次の接続があります。

  1. 1対多(映画セッション、コメント);
  2. 多対一(映画のグループ);
  3. 多対多(タグ);
  4. 1対1(映画に関する情報)。


追加機能:

  1. 未公開のコメントは、著者のみが利用できます。
  2. 管理者とモデレーターは、すべてのコメントにアクセスできます。
  3. 管理者またはモデレーターのグループに属するユーザーが作成したコメントは自動的に公開されます。


ユーザーとユーザーグループ



ユーザーグループ:

  1. 管理者 管理システム内のすべてのレコードへのフルアクセスがあります。
  2. モデレーター 管理システムへのアクセスは制限されています(コメントの編集と削除のみが可能です)。
  3. ユーザー 管理システムにアクセスできません。 APIアプリケーションを介して、新しいコメントを追加したり、独自のコメントのみを編集および削除できます。


プリセットユーザーセット:

  1. 管理者 グループ:管理者。 ログイン:admin、パスワード:qwerty
  2. モデレーター グループ:モデレーター。 ログイン:ログイン:モデレーター、パスワード:qwerty
  3. ユーザー1.グループ:ユーザー。 ログイン:ログイン:ユーザー-1、パスワード:qwerty
  4. ユーザー2.グループ:ユーザー。 ログイン:ログイン:ユーザー-2、パスワード:qwerty


シナリオ



シナリオ1。



ユーザーはログインしていません。 モデレーターによって確認されたすべての映画とコメントは、彼が利用できます。 ユーザーはコメントを作成できず、管理システムにアクセスできません。



シナリオ2。



ユーザーは許可されています。 ユーザーグループに属します。 モデレーターによって確認されたコメントと自身のコメントも利用できます。 ユーザーはコメントを作成し、コメントに画像を添付し、コメントを編集および削除できます。 管理システムは利用できません。



シナリオ3。



ユーザーは許可されています。 「モデレーター」グループに属します。 彼はすべての映画とコメントを入手できます。 モデレーターはコメントを作成し、コメントに画像を添付し、コメントを編集および削除できます。 管理システムの限られた機能が利用可能です-コメントへのアクセス。



シナリオ4。



ユーザーは許可されています。 管理者グループに属します。 彼はすべての映画とコメントを入手できます。 コメントを作成し、コメントに画像を添付し、コメントを編集および削除できます。 完全な管理システム機能が利用可能です。



管理システム



管理システムは、管理者とモデレーターが利用できます。



URL:/管理者



APIリクエストの例



APIドキュメントへのURL:/ api / doc



特別なパラメーター



_scope



このパラメーターを使用すると、デフォルトとは異なるミサゴを判別できます。 たとえば、idとnameのみで構成される映画の短縮リストを取得します。



 GET /api/movies?_scope=list_short
      
      





_oprs



「_oprs」パラメーターを使用すると、フィルターに渡される値の演算子を定義できます。 つまり 演算子にパラメーターの最初の文字をフィルターに渡すことができない場合、これはパラメーター「_oprs」を使用して実行できます。 たとえば、これは「NOT IN」を渡す必要がある場合に配列に役立ちます。



 GET /api/articles?_oprs[type]=<>&type=photo_report
      
      





_sort



このパラメーターを使用して、ソート順を決定できます。 たとえば、すべての記事を名前(最後から最初の文字)とID(最小から最大)でソートします。



 GET /api/articles?_sort[name]=DESC&_sort[id]=ASC
      
      





_offset



「_offset」パラメーターを使用して、リストの位置を決定できます。 たとえば、11日から始まるすべてのレコードを取得するには、「_ offset = 10」を渡します。



 GET /api/articles?_offset=10
      
      





_limit



このパラメーターは、リストの制限を定義します。 たとえば、最初の10件の記事を取得します。



 GET /api/articles?_limit=10
      
      





フィルターの例



文字列フィルター



文字列フィルターの場合、デフォルトでは、検索は部分文字列の出現によって実行されます。



 GET /api/articles?name=Dolorem
      
      





反対が必要な場合、つまり 特定の部分文字列が存在しないすべてのエントリが見つかった場合、「!」を渡す必要があります。 値の前:



 GET /api/articles?name=!Dolorem
      
      





完全な比較が必要な場合は、値の前に「=」記号を置く必要があります。



 GET /api/articles?name==Dolorem+eaque+libero+maxime.
      
      





等しくない、次のように定義されます(値の前の文字「<>」):



 GET /api/articles?name=<>Dolorem+eaque+libero+maxime.
      
      





列挙型



列挙型は、完全な比較(=)によって検索されます。



タイプnewsの記事を見つけるには、ニュースを渡す必要があります。



 GET /api/articles?type=news
      
      





このようなクエリは空の結果を返します。



 GET /api/articles?type=new
      
      





配列



値の配列(IN)でフィルタリングするには、次のように配列を渡す必要があります["name1"、 "name2"、 "name3 '...]。 たとえば、photo_reportおよびnewsタイプの記事を取得するには:



 /api/articles?type=["photo_report","news"]
      
      





newおよびphoto_reportを除くすべての記事を取得します。



 /api/articles?_oprs[type]=<>&type=["photo_report",+"news"]
      
      





日程



数値フィルターと日付フィルターの両方で、より多く/より少ない演算子を使用できます。 たとえば、2016-06-07以降の記事を取得します。



 /api/articles?publishAt=>2016-06-07
      
      





2016-06-07より前の記事を取得:



 /api/articles?publishAt=<2016-06-07
      
      





日付範囲は次のようにして取得できます。



 GET /api/articles?publishAt=[">2016-03-06","<2016-03-13"]
      
      







おわりに



これがコードでどのように実装されているかを知りたい人-githubへのリンク



All Articles