Powered by CodeIgniter

Уроки

(28)
14
16 голосов
Учиться, учиться и еще раз учиться — развитие личности идет таким путем.
Чем больше пишу про cogear², тем больше хочется про него рассказывать и творить дальше. Вошел во вкус.
Итак, мы подобрались к заветному моменту любого движка — работе с формами. Ведь достаточно большую часть времени мы проводим именно вводя информацию на сайте. Чаще всего набирая коммент, конечно. И тем ценнее они для нас.
Мы помним, что первый cogear явил миру достаточно интересный метод работы с формами. Как и везде, здесь в ход идет системный анализ, обобщение, стандартизация. Во второй версии мы добились желаемых целей. Равно как и другие жизненно важные ключевые звенья cogear² шестеренка Form блещет своей ООП-реализацией. Теперь классами описывается все — форма, ее элементы, валидаторы и фильтры.
Устроим экскурс во внутренне устройство нашего Form API.
Начнем с того, что разложим форму на элементы. Элементами формы являются поля. В cogear² каждый элемент форумы представлен своим классом.
Как же создать форму? Для удобства хранения информации здесь мы отошли от явного наращивания дерева объектов. Как и конфиги формы хранятся в массивах.

Вы можете сохранить массив с формой в файл или же скормить его напрямую в виде параметра в конструктор объекта формы.

Случай первый. Форма из файла.

Рассмотрим шестеренку User.
В корне шестеренки мы найдем папку forms, а в ней файл profile.php со следующим содержимым:
<?php return array( 'name' => 'user-profile', // другие параметры формы типа action, enctype // если не укажете — будут использованы значения класса Form_Object по-умолчанию … 'elements' => array( 'personal' => array( 'label' => t('Personal','User'), 'type' => 'tab', ), 'avatar' => array( 'label' => t('Avatar', 'User'), 'type' => 'image', 'preset' => 'avatar.photo', 'path' => UPLOADS . DS . 'avatars' . DS . cogear()->user->id, //'rename' => cogear()->user->id, ), 'realname' => array( 'label' => t('Real name','User'), 'type' => 'text', 'access' => access('user_edit_realname'), 'validators' => array(array('Length',5),'Name'), ), 'login' => array( 'label' => t('Login', 'User'), 'type' => 'text', 'access' => access('user edit_login'), 'validators' => array(array('Length', 3), 'AlphaNum', 'Required', array('User_Validate_Login', User_Validate_Login::EXCLUDE_SELF)), ), 'email' => array( 'label' => t('E-Mail', 'User'), 'type' => 'text', 'validators' => array('Email', 'Required', array('User_Validate_Email', User_Validate_Email::EXCLUDE_SELF)), ), 'password' => array( 'label' => t('Password', 'User'), 'type' => 'password', 'validators' => array(array('Length', 3), 'AlphaNum') ), // 'role' => array( // 'label' => t('Role','User'), // 'type' => 'select', // 'validators' => array('Required'), // 'callback' => 'User_Gear->getRoles', // ), // 'options' => array( // 'label' => t('Test','User'), // 'type' => 'checkbox', // ), 'submit' => array( 'type' => 'submit', 'label' => t('Update'), ), 'delete' => array( 'type' => 'submit', 'label' => t('Delete'), 'access' => access('users delete_all'), ) ) );
Кажется, что код настолько простой, что и пояснять нечего. Если вопросы есть, пишите в комменты.

И такую форму мы вызываем следующим образом:
$form = new Form('User.profile');

Случай второй. Форма из массива.

Обратим внимание на шестеренку Comments, которую я начал писать сегодня.
$form = new Form(array( 'name' => 'addComment', 'elements' => array( 'info' => array( 'type' => 'div', 'value' => t('Leave comment ').' '.$this->user->getAvatarLinked().' '.$this->user->getLink().' ↓ ', ), 'body' => array( 'type' => 'textarea', 'validators' => array('Required', array('Length', 5)), 'filters' => array(array('strip_tags', '<b><i><u>')), ), 'submit' => array( 'type' => 'submit', 'label' => t('Post'), ), ) )); Собственно, разницы никакой, кроме того, что форму из файла можно использовать в разных местах, в то время, как форма из массива используется только в конкретный момент.

Обработка данных

Надеюсь, что еще с первым cogear вы забыли о той рутине, которая обычно связана с обработкой результатов отправки формы. Если еще не до конца, то с cogear² точно забудете.

Проще не придумаешь. Смотрите сами.
if ($result = $form->result()) { $comment = new Comments_Object(); $comment->pid = $Page->id; $comment->aid = $this->user->id; $comment->created_date = time(); $comment->body = $result->body; $comment->ip = $this->session->ip; if ($comment->save()) { $Page->comments = $this->db->where('pid',$Page->id)->count('comments'); $Page->save(); flash_success(t('Your comment has been successfully posted!')); redirect($Page->getUrl()); } } $form->show();
Опять-таки, если вопросы есть — комментируйте.

Вернемся же в шестеренку User и поглядим на более сложный процесс обработки данных.
$user = new User_Object(); $user->where('id', $id); $user->find(); $form = new Form('User.profile'); $user->password = ''; $form->object($user->object()); if ($form->elements->avatar->is_ajaxed && Ajax::get('action') == 'replace') { $user->avatar = ''; $user->update(); } if ($result = $form->result()) { if ($user->login != $result['login']) { $redirect = Url::gear('user') . $result['login']; } if ($result->delete && access('users delete_all')) { $user->delete(); flash_success(t('User <b>%s</b> was deleted!')); redirect(Url::link('/users')); } $user->merge($result); if ($result->password) { $user->hashPassword(); } else { unset($user->password); } if ($user->update()) { d('User edit'); flash_success(t('User data saved!'), t('Success')); d(); if ($user->id == $this->id) { $this->store($user->object()->toArray()); } redirect(Url::gear('user') . $user->login); } } append('content', $form->render());
Обратим свое внимание на полезный метод:
$form->object($user->object()); Он не просто выставляет данные элементам формы, но еще и хранит ссылку на объект. Очень удобно для хуков.

Также при помощи джедайского аякса мы можем творить чудеса:
if ($form->elements->avatar->is_ajaxed && Ajax::get('action') == 'replace') { $user->avatar = ''; $user->update(); } О таких тонкостях будет отдельный материал.

Если мы, как админ, редактируем чужой профиль:
if ($user->login != $result['login']) { $redirect = Url::gear('user') . $result['login']; }
Если была нажата кнопка «Удалить»:
if ($result->delete && access('users delete_all')) { $user->delete(); flash_success(t('User <b>%s</b> was deleted!')); redirect(Url::link('/users')); }
Объединяем полученные из формы данные с уже существующим объектом пользователя:
$user->merge($result); if ($result->password) { $user->hashPassword(); } else { unset($user->password); }
Сохраняем результат:
if ($user->update()) { d('User edit'); flash_success(t('User data saved!'), t('Success')); d(); if ($user->id == $this->id) { $this->save($user->object()->toArray()); } redirect(Url::gear('user') . $user->login); } И в конце, если не ушли по редиректу, показываем форму.
append('content', $form->render()); Последняя запись эквивалентна
$form->show(); Просто данный конкретный кусок кода был создан еще до столь удобных наработок.

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

P.S. Завтра презентую бета-версию. Скорее всего сделаю видео-ролик, где расскажу обо всех тонкостях.
23:27 ← 31 июля 2011 Отправить в Твиттер adminadmin  RSS comments 21

Комментарии (21) ↓

Isildien Isildien time 00:12 ← 01 августа 2011 #
То есть что бы добавить доп.поле например к профилю, придется лопатить файлы шестеренки? И при обновлении следить чтобы файлы не перезаписались?
Автор
admin admin time 00:19 ← 01 августа 2011 #
Конечно, нет.
Чтобы добавить доп.поле, надо просто хукнуть форму.
Вот как это сделано в шестеренке Loginza:
hook('form.user-profile.init', array($this, 'hookUserProfile')); … /** * Hooking user profile form * * @param object $Form */ public function hookUserProfile($Form) { $data['social'] = array( 'type' => 'tab', 'label' => t('Social') ); $data['social_info'] = array( 'type' => 'div', 'value' => t('<p>There you can attach social accounts to integrate with your profile. It will help you for quick login.</p>'), ); if ($connected_accounts = $this->db->where('uid', $Form->object->id)->get('users_loginza')->result()) { $tpl = new Template('Loginza.accounts'); $tpl->accounts = $connected_accounts; $data['loginza_accounts'] = array( 'type' => 'div', 'value' => $tpl->render(), ); } js('http://loginza.ru/js/widget.js'); $data['loginza'] = array( 'type' => 'div', 'value' => HTML::a($this->api->getWidgetUrl(Url::link() . 'loginza'), HTML::img($this->folder . '/img/sign_in_button_gray.gif', t('Log in via social services', 'Loginza')), array('class' => 'loginza')), ); $Form->elements->place($data, 'submit', Form::BEFORE); }
Freem Freem time 08:31 ← 01 августа 2011 #
Дима, здорово. В cogear one я залип на принцип работы форм. Надеюсь с выходом рабочей версии, мы будем только улыбаться создавая даже очень сложные сайты =)
JiLiZART JiLiZART time 09:26 ← 01 августа 2011 #
Код как воздушный бисквит, так и тает во рту :) Его так легко читать.
ЗЫ
С нетерпением жду статью про джедайский аякс
inetlover inetlover time 12:04 ← 01 августа 2011 #
Чем больше пишу про cogear², тем больше хочется про него рассказывать и творить дальше.

Чем больше читаю то, что пишет Дима, тем больше хочется взять Cogear2 и начать с ним работать.
Автор
admin admin time 12:14 ← 01 августа 2011 #
Саша, так никто не мешает. Скачай, поставь и пробуй. Я сейчас структурно редко что совершенствую, только шестеренки пишу новые. Все базовые моменты уже готовы к употреблению.
inetlover inetlover time 12:41 ← 01 августа 2011 #
Да, но шестеренки комментариев нет, вот я и жду в первую очередь ее.
Автор
admin admin time 16:14 ← 01 августа 2011 #
Надо чаще встречаться заглядывать на сайт бета-версии ;-)
Isildien Isildien time 16:22 ← 01 августа 2011 #
А что с elfinder? :)
Если пользователь не захочет например создавать подпапки и лить всё в 1 и зальет тысяч 10 тормозить не будет?
Автор
admin admin time 16:23 ← 01 августа 2011 #
Попробуй, узнаешь. На бете он работает.
Isildien Isildien time 16:44 ← 01 августа 2011 #
Зачем мне пробовать? Я просто уже не однократно читал об этом на разных сайтах. Вообще я против elfinder ничего не имею плохого мне как админу будет с ним только удобнее, но вот пользователям…
Автор
admin admin time 16:54 ← 01 августа 2011 #
Можно ввести лимит на загрузку более N файлов в одну папку.
Isildien Isildien time 17:01 ← 01 августа 2011 #
В принципе можно F.A.Q на сайте присобачить где будут правила написаны для загрузки, но все всё равно соблюдать все не будут, тогда думаю разумно будет поставить какойто лимит…
Isildien Isildien time 17:05 ← 01 августа 2011 #
Ах да, забыл, я так понимаю каждому пользователю дается своя папка для загрузки так? А если пользователей сотни тысяч или миллионы, что тогда? Опять же получится куча папок при обращение к которым как мне кажется будет повышаться нагрузка на процессор, возможно ли реализовать разбивку папок пользователей по подпапкам, как например список пользователей вконтакте?.
Извиняюсь если достал со всем этим, просто хочется что бы были продуманы все моменты.
Fhtang Fhtang time 18:38 ← 01 августа 2011 #
P.S. Завтра презентую бета-версию. Скорее всего сделаю видео-ролик, где расскажу обо всех тонкостях.
Т.е. Cogear2 будет явлен миру завтра?
А во сколько?
Isildien Isildien time 18:42 ← 01 августа 2011 #
То есть сегодня, на дату поста посмотри =)
Fhtang Fhtang time 19:38 ← 01 августа 2011 #
Вот я спать-то :O
Автор
admin admin time 19:10 ← 01 августа 2011 #
Сегодня. Всегда охота тысячу мест доделать, прежде чем показывать.
Сейчас, еще пару штук допилю, и иду делать видео.
Fhtang Fhtang time 19:41 ← 01 августа 2011 #
Допиливать нужно будет с 1 по 31 августа ёба :D
Пойду помоюсь и подстригу ногти, сегодня будет ночь танцующих кинжалов — сегодня буду пробовать Cogear2.
Так что, о великий одминэ, поспать тебе не получится ;)
Автор
admin admin time 19:43 ← 01 августа 2011 #
Только без шалостей :-)
Nailed Nailed time 09:30 ← 28 сентября 2011 #
admin, сдесь уместны транзакции. Приучай народ к хорошему :-)