Witajcie. Programuję ostatnio we frameworku symfony i ze względu na sporą przerwę trafiam na różne problemy związane ze białymi plamami wiedzy na temat funkcjonowania pewnych małych trybików, bez których jednak ciężko byłoby ogarnąć działanie całego projektu. Niedawno niemałą zagwozdkę dał mi komponent sfWidgetFormI18nDate, który jest odpowiedzialny za wyświetlanie elementu formularza pozwalającego na wprowadzenie daty, a dokładnie sama część „roku”. Problem niby prosty, ale nadal problem, dlatego zapraszam do lektury. ;]
symfony: klasa sfWidgetFormI18nDate i problem z podawaniem zakresu lat.
Moja konfiguracja tego pola wyglądała następująco:
'birthdate' => new sfWidgetFormI18nDate(array(
'culture' => 'pl',
'format' => '%day%/%month%/%year%',
'years' => range(date('Y'), 1970),
)),
Problem polegał na tym, że przy danej konfiguracji walidatora:
'birthdate' => new sfValidatorDate(
array(
'required' => true,
'max' => strtotime('19 years ago'),
),
array(
'required' => 'To pole jest wymagane.',
'max' => 'Musisz mieć co najmniej 19 lat.',
'invalid' => 'Niepoprawne dane.',
)
),
podczas sprawdzania formularza nie działo się absolutnie… nic. Jakkolwiek nie wybierałbym daty, czy też innych pól formularza, nigdy nie pokazywał się ani komunikat o błędzie, a formularz przechodził walidację bez żadnego problemu.
Przez kilkanaście minut ogarnęła mnie głęboka konsternacja, bo to w końcu nie pierwsza wersja stabilna symfony, a więc błąd frameworka można z całą pewnością wykluczyć. Ten komponent z powodzeniem wykorzystywały tysiące innych osób, więc dlaczego miałoby to nie działać w moim przypadku?
Z pomocą przyszła chłodna analiza przypadku. Jak to mówią: „urządzenia elektryczne najlepiej działają, jak się je podłączy do prądu” – podążając za sensem tego powiedzenia sprawdziłem kod HTML generowany przez formularz. Okazało się, że problem został zlokalizowany, ponieważ wynik przedstawiał się następująco:
(...) <select name="register[birthdate][year]" id="register_birthdate_year"> <option value="" selected="selected"></option> <option value="0">2011</option> <option value="1">2010</option> <option value="2">2009</option> <option value="3">2008</option> <option value="4">2007</option> (...) <option value="37">1974</option> <option value="38">1973</option> <option value="39">1972</option> <option value="40">1971</option> <option value="41">1970</option> </select> (...)
Okazało się, że problem polega na tym, że generowane są poprawne napisy po stronie użytkownika, ale identyfikatory danych przekazywane na serwer nie są takie cacy. Zamiast wartości z zakresu 1970-2011 przesyłany jest tylko kolejny numer elementu: 0-41.
Długo się nie namyślając zacząłem szukać rozwiązania. Z pomocą przyszła funkcja array_combine(), która potrafi utworzyć tablicę asocjacyjną na podstawie dwóch tablic zawierających odpowiednio klucze i wartości tablicy wynikowej. Kod, którego potrzebowałem prezentuje się następująco:
array_combine(range(date('Y'), 1970), range(date('Y'), 1970))
Wygląd całej deklaracji pola formularza:
'birthdate' => new sfWidgetFormI18nDate(array(
'culture' => 'pl',
'format' => '%day%/%month%/%year%',
'years' => array_combine(range(date('Y'), 1970), range(date('Y'), 1970)),
)),
Oczywiście można by nieco ulepszyć działanie tego kodu przez co najmniej zapisanie wyniku funkcji range() do zmiennej i potem przekazanie dwóch jej odczytów do array_combine(), ale jako szybki sposób na załatwienie problemu jest jak znalazł.
Morał z tej historii? Nie próbuj nigdy robić niczego na pamięć. Dokumentacja nie gryzie. ;]
Warto przeczytać.
Trwa ładowanie…
Może trochę nie na temat, ale po częśći związane z InputDate, no i korzystam z poznania nowego sf experta za pomocą symfonylab.pl :)
Czy znasz może jakiś sposób na GLOBALNĄ podmiankę widgetów dat w filtrach admina (u mnie konkretnie chodzi o propelowego admina)?
Na tę chwilę propel generuje dwa widgety InputDate złożone z trzech selectów, a opatrzonych labelami „From:” i „To:”. Powiedzmy, że chciałbym podmienić je na np. własne jQueryDateWidget’y. Można to jakoś zrobić automatem?
Podobną podmiankę chciałbym też zrobić z polami booleanowskimi (select z „Yes”, „No”, „Yes or no” zamieniłbym sobie na dwa checkboxy), ale to już pewnie opanuję jak się dowiem jak zrobić to z datami :)
Pozdrawiam
Rozumiem, że masz na myśli formularze generowane poprzez konsolową akcję:
Wtedy dostajesz wygenerowane pliki klas formularzy w katalogach /lib/form, w których konfiguracji możesz podmienić sobie określone klasy pól na własne.
Ew. do dyspozycji masz jeszcze możliwość wynikającą z samej budowy sfWidget’a – utwórz sobie partial, albo nawet w samym widoku napisz kod, ktory będzie kolejno generował elementy widgeta. Informacje na ten temat znajdują się tutaj: http://www.symfony-project.org/gentle-introduction/1_4/en/10-Forms .
Gdyby żadna z tych metod nie pomogła – napisz tutaj, co jest źle i będziemy się bawić dalej. ;]
// edit
Nie wziąłem pod uwagę, że Tobie chodzi o globalną podmianę kodu HTML widgetu. Nie znam globalnej metody podmiany jednego widgetu na drugi bez zmiany jego nazwy. Dlatego będziesz chyba musiał przejechać po wszystkich plikach formularzy i po prostu zmienić nazwę klasy widgetu.
Przy okazji – dziękuję za miłe słowa. ;]
Linkdump 38- A niech Cię font!
Tak, chodziło mi o to drugie :)
Niestety, „jechanie” po klasach formularzy mi się nie uśmiecha, bo projekt jest dość spory. No i zawsze mogą dojść nowe pola, które znów będzie trzeba nadpisywać…
Spróbuję pogrzebać w kodzie tasku propel:build (–all-classes), bo to chyba tam się całość zaczyna, może znajdę jakieś generatory formów, które da się przesłonić.
Pozdrawiam
Cieszę się, że jednak udało mi się pomóc. Jak już coś uda Ci się osiągnąć, napisz – może nawet jakiś wpis udałoby się skrobnąć? ;]
Linkdump 38- A niech Cię font!
Hej,
Udało mi się osiągnąć chyba ten efekt (dla Propela).
Wystarczyło utworzyć sobie klasę i zapisać ją do np. lib/generator naszego projektu. Tutaj jest jej źródło: http://pastebin.com/XsUUQGrs
W klasie tej przesłaniam tylko jedną metodę getWidgetOptionsForColumn klasy sfPropelFormFilterGenerator i zastępuję w niej wywołanie konstruktorów sfWidgetFormDate moimi myWidgetFormRichDate (opartych na jquery’owych datepickerach).
Od teraz wykonując
symfony propel:build-filters --generator-class=myFilterGeneratormamy już ładne filtry :)Jak widać, przy okazji udało mi się „załatwić” problem z nieprzetłumaczalnymi dotychczas „yes or no”, „yes” i „no” przy polach typu booleanowskiego.
Niestety, nie dokopałem się jeszcze do tego gdzie mogę moim polom myWidgetFormRichDate ustawić labele np. „data od” zamiast „from” i „doda do” zamiast „to”…
Pozdrawiam!
Szybkie losowanie rekordów w SQL – jeszcze jeden sposób
OK, znalazłem też rozwiązanie na drugi problem. Wystarczy do sfWidgetFormFilterDate dodać opcję ‘filter_template’, w której możemy sobie skomponować wygląd etykiet :)
Szybkie losowanie rekordów w SQL – jeszcze jeden sposób
http://pastebin.com/hbgWfuL9 – ostateczna(?) wersja mojej klasy :)
Sorry za flood, jeśli możesz to połącz proszę moje komentarze :)
Szybkie losowanie rekordów w SQL – jeszcze jeden sposób
Nie ma sprawy – fajnie, że udało Ci się znaleźć dobre rozwiązanie i podzielić się z nami. Ja jestem dosyć zdecydowanym wyznawcą Doctrine, aczkolwiek jeśli kiedyś ktoś będzie szukał odpowiedzi na taki problem, to na pewno wskażę mu Twoje komentarze. ;]
PHP- Sprawdzanie istnienia adresu email
Pingback: symfony: sfValidatorChoice i ciągły błąd „Invalid.”. « Tomasz Kowalczyk