Каковы лучшие методы для предотвращения ползучести SQL? - PullRequest
4 голосов
/ 23 сентября 2010

У меня есть веб-приложение, написанное на PHP с использованием базы данных MySQL.Этот вопрос может быть так же легко применим к любому языку и приложению, которые пытаются использовать базу данных SQL и конструкцию ООП MVC.

Как ограничить использование кода SQL для Модели?

За этим вопросом стоит довольно длинная история, характерная для моего случая.Как упоминалось ранее, я работаю над сайтом PHP / MySQL / AJAX.Я разработал его, используя принципы проектирования ООП и MVC - с моделью, видом и контроллером.Мне удалось сохранить элементы представления - такие как разметка и стилизация - полностью ограниченными видом и сделать их многоразовое использование довольно легко.Я думал, что сделал то же самое с кодом SQL.Но по мере продвижения работы стало ясно, что Модель нуждается в серьезном рефакторинге.

Я нашел способ сохранить SQL в Модели - заключить в капсулу каждый отдельный запрос SQL в своем собственном объекте Query.И затем, когда мне нужно было вызвать какой-то SQL в View или Controller, я получал доступ к запросу через фабрику.В контроллере или представлении не существует кода SQL.

Но это стало чрезвычайно утомительным.Я не думаю, что на самом деле что-то получаю, делая это, и трачу слишком много времени на создание запросов с именами, такими как «SelectIdsFromTableWhereUser».Фабрика для запросов приближается к тысячам строк.Небольшой поиск в Eclipse показал, что подавляющее большинство этих запросов используется в одном или двух местах и ​​никогда больше.Не хорошо.

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

Так ли важно отделить SQL от Controller или View?Что получается, делая это?Что теряется, позволяя ему распространяться?Как вы решаете эту проблему?

Редактировать По запросу, здесь немного подробнее о моей модели.

Есть две части.Часть таблиц и часть запросов.Часть Таблицы содержит объекты домена - в основном разработанные как обертки вокруг объектов класса, которые являются точными аналогами таблиц в базе данных.Например, это может быть таблица базы данных Foo с полями id, name и type.Будет объект Table (class FooTable), который имеет массив с полями 'id', 'name' и 'type'.Это будет выглядеть так:

class FooTable extends MySQLTable {
    private $id;
    private $data;
    private $statements;

    public function __construct($id, $data=NULL, $populate=false) {
         // Initialize the table with prepared statements to populate, update and insert.  Also,
         // initialize it with any data passed in from the $data object.
    }

    public function get($field) {}
    public function set($field, $value) {}
    public function populate() {}
    public function update() {}
    public function insert() {}
}

Если есть таблица базы данных fooBar, которая имеет отношение один ко многим (один Foo много Bars) с полями id, fooIDbar тогда будет объект FooBar Table (class FooBarTable), который будет выглядеть почти так же, как и выше FooTable.

FooTable и множество FooBarTable объектовоба будут содержаться в объекте Foo.Присвойте фабрике объектов Foo идентификатор таблицы Foo, и она заполнится данными Foo и всеми ее Bar и их данными.

Объекты Query используются для извлечения идентификаторов Foo в том порядке, в котором они необходимы.Итак, если я хочу, чтобы объекты Foo были упорядочены по дате, голосу или имени, мне нужен другой объект запроса, чтобы сделать это.Или если я хочу выбрать все Foo объекты, которые имеют Bar в определенном диапазоне.Мне нужен объект запроса.

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

Во время оригинального дизайна я не думал, что будет слишком много запросов, и я думал, что это будут вещи, которые увидят повторное использование.Так как может быть несколько мест, где я бы хотел Foo в порядке дат.Но так не получилось.Их гораздо больше, чем предполагалось, и большинство из них одноразовые, используются один раз в каком-то View или Command, а затем никогда больше.Я также думал, что запросы могут инкапсулировать довольно сложный SQL, и было бы хорошо иметь их в качестве объектов, чтобы я всегда мог предоставить им необходимые данные, и это была бы относительно дезинфицированная среда для тестирования самого запроса SQL,Но опять же, это не сработало.Большинство из них содержат довольно простой SQL.

Ответы [ 3 ]

3 голосов
/ 23 сентября 2010

Невозможно дать хороший совет, не зная, что вы делаете, но ясно, что что-то, вероятно, очень неправильно.

Из того, что вы сказали, звучит так, будто вы правы, думая, что ваша модель нуждается в серьезном рефакторинге. На самом деле, похоже, что он нуждается в серьезной доработке (то есть API, к которому ваши контроллеры и представления будут обращаться, изменится).

Некоторые мысли:

Вы говорите:

Как я нашел способ сохранить SQL в Модель должна была заключать в капсулу каждый один SQL-запрос в своем собственном Query объект. А потом, когда мне нужно было позвонить немного SQL в представлении или контроллере I будет получать доступ к запросу через завод. В SQL нет кода SQL Контроллер или View.Controller или View.

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

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

3 голосов
/ 23 сентября 2010

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

В учебнике NerdDinner есть хороший пример использования репозитория в контексте MVC; хотя он не в PHP, надеюсь, он даст вам представление о том, как работает этот шаблон.

2 голосов
/ 23 сентября 2010

Начнем с последнего вопроса: то, что получено, - это разделение интересов или, говоря простым языком, «Хранение вещей, которые связаны» Ключевое слово здесь принадлежит, это довольно субъективное слово.

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

Когда вещи растут, чувство принадлежности меняется. Когда мы получаем сложную модель данных, которую необходимо поддерживать согласованной и которая должна со временем развиваться, то внезапно операции на этой «модели» начинают «принадлежать» вместе, потому что становится слишком сложно разбирать операции и запросы SQL во всем место. Таким образом, чтобы сохранить контроль, нам нужны все операции модели на одной странице.

В качестве практического подхода мне нравится рисовать линию в песке между пользовательским интерфейсом и моделью и определять там API, который инкапсулирует цели мини-пользователя (пользователь хочет видеть рекламные акции -> нужны методы getPromotions, пользователь хочет чтобы добавить что-то в корзину: нужен метод addToCart и т. д.).

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

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

Такие вещи, как репозитории и т. Д., Являются способом структурирования этого уровня обслуживания.

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

...