Intereting Posts
Идентификатор процесса проверки PHP Класс DateTime против встроенных функций даты PHP Почему я не могу напрямую получить доступ к массиву с индексом? Правильный способ избежать обратной косой черты в регулярном выражении PHP? PHP получить список файлов, включая подкаталоги Как получить дату истечения срока действия из файла сертификата SSL в PHP laravel как обновить поле в базе данных Удалите пробелы вокруг определенного символа с помощью preg_replace PHP: функция __autoload никогда не называлась Как выполнить проверку в диалоговом окне JQuery? Можно ли отправлять почту с использованием PHP, давая обратную связь с пользователем при доставке? Получение результата из процедуры в laravel – удаление квадратных скобок из ответа PHP – сбрасывает определенную строку из строки Получение данных с веб-сайта cURL после входа в систему? Мета-боксы WordPress – учебник WP tuts

Условия проектирования MVC: CRUDing унаследованные модели

Вот моя структура базы данных:

схема базы данных

В моем приложении у меня есть управление компанией клиентов, сотрудников и филиалов.

Клиенты и сотрудники связаны с одним Лицом, одним Физическим лицом и одним Пользователем. Филиалы связаны с одним Лицом и одной Компанией.

Таким образом, чтобы вставить нового клиента или сотрудника, я должен сначала вставить свои общие данные в таблицу Person , его личные данные в таблицу Person , затем свои пользовательские данные в таблицу User и, наконец, создать новую запись в таблице Customer или Employee .

Чтобы вставить новую ветку, я должен сначала вставить свои общие данные в таблицу Person , затем ее данные Company таблицу Company и, наконец, создать новую запись в таблице Branch .

Я новичок в концепции MVC, и я немного потерял то, как я должен проектировать классы модели для CRUD. Я использую схему CodeIgniter, но я думаю, что здесь это не имеет значения. Должен ли я создать отдельную модель класса для каждой таблицы базы данных? Если да, то как я должен это кодировать? (только теория)

Например, чтобы вставить нового клиента …

  1. Начать новую транзакцию
  2. Создать нового человека
  3. Заполнить данные лица
  4. Сохранить человека
  5. Создать нового человека
  6. Заполните данные Физика и обратитесь к его Лицу
  7. Сохранить индивидуальное
  8. Создать нового пользователя
  9. Заполнять данные пользователей и относиться к его персоналу
  10. Сохранить пользователя
  11. Создайте новый Costumer
  12. Заполните данные Costurer и обратитесь к его персоналу
  13. Сохранить Costumer
  14. Конец транзакции

Это верно? Где этот код должен быть, в контроллере заказчика? Любое лучшее соглашение для использования этой структуры базы данных (мне нужно использовать третий дизайн нормальной формы)?

Хорошо, интересно, почему вы хотите использовать 3NF?

В веб-средах MVC (таких как Codeigniter, Zend Framework или Ruby on Rails) вам не требуется 3NF или даже заботиться о разных таблицах. То, что вы пытаетесь, я бы сказал, это множественное наследование таблиц, что не очень часто встречается.

Итак, у вас есть, скажем, класс Custumer , который имеет все атрибуты, которые у вас есть у вашего Клиента, пользователя и человека. Чтобы придерживаться подхода MVC и тонкого контроллера , у вас будет действие save в вашем классе CustomerController которое принимает эти атрибуты и создает экземпляр объекта из этого класса Costumer :

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

(Убедитесь, что вы проверяете параметры для инъекций SQL, но ваша инфраструктура должна как-то справиться с этим).

У вашего класса customor должен быть конструктор, который должен принимать все его атрибуты и сохранять их в соответствующих таблицах. То есть:

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

Как вы могли заметить, я сократил инструкции Sql и некоторые из кода, так как я не знаю CodeIgniter. Но идея состоит в том, чтобы предоставить все данные, которые вам нужны, для метода сохранения, который сохраняет его в соответствующие таблицы в вашей базе данных. Помните: только класс модели взаимодействует с базой данных в MVC.

Проблема заключается в идентификаторе, который вы используете в качестве внешнего ключа в других таблицах. Поэтому после вставки ваших данных человеку вам нужно запросить таблицу для только что вставленного ID и использовать ее в другой таблице.

Вот почему некоторые фреймворки (так сказать: все, что я знаю ) используют по умолчанию одностраничное наследование. Сделайте это так:

Как вы описали его, ваш Costumer наследует от User который наследует от Person . Все эти таблицы объединены в одну таблицу, которая также содержит атрибут type , в котором указывается тип, который является объектом этой строки, в нашем случае: Customer , User или Person . Если вы добавите пользователя, все неиспользуемые атрибуты будут установлены в NULL . Но это, конечно, сломало бы ваш 3NF.

Однако я не понимаю, почему вы всегда нуждаетесь в соотношении 1: 1. Например, между Person и Company . О, теперь я получаю это, поскольку в Person нет ни одной companyId (FK) , многие Person могут находиться в одной Company . Но почему у Branch есть personId (FK) . Обычно в филиале больше людей, нет?

Вернуться к вашему вопросу

Вы можете использовать тонкую модель-жир-модель или тонкую модель с жировым контролем , хотя я предпочитаю первую. В этом случае у вас будет только несколько строк в вашем контроллере и больше логики в вашей модели (как в моем примере выше). Связь с базой данных происходит только в ваших классах моделей. Обычно у вас нет подхода one-table-equals-one-model-type, но вы также можете иметь более одного типа модели, обращающихся к одной таблице (см. Однонамерное наследование).

Другой подход

У вас может быть модель 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 или видеть, какие функции предлагает ваша инфраструктура для этого.

Извините за любые ошибки в коде, я был только «кодированием» из памяти, и даже синтаксис не анализировал его.

НТН

TL; DR . Нет. Вы не должны создавать отдельную модель для каждой таблицы.


Прежде всего, ваша диаграмма DB неверна. Фрагментация User , Person и Person . У всех их есть отношения 1: 1, и таблица User кажется чрезвычайно избыточной. Ох .. и что такое единое соглашение об именах для таблиц?!

Так или иначе ..


На самом деле то, что вы называете «моделями», на самом деле являются объектами домена . Они всего лишь часть модельного слоя. И да, модель в MVC – это слой. Не класс и не объект.

При правильной реализации ваши объекты домена будут отделены от классов, которые реализуют логику хранения (обычно: данные ). Помимо соблюдения SRP такая реализация также предоставит вам возможность для объекта домена быть независимым от формы хранения. Преобразователь данных может сопоставлять несколько таблиц с объектом одного домена, в отличие от шаблона активной записи (анти), который объединяет логику с механизмами хранения.

Что касается контроллеров … ну. У вас должен быть один контроллер для каждого вида. Контроллеры должны только изменять состояние уровня модели и текущего представления, передавая данные из входящего запроса.

Ваш вопрос, похоже, указывает на то, что у вас есть бизнес-логика домена, протекающая с уровня модели в уровень представления. Вместо этого вы должны создавать сервисы (вы можете считать их «объектами домена более высокого порядка»), которые облегчают взаимодействие между несколькими объектами домена и выбранными вами абстракциями хранилища (карты данных, DAO, репозитории, единицы работы и т. Д.). Контроллер должен использовать только службу управления пользователями: «вот данные, создайте новую учетную запись пользователя».


PS : вы можете найти этот пост релевантным.

Вероятно, вы получите много разных мнений. Не зная слишком много деталей о вашем приложении, одним из возможных подходов было бы включить это в сервисы приложений.

Тогда ваше веб-приложение будет использовать надлежащую службу приложений для выполнения этой логики. Это приведет к отмене вашего веб-интерфейса из логики приложения, что позволит некоторым другим приложениям (например, некоторым службам внутреннего аудита) использовать ту же логику, не дублируя логику реализации.

Я не знаю PHP, поэтому я буду использовать некоторый псевдо-код .NET-ish без какой-либо конкретной технологии (поэтому вызовы методов будут сознательно не использоваться для какой-либо популярной структуры веб-приложений).

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

Затем в вашем веб-приложении у вас будет метод, который обрабатывает веб-запрос

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

Одна вещь, которая на самом деле не очевидна, – это место, где нужно обрабатывать транзакции. Он может находиться внутри службы приложений или внутри метода обработчика веб-запросов.

Хотя первое кажется более интуитивным, проблема заключается в том, что часто службы приложений не знают достаточно контекста для принятия решения о обработке транзакций. Представьте себе этот популярный пример перевода денег:

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

Служба не может обрабатывать транзакции внутри вызовов метода службы, потому что, если первый метод завершается успешно, а второй терпит неудачу, мы оказываемся в несогласованном состоянии, когда деньги были сняты с одной учетной записи, но не достигли другой.

Следовательно, транзакция должна управляться извне:

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

Вам решать, что лучше всего подходит для вашего приложения.