MVC на phppatterns.com

Духовность™

Продвинутый новичок
whirlwind
когда такой код пишешь, пиши комментарии, а то если честно не очень понятно со стороны)

Ну хорошо. В какой-то мере до меня доходит идеология. Что будет на практике?... или может что-то посложнее стоит сейчас сделать?
 

whirlwind

TDD infected, paranoid
А чего там комментировать, там же все написано почти по англицки :) Спрашивай конкретно что непонятно - откомментирую.

Посложнее нужно делать тогда, когда в этом посложнее возникнет необходимость. Вот, например, валидаторы мы еще никаким боком не рассматривали. Но их еще рано. Сначала надо прочуствовать пользу от model + view + controller, а потом уже унифицировать прочее.
 

whirlwind

TDD infected, paranoid
Ты сделай рабочий пример того, что обсуждали. Не просто рабочий, а что бы он работал на странице где нить. Как мы выявим закономерности, что бы вынести их в базовые классы, на основе сферических коней?
 

AmdY

Пью пиво
Команда форума
triumvirat
1. это для разыменования и постороения красивой ёлочки ->*->*
2. впринципе нужно делать проверку изменившихся данных, а save становится единой точкой для сохранения всех изменений
3. поэтому я предлогал возвращить объект, а массив можно получить $user->getObjectById($request->id_user)->toArray();

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

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

whirlwind

TDD infected, paranoid
Добавь гестбук, что бы автором сообщения был User. И на странице соотвественно две модели. Вот и попробуй найти хорошее решение, когда у тебя есть контроллеры и модели.
 

Духовность™

Продвинутый новичок
Времени уже на сегодня не остается, вот что сделал (если у вас хватит терпения изучить этот код))

http://mensland.ru/phpclub/ - пока только вывод сообщений

http://mensland.ru/phpclub/code/ - коды

Структура классов:

class.BaseModel.php - базовый класс любой модели
class.Guestbook.php - модель гостевой
class.User.php - модель пользователя

class.BaseController.php - базовый контроллер
class.GuestbookController.php - контроллер гостевой

class.View.php - вью
class.Request.php - класс запроса
class.database.php - обертка для СУБД

sql.txt - SQL
template.txt - шаблон

Что очень смущает: это инстанцирование моделей в конструкторе контроллера
PHP:
$ctrl = new GuestbookController(new User(), new Guestbook());
А потом, в контроллере, приходится всё равно делать

PHP:
$user = $this->modelUser->getObjectById($request->id_user);
для получения объекта реальной сущности. Тем самым мы получаем ДВА объекта. Объект $user, который содержит информацию о реальном пользователе, и объект модели пользователя, переданный через конструктор, который не привязан к конкретному пользователю, а является просто объектом.

Поэтому мне всётаки кажется более правильным инстанцирование любых моделей не в конструкторе контроллера, а непосредственно в его коде, там, где это необходимо, когда мы знаем, с каким параметром (ID) инстанцировать класс модели. Т.е. в идеале я вижу решение, когда в конструктор контроллера ничего не предается, а объекты модели создаются через статические getObjectById:

PHP:
$user = modelUser::getObjectById($request->id_user);
// далее работаем с этим юзером, имея всего один объект модели - $user
if (!$user->getId())
{
    $this->error = 'Хакер? Хе-хе....';
}

echo 'Тебя зовут '.$user->user_name;
 

whirlwind

TDD infected, paranoid
Разве вот это нельзя вынесть в базовый контроллер?

PHP:
       // Наполняем view данным для подстановки в шаблон
        $this->view->error = $this->error;

        foreach ($this->bufer as $key => $value)
        {
            $this->view->$key = $value;
        }
и вообще какой смысл переливать из буфера в вид? Почему не сразу в вид? Ниже ответ на вопрос - зачем.

А потом, в контроллере, приходится всё равно делать


$user = $this->modelUser->getObjectById($request->id_user);
а я разве не говорил что нужно сделать findById() ?
тогда

PHP:
$this->modelUser->findById($id);
едем дальше. В GuestbookController нет два контроллера. Это один контроллер. User туда привинчен на соплях. Потому что иначе возникли бы определенные вопросы по виду, например такие как - как избежать конфликтов одноименных атрибутов разных моделей в одном View.

-~{}~ 14.11.08 19:26:

PS. Про статические методы забудь. Если тебя смущает то, о чем ты говоришь больше того, что у тебя получается в контроллере, то бегом читать DataMapper и разделять модель и сторадж. Если разбираем MVC, то делаем модель агрегатом и через агрегат работаем с моделью, а не через десятый экземпляр полученный в результате последовательных вызовов getObjectBy что-то там.
 

Духовность™

Продвинутый новичок
Почему не сразу в вид?
верно!

В GuestbookController нет два контроллера
я отпечатался, я говорил о моделях, да.


Вот что не понятно мне очень.

Если мы агрегируем в контроллер модели, то подразумевается, что это уже инстанцированные объекты. Дальше ты предлагаешь делать
PHP:
$this->modelUser->findById($id);
Что возвратит этот код? Объект? Если да, то мой вопрос относительно 2-х инстанцированных объектов остается открытым - у нас получается опять же ДВА объекта модели - агрегируемый и полученный с помощью findById.

Или же этот код просто заполняет уже существующий объект модели свойствами?
 

whirlwind

TDD infected, paranoid
> Что возвратит этот код?

Палец, относится к ладони? А команда разогнуть или согнуть палец относится так же к ладони? Это метод управления состоянием объекта ладонь? А теперь ответь на вопрос - куда нужно передать средний палец после вызова метода ладони "показатьСреднийПалец"? Так откуда у тебя берется второй объект модели?
 

Духовность™

Продвинутый новичок
whirlwind
:)

Так откуда у тебя берется второй объект модели?
Ну я просто подумал, что findById должен возвращать объект.. по аналогии со статическим методом ActiveRecord. Я просто в контексте ООП только так пока делал....

Значит, findById заполняет свойства User! Понятно! Вот что получилось:

PHP:
// заполняем модель $this->modelUser данными пользователя из БД

$this->modelUser->findById($request->id_user);

if (!$this->modelUser->getId())
{
      $this->view->error = 'BAD_USER';
}
-~{}~ 24.03.09 11:27:

Читаю тему, то, что раньше не понимал тут написанное, уже хоршо понимаю... Но это термины. У меня так и не получилось сделать гибко. Сделать контроллеры, способные работать в различных вариациях (если требуется одно и тоже действие).

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

Пример (редактирование пользователя в админке):

PHP:
class Controler.....
{
    public function edit()
    {
        $this->view->menu_data = $this->menu();
        $this->view->setErrorKeys($this->user_mapper->getModelAttributes());
        $this->view->setErrorKeys(array('user_password_1', 'user_password_2'));

        $params = array
        (
            'what' => '`id_country`, `country_name'.(LANG == 'en' ? '_en' : '').'` as `country_name`',
            'order' => array('country_name' => 'ASC'),
        );
        $this->view->countries = Country::findByParams($params)->getData()->getDataAsArray();
        $this->view->user_password_1 = $this->view->user_password_2 = '';
        $this->view->user_phones = array();

        $obj_phone_types = new Phone_Type();
        $this->view->form_phones = $obj_phone_types->findAll()->getData()->getDataAsArray();

        $group_mapper = new Group_Mapper();
        // выбираем все группы, за исключением группы "гости".
        $groups_list = $group_mapper->getObjectList_(array('where' => 'id <> 3'));
        // Массив для формирования статической формы.
        $this->view->setGroupsList($groups_list, 'form_groups');

        $phone_mapper = new Phone_Mapper();

        if (empty($this->user))
        {
            $this->user = $this->user_mapper->createNew();

            foreach ($this->view->form_phones as $phone_type)
            {
                $this->view->user_phones[$phone_type->id] = $phone_mapper->createNew()->getData()->getDataAsArray();
            }
        }

        $params = array
        (
            'where'=> array('id_user = ?' => array((int)$this->user->id))
        );
        $user_phones = $phone_mapper->getObjectList_($params);

        foreach ($user_phones->getData() as $phone)
        {
            $this->view->user_phones[$phone->id_type] = $phone->getData();
        }

        // Неизменяемые данные формы.
        $this->view->html_header = $this->user->user_name.' '.$this->user->user_last_name;
        $this->view->id = $this->user->id;
        $this->view->user_ip = $this->user->user_ip ? int2ip($this->user->user_ip) : 0;
        $this->view->user_regdate = $this->user->user_regdate;
        $this->view->user_visitdate = $this->user->user_visitdate;
        $this->view->return_on_page = $this->request->get('return_on_page', 'int');

        if (Request::isPost())
        {
            $this->user = $this->user_mapper->createFromArray($this->request->getData());

            $validator = new User_Validator('User');
            $validator->setDomainObject($this->user);
            $validator->checkCorrectLogin('user_login');
            $validator->checkExistLogin('user_login');

            if (!$this->user->id OR 
                 $this->user->id && (!empty($this->user->user_password_1) || !empty($this->user->user_password_2) ))
            {
                $validator->checkCorrectPassword('user_password_1');
                $validator->checkCorrectPassword('user_password_2');
            }

            $validator->checkPasswords('user_password', 'user_password_1', 'user_password_2');
            $validator->checkCorrectMail('user_mail');
            $validator->checkExistMail('user_mail');
            $validator->checkName('user_name');
            $validator->checkAge('user_age_day', 'user_age_month', 'user_age_year');
            $validator->checkSex('user_sex');
            $validator->checkPlace('user_city', 'user_region', 'user_country');
            //$validator->isEmpty('zip_code');
            $validator->checkIcq('user_icq');
            $validator->checkUrl('user_url');

            // создаем массив объектов типа Phone на основе полученных данных
            $user_phones = new Object_List();

            foreach ($this->request->phones as $phone_data)
            {
                $user_phones->add($phone_mapper->createFromArray($phone_data), $phone_data['id_type']);
            }

            if ($err = $validator->getErrors())
            {
                $this->view->setErrorMessage($err);
                $this->view->user_phones = $user_phones->getData()->getDataAsArray();
            }
            else
            {
                $this->user->user_ip = $_SERVER['REMOTE_ADDR'];

                $this->user_mapper->save($this->user);

                foreach ($user_phones as $phone)
                {
                    $phone->id_user = $this->user->id;
                    $phone_mapper->save($phone);
                }

                $redirect = new Redirect();
                $redirect->setMessage('user_edit_ok');
                $redirect->addParam('user_name', Format::hsc($this->user->user_name.
                                                 ($this->user->user_last_name ? ' '.$this->user->user_last_name : '')
                                                 )
                                   );
                $redirect->addParam('id_user', $this->user->id);
                $redirect->setRedirectUrl($this->request->get('return_on_page', 'int')
                                          ? '/admin/user/edit/?id='.$this->user->id
                                          : ($this->request->referer ? $this->request->referer : '/admin/user/')
                                         );
                $redirect->run();
            }
        }

        $this->view->id_group = $this->user->id_group;

        $this->view->user_active = $this->user->user_active;
        $this->view->user_login = $this->user->user_login;
        $this->view->user_mail = $this->user->user_mail;
        $this->view->user_name = $this->user->user_name;
        $this->view->user_last_name = $this->user->user_last_name;

        $this->view->user_password_1 = $this->user->user_password_1;
        $this->view->user_password_2 = $this->user->user_password_2;

        $this->view->user_age_day = $this->user->user_age_day;
        $this->view->user_age_month = $this->user->user_age_month;
        $this->view->user_age_year = $this->user->user_age_year;

        $this->view->user_sex = $this->user->user_sex;

        $this->view->user_country = $this->user->user_country;
        $this->view->user_region = $this->user->user_region;
        $this->view->user_city = $this->user->user_city;
        $this->view->zip_code = $this->user->zip_code;

        $this->view->user_icq = $this->user->user_icq;
        $this->view->user_url = $this->user->user_url;
        $this->view->user_about = $this->user->user_about;
    }
вот видите... этот код по сути можно использовать только для редактирования пользователя в админе, но никак не на фронтенде, из-за обилия кода, специфичного для административной части. Т.е. мои контроллеры в рамках ООП - это ООП ради ООП.

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

PHP:
$this->view->menu_data = $this->menu();
этот код уже убивает универсальность контроллера. Но я не понимаю, как сделать иначе :(

Подскажите что-нибудь.
 

AmdY

Пью пиво
Команда форума
во первых, сделал бы
foreach($this->user->getFields() AS $key => $value) {
$this->view->{$key} = $value;
}

про меню я не понял что тебе не нравится?
 

Духовность™

Продвинутый новичок
во первых, сделал бы
да, сейчас всё нормально, это один из перывых кодов...

про меню я не понял что тебе не нравится?
то, что оно в контроллере маячит. То, что контроллер наполняется не только своей основной функциональностью, а ещё и всякими getMenu().

Это всё говнокод, имхо.
 

john.brown

просто кулибин
Ну, я это решаю вводом некоего LayoutManager. Смысл в том, что в основном шаблоне все поделено на блоки, типа, TOP, LEFT, MAIN, RIGHT. Результаты работы основного модуля, который запрошен, всегда помещается в MAIN, а LayoutManager уже смотрит, какие модули прописаны в конфиге для остальных блоков, при данном запрошенном модуле, и вызывает их.
 
Сверху