Zend Framework: использование моделей и представлений, лучшие практики

Я относительно новичок в объектно-ориентированном программировании. Я в значительной степени понимаю концепции, но с практической точки зрения мне очень трудно найти информацию о том, как лучше всего использовать модели в моих приложениях Zend Framework.

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

В моем контроллере:

$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object;

На мой взгляд, шаблон:

<h2><?= $this->object->getName() ?></h2>

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

<h2><?= $this->object->name ?></h2>

Я не хочу, чтобы мой контроллер выполнял всю работу по необходимости знать все о модели:

$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object;
$this->view->object->name = $object->getName();

Как лучше всего использовать модели в Zend Framework? Может ли кто-нибудь порекомендовать какой-либо учебник, который поможет мне понять эту дилемму модель / представление в Zend Framework?


person Andrew    schedule 02.03.2009    source источник


Ответы (4)


Одна из возможностей - использовать волшебные методы __set и __get в PHP. Я использую их вот так в своем абстрактном классе модели:

abstract class Model_Abstract
{
    protected $_data;

    // Private Data Members assigned to protected $_data
    public function __construct($data = null)
    {
        // Makes it so that I can pass in an associative array as well as 
        // an StdObject.
        if(!is_object($data)) {
            $data = (object) $data;
        }

        $this->_data = $data;

    }

    public function __get($key)
    {
        if (method_exists($this, '_get' . ucfirst($key))) {
            $method = '_get' . ucfirst($key);
            return $this->$method();            
        }
        else {
            return $this->_data->$key;
        }   
    }

    public function __set($key, $val)
    {
        if ( method_exists( $this, '_set' . ucfirst($key) ) ) {
            $method = '_set' . ucfirst($key);
            return $this->$method($val);            
        }
        else {
            $this->_data->$key = $val;
            return $this->_data->$key;
        }
    }
}


class Model_User extends Model_Abstract
{
    //Example overriding method for the property firstName in the $_data collection.
    protected function _getFirstName()
    {
        // Do some special processing and then output the first name.
    }
}

Это делает так, что вы можете указывать геттеры и сеттеры для свойств по мере необходимости, но делает так, что вам не нужно определять стандартные функции для каждого свойства, а только те, в которых вы хотите выполнить какую-то обработку, прежде чем возвращать ценность. Например, я использую эту функциональность в нескольких местах, чтобы изменить даты, соответствующие ISO (хранящиеся в MySQL), в более компактный и читаемый формат для пользователей.

Что касается того, что разместить в вашем контроллере, я бы рекомендовал посмотреть this post, чтобы получить конкретную информацию о том, какую обработку разместить в вашем контроллере.

Некоторые считают, что они предпочли бы иметь помощника, который автоматически загружает модели в представление и полностью обходит контроллер. Лично я бы сказал, что в контексте Zend Framework и PHP имеет смысл передавать модели в представление из контроллера, потому что состояние моделей в представлении часто зависит от того, что пришло из запроса (который обязательно должен быть обработан. в контроллере).

Обновление: в соответствии с критикой в ​​комментариях, я хотел бы указать на то, что уровень доступа к базе данных и уровень домена (или модели) на самом деле две разные вещи, хотя с Active Record они смешаны вместе. Я задал этот вопрос некоторое время назад и получил несколько полезных отзывов. по этому поводу. Что бы вы ни решили делать с моделью, вы захотите предоставить согласованный API для всех объектов домена, независимо от того, откуда поступают данные для модели.

Я полагаю, что одно из преимуществ, предлагаемых ответом Саема, заключается в том, что он предлагает возможность напрямую отображать возвращаемые значения свойств / функций из одного или нескольких объектов домена в объект представления. Теоретически использование в представлении выглядит так:

// Mapped from Model_User::_data->last_name and Model_User::_data->first_name
$this->name 
person Noah Goodrich    schedule 02.03.2009
comment
На самом деле это плохой способ делать что-то. Вы не только предоставляете произвольный доступ ко всем свойствам, независимо от того, должны ли они быть (не) сериализованы в / из формы или нет, но также перегружает модель концепцией сопоставления, о чем на самом деле и просили. - person Saem; 03.03.2009
comment
Включение логики формы в модель смешивает представление и модель, что также является плохой формой. И, как я уже сказал, переопределение поведения по умолчанию очень просто и позволяет избежать создания глупых шаблонных методов получения и установки. - person Noah Goodrich; 03.03.2009
comment
И если вы посмотрите на класс Zend_Db_Table_Row, вы обнаружите, что Zend на самом деле использует аналогичный принцип для отображения полей и их значений из таблиц. - person Noah Goodrich; 03.03.2009

Если с шаблонами будут работать только другие разработчики, я бы рекомендовал просто передать модели. Вот ссылка на сообщение Джеффа Этвуда в MVC Понимание модели-представления-контроллера

person Hawk Kroeger    schedule 02.03.2009

Это не особо ориентировано на фреймворк zend, но, на мой взгляд, проблема носит довольно общий характер.

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

Было бы проще написать несколько функций сопоставления, что было бы хорошо, если бы все, чего вы избегали, - это сопоставление одного и того же снова и снова.

Если вам нужно более общее решение, которое также направлено на то, чтобы не писать этот шаблонный код и сохранять вещи более СУХИМИ, я предлагаю создать класс сопоставления.

Вы можете создать ViewModelMapper, который возьмет модель или несколько моделей и сопоставит их с представлением.

class ViewModelMapper
{
    public function __construct($view)
    {
        //set the properties
    }

    public function addModel($model, $overrideViewProperty = null)
    {
        //add the model to the list of models to map, use the view's property 
        // name to figure out what to map it to? Allow for an override just in case.
    }

    public function getMappedView()
    {
        //take the view, map all the models
    }
}

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

person Saem    schedule 02.03.2009
comment
Приведите соответствующий пример, в котором используется этот класс Mapper. На данный момент все, что я вижу, - это способ сделать добавление моделей в представление более сложным. - person Noah Goodrich; 03.03.2009

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

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

//controller
$object = new Object();
$object->setName('Foo Bar');
$this->view->object = $object->getViewClass();

//view template
<h2><?= $this->object->name ?></h2>

Я не знаю, есть ли лучший способ выполнить эту работу в Zend Framework, но это одно из решений.

person Andrew    schedule 10.03.2009