Всем привет. Возник вопрос о том, как правильно реализовать работу структура MVC + Service Layer + ORM. Примеры приведу по упрощенному коду kohana 3.1 Основной вопрос в том, если использовать сервисный слой, как быть с получением данных из базы данных. С вставкой данных, обновлением вроде все понятно и вопросов не возникает. Пример простой - новости и категории новостей. Модели новость и категория новости PHP: <?php class Model_News extends ORM { protected $_table_name = 'news'; protected $_primary_key = 'news_id'; protected $_belongs_to = array( 'category' => array( 'model' => 'news_category', 'foreign_key' => 'news_category_id', ), ); public function rules() { return array(); } public function get_news($offset = 0, $items_per_page = 10, $with_not_active = FALSE) { $this->with('category'); if (!$with_not_active) $this->where('news_active', '=', 1); $this->order_by('news_date', 'DESC'); $this->limit($items_per_page); $this->offset($offset); return $this->find_all(); } public function get_count_news($with_not_active = FALSE) { if (!$with_not_active) $this->where('news_active', '=', 1); return $this->count_all(); } } class Model_News_Category extends ORM { protected $_table_name = 'news_category'; protected $_primary_key = 'category_id'; protected $_has_many = array( 'news' => array( 'model' => 'news', 'foreign_key' => 'news_category_id', ), ); public function get_category_list() { $this->order_by('category_name', 'DESC'); return $this->find_all(); } public function get_category_by_id($category_id) { $this->where('category_id', '=', $category_id); $this->limit(1); return $this->find(); } } Писал, конечно, не все функции - общая картина думаю понятна. Сервисный слой Кстати, если и с вставкой проблемы в моей логике - тоже можно пнуть. В самой модели происходит обычный $this->save() и преобразование данных если нужно, о которых знает только модель. PHP: <?php class Service_News { public function add_news(array $data, $external_validation = NULL) { $news = ORM::factory('news'); try { $news->add_news($data, $external_validation); return new Result(TRUE, array(), $news); } catch (ORM_Validation_Exception $e) { $errors = $e->errors('models'); return new Result(FALSE, $errors, Arr::overwrite($news->as_array(), $data)); } } public function edit_news($news_id, array $data, $external_validation = NULL) { $news = ORM::factory('news', $news_id); if ($data) { try { $news->edit_news($data, $external_validation); return new Result(TRUE, array(), $news); } catch (ORM_Validation_Exception $e) { $errors = $e->errors('models'); return new Result(FALSE, $errors, Arr::overwrite($news->as_array(), $data)); } } return new Result(FALSE, array(), $news->as_array()); } public function get_news_by_id($news_id) { return ORM::factory('news')->get_news_by_id($news_id); } public function get_news_by_category_id($category_id, $offset, $limit, $with_not_active = FALSE) { return ORM::factory('news')->get_news_by_category_id($category_id, $offset, $limit, $with_not_active); } public function get_count_news($category_id = FALSE) { if (!$category_id) $count = ORM::factory('news')->get_count_news(); else $count = ORM::factory('news')->get_count_news_by_category_id($category_id); return $count; } public function get_news($offset, $limit, $category_id = FALSE, $with_not_active = FALSE) { if ($category_id) $data = ORM::factory('news')->get_news_by_category_id($category_id, $offset, $limit, $with_not_active); else $data = ORM::factory('news')->get_news($offset, $limit, $with_not_active); return $data; } public function get_category_list() { return ORM::factory('news_category')->get_category_list(); } public function get_category_by_id($category_id) { return ORM::factory('news_category')->get_category_by_id($category_id); } } Ну с контроллером думаю все понятно и так. При выводе всех новостей, через сервисный слой запрашиваются новости с нужными параметрами, возвращается набор объектов model_news. Его мы передаем в отображение, проходим по набору и выводим результат. Категория выведется легко, $news->category->name (она у нас изначально была в запросе привязана при помощи $this->with('category')) Но тут мы в контроллере через сервисный слой получаем объект model_news_category и хотим получить список новостей с параметрами, связанных с данной категорией. Без сервисного слоя можно было бы в контроллере использовать $category->news->find_all() с нужными параметрами. (Кстати, в отображении же так делать тоже плохо? То есть если мы заранее готовим данные в контроллере, нам надо пройтись по нужным коллекциям, преобразовать все связи в нужный полный массив, и только потом его передавать в отображение? Не будем же мы в отображении делать $category->news->find_all(). ) А как правильно работать при наличии сервисного слоя. При запросе в сервисном слое подготавливать полный массив данных и отдавать его сразу контроллеру? В контроллер, на сколько я понимаю, передать просто объект model_news_category и уже в контроллере делать $category->news->find_all() тоже неправильно, т.к. контроллер должен общаться с моделью только через сервисный слой. Если сложно написал и надо подвести итог в вопросе, то как правильно при наличии сервисного слоя получать, обрабатывать и передавать данные в отображение, если связи между моделями сложные. Или может архитектура совсем неправильная и надо делать иначе, тогда как?
Вот это не понятно. Вроде конкретные модели приводите, и такое абстрактное "параметры". Но в двух словах... или в контроллере или в сервисном слое В данном случае скорее в сервисном слое, ибо новости и категории новостей - взаимосвязаны.
Про параметры я думал понятно будет, имеется в виду сортировка или, допустим, когда нужен постраничный вывод, было бы $category->news->limit(10)->offset(0)->find_all() . А вот ответ не очень понял, в сервисном слое подготавливать массив со всеми данными и отдавать контроллеру?
Ну что такое сервисный слой? Это ответ на ТТУК. Т.е. сначала мы пихали все, что могли в контроллер, а потом оказалось, к примеру, что новости нужны еще где-то. И вообще в контроллере оказалось слишком много "лишнего". А модель - она вообще только носитель данных. Вот и добавляют сервисный слой, т.е. то, что будет собирать из одной или нескольких моделей пачки данных и отдавать контроллеру. Сортировку можно добавить туда как параметр или отедльным методом, например, get_new_by_date и get_new_by_name. С постраничным выводом как правило чуточку сложнее, обычно бывает еще отдельно класс пейджера... но тут лучше сами погуглите, наверняка в кохане есть что-то для этого включая и best practice. Но вообще что скажу... если вы делаете простенький сайт без уверености в его росте... можен ну его нафиг этот сервисный слой? Имхо, сущности плодить нужно по мере необходимости - или сразу, если это спроектировано и оправдано, или потом в процессе рефакторинга. Т.е. когда понимаете, что у вас контроллер "толстый" и тамкуча бизнес логики... часть из которой приходится копировать в другие контроллеры - вот тут пора выделять бизнес логику в сервисный слой.