マルチストア。 初心者のWebプログラマ向けの記事

興味深い問題を解決した結果、多くの興味深い技術的解決策が生まれました。 そして、誰がエンジニアのためにそのような問題を発明または作成しますか? 答えはもちろんユーザーです。 そのため、この記事では、このような興味深い問題とその解決策について説明します。



だから。 一般的に、ユーザーによると、この問題は1つのメインオンラインストアと複数の追加ストアを作成する必要があるように見えます。 本店には商品の完全なデータベースがあります。 追加ストアの商品は、メインストアに商品のリストを要求することによって形成されます。 追加ストアからのリクエストで識別子を受け取ったメインストアは、適切な製品を提供します。



ここで、メインストアと追加ストアの製品データベースが同じサーバー上にある場合、エンジニアの目を通して問題を検討します。 追加店舗の数はわかりませんが、メイン店舗の負荷が大きいことは明らかです。 多数の異なる製品では、商品とテーブルの構造を正確に形成できません。 製品の特性は、そのカテゴリによって大きく異なります。 データを要約すると、メインストアのデザイナーのタスクは次のように聞こえます。



商品のサンプリング速度が非常に速く、商品の特性、その属性が動的になるように、メインストアのデータベースを設計します。



分割して征服する!





「与えられた」から、2つのエンティティの存在が続きます。 これはメインストアであり、オプションです。 それらをそれと呼びましょう。



プライマリ-PrimaryShop

オプション-SecondaryShop



また、追加ストア内から、メインデータにアクセスするために内部データと外部データの両方にアクセスする必要があることも明らかです。 便宜上、次の2つの関数を作成します。



/* *    ,            . * @shopId -   * @return false | object PrimaryGoods */ function PrimaryShop($shop = null) { global $primaryshop, $shopId; /* *  ,          *  ,    ,   *   shopId     *  ,      ,    */ if (!isset($shop) && !isset($shopId)) { die("  shopId  "); } else { $shopId = isset($shop) ? $shop : $shopId; } if (Database::Setup(Config::Get("primaryDbHost"), Config::Get("primaryDbName"), Config::Get("primaryDbUser"), Config::Get("primaryDbPass"))->Connect()) { return isset($primaryshop) ? $primaryshop : new PrimaryGoods($shopId); } else { die("      "); } } /* *    ,            * @return false | object SecondaryGoods */ function SecondaryShop() { if (Database::Setup(Config::Get("secondaryDbHost"), Config::Get("secondaryDbName"), Config::Get("secondaryDbUser"), Config::Get("secondaryDbPass"))->Connect()) { return new SecondaryGoods(); } else { die("      "); } }
      
      







したがって、各店舗の商品を扱う方法を実装するSecondaryGoodsおよびPrimaryGoodsクラス。



それだけですか? もちろん違います。 これらの関数はラッパーです。 彼らは私たちを最終目標に近づけません。 さらに推論してみましょう。 原則として、オンラインストアは、2つのモードでユーザーに製品を表示します。 リスト表示モードと製品カード表示モード。 リスト表示モードのデータを受信する頻度は明らかに非常に高く、製品カードを表示するためのデータを受信する頻度よりもはるかに高いです。 ここからは簡単な結論になります。 メインデータベースに特別なテーブル(製品リストビューモードで表示するための準備済みデータを含むテーブル)がある場合、データを受信する速度は、複数のテーブルを1つに結合してこのデータを生成する速度よりも速くなります。 カードモードで製品を表示するためのデータは、いくつかのテーブルを1つに結合することにより、標準的な方法で生成されます。



複数のテーブルを1つに結合することによる、メインデータベースへのクエリの例。 このようなリクエストは、接続のない単純なリクエストよりもアプリオリに遅くなります。



 --    SELECT * FROM `data_goods` AS `a` LEFT JOIN `data_goods_price` AS `b` ON `b`.`id` = `a`.`priceId` LEFT JOIN `data_goods_images` AS `c` ON `c`.`id` = `a`.`imagesId` LEFT JOIN `data_goods_attr` AS `d` ON `d`.`id` = `a`.`attrId` --  ,   SELECT * FROM `data_goods_short`
      
      







まとめると。 ラッピング関数を介して特定のストアを参照します。 商品のリストを含むページを作成するために、このために特別に設計された特別なテーブルを作成します。



マルチテナンシー





製品Aに以下の特性を持たせる方法





製品Bには次の特性がありました





商品のすべての可能な特性を事前に設定する方法はありません。 実際、空の列を持つ同じテーブル(Col1、Col2、... ColN)も同じではありません。



最初に思い浮かぶのは、追加のテーブル、参照を作成することです。 このテーブルには、製品グループに応じて、可能な製品属性のセットが格納されます。 製品カードを取得するプロセスは、2段階で取得されます。 ステージ1:可能な属性のリストを取得します。 ステージ2:このリストに従って、必要な表に目を向けて情報を収集します。 ご理解のとおり、プロセスは高速ではありません。 彼は合う? 答えはノーです。



製品カードの内容をさらに詳しく見ると、製品のプロパティを2つのテーブルにグループ化できます。 テーブルを1つにすると、data_goods_imagesにはすべての製品イメージのリストが含まれ、data_goods_attrテーブルには追加の属性が含まれます。



 CREATE TABLE IF NOT EXISTS `data_goods_full` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goodsId` int(11) NOT NULL, `categoryId` int(11) NOT NULL, `shopId` int(11) NOT NULL, `article` varchar(150) NOT NULL, `intro` varchar(255) NOT NULL, `name` varchar(150) NOT NULL, `description` text NOT NULL, PRIMARY KEY (`id`), KEY `categoryId` (`categoryId`), KEY `goodsId` (`goodsId`), KEY `shopId` (`shopId`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=501 ; CREATE TABLE IF NOT EXISTS `data_goods_images` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goodsId` int(11) NOT NULL, `shopId` int(11) NOT NULL, `type` enum('','','') NOT NULL, `value` varchar(150) NOT NULL, PRIMARY KEY (`id`), KEY `goodsId` (`goodsId`), KEY `shopId` (`shopId`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `data_goods_attr` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goodsId` int(11) NOT NULL, `type` enum('','','','') DEFAULT NULL, `value` varchar(150) NOT NULL, PRIMARY KEY (`id`), KEY `goodsId` (`goodsId`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
      
      







テーブルdata_goods_imagesおよびdata_goods_attrのtypeフィールドに注意してください。 このフィールドのタイプはENUMです。 このタイプは、リストから値を取得できます。 リクエストを作成し、これらの3つのテーブルを組み合わせて、製品のすべての特性を取得します



 SELECT * FROM `data_goods_full` AS `a` LEFT JOIN `data_goods_images` AS `b` ON `b`.`goodsId` = `a`.`goodsId` LEFT JOIN `data_goods_attr` AS `c` ON `c`.`goodsId` = `a`.`goodsId`
      
      







接続を確立せずに、特定のプロパティを個別に取得することもできます。 たとえば、小さな写真(製品B)のみを取得したい。製品識別子、100に等しくする



 SELECT * FROM `data_goods_images` WHERE `type` = '' AND `goodsId` = '100'
      
      







それでは、この章の冒頭で表明された質問に戻りましょう。 商品Aに1つのプロパティを設定し、商品Bに異なるプロパティを設定する方法。 この場合、小売価格、卸売価格、小売価格、特別など、追加のタイプの製品プロパティをdata_goods_attrテーブルに追加する必要があります。 これを行うには、次のコマンドを使用します。



 ALTER TABLE `data_goods_attr` CHANGE `type` `type` ENUM('','','','',' ',' ',' , ')
      
      







データベースに商品Aと商品B(goodsId 100とgoodsId 101)を追加します



 --     INSERT INTO `data_goods_full` (`id`, `goodsId`, `categoryId`, `shopId`, `article`, `intro`, `name`, `description`) VALUES (null, '100', '1', '1', '-100', ' ', ' ', ''); --   INSERT INTO `data_goods_images` (`id`, `goodsId`, `shopId`, `type`, `value`) VALUES (null, '100', '1', '1', '', 'small_a.png'), (null, '100', '1', '1', '', 'big_a.png'); --   INSERT INTO `data_goods_attr` (`id`, `goodsId`, `shopId`, `type`, `value`) VALUES (null, '100', '1', '1', '', 'XL'), (null, '100', '1', '1', '', ''), (null, '100', '1', '1', ' ', '100 .'), (null, '100', '1', '1', ' ', '125 .'); --     ,     --     INSERT INTO `data_goods_full` (`id`, `goodsId`, `categoryId`, `shopId`, `article`, `intro`, `name`, `description`) VALUES (null, '101', '1', '1', '-101', ' ', ' ', ''); --   INSERT INTO `data_goods_images` (`id`, `goodsId`, `shopId`, `type`, `value`) VALUES (null, '101', '1', '1', '', 'midle_b.png'), (null, '101', '1', '1', '', 'big_b.png'); --   INSERT INTO `data_goods_attr` (`id`, `goodsId`, `shopId`, `type`, `value`) VALUES (null, '101', '1', '1', ' ', '125 .'), (null, '101', '1', '1', ' , ', '120 .');
      
      







さて、データベースに入れたすべてのものを読んでみましょう。 小さい後

クエリ結果を操作すると、次の結果が得られます。

 Array ( [property] => stdClass Object ( [id] => 1 [categoryId] => 1 [article] => -100 [intro] =>  [name] =>   [text] => ) [images] => Array ( [0] => stdClass Object ( [imageType] =>  [imageValue] => small_a.png ) [1] => stdClass Object ( [imageType] =>  [imageValue] => big_a.png ) ) [attributes] => Array ( [0] => stdClass Object ( [attrType] =>  [attrValue] => XL ) [1] => stdClass Object ( [attrType] =>  [attrValue] =>  ) [2] => stdClass Object ( [attrType] =>   [attrValue] => 100 . ) [3] => stdClass Object ( [attrType] =>   [attrValue] => 125 . ) ) ) Array ( [property] => stdClass Object ( [id] => 2 [categoryId] => 1 [article] => -101 [intro] =>  [name] =>   [text] => ) [images] => Array ( [0] => stdClass Object ( [imageType] =>  [imageValue] => midle_b.png ) [1] => stdClass Object ( [imageType] =>  [imageValue] => big_b.png ) ) [attributes] => Array ( [0] => stdClass Object ( [attrType] =>   [attrValue] => 125 . ) [1] => stdClass Object ( [attrType] =>  ,  [attrValue] => 120 . ) ) )
      
      







おわりに





列挙型のフィールドを操作し、プロパティを削除および追加するには、これらのフィールドの値のリストを取得できる必要があります。 このリストは、SQL DESCRIBEコマンドを使用して取得できます。 以下に、そのような関数の例を示します。



  /* *    ,    *  type   images */ function GetImagesSet() { $query = "\n DESCRIBE `data_goods_images` `type`"; if ($result = Database::Exec($query)->Read(0, "Type")) { $result = str_replace(array("enum", "(", ")", "'"), array("", "", "", ""), $result); return $result ? explode(",", $result) : $result; } } /* *       . * , ..  .. */ function AddImagesSet($type = null) { if (isset($type)) { $result = $this->GetImagesSet(); if (!empty($result)) { foreach ($result as $item) { $enum[] = "'".$item."'"; } $enum = implode(",", $enum).","; } else { $enum = ""; } $enum = $enum.str_replace(" ", "", $type); $query = "\n ALTER TABLE `d ata_goods_images` CHANGE `type` `type` ENUM(".$enum.")"; return Database::Exec($query)->Read(); } else { return false; } } /* *    . * @example: '', '', '' DelImagesSet('') => '', '' * @return true | flase */ function DelImagesSet($type = null) { if (isset($type)) { foreach(self::GetImagesSet() as $value) { if ($type != $value) { $enum[] = "'".$value."'"; } } $query = "\n ALTER TABLE `data_goods_images` CHANGE `type` `type` ENUM(".implode(",", $enum).")"; return Database::Exec($query)->Read(); } else { return false; } }
      
      







すべてを1つのファイルにダウンロードすることもできます。



All Articles