MVC design convention: CRUDing inherited models [closed]

断了今生、忘了曾经 提交于 2019-12-06 11:16:00

Ok, interesting, why do you want to use 3NF?

In MVC web frameworks (like Codeigniter, Zend Framework or Ruby on Rails) you don't require 3NF or even care about the different tables. What you're trying, I'd say, is multiple table inheritance, which is not very common.

So actually you have a, say, Custumer class, which has all the attributes you have your Customer, User and Person table. To stick to MVC and the thin controller approach, you'd have a save action in your CustomerController class which accepts these attributes and instantiates an object from this Costumer class:

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

(Make sure, you check the parameters for SQL injections but your framework should handle that somehow.)

Your customor class should have a constructor which should accept all its attributes and save them to the appropriate tables. I.e.:

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 . '";
        ...
    }
}

As you may have noticed, I've shortened the Sql statements and some of the code since I don't know CodeIgniter. But the idea is to give all the data you need to the save method which saves it to the appropriate tables in you database. Remember: Only the model class communicate with the database in MVC.

The problem is the ID you use as a foreign key in other tables. So after inserting your data to person you need to query the table for the ID it just inserted and use it in another table.

That is why some frameworks (so to say: all I know), use single table inheritance by default. Picture it like this:

The way you described it, your Costumer inherits from User which inherits from Person. All these tables are merged into one table, which also holds a type attribute, which says which type that row's object is, in our case: Customer, User or Person. If you only add a user, all unused attributes will be set to NULL. But this would break your 3NF, of course.

What I don't understand, though, is why you always require 1:1 relationshipts. For example between Person and Company. Oh, now I get it, since there is no companyId (FK) in Person, many Persons can be in one Company. But why does Branchhave a personId (FK). Usually there are more person working in a branch, no?

Back to your question

You can either use thin-controller-fat-model or fat-controller-thin-model, though I prefer the first one. In this, you'd only have a few lines in your controller and more logic in your model (as in my example above). The communication with the database happens only in your model classes. You usually don't have a one-table-equals-one-model-type approach, but you may also have more than one model type accessing one table (see the single table inheritance thing).

Another approach

Your could have a Costumermodel that accepts your data and "group" it the way your tables are "scattered":

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

But... well, as I said, I'd suggest you leave the 3NF approach and use STI (single table inheritance).

Don't forget to make sure to quote form data to avoid SQL injections or see, which functions your framework offers to do that for you.

Sorry for any mistakes in the code, I was only "coding" from memory and didn't even syntax parse it.

HTH

tereško

TL;DR. No. You should not create separate model for each table.


First of all , your DB diagram is wrong. The fragmentation of User, Individual and Person. They all have 1:1 relations and the User table seems extremely redundant.Oh .. and what's with the singular naming convention for tables ?!

Anyway ..


Actually, what you refer to as "models" are actually domain objects. They are just a part of model layer. And yes, model in MVC is a layer. Not a class nor an object.

When implemented properly, your domain objects would be separate from classes, that implement storage logic ( usually: data mappers ). Besides adhering to SRP such implementation would also grant you ability for domain object to be independent from storage form. A data mapper can map multiple tables to single domain object, unlike the active record (anti)pattern, which lumps the logic with storage mechanisms.

As for the controllers ... well. You should have a single controller for each view. Controllers are supposed only to change the state of model layer and current view, by passing along data from incoming request.

Your question seems to indicate that you have domain business logic leaking from model layer into presentation layer. Instead you should create services ( you can think of them as "higher order domain objects" ), which facilitate interaction between multiple domain objects and your chosen storage abstractions (data mappers, DAOs, repositories, units of work .. etc). The controller should only the user management service: "here is data, create me a new user account."


P.S. : you might find this post relevant.

You will probably get many different opinions. Without knowing too many details about your application, one of the possible approaches would be to put this into application services.

Then your web app will use proper app service to execute this logic. This will decouple your web interface from the application logic, thus enabling some other application (e.g. some internal auditing service) to use the same logic without duplicating the implementation logic.

I don't know PHP, so I will use some .NET-ish pseudo code, without any specific technology in mind (that's why method calls will deliberately not be made for any popular web app framework).

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); 
}

Then in your web app, you will have a method which handles the web request

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");
   }
}

One thing which is not really obvious is where to put the transaction handling. It can be inside the application service, or inside the web request handler method.

Although the first seems more intuitive, the problem is that often app services don't know enough context to decide on transaction handling. Imagine this popular money transfer example:

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

The service can't handle transactions inside the service method calls, because if the first method succeeds and second fails, we end up with inconsistent state where money was withdrawn from one account, but never reached the other one.

Therefore, transaction must be managed externally:

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

It is up to you to decide what is the best fit for your app.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!