CleverStyle Frameworkでデータベースを操作する

データベースを犠牲にして、一見すると、すぐに使用できる機能は非常に少ないように思えるかもしれません。 これは部分的に真実ですが、兵器庫が非常によく考えられ、タスクを解決し、パフォーマンスに焦点を合わせているという事実によって相殺されます。


さらに機能的なツールが必要な場合は、いつでも好きなようにインストールできます。複雑で遅いモンスターを切るよりもずっと簡単です。


この記事では主なインターフェースについて説明しますが、 cs\CRUDおよびcs\CRUD_helpersは今後もcs\CRUD_helpers


ORMおよびクエリビルダーなし


ORMはフレームワークのイデオロギーに適合しません(たとえば、依存関係のないdoctrine/ormdoctrine/*パッケージを考慮しなくても、フレームワークの1.5倍のサイズです)。


クエリビルダーも遠すぎます。たとえば、なぜ人々がこれを書くのか理解できません(Laravel 5.2)。


 DB::table('users')->where('name', 'John')->first() 

これの代わりに:


 SELECT * FROM `users` WHERE `name` = 'John' LIMIT 1 

または(Yii2):


 new \yii\db\Query()) ->select(['id', 'email']) ->from('user') ->where(['last_name' => 'Smith']) ->limit(10) 

再び、代わりに:


 SELECT `id`, `email` FROM `user` WHERE `last_name` = 'Smith' LIMIT 10 

読みやすさ(IMHO)はさらに悪く、構文の強調表示、構文チェック、静的分析、およびコマンドとフィールドの自動補完(IDEが構成されている場合)はありません。クエリを複雑にする場合、クエリビルダーの複雑さを理解するよりも純粋なSQLを記述する方が簡単です。


そのため、このアプローチで明らかになりました-SQLを作成します。


SQLは異なります


執筆時点のフレームワーク(バージョン5.32.x)は、MySQL、SQLite、PostgreSQLの3つのデータベースエンジンをサポートしています。
ここでの問題は、これらのDBMSでサポートされている構文が、よく使用されるものであっても100%オーバーラップしないことです。


このフレームワークは、MySQLダイアレクトの一部を透過的に変換することにより、SQLiteおよびPostgreSQLに適したものになります。


ほとんどの場合、次の例はドキュメントを複製します


Sqlite

非常に小さな非互換性は1つだけです。


 --  INSERT IGNORE INTO `table_name` ( `text` ) VALUES ( ? ) --  INSERT OR IGNORE INTO `table_name` ( `text` ) VALUES ( ? ) 

PostgreSQL

ここではすべてがより複雑ですが、それにもかかわらず、単純な変換は依然として非常に単純です。


最初は引用符です:


 --  SELECT `id` FROM `table_name` --  SELECT "id" FROM "table_name" 

その後、再度INSERT IGNORE INTO場合、 INSERT INTO ... ON CONFLICT DO NOTHINGに変わります(したがって、フレームワークを使用するにはPostgreSQL 9.5以降が必要です)。


 --  INSERT IGNORE INTO "table_name" ( "text" ) VALUES ( ? ) --  INSERT INTO "table_name" ( "text" ) VALUES ( ? ) ON CONFLICT DO NOTHING 

別の同様のREPLACE INTOコマンドは、大幅に長いINSERT INTO ... ON CONFLICT ON CONSTRAINT "{table_name}_primary" DO UPDATE SET ...書き換えられINSERT INTO ... ON CONFLICT ON CONSTRAINT "{table_name}_primary" DO UPDATE SET ...


 --  REPLACE INTO "table_name" ( "id", "item", "value" ) VALUES ( ?, ?, ? ) --  INSERT INTO "table_name" ( "id", "item", "value" ) VALUES ( ?, ?, ? ) ON CONFLICT ON CONSTRAINT "table_name_primary" DO UPDATE SET "id" = EXCLUDED."id", "item" = EXCLUDED."item", "value" = EXCLUDED."value" 

この場合、フレームワークは、テーブル名と接尾辞_primary持つテーブルのconstraintがあることを期待していることに注意することが重要です(たとえば、システムテーブル[prefix]users場合)。


 ALTER TABLE ONLY "[prefix]users" ADD CONSTRAINT "[prefix]users_primary" PRIMARY KEY ("id"); 

最後のニュアンスは、PostgreSQLがサーバーで準備された式を受け取りたい形式に関連しているため、常に使用できます?


 --  SELECT "id" FROM "table_name" WHERE `number` > ? AND `age` < ? LIMIT ? --  SELECT "id" FROM "table_name" WHERE `number` > $1 AND `age` < $2 LIMIT $3 

一般的なデータベースについて少し


フレームワークには、最初は複数のデータベースが存在する可能性があるという概念があります。 各データベースは、異なるエンジンまたは同じエンジンを異なる構成で使用できます。 データベースミラーと、マスター/マスターおよびマスター/スレーブ構成でのクエリの単純な配布もサポートされています。


データベースを使用するモジュールは、 meta.json 2でデータベースに関連するキーを示します(システムモジュールの例)。


 { ... "db" : [ "keys", "texts", "users" ], "db_support" : [ "MySQLi", "PostgreSQL", "SQLite" ], ...} 

db_support 、モジュールが基本的に動作できるエンジンdb_support示され、 dbインストール中に既存のデータベースのいずれかに関連付けられるデータベースdb名前が示されます。


タスクに最適なデータベースを選択できるようにするために、異なる名前が使用されます。 もちろん、テーブルは、異なるデータベース間でJOIN行わないように分散する必要があります。


後で、名前に関連付けられたデータベースのIDを取得する必要がある場合、これを実行できます。


 $db_id = \cs\Config::instance()->module('System')->db('users'); 

次に、識別子を使用して、目的のデータベースへの接続を持つオブジェクトを取得します。


 $write_connection = \cs\DB::instance()->db_prime($db_id); $read_connection = \cs\DB::instance()->db($db_id); 

すでにここで、開発者はデータベースに何かを書き込むかどうかを明確に示します。 適切な構成のミラーの選択はこれに依存します。


DBAL


ここでは、原則を理解するとすぐにすべてが簡単になります。目を閉じて、非常に生産的にクエリを作成できるようになります。


簡単なクエリ実行

簡単なクエリ実行:


 $result = $read_connection->q('SELECT `id` FROM `table_name`'); 

qquery略です。 このメソッドには、いくつかの構文オプションがあります。


 ->q($query_string : string) ->q($query_string : string, ...$parameters : array) ->q($query_string : string, $parameters : array) ->q($query_string : string[]) ->q($query_string : string[], ...$parameters : array) ->q($query_string : string[], $parameters : array) 

クエリ自体は、サーバー側で準備された式として使用できます。


 $write_connection->q( [ 'DELETE FROM `items` WHERE `id` = ?' 'DELETE FROM `items_tags` WHERE `item` = ?' ], $item_id ); 

sprintf()関数の構文としてのクライアント側のフォーマットも同様です。


 $write_connection->q( [ 'DELETE FROM `items` WHERE `id` = %d' "DELETE FROM `items_tags` WHERE `item` = '%s'" ], $item_id ); 

最後の例では、置換の前にデータがそれに応じて処理されるため、 '%s'はSQLインジェクションがありません。


サーバー側の準備された式では、すべての引数を使用できるわけではありません(ネイティブインターフェイスを直接使用する場合とは異なります)。


 $write_connection->q( [ "DELETE FROM FROM `[prefix]articles` WHERE `id` = ?", "DELETE FROM FROM `[prefix]articles_comments` WHERE `article` = ? OR `date` < ?", "DELETE FROM FROM `[prefix]articles_tags` WHERE `article` = ?" ], [ $article_to_delete, time() - 24 * 3600 ] ); 

データサンプリング

2番目の便利な方法は、データを直接取得する方法です。


 $read_connection->f($result); 

ffetch略です。 このメソッドには、いくつかのオプションパラメータもあります。


 ->f($query_result, $single_column = false : bool, $array = false : bool, $indexed = false : bool) 

$single_column === true列の配列ではなく$single_column === trueは、最初の列のスカラー値を返します。


 $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` = 1') ); // ['id' => 1] $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` = 1'), true ); // 1 

$array === true 1行ではなく$array === trueすべてをカウントし、結果を配列として返します。


 $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` < 3'), false, true ); // [['id' => 1], ['id' => 2]] $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` = 1'), true, true ); // [1, 2] 

$indexed === trueは、連想配列ではなくインデックス付き配列を返します。


 $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` < 3'), false, false, true ); // [1] $read_connection->f( $read_connection->q('SELECT `id` FROM `table_name` WHERE `id` = 1'), false, true, true ); // [[1], [2]] 

そして今、興味深い略語


 ->qf() === ->f(->q(...)) ->qfa() === ->f(->q(...), false, true) ->qfs() === ->f(->q(...), true) ->qfas() === ->f(->q(...), true, true) 

a from array 、およびs from single


たとえば、次の2つの構造は同等ですが、2番目の構造は読みやすく、保守がはるかに簡単です。


 $read_connection->f( $read_connenction->q('SELECT `id` FROM `table_name` WHERE `id` = ?', 1), true, true ); // [1, 2] $read_connection->qfas( 'SELECT `id` FROM `table_name` WHERE `id` = ?', 1 ); // [1, 2] 

データ挿入

データを挿入するための別の便利な方法があります。


 $write_connection->insert( 'INSERT INTO `table_name` (`id`, `value`) VALUES (?, ?)', [ [1, 12], [2, 13], [3, 14] ] ); 

構文は次のとおりです。


 ->insert($query : string, $parameters : array|array[], $join = true : bool) 

$join === true 、上記の例は次のように書き換えられます。


 $write_connection->q( 'INSERT INTO `table_name` (`id`, `value`) VALUES (?, ?), (?, ?), (?, ?)', [ 1, 12, 2, 13, 3, 14 ] ); 

それ以外の場合、行は1つずつ挿入されます。


その他の方法

便利なメソッドがいくつかあります。たとえば、 ->id()は最後に挿入された行の識別子を返します。- ->transaction()使用すると、トランザクションで操作の実行をラップできます。


 $write_connection->transaction(function ($c) { // `$c`   ,   `$write_connection` $c->insert(...); //      ,       $c->transaction(function ($c) { $c->id(); }); //      `false`     ,     }); 

サポートされているすべてのデータベースおよびその他の補助的なものに対して同じように機能するテーブルおよびテーブル内の列のリストを取得するためのメソッドがあります。


一般に、詳細についてはドキュメントを参照してください。


これは、データベースの基本的な作業の概要です。


多くのモジュールは、直接要求ではなく、便利な特性cs\CRUDおよびcs\CRUD_helpers使用します。
最初の1つは、ボンネットの下での4つの単純なデータベース操作に加えて、多言語、正規化、一部のデータ処理(たとえば、書き込みおよび読み取り中にJSONを前後に変換する)、ダウンロードしたファイルの処理、およびリンクテーブル(1対1また、言及されたすべての善意の支援を受けて。


2番目の特性には(実際には、フィルターである)要素を検索するメソッドがあります。ここでも、いくつかのフィールド/テーブルの多言語性を考慮すると、関連テーブルのサポートも含まれます。


両方の特性の説明を記事に追加すると、一度に大きすぎるため、次回になります。


インターフェイスの利便性についての考えと、より便利な(あなたの意見では)代替の例は歓迎です。これらの点を建設的な方法で議論し、将来のリリースでのフィードバックを考慮したいと思います。


GitHubリポジトリ
» フレームワークのドキュメント



Source: https://habr.com/ru/post/J308204/


All Articles