Formo – moduł formularzy – modyfikacje
W poprzednich notkach związanych z Kohaną opisałem obsługę modułu Formation umożliwiającego pracę z formularzami. Jak można było zauważyć spełniał on swoje zadanie w większości sytuacji. Ma on niestety jeden dosyć poważny minus, od ponad roku nie jest rozwijany (i zapewne już nie będzie), tak więc chcąc korzystać z niego w swoich projektach ciąży na nas konieczność modyfikowania go tak, aby działał z najnowsza wersją frameworka.
Przez długi czas nie było zamiennika, który udostępniałby funkcjonalność znaną z Formation. Na szczęście jakiś czas temu sytuacja ta się zmieniła. Od kilku miesięcy intensywnie rozwijany jest moduł Formo, który poza możliwościami znanymi z Formation udostępnia możliwość łatwego rozszerzenia swojej funkcjonalności wykorzystując do tego celu pluginy.
Standardowo poznawanie nowego modułu zaczniemy od drobnych przeróbek.
Komunikaty błędów
Chcąc dodać regułę walidacji do pola formularza wystarczy skorzystać z metody add_rule. Pobiera ona trzy parametry, z czego ostatni stanowiący komunikat błędu jest opcjonalny. W przypadku, gdy nie podamy indywidualnego komunikatu błędu, a reguła walidacji nie zostanie spełniona zobaczymy mało mówiący komunikat Invalid. Oczywiście moglibyśmy zdefiniować komunikat błędu dla każdego pola, lecz jest to zadanie pozbawione sensu, gdy mamy wiele formularzy / pól w formularzu i dla większości chcemy wyświetlić taką samą treść błędu.
Naturalnym działaniem byłoby wyświetlanie komunikatu błędu na podstawie nazwy funkcji walidującej dane – tak jak miało to miejsce w Formation. Aby tego dokonać musimy rozszerzyć główną klasę modułu Formo. O rozszerzaniu można dowiedzieć się więcej z dokumentacji Kohany.
public function add_rule( $element, $rule, $message = '' ) { if( !$message ) { $rule_name = $this -> get_rule_name( $rule ); $message = Kohana :: lang( 'validation.' . $rule_name[ 0 ], $rule_name[ 1 ] ); } return parent :: add_rule( $element, $rule, $message ); }
Zadaniem powyższej metody jest dodanie komunikatu błędu, gdy ten nie został określony. Komunikat pobierany jest z pliku z tłumaczeniami (i18n) na podstawie nazwy funkcji użytej do walidacji danych. Jako, że regułą walidacji może być zarówno nazwą funkcji, nazwą funkcji z parametrami oraz funkcją zwrotną nie możemy w prosty sposób wykorzystać zmiennej $rule do określenia nazwy funkcji, która zostanie użyta. Musimy posłużyć się dodatkową metodą, która tego dokona.
private function get_rule_name( $rule ) { // 1 $rule_name = $rule; if( $pos = strpos( $rule_name, '::' ) ) $rule_name = substr( $rule_name, $pos +2 ); // 2 if (preg_match('/^([^[]++)[(.+)]$/', $rule_name, $matches)) { $rule_name = $matches[1]; $args = preg_split('/(?<!\\),s*/', $matches[2]); $args = str_replace(',', ',', $args); } $rule_name = str_replace( '[]', '', $rule_name ); return array( $rule_name, $args ); }
Powyższa metoda składa się z dwóch części. W pierwszej wyciągana jest nazwa metody w chwili, gdy przekazana reguła filtracji jest funkcją zwrotną, w drugiej następuje sprawdzenie czy wraz z regułą zostały przekazane dodatkowe parametry po czym następuje rozdzielenie nazwy funkcji od parametrów. Kod drugiego etapu został zaczerpnięty z klasy Validation.
Usuwanie domyślnych filtrów
Korzystając z pliku konfiguracyjnego możemy określić filtry, które mają być automatycznie stosowane dla wszystkich pól formularza. Dzięki temu w prostu sposób możemy np. filtrować znaczniki HTML.
$config[ 'pre_filters' ][ 'all' ] = array( 'trim', 'strip_tags', 'htmlspecialchars' );
Problem pojawia się w chwili, gdy w jednym z pól chcemy mieć możliwość umieszczenia kodu HTML. Domyślnie nie istnieje sposób na usunięcie dodanego filtru, aby obejść to ograniczenie do naszej klasy rozszerzającej Formo należy dodać poniższy kod.
protected $_remove_filter = array( ); public function remove_pre_filter( $element, $filter ) { $this -> _remove_filter[ $element ][ $filter ] = true; return $this; }
Niestety nie możemy w prostu sposób usunąć dodanego filtru, ponieważ filtry dotyczące wszystkich elementów trzymane są w jednej tablicy, z której usunięcie filtru skutkuje usunięciem go ze wszystkich pól formularza. Konieczna staje się edycja metody _all_pre_filters, która niestety zadeklarowana jest jako prywatna przez co nie mamy możliwości jej nadpisania. Konieczna staje się bezpośrednia edycja pliku Formo.php. W okolicach 394 linii znajduje się kod odpowiedzialny za przypisanie filtrów do konkretnych elementów.
$this->$element->pre_filter($filter);
Umieszczając przed nim poniższy kod sprawimy, że określone przez nas przy użyciu metody remove_pre_filter filtry nie zostaną uwzględnione.
if( $this -> _remove_filter[ $element ][ $filter ] ) continue;
Uwzględnienie filtrów przez plugin ORM
Korzystając z pluginu ORM zyskujemy możliwość generowania formularzy na podstawie schematu tabel z bazy danych, tracimy natomiast obsługę zdefiniowanych globalnie filtrów. Tak więc filtry zdefiniowane w sposób przedstawiony w poprzednim punkcie nie zostaną wykonane. Dzieje się tak z powodu pobierania danych bezpośrednio z tablicy superglobalnej ($_POST / $_GET) zamiast z tablicy generowanej przez sam moduł. Aby naprawić tę niedogodność musimy wyedytować plik orm.php znajdujący się w katalogu formo_plugins_core, a dokładnie metodę post_addpost.
Musimy zacząć od usunięcia jej z kolejki uruchamianych po wywołaniu zdarzenia post_addpost, które ma miejsce podczas filtracji danych. W tym celu na samym początku metody należy dodać:
Event :: clear( 'formo.post_addpost', array( $this, 'post_addpost' ) );
Pominięcie powyższego kodu będzie skutkowało zapętleniem się skryptu. Następnie musimy zmienić miejsce, z którego pobierane są dane osiągniemy to zmieniając
$post = Input::instance();
na
$post = $this -> form -> get_values( );
Dodatkowo musimy zmienić sposób odwoływania się do poszczególnych pól nowej tablicy zmieniając
$this->model[$model]->$model_field = $post->$type($form_field);
na
$this -> model[ $model ] -> $model_field = $post[ $form_field ];
Ostatnim krokiem jest ponownie dodanie metody do kolejki obsługującej zdarzenie post_addpost
Event :: add( 'formo.post_addpost', array( $this, 'post_addpost' ) );
Wczytywanie domyślnych ustawień dla grup
Chcąc zdefiniować w pliku konfiguracyjnym domyślne ustawienia dla elementu grupy natkniemy się na małą niespodziankę. Okazuje się, że nie możemy dodać domyślnych ustawień dla grupy na podstawie nazwy typu tzn. korzystając z następującego kodu
$config[ 'defaults' ][ 'group' ]
musimy odwoływać się do elementu na podstawie jego identyfikatora (właściwości id). W przypadku korzystania z grup w wielu miejscach takie rozwiązanie staje się problematyczne, aby temu zaradzić należy w pliku Formo.php w metodzie add_group ( okolice linii 845 ) zamienić
$defaults = $this->_make_defaults($add_name, $add_name);
na
$defaults = $this -> _make_defaults( 'group', $add_name );
dzięki czemu wczytywaniem domyślnych ustawień będzie odbywało się na podstawie typu, a nie identyfikatora.
Zobacz także:
- Dokumentacja modułu Formo
- Strona projektu
- Zmodyfikowane przeze mnie pliki
Tagi: formation, Formo, formularze, Kohana
Kategoria: Kohana, Kohana, Moduły
W funkcji get_rule_name() należy zainicjować zmienną $args = ”; w przeciwnym razie wyskakuje błąd.
U mnie błąd pojawił się gdy dodałam regułę zdefiniowaną w odpowiedniej funkcji (tzw custom rule) w następujący sposób:
add_rule(‚login’,’custom_rules::user_exist’)
W takim układzie warunek nr 2 nie jest nigdy spełniony i dochodzi do wstawienia do tablicy zmiennej, która nie jest zainicjowana.
Jeśli zaś chodzi o wyrażenia regularne w tym kodzie:
if (preg_match(‚/^([^[]++)[(.+)]$/’, $rule_name, $matches))
{
$rule_name = $matches[1];
$args = preg_split(‚/(?<!\\),s*/', $matches[2]);
$args = str_replace(',', ',', $args);
}
to są totalnie skopane.
Jeśli zaś chodzi o wyrażenia regularne w funkcji get_rule_name() to są totalnie skopane i najnormalniej w świecie nie działają.
SOA #1 u mnie działa :) co więcej ten kod pochodzi z Kohany tak więc zapewne działa większej ilości osób :-)
Działa? Z tak skonstruowanym warunkiem nr 2:
if (preg_match(‚/^([^[]++)[(.+)]$/’, $rule_name, $matches))
a no :)
==
a co będzie jak zamiast lorem[foo,bar] będzie lorem::foo ??
też zadziała :)
=
foo :)
A to ciekawostka. U mnie nie działało :) Masz jakiś pomysł dlaczego? Zmodyfikowałam wyrażenie regularne na takie: ‚/^(\w+?)\[(.+?)\]$/’ i śmiga
PS: Ale nie wywala się z powodu niezainicjowania zmiennej $args, bo u mnie się właśnie na tym wywalał. No i czemu ma służyć linijka:
$args = str_replace(‚,’, ‚,’, $args);
czyli zamiana przecinków na przecinki.
mam wyłączone pokazywanie notice’ów tak więc u mnie „nie ma problemu”
co do zamiany to ten kod jest skopiowany z kodu Kohany tak więc nie do mnie pytanie :) z tego co tam widze to tam jest nie tyle zamieniany przecinek co „\,”
W listingu w powyższym artykule jest dokładnie przecinek na przecinek czyli tak:
$args = str_replace(‚,’, ‚,’, $args);
fakt :) widocznie WP coś zmiesza :) w załączonym pliku na szczeście wsio jest ok :)
Zajrzałam. W listingu, w wyrażeniu regularnym też są bzdury (tak jak mi się wydawało) w pliku jest dobrze. Może czas popracować nad tymi listingami, żeby nie było takich kwiatów.
PS. Nie jestem wirtuozem wyrażeń regularnych. Co oznacza podwójny plus w wyrażeniu:
([^\[]++)
Czy wie ktoś może dlaczego nie działa metoda wyświetlania elementów w widoku jako tablicy pięknie opisana w dokumentacji: $array = $form->get(TRUE) u mnie zawsze zamienia na string :/
stringa o jakiej treści?
Stringa o wartości całego formularza.
Znalazłem odpowiedź – zapis ” get(TRUE) ” przestał być już wspierany (nie dokonano aktualizacji manuala), teraz w każdej chwili można odwołać się do pól formularza obiektowo np. $form->username->value.