Является ли $view в программе MVC php правильным использованием глобальных переменных?

Я понимаю, что глобальных переменных следует избегать вообще. Как новичок, я пытаюсь понять, является ли создание переменной $view глобальной в динамической веб-программе, построенной в соответствии с принципами MVC, одним из тех случаев, когда глобальные переменные являются хорошей идеей.

В моей программе я создаю $view (как объект, содержащий пустой массив) в index.php и превращаю его в глобальный объект в контроллерах, которые его используют.

Спасибо!

ДжейДелаж


person JDelage    schedule 06.09.2010    source источник


Ответы (4)


В моей программе я создаю $view как пустой массив() в index.php и превращаю его в глобальный в контроллерах, которые его используют.

Почему? Если вашему контроллеру нужен $view, просто передайте его через конструктор или сеттер. Зависимости от глобальных переменных всегда можно устранить с помощью внедрения зависимостей.

// index.php
$view = array();
$controller = new Controller($view);
$controller->doAction();

Кроме того, подумайте, должно ли представление быть просто массивом. Представление должно быть отображено в какой-то момент. Рендеринг — это ответственность, которую я вижу в представлении. Массив не может отображать себя, поэтому я предполагаю, что вы делаете это в другом месте. Это было бы нарушением принципа единой ответственности. Я бы предпочел сделать что-то вроде (упрощенно):

// index.php
$view = new View;
$controller = new Controller($view);
$controller->doAction();

// controller.php
...
public function doAction()
{
    ...
    $this->view->setTemplate('/path/to/template');
    $this->view->setVar('foo', 'bar');
    $this->view->render();
}
...
person Gordon    schedule 06.09.2010

Следует избегать глобальных переменных, true. Однако они полезны для хранения глобального состояния, что не приводит к взаимосвязи, как, например, в массиве/объекте $config.

Но в вашем случае я не уверен, что $view - хорошая идея. Во-первых, я считаю, что вы собираете выходные данные. «Представление» в MVC - это скорее выходной шаблон, каким, я полагаю, является ваш index.php? Если это так, то вы на самом деле собираете $model. Неопределенно.
Теоретически лучше явно передать эту переменную контроллерам. Однако, если на самом деле это всего лишь одна глобальная переменная, и вы обещаете не переусердствовать с ней, можно передавать ее как глобальную.

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

function view() {
    static $view;
    if (!isset($view)) { $view = new ArrayObject(); }
    return $view;
}

Таким образом, вы можете использовать $view=view(); для его локализации в своих контроллерах, продолжая использовать его как массив $view["xy"]= и как объект $view->xy= одновременно.
Или просто напишите print view()->title; в шаблоне.

person mario    schedule 06.09.2010

Делать глобальную переменную не очень хорошая идея, если вы используете шаблоны MVC в проекте. Есть и другие решения, которые будут более разумно использовать ваш MVC.

Если вы должны иметь однократное использование ресурса, используйте одноэлементный шаблон. Что-то вроде

class My_View {

   private $_instance;

   static public function getInstance() {
     if (null === self::$_instance) {
        self::$_instance = new self();
     }
     return self::$_instance;
   }

   private function __construct() { }

   public function __clone() {
      trigger_error("Cannot make copy of this object", E_USER_ERROR);
   }

   // add public methods and/or properties here...
}

и получить этот объект просмотра в любом месте с помощью

$view = My_View::getInstance();

Таким образом, у вас нет глобальных переменных, и вы используете лучшие практики объектно-ориентированного программирования.

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

Представление (не являющееся одноэлементным) также может быть общедоступным свойством или доступным через общедоступный метод одноэлементного приложения; My_Application::getInstance()->getView();, который также может содержать текущую конфигурацию, пути,

Предлагаемое имя My_view является просто примером. Использование некоторого соглашения об именах помогает организовать код и помогает избавиться от всех вызовов include/require в заголовках скриптов. Это не входит в сферу вашего вопроса, однако для ясности я кратко объясню:

В начальной загрузке вы объявляете свою функцию автозагрузки (как определено в руководство по PHP):

// My_Application.php located in /path/to/lib/My/Application.php
class My_View {

   private $_instance;

   static public function getInstance() {
     if (null === self::$_instance) {
        self::$_instance = new self();
     }
     return self::$_instance;
   }

   private $_basePath;
   public $view;

   private function __construct() { }

   public function __clone() {
      trigger_error("Cannot make copy of this object", E_USER_ERROR);
   }

   public function initAutoloader($base) {
      $this->_basePath = $base;
      spl_autoload_register(array($this, 'autoload'));
   }

   public function autoload($name) {
      require $this->_basePath . str_replace('_', '/', $name) . '.php';
   }

   // get the application global view
   public function getView() {
      if (null === $this->view) {
         $this->view = new My_View();
      }
      return $this->view;
   }
}
// perhaps have the parameter being a constant. I.g. APP_BASE_PATH
My_Application::getInstance()->initAutoload('/path/to/lib/');

И просто включите файл '/path/to/lib/My/Application.php', и когда вы получите доступ к My_View, функция load будет вызвана с $name = 'My_View', и функция просто потребует от вас файл '/path/to/lib/My/View.php'. Это немного для одного файла, но если все классы являются такими пространствами имен, вам нужен только один include (Autoloader), а все остальное загружается автоматически.

person Yanick Rochon    schedule 06.09.2010
comment
НО: использование синглетонов обычно является плохой практикой, безопасной для нескольких особых случаев. - person Jacco; 06.09.2010
comment
Синглтоны - это просто сложный семантический обходной путь, позволяющий иметь глобальные переменные под другим именем. - person mario; 06.09.2010
comment
это плохая практика, только когда этот шаблон становится предпочтительным шаблоном :) В противном случае (как в этом случае) это гораздо лучшее решение, чем использование глобальной переменной. Вместо того, чтобы иметь два или более синглтона, следует создать уникальный синглтон, содержащий все, что нужно разделить. ...В качестве примера можно привести множество сценариев, но принцип остается; это не должно быть первым шаблоном, который приходит на ум, я согласен. - person Yanick Rochon; 06.09.2010
comment
@mario, разница в том, что вы не можете «установить» синглтон, тогда как вы можете легко заменить глобальную переменную. Синглтоны обеспечивают определенную целостность приложения. - person Yanick Rochon; 06.09.2010
comment
Не Singleton обеспечивает целостность класса. Это сам класс. Синглтоны не являются лучшей практикой объектно-ориентированного программирования. Это запах кода. My_View — жестко закодированное имя класса. Таким образом, это зависимость от глобальной области видимости (или текущего пространства имен). Это ничуть не лучше, чем использование глобальной переменной. На самом деле это еще хуже, потому что global $view гораздо проще заменить Mock, чем жестко закодированный класс. Это не значит использовать глобальные переменные. Оба отстой. Кроме того, почему должен быть только один экземпляр View? - person Gordon; 06.09.2010
comment
@ Гордон, я сказал, что это обеспечивает некоторую целостность приложения, я никогда не говорил о целостности класса. И как класс может быть жестко запрограммирован в глобальной области видимости? ..это имя класса! PHP ведет реестр имен классов, и где находится код, я могу утверждать, что функция в глобальной области видимости... также находится в глобальной области видимости, даже в худшем случае! Не может быть реальной структуры с глобальными функциями; ни инкапсуляции, ни автозагрузки, но куча операторов include и require по всему приложению. В этом смысле Wordpress и Drupal — кошмар; Где находится тело этой функции xyz? - person Yanick Rochon; 06.09.2010
comment
@Gordon, в вопросе упоминается использование шаблона MVC, предполагая, что другой шаблон просто логичен. И поскольку создание глобальной переменной $view в динамической веб-программе было частью описания вопроса, предлагается шаблон Singleton. Кроме того, этот ответ организует различные компоненты в пространствах имен, что позволяет автоматически загружать класс. Наконец, я согласен с тем, что представление не должно иметь единственного экземпляра. Я работаю с ZF, и, несмотря на то, что я обычно работаю с одним зарегистрированным экземпляром представления, сам класс Zend_View не является синглтоном, но это не было предусмотрено OP. - person Yanick Rochon; 06.09.2010
comment
Как это логично? MVC не имеет ничего общего с шаблоном Singleton. Это даже не шаблон веб-презентации. Синглтон гарантирует, что конкретный класс может быть создан только один раз. Это все. ОП об этом не спрашивал. Синглтон доступен везде исключительно из-за того, что к нему обращаются статически с помощью метода getInstance(). Но - если вы не используете пространства имен - выполнение MyClass::foo() ничем не отличается от использования обычной функции, например. myclass_foo(), потому что тогда статический вызов — это вызов глобального пространства имен с тем же риском конфликта. - person Gordon; 06.09.2010
comment
@Yanick Rochon, вы заявляете: этот ответ организует разные компоненты в пространствах имен. Но на самом деле вы организуете разные компоненты в классы. класс != пространство имен. Хотя оба предлагают форму инкапсуляции. - person Jacco; 07.09.2010
comment
@Jacob, поскольку PHP 5.3 еще не реализован на каждом веб-хостинге, это замена My\View. Это соглашение об именах, принятое Zend, и оно работает очень хорошо. Но в любом случае, кто я такой, чтобы спорить, если кому-то нужна куча глобальных функций? - person Yanick Rochon; 07.09.2010
comment
@Yanick: Разве static public getInstance() не должно быть static public function getInstance()? - person JDelage; 23.09.2010

Решение этой проблемы, которое я видел, заключается в расширении контроллеров от базового контроллера. Внутри этого базового контроллера создается экземпляр свойства $data; новые данные представления добавляются к этому свойству. Например:

class PageController extends BaseController {

    function IndexAction() {
        $this->data['title'] = "Page Title";
        $this->data['content'] = "<p>Page content.</p>";

        $this->render();
    }
}

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

Надеюсь, это поможет, и вы сможете реализовать рабочее решение.

person Martin Bean    schedule 06.09.2010
comment
Что ж, тогда это должно быть хорошо, но также не использует глобальные переменные. Не уверен, что вы не понимаете приведенный выше пример или определение global в PHP. - person Martin Bean; 06.09.2010