За последние 24 часа нас посетили 55600 программистов и 1729 роботов. Сейчас ищут 827 программистов ...

MVC + Service Layer + ORM

Тема в разделе "Прочие вопросы по PHP", создана пользователем bingo, 28 апр 2011.

  1. bingo

    bingo Активный пользователь

    С нами с:
    28 апр 2011
    Сообщения:
    3
    Симпатии:
    0
    Всем привет. Возник вопрос о том, как правильно реализовать работу структура MVC + Service Layer + ORM. Примеры приведу по упрощенному коду kohana 3.1

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

    Модели новость и категория новости
    PHP:
    1. <?php
    2. class Model_News extends ORM {
    3.  
    4.     protected $_table_name = 'news';
    5.     protected $_primary_key = 'news_id';
    6.  
    7.     protected $_belongs_to = array(
    8.         'category' => array(
    9.             'model' => 'news_category',
    10.             'foreign_key' => 'news_category_id',
    11.         ),
    12.     );
    13.  
    14.  
    15.     public function rules() {
    16.         return array();
    17.     }
    18.  
    19.  
    20.     public function get_news($offset = 0, $items_per_page = 10, $with_not_active = FALSE) {
    21.         $this->with('category');
    22.         if (!$with_not_active)
    23.             $this->where('news_active', '=', 1);
    24.         $this->order_by('news_date', 'DESC');
    25.         $this->limit($items_per_page);
    26.         $this->offset($offset);
    27.         return $this->find_all();
    28.     }
    29.  
    30.  
    31.     public function get_count_news($with_not_active = FALSE) {
    32.         if (!$with_not_active)
    33.             $this->where('news_active', '=', 1);
    34.         return $this->count_all();
    35.     }
    36. }
    37.  
    38. class Model_News_Category extends ORM {
    39.  
    40.     protected $_table_name = 'news_category';
    41.     protected $_primary_key = 'category_id';
    42.  
    43.     protected $_has_many = array(
    44.         'news' => array(
    45.             'model' => 'news',
    46.             'foreign_key' => 'news_category_id',
    47.         ),
    48.     );
    49.    
    50.     public function get_category_list() {
    51.         $this->order_by('category_name', 'DESC');
    52.         return $this->find_all();
    53.     }
    54.  
    55.     public function get_category_by_id($category_id) {
    56.         $this->where('category_id', '=', $category_id);
    57.         $this->limit(1);
    58.         return $this->find();
    59.     }
    60.  
    61.  
    62. }
    63.  
    64.  
    Писал, конечно, не все функции - общая картина думаю понятна.


    Сервисный слой
    Кстати, если и с вставкой проблемы в моей логике - тоже можно пнуть. В самой модели происходит обычный $this->save() и преобразование данных если нужно, о которых знает только модель.
    PHP:
    1. <?php
    2. class Service_News {
    3.  
    4.  
    5.     public function add_news(array $data, $external_validation = NULL) {
    6.  
    7.         $news = ORM::factory('news');
    8.         try {
    9.             $news->add_news($data, $external_validation);
    10.             return new Result(TRUE, array(), $news);
    11.         }
    12.         catch (ORM_Validation_Exception $e) {
    13.             $errors = $e->errors('models');
    14.             return new Result(FALSE, $errors, Arr::overwrite($news->as_array(), $data));
    15.         }
    16.  
    17.     }
    18.  
    19.     public function edit_news($news_id, array $data, $external_validation = NULL) {
    20.         $news = ORM::factory('news', $news_id);
    21.         if ($data) {
    22.             try {
    23.                 $news->edit_news($data, $external_validation);
    24.                 return new Result(TRUE, array(), $news);
    25.             }
    26.             catch (ORM_Validation_Exception $e) {
    27.                 $errors = $e->errors('models');
    28.                 return new Result(FALSE, $errors, Arr::overwrite($news->as_array(), $data));
    29.             }
    30.         }
    31.         return new Result(FALSE, array(), $news->as_array());
    32.  
    33.     }
    34.  
    35.     public function get_news_by_id($news_id) {
    36.         return ORM::factory('news')->get_news_by_id($news_id);
    37.     }
    38.  
    39.     public function get_news_by_category_id($category_id, $offset, $limit, $with_not_active = FALSE) {
    40.         return ORM::factory('news')->get_news_by_category_id($category_id, $offset, $limit, $with_not_active);
    41.     }
    42.  
    43.     public function get_count_news($category_id = FALSE) {
    44.         if (!$category_id)
    45.             $count = ORM::factory('news')->get_count_news();
    46.         else
    47.             $count = ORM::factory('news')->get_count_news_by_category_id($category_id);
    48.  
    49.         return $count;
    50.     }
    51.  
    52.     public function get_news($offset, $limit, $category_id = FALSE, $with_not_active = FALSE) {
    53.         if ($category_id)
    54.             $data = ORM::factory('news')->get_news_by_category_id($category_id, $offset, $limit, $with_not_active);
    55.         else
    56.             $data = ORM::factory('news')->get_news($offset, $limit, $with_not_active);
    57.  
    58.         return $data;
    59.     }
    60.  
    61.  
    62.     public function get_category_list() {
    63.         return ORM::factory('news_category')->get_category_list();
    64.  
    65.     }
    66.  
    67.     public function get_category_by_id($category_id) {
    68.         return ORM::factory('news_category')->get_category_by_id($category_id);
    69.     }
    70.  
    71. }
    72.  
    Ну с контроллером думаю все понятно и так.

    При выводе всех новостей, через сервисный слой запрашиваются новости с нужными параметрами, возвращается набор объектов 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() тоже неправильно, т.к. контроллер должен общаться с моделью только через сервисный слой.

    Если сложно написал и надо подвести итог в вопросе, то как правильно при наличии сервисного слоя получать, обрабатывать и передавать данные в отображение, если связи между моделями сложные. Или может архитектура совсем неправильная и надо делать иначе, тогда как?
     
  2. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Вот это не понятно. Вроде конкретные модели приводите, и такое абстрактное "параметры".
    Но в двух словах... или в контроллере или в сервисном слое ;) В данном случае скорее в сервисном слое, ибо новости и категории новостей - взаимосвязаны.
     
  3. bingo

    bingo Активный пользователь

    С нами с:
    28 апр 2011
    Сообщения:
    3
    Симпатии:
    0
    Про параметры я думал понятно будет, имеется в виду сортировка или, допустим, когда нужен постраничный вывод, было бы $category->news->limit(10)->offset(0)->find_all() . А вот ответ не очень понял, в сервисном слое подготавливать массив со всеми данными и отдавать контроллеру?
     
  4. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Ну что такое сервисный слой? Это ответ на ТТУК. Т.е. сначала мы пихали все, что могли в контроллер, а потом оказалось, к примеру, что новости нужны еще где-то. И вообще в контроллере оказалось слишком много "лишнего". А модель - она вообще только носитель данных. Вот и добавляют сервисный слой, т.е. то, что будет собирать из одной или нескольких моделей пачки данных и отдавать контроллеру. Сортировку можно добавить туда как параметр или отедльным методом, например, get_new_by_date и get_new_by_name. С постраничным выводом как правило чуточку сложнее, обычно бывает еще отдельно класс пейджера... но тут лучше сами погуглите, наверняка в кохане есть что-то для этого включая и best practice.
    Но вообще что скажу... если вы делаете простенький сайт без уверености в его росте... можен ну его нафиг этот сервисный слой? Имхо, сущности плодить нужно по мере необходимости - или сразу, если это спроектировано и оправдано, или потом в процессе рефакторинга. Т.е. когда понимаете, что у вас контроллер "толстый" и тамкуча бизнес логики... часть из которой приходится копировать в другие контроллеры - вот тут пора выделять бизнес логику в сервисный слой.