2016-10-04 2 views
0

Итак, я прочитал некоторые уже существующие вопросы (например, this и this), а также я читаю книгу, но я все еще не могу решить. Извините за длинный пост, я разместил здесь достойное количество кода.Еще один проект php MVC

Работа по созданию маршрутизации, созданию контроллера и действиям прямо сейчас. До этого момента я только что прочитал простой текст в действии контроллера, чтобы узнать, работает ли он.

После этого я представил простой вид класс с функцией визуализации(), который возвращает «визуализируемый» HTML код:

public function render() 
{ 
    if (!isset($this->file)) { 
     return ""; 
    } 

    if (!empty($this->data)) { 
     extract($this->data); 
    } 

    // dirty-hack: use output buffering so we can easily check if a file can be included 
    // if not, simply reject the output and throw an exception (let the caller handle it) 
    // if the inclusion was successfull then return with the buffered content 
    ob_start(); 
    if (!include($this->file)) { 
     ob_end_clean(); 
     throw new \Exception("View file \"{$this->file}\" not found"); 
    } 

    return ob_get_clean(); 
} 

Класса контроллера также есть рендер() и защищенная setView () функции

public function render() 
{ 
    if (isset($this->renderView)) { 
     echo $this->renderView->render(); 
    } 
} 

protected function setView($view) 
{ 
    if (!is_object($view) || !($view instanceof View)) { 
     throw new \Exception("The \"view\" parameter have to be a View instance"); 
    } 

    $this->renderView = $view; 
} 

пример, контроллер с примера действия:

public function viewHome() 
{ 
    $contentView = new Framework\View("HomeView.php"); 

    // fill contentView with variables, using $contentView->setData("name", value) 

    $this->generateView($contentView); 
} 

protected function generateView($contentView) 
{ 
    if (!is_object($contentView) || !($contentView instanceof Framework\View)) { 
     throw new \Exception("Invalid \"contentView\" parameter"); 
    } 

    $layout = new Framework\View("MainLayout.php"); 
    $layout->setData("content", $contentView->render()); 

    $this->setView($layout); 
} 

Фактически, действие контроллера присваивает данные представлению (Редактировать: На самом деле это не представление, больше похожее на шаблон, но это мое соглашение об именах), и представление просто использует данные. Это может быть «отменено», поэтому я мог бы создавать подклассы вида, которые получат ссылку на контроллер и используют контроллер для получения/установки данных в/из презентации. По какой-то причине я придерживаюсь первого (текущего) решения, но я могу изменить свое мнение. :)

Соответствующий тест MainLayout.php файл следующие

<div style="margin-left: auto; margin-right: auto; padding: 5px; width: 800px; border: 3px solid green;"> 
    Main header <br /> 
    <br /> 
    Content: <br /> 
    <?php 
    if (!isset($content)) { 
     die("The \"content\" variable is not available"); 
    } 
    echo $content; 
    ?> 
</div> 

Обратите внимание, что коды, которые я отправил служить целям тестирования, вещи могут быть различными в дальнейшем. Однако моя настоящая проблема связана с модельным слоем. Я читал два разных подхода к шаблону MVC:

Первое говорит, что модель является «глупым» слоем, простым представлением данных памяти/объекта. Реальная работа (так что доступ DAO, запросы, бизнес-логика) находится в контроллере.

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

И еще один: допустим, я выбрал второй подход (описанный выше), поэтому у меня есть класс модели, например. Пользователь. Этот класс имеет разные (статические) функции для запроса вещей, такие как «получить всех пользователей», «добавить нового пользователя» и т. Д. Однако я не хочу подключать модель к базе данных так сильно. Моя первая идея состоит в том, что я должен создать (по крайней мере) 3-х классов для одной модели:

  • абстрактный класс/интерфейс, который определяет «модель» сама
  • по крайней мере один подкласс (реализация) для каждого тип доступа к данным
  • и завод для модели, которая создает подходящий класс. Если нам нужна макетная модель для тестирования, она вернет экземпляр UserMock вместо экземпляра UserDB.

У вас есть идеи?

Edit:

На основании принятого ответа, я решил использовать дизайн сервиса. Вот простой (образец) Класс:

abstract class UserService 
{ 
    private static $instance; 

    public static function setInstance($instance) 
    { 
     if (!is_object($instance) || !($instance instanceof UserService)) { 
      throw new \Exception("Invalid \"instance\" parameter"); 
     } 

     self::$instance = $instance; 
    } 

    public static function get() 
    { 
     if (!isset(self::$instance)) { 
      throw new \Exception("Instance is not set"); 
     } 

     return self::$instance; 
    } 

    public abstract function findAll(); 

    public abstract function findById($id); 

    public function add($user) 
    { 
     if (!is_object($user) || !($user instanceof User)) { 
      throw new \Exception("Invalid \"user\" parameter"); 
     } 

     $id = $this->addAndReturnId($user); 
     $user->setId($id); 
    } 

    protected abstract function addAndReturnId($user); 
} 

Таким образом я могу зарегистрировать реализацию сервиса (который использует базу данных или просто заполнен тестовые данные). Однако для каждой службы я должен скопировать-вставить код set/get. Я мог бы использовать базовый класс для этого, но проверка «instanceof» отличается для каждого подкласса.

Я знаю, что это немного не по теме, но есть ли у вас хорошая идея для этого? Или мне нужно скопировать-вставить две функции для каждой службы?

+1

TL; DR. Является ли это более применимым к http://codereview.stackexchange.com/? –

+0

@ JonStirling тоже может быть, но я не спрашиваю о размещенном коде, я спрашиваю о еще не реализованном коде. Я просто хотел показать, что (я думаю) у меня есть основная идея, и некоторые вещи работают, извините, если это вводит в заблуждение. – csisy

ответ

1

Примечание: Я много работаю с Symfony/Doctrine, поэтому моя точка зрения, вероятно, очень сильно зависит от этого. Но я думаю, что они довольно хорошо продуманные библиотеки, поэтому я надеюсь, что это не проблема.


Почему бы не использовать оба подхода? Пусть контроллеры и модели будут тупыми. Модельные классы должны заботиться только о хранении данных. В основном просто класс с некоторыми свойствами и методами доступа для этих свойств; ничего больше. Также контроллеры не должны содержать слишком много кода и оставлять все «интересные вещи» другими классами: Сервисы.

Symfonys документация описывает следующие услуги:

Проще говоря, услуга любой PHP объект, который выполняет какую-то «глобальной» задачи. Это целенаправленное имя, используемое в информатике для описания объекта, созданного для определенной цели (например, доставки электронных писем). Каждая служба используется во всем вашем приложении, когда вам нужна конкретная функциональность, которую она предоставляет. Вам не нужно делать ничего особенного, чтобы сделать службу: просто напишите класс PHP с некоторым кодом, который выполняет определенную задачу. Поздравляем, вы только что создали сервис!

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

Большое преимущество: если вы также создаете соответствующие интерфейсы (например, UserRepositoryInterface), вы можете просто обменять эти классы репозитория в будущем, если хотите сохранить свою модель в другой DBS или даже в текстовых файлах вместо старой. Вам не нужно будет менять свои модели или контроллеры вообще.

Возможно, вам стоит взглянуть на то, как Doctrine справляется с этими вещами.

Также обратите внимание на шаблон дизайна Dependency Injection, если вам нравится идея услуг.


Edit (05.10.2016)

Ваше решение работает, поэтому, если следующее является слишком сложным для вас прямо сейчас, просто придерживаться его. Но это помогло мне, поэтому я постараюсь рекламировать его.: P

Ваш UserService должен заботиться только о реальных методах управления пользователями, а не о получении/установке экземпляров. Ваш подход использует вариацию шаблона Singleton, и Singletons не нужны то, что вы действительно хотите иметь (see here).

Если вы хотите работать с «услугами», вы, вероятно, должны попасть в «Injection Dependency» (как уже упоминалось выше). Вначале это трудно понять, но как только вы знаете, как использовать его, он значительно улучшает качество вашего кода (по крайней мере, для меня). Это введение кажется действительно хорошим: PHP: The Right Way. Symfony также предоставляет компонент DI, который вы могли бы использовать в своем проекте, чтобы не беспокоиться о деталях реализации: Symfony Dependency Injection component

+0

Мне действительно нравится, когда кто-то ссылается (или под влиянием) на известную и действующую библиотеку. Таким образом, модели являются классами Java Bean, контроллеры - это «подающие фиды» - как я описал в OP. На самом деле я молча начал работать над чем-то похожим с небольшой разницей, я назвал класс XyDAO not Service, но Служба подходит лучше. :) Мне нравится это дизайнерское решение, поэтому я предполагаю, что принимаю это как решение. В качестве побочного примечания эти классы могут быть также классами MVC, где View будет текущим контроллером, контроллером будет Service. – csisy

+0

Я обновил OP, я был бы счастлив, если бы вы могли это проверить. :) – csisy

+1

Добавил что-то к моему ответу. Не рассматривайте его как «нужно делать это так», это всего лишь намек на то, как мне нравится делать такие вещи. –