Вот моя структура базы данных:
В моем приложении у меня есть управление компанией клиентов, сотрудников и филиалов.
Клиенты и сотрудники связаны с одним Лицом, одним Физическим лицом и одним Пользователем. Филиалы связаны с одним Лицом и одной Компанией.
Таким образом, чтобы вставить нового клиента или сотрудника, я должен сначала вставить свои общие данные в таблицу Person
, его личные данные в таблицу Person
, затем свои пользовательские данные в таблицу User
и, наконец, создать новую запись в таблице Customer
или Employee
.
Чтобы вставить новую ветку, я должен сначала вставить свои общие данные в таблицу Person
, затем ее данные Company
таблицу Company
и, наконец, создать новую запись в таблице Branch
.
Я новичок в концепции MVC, и я немного потерял то, как я должен проектировать классы модели для CRUD. Я использую схему CodeIgniter, но я думаю, что здесь это не имеет значения. Должен ли я создать отдельную модель класса для каждой таблицы базы данных? Если да, то как я должен это кодировать? (только теория)
Например, чтобы вставить нового клиента …
Это верно? Где этот код должен быть, в контроллере заказчика? Любое лучшее соглашение для использования этой структуры базы данных (мне нужно использовать третий дизайн нормальной формы)?
Хорошо, интересно, почему вы хотите использовать 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); }
Вам решать, что лучше всего подходит для вашего приложения.