2

これが私のデータベース構造です:

データベーススキーマ

私のアプリでは、顧客、従業員、支店の会社管理を行っています。

顧客と従業員は、1人の個人、1人の個人、および1人のユーザーに関連付けられています。支店は1人の個人と1つの会社に関連付けられています。

したがって、新しいコスチュームまたは従業員を挿入するには、最初に共通データをPersonテーブルに挿入し、次に個人データをPersonテーブルに挿入し、次にユーザーデータをUserテーブルに挿入し、最後に新しいレコードをCustomerまたはEmployeeテーブルに作成する必要があります。

新しいブランチを挿入するには、最初に共通データをPersonテーブルに挿入し、次に会社データをCompanyテーブルに挿入し、最後に新しいレコードをBranchテーブルに作成する必要があります。

私はMVCの概念に不慣れであり、モデルクラスをCRUDするように設計する方法に少し迷っています。私はCodeIgniterフレームワークを使用していますが、ここでは関係ないと思います。データベーステーブルごとに個別のクラスモデルを作成する必要がありますか?もしそうなら、私はそれをどのようにコーディングする必要がありますか?(理論のみ)

たとえば、新しい貸衣装を挿入するには...

  1. 新しいトランザクションを開始します
  2. 新しい人をインスタンス化する
  3. 人のデータを入力する
  4. 人を救う
  5. 新しい個人をインスタンス化する
  6. 個人のデータを入力し、その個人に関連する
  7. 個人を保存
  8. 新しいユーザーをインスタンス化する
  9. ユーザーのデータを入力し、その個人に関連する
  10. ユーザーを保存
  11. 新しいコスチュームをインスタンス化する
  12. コスチュームのデータを入力し、その人に関連付けます
  13. コスチュームを保存
  14. トランザクションを終了します

あれは正しいですか?このコードは、貸衣装のコントローラーのどこにあるべきですか?そのデータベース構造を使用するためのより良い規則はありますか(3番目の正規形の設計を使用する必要があります)?

4

3 に答える 3

2

TL;DR。いいえ。テーブルごとに個別のモデルを作成しないでください。


まず第一に、DB ダイアグラムが間違っています。UserIndividualおよびの断片化Person。それらはすべて 1:1 の関係を持っており、Userテーブルは非常に冗長に見えます.ああ..そして、テーブルの単一の命名規則とは何ですか?!

ともかく ..


実際、「モデル」と呼ばれるものは、実際にはドメイン オブジェクトです。それらはモデル層の一部にすぎません。はい、MVC のモデルはレイヤーです。クラスでもオブジェクトでもありません。

適切に実装すると、ドメイン オブジェクトは、ストレージ ロジックを実装するクラス (通常はデータ マッパー) から分離されます。SRPに準拠するだけでなく、そのような実装により、ドメイン オブジェクトをストレージ形式から独立させることもできます。データ マッパーは、複数のテーブルを 1 つのドメイン オブジェクトにマップできます。アクティブ レコード(アンチ) パターンとは異なり、ロジックをストレージ メカニズムにまとめます。

コントローラーに関しては…そうですね。ビューごとに 1 つのコントローラーが必要です。コントローラーは、着信リクエストからデータを渡すことによって、モデルレイヤーと現在のビューの状態を変更することのみを想定しています。

あなたの質問は、ドメイン ビジネス ロジックがモデル層からプレゼンテーション層に漏れていることを示しているようです。代わりに、複数のドメイン オブジェクトと選択したストレージの抽象化 (データ マッパー、DAO、リポジトリ、作業単位など) の間の相互作用を容易にするサービスを作成する必要があります (「高次ドメイン オブジェクト」と考えることができます)。コントローラーは、ユーザー管理サービスのみを実行する必要があります。「ここにデータがあります。新しいユーザー アカウントを作成してください。」


PS :この投稿に関連性があると思われるかもしれません

于 2012-09-30T17:33:12.703 に答える
1

わかりました、興味深いですが、なぜ3NFを使用したいのですか?

MVC Webフレームワーク(Codeigniter、Zend Framework、Ruby on Railsなど)では、3NFは必要なく、さまざまなテーブルについても気にする必要はありません。あなたが試みているのは、複数のテーブルの継承ですが、これはあまり一般的ではありません。

つまり、実際には、たとえば、CustumerCustomer、User、Personテーブルにあるすべての属性を持つクラスがあります。MVCとシンコントローラーのアプローチに固執するには、これらの属性を受け入れ、このクラスからオブジェクトをインスタンス化するsaveアクションをクラスに含める必要があります。CustomerControllerCostumer

class CostumerController {
    function create() {
        Costumer c = new Customer();
        c.save($_POST['costumer']);
    }
}

(必ず、SQLインジェクションのパラメーターを確認してください。ただし、フレームワークはそれを何らかの方法で処理する必要があります。)

カスタムクラスには、すべての属性を受け入れて適切なテーブルに保存するコンストラクターが必要です。すなわち:

class Costumer {
    function save(params) {
        $sql = 'INSERT INTO person SET `phoneNumber` = "' . params['phonenumber'] . '"';
        $dbh->query($sql);
        $lastid = PDO::lastInsertId;
        $sql = 'INSERT INTO user SET `password` = "'. md5(params['password']) . '", `personId` = "' . $lastid . '";
        ...
    }
}

お気づきかもしれませんが、CodeIgniterがわからないため、SQLステートメントと一部のコードを短縮しました。ただし、必要なすべてのデータを、データベース内の適切なテーブルに保存するsaveメソッドに渡すという考え方です。 注意: MVCのデータベースと通信するのはモデルクラスのみです。

問題は、他のテーブルで外部キーとして使用するIDです。したがって、データを人に挿入した後、挿入したIDをテーブルに照会して、別のテーブルで使用する必要があります。

そのため、一部のフレームワーク(つまり、私が知っているすべてのフレームワーク)は、デフォルトで単一テーブル継承を使用します。このように想像してください:

あなたがそれを説明したように、あなたのCostumer継承者はからUser継承しPersonます。これらのテーブルはすべて1つのテーブルにマージされます。このテーブルには、typeその行のオブジェクトがこの場合は、、またはのどのタイプであるかを示す属性CustomerUserありますPerson。ユーザーのみを追加する場合、未使用の属性はすべてに設定されNULLます。もちろん、これはあなたの3NFを壊します。

しかし、私が理解していないのは、あなたが常に1:1の関係を必要とする理由です。たとえば、Personとの間Company。ああ、今私はそれを手に入れました、にがないのでcompanyId (FK)Person多くPersonのsが1つに入ることができますCompany。しかし、なぜがBranchありますかpersonId (FK)。通常、支店で働く人はもっといますよね?

質問に戻る

私は最初のものを好みますが、 thin-controller-fat-modelまたはfat-controller-thin-modelのいずれかを使用できます。この場合、コントローラーには数行しかなく、モデルにはさらに多くのロジックがあります(上記の例のように)。データベースとの通信は、モデルクラスでのみ行われます。通常、one-table-equals-one-model-typeアプローチはありませんが、複数のモデルタイプが1つのテーブルにアクセスする場合もあります(単一テーブル継承を参照)。

別のアプローチ

Costumerデータを受け入れ、テーブルが「分散」する方法でデータを「グループ化」するモデルを作成できます。

class Customer {
    function save(params) {
        Person $person = new Person();
        $person.save(params['person']);
        User $user = new User();
        $user.save(params['user'], $person->id);
        ....
    }
}

しかし...まあ、私が言ったように、私はあなたが3NFアプローチを離れて、STI(単一テーブル継承)を使用することを提案します。

SQLインジェクションを回避するためにフォームデータを引用することを忘れないでください。または、フレームワークがそれを実行するために提供する関数を確認してください。

コードに誤りがあったことをお詫びします。私はメモリから「コーディング」しているだけで、構文解析もしていませんでした。

HTH

于 2012-09-30T16:18:05.843 に答える
0

おそらく、さまざまな意見が寄せられるでしょう。アプリケーションに関する詳細をあまり知らなくても、考えられるアプローチの 1 つは、これをアプリケーション サービスに組み込むことです。

次に、Web アプリは適切なアプリ サービスを使用してこのロジックを実行します。これにより、Web インターフェイスがアプリケーション ロジックから分離され、他のアプリケーション (内部監査サービスなど) が実装ロジックを複製することなく同じロジックを使用できるようになります。

私は PHP を知らないので、特定のテクノロジを考慮せずに、.NET 風の疑似コードを使用します (そのため、一般的な Web アプリ フレームワークでは意図的にメソッド呼び出しが行われません)。

class CustomerService // typically it will be an interface ICustomerSvc, but nevermind...
{ 
   // this will implement your logic to add customer - points 1-14
   // it might return the ID of the customer or not (CustomerID is typically
   // an integer, string, GUID, etc
   CustomerID AddCustomer(CustomerInfo info); 
}

次に、Web アプリで、Web 要求を処理するメソッドを作成します。

void AddCustomer(CustomerData data) 
{
   // note: data is not necessarily the same CustomerInfo type.
   // this is your web app model, and can be same but doesn't have to
   try {
      // m_customerSvc - can be instantiated withing class, provided in constructor, etc
      var id = m_customerSvc.AddCustomer(data);      // add a customer
      RedirectTo("confirmation_page_for_user_", id); // show confirmation page
   }
   catch(...) {
      RedirectTo("error_page");
   }
}

明らかでないことの 1 つは、トランザクション処理をどこに配置するかです。これは、アプリケーション サービス内または Web 要求ハンドラー メソッド内にある可能性があります。

前者の方が直感的に見えますが、問題は、多くの場合、アプリ サービスがトランザクション処理を決定するのに十分なコンテキストを認識していないことです。この一般的な送金の例を想像してみてください。

 m_svc.TakeMoney(account1, amount);
 m_svc.AddMoney(account2, amount);

最初のメソッドが成功し、2 番目のメソッドが失敗した場合、一方のアカウントからお金が引き出されたが、もう一方のアカウントには届かないという一貫性のない状態になるため、サービスはサービス メソッド呼び出し内でトランザクションを処理できません。

したがって、トランザクションは外部で管理する必要があります。

using(var tx = new Transaction())
{
   // now both execute within same transaction
   m_svc.TakeMoney(account1, amount);
   m_svc.AddMoney(account2, amount);
}

アプリに最適なものを決定するのはあなた次第です。

于 2012-09-30T14:14:09.600 に答える