vakorovin
@vakorovin
Разработчик

Второй вопрос к толковым web-разработичкам, знакомым с паттернами

После предыдущего вопроса возникли новые. 2 суток обдумывал, всё что вы мне ответили, большое всем спасибо. Сказать, что всё прояснилось — не могу. Поэтому на конкретном примере прошу показать вектор, по которому двигаться дальше. Да, я разглядывал yii и symfony, и даже небольшой опыт работы с ними есть. Но. У меня не стоит задачи построить фреймворк, все что мне надо — достичь читаемого кода, добиться независимости от хранения данных и не строить запросы в контроллере.

Прошу вникнуть в то, что опишу ниже.

Скажем, есть класс-контроллер userController. У него определены следующие методы-экшены:

search — вызывается по умолчанию;
view — карточка абонента;
create — добавление абонента в биллинг;
remove — удаление абонента;
и т.д. (порядка 10 экшенов, речь не о них).

Есть класс-модель user, реализующий интерфейс ArrayAccess (писал в прошлом вопросе, но повторюсь — для доступа к объекту Абонент как к массиву).
В этом классе определены методы:
__construct(), принимающий массив, в котором передается либо id, либо login абонента. И то, и другое уникально, но иногда надо получить абонента по логину (при платежах). Мне уже подсказали, что можно реализовать не так, как делал я:

$user = new user(array(
'id'=>$id
));

а

$user=user->find_by('id', $id);

Такой подход мне по душе. Но. Анализируя по цепочке дальнейший рефакторинг, натыкаюсь на некое непонимание. Связано оно вот с чем. Пытаясь разгрузить контроллер, я нагружаю модель. И если изначально модель была всего лишь описанием объекта, то сейчас там происходит валидация, запись в лог, статические методы для обработки таблиц БД, хранящих эти объекты. Вроде бы это не плохо. Результатом этого подхода стало то, что я получил много моделей, с начальных 5-6 их стало несколько десятков. И хоть их стало больше, код упростился и я не задумываюсь о проверках перед обновлением какой-то сущности в разных частях системы — модель сама проверяет (правда пока не везде, но речь не о том), и это здорово. Сейчас я понимаю, что нужно разгрузить модель, отделив само получение данных из Хранилища (не важно, будь то БД, или файл конфигурации). Своими комментариями вы меня подтолкнули к мапперу. Вчитываюсь, но нет окончательного понимания, в какой момент он вступает в работу и он ли создает объект или нет. Т.е. если я хочу получить объект user, модель ли (в моем случае) создает себя сама, или же маппер принимает параметры, получает запись из Хранилища и возвращает объект класса user? Или же в модели user происходит обработка входящих условий и на их основе делается запрос в маппер, который возвращает результат и на основе этого ответа модель создает объект? Смотрю на примеры в интернете, но не допонимаю как это применить в моем случае.

И следующий момент. Как я уже писал выше, в контроллере userController есть экшн search. Касательно его работы есть вопросы.
Сейчас поиск абонентов в этом экшене происходит так:

$params=array();

$tariff=get_param(‘tariff’, ‘int’, 0);
if ($tariff>0) $params[]=“tariff=$tariff”;

$cities=get_param(‘city’, ‘int_array’, array());
if (!empty($cities)) $params[]=”city IN (”.implode(‘, ’, $cities).”)”;

$users=user::search($params);

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

Спасибо, что дочитали этот поток сознания )) Очень жду ваших ответов.
  • Вопрос задан
  • 3488 просмотров
Пригласить эксперта
Ответы на вопрос 5
Anonym
@Anonym
Программирую немного )
1. Используйте DBAL, хотя бы PDO.
2. Создайте модель UserCollection для коллекции пользователей. Логичнее будет как-то так
$users = new UserCollection();
$users->findByParams(array(
  'tariff' => $tariff,
  'city' => $cities,
));

или
$users = UserCollection::findByParams(array(
  'tariff' => $tariff,
  'city' => $cities,
));
Ответ написан
Комментировать
fear86
@fear86
Developer
У нас в движке как раз все реализовано через мапперы, по умолчанию они у нас бывают двух типов, Коллекции и Ресурс модели, первая реализовывает работу с набором сущностей, вторая с одной конкретной.

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

ps: использование статических методов и синглтонов — моветон.
Ответ написан
cawakharkov
@cawakharkov
попробуйте почитать про Service Layer, такой подход разгружает и модель и контроллер
Ответ написан
Комментировать
cawakharkov
@cawakharkov
если конечно я вас правильно понял, реализуйте класс UserService который оперирует моделями типа User и выполняет все манипуляции
Ответ написан
Комментировать
@Masterme
здесь я уже писал свои соображения по теме, так что просто дополню:

нужно правильно обработать ситуацию с пустым массивом $cities, иначе получим ошибку SQL, если в нём 0 элементов — то не включать условие

class SomeController extends BaseController {
  function search($params){
    $where = array();
    if ($params['tariff']) $where['tariff'] = $params['tariff'];
    if (is_array($params['cities']) and count($params['cities'])) $where['cities'] = $params['cities'];
    $uses = $this->Users->find($where); // массив cities будет автоматически сджойнен
    return compact('users');
  }
}


лучше сделать $params объектом с интерфейсом ArrayAccess, и на все несуществующие параметры возвращать null

то что Вы сейчас пишете — это инъекция на инъекции, достаточно в tariff передать что-нибудь вроде ROBIN DROP TABLE `users`…
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы