<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xlab &#187; symfony</title>
	<atom:link href="http://xlab.pl/tag/symfony/feed/" rel="self" type="application/rss+xml" />
	<link>http://xlab.pl</link>
	<description>XSolve laboratory - dzielimy się tym co wiemy...</description>
	<lastBuildDate>Mon, 09 Jan 2012 15:29:06 +0000</lastBuildDate>
	<language>pl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Kiedy i dlaczego tworzyć oprogramowanie pod klucz i dlaczego framework Symfony2 pasuje tu jak ulał?</title>
		<link>http://xlab.pl/kiedy-i-dlaczego-tworzyc-oprogramowanie-pod-klucz-i-dlaczego-framework-symfony2-pasuje-tu-jak-ulal/</link>
		<comments>http://xlab.pl/kiedy-i-dlaczego-tworzyc-oprogramowanie-pod-klucz-i-dlaczego-framework-symfony2-pasuje-tu-jak-ulal/#comments</comments>
		<pubDate>Tue, 20 Sep 2011 07:38:03 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[oprogramowanie dedykowane]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[symfony2]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1843</guid>
		<description><![CDATA[Gotowe rozwiązania Open Source Na rynku istnieje mnóstwo rozwiązań Open Source, rozwiązujące różne klasy zagadnień. Mowa tu o produktach e-commerce (Magento, Presta Shop), CMS (Drupal, Joomla, WordPress), systemach klasy CRM, ERP, czy DMS, a także „community builders”, czyli np. fora internetowe (PHP BB). W zdecydowanej większości powyższych przypadków nie potrzeba wiedzy programistycznej aby wdrożyć i [...]]]></description>
			<content:encoded><![CDATA[<h2>Gotowe rozwiązania Open Source</h2>
<p>Na rynku istnieje mnóstwo rozwiązań Open Source, rozwiązujące różne klasy zagadnień. Mowa tu o produktach e-commerce (Magento, Presta Shop), CMS (Drupal, Joomla, WordPress), systemach klasy CRM, ERP, czy DMS, a także „community builders”, czyli np. fora internetowe (PHP BB). W zdecydowanej większości powyższych przypadków nie potrzeba wiedzy programistycznej aby wdrożyć i rozwijać serwisy. Mają one obszerne zbiory rozszerzeń i wtyczek (plugins), które pozwalają dostosowywać systemy do potrzeb. Takie podejście pozwala na znaczne zmniejszenie kosztów wdrożenia aplikacji.</p>
<p>Pomimo tych zalet, często jest tak, że systemy te pasują do prostych modeli biznesowych, które w życiu codziennym występują nad wyraz rzadko. W starciu z rzeczywistością, wdrażanie takich systemów, a przede wszystkim utrzymanie i reakcje na rozwój rynku mają przełożenie na zasadę Pareto, która mówi o tym, że 80% funkcjonalności jesteśmy w stanie osiągnąć w 20% czasu, a 20% krytycznych funkcji systemu konsumuje 80% budżetu.</p>
<p>Głównymi problemami z jakimi spotykają się wdrożenia oparte o gotowe rozwiązania to przede wszystkim komplikacja logiki biznesowej systemu, której nie da się odwzorować na możliwości produktu. Często dochodzi do paradoksalnych sytuacji, w których model biznesowy trzeba dopasowywać do systemu, co nigdy nie powinno mieć miejsca. Inną kwestia jest rozwój aplikacji i zwiększenie ruchu, które powoduje, że dobrze działające oprogramowanie dla małej liczby użytkowników staje się bezużyteczne przy wielkim obciążeniu. Poza tym istnieje potencjalne ryzyko związane z atakami hakerskimi, gdyż kod jest otwarty i dostępny dla potencjalnych włamywaczy. Aby dostrzec skalę problemu, wystarczy przejrzeć serwisy z dostępnymi exploitami. Ostatnią kwestią, która przychodzi w tym momencie na myśl, jest to, że czasem społeczność rozwijająca dany produkt, w pewnym etapie może zaprzestać dalszych prac. Powodów może być kilka, raz jest to skłócenie zespołu, innym razem przejście na nową technologię, lub do innego projektu, pozostawiając dotychczasowych użytkowników samych sobie.</p>
<h2>Zalety tworzenia oprogramowania dedykowanego</h2>
<p>Wszystkie problemy opisane powyżej rozwiązuje oprogramowanie dedykowane, a nawet można się pokusić o stwierdzenie, że wnosi tutaj wiele wartości dodanych. Przede wszystkim klient dostaje dokładnie to czego potrzebuje i za co płaci. Oczywiście trzeba mieć na względzie, że nie każdy klient jest osobą techniczną i nie koniecznie musi mieć doświadczenie z systemami informatycznymi, więc doradztwo technologiczne jest tutaj nieocenioną wartością. Firma programistyczna na etapie przygotowania oferty doradza klientowi najlepsze rozwiązania, proponuje pewne opcje, tworząc przy tym najlepszą możliwą ofertę, budując relację i zaufanie z zamawiającym. Dzięki temu klient odzwierciedla swój model biznesowy w tworzonym systemie, nastawiając się na zysk z inwestycji w przyszłości. Poza tym oprogramowanie jest gotowe na rozwój, co jest bardzo ważnym czynnikiem, gwarantującym skuteczność przedsięwzięcia i reakcję na sygnały spływające od użytkowników lub na trendy rynkowe. Ważnym czynnikiem  jest tutaj również samopoczucie programistów, którzy nie klikają  w panelach administracyjnych Drupala czy Magento, ale programują i rozwiązują problemy, realizując się przy tym zawodowo.</p>
<h2>Nie wynajdujemy koła od nowa, czyli o frameworkach słów kilka</h2>
<p>Framework to z definicji zestaw uniwersalnych narzędzi na bazie których tworzy się aplikacje. Nie wyobrażalne jest, aby w tych czasach tworzyć systemy informatyczne bez wykorzystania takiego narzędzia. Aby lepiej zobrazować temat, możemy porównać do płyty podwoziowej samochodu. Daje nam ona wszelkie potrzebne mechanizmy, czyli ramę całego auta, łożyska, układ wydechowy, wiązki elektryczne. Na takiej ramie możemy stworzyć wiele rodzajów samochodów, np. pickup, miejski hatchback, średniej klasy sedan, lub ekskluzywny, drogi wóz. Podobnie jest z frameworkiem – zapewnia mechanizmy potrzebne w większości systemów. Mowa przede wszystkim o kontrolerze aplikacji, reagującym na żądania użytkownika, warstwie widoku, prezentującą dane w formie graficzne, warstwie dostępu do bazy danych, mechanizmie bezpieczeństwa, pozwalającą na autoryzowanie użytkowników i ograniczanie dostępu do poszczególnych części serwisu. To tylko niektóre z rozwiązań, nowoczesne frameworki dostarczają wiele więcej. Inną kwestią jest standaryzacja kodu tworzonego przy ich użyciu. Jest o wiele łatwiej nowej osobie wejść w projekt, jeśli framework narzuca standardy kodowania i rozwiązania umieszczane są w odpowiedniej strukturze kodu. Pozwala to na szybsze wdrażanie się nowych członków zespołu, a co za tym idzie na przyspieszenie całego rozwoju.</p>
<p>Jest jeszcze jedna ważna kwestia, która decyduje o sukcesie danego frameworka – liczba gotowych rozwiązań w postaci wtyczek lub komponentów. Jest to sytuacja analogiczna do gotowych rozwiązań Open Source, z tym, że tutaj łatwo rozszerzyć dany komponent oraz dostosować go do wymagań systemu. Mamy też zapewnione wsparcie społeczności w kwestii rozwiązywania problemów, tworzenia nowych funkcjonalności czy aktualizacji i wydawania poprawek.</p>
<h2>Dlaczego Symfony2 pasuje tutaj jak ulał?</h2>
<p>Symfony2 kontynuuje dobrą passę swojego poprzednika, który istnieje już 6 lat. Programiści wyciągnęli naukę z poprzedniej wersji, podpatrzyli rozwiązania z innych platform (Java, Ruby On Rails, Python i Django, czy Zend Framework) i przepisali całość od podstaw. Dzięki temu powstał najlepszy aktualnie framework PHP, który wygrywa przede wszystkim bardzo dobrą strukturą niezależnych modułów, które można rozwijać oddzielnie, a nawet wykorzystywać w innych projektach. Autorzy przewidzieli strukturę projektu tworząc tzw. Bundle, które są modułami, gotowymi do użycia w innych projektach (nawet wnętrze frameworka się z nich składa). W tym momencie warto wspomnieć o Bundlach dostarczanych przez programistów i firmy zewnętrzne (jest ich już prawie 600). Całość systemu jest oparta o nowoczesny kontener wstrzykiwania zależności (Dependency Injection Container), przez co podmiana kluczowych części systemu może być zrobiona w mgnieniu oka (to rozwiązanie bardzo szeroko stosuje się w Java EE oraz innych dojrzałych rozwiązaniach). Warto też zauważyć, że autorzy duży nacisk kładą na wydajność i szybkość działania, co jest zauważalne na pierwszy rzut oka, bez wnikliwych badań obciążeniowych.</p>
<p>O powadze sytuacji świadczy też fakt, że Symfony2 zostało w całości przeniesione do gita i jego kod jest utrzymywany w serwisie Github.com.  Dzięki temu każdy może go sklonować kod źródłowy, pracować na nim lokalnie wprowadzając poprawki czy tworząc nowe funkcjonalności, a później jednym kliknięciem wysłać prośbę o włączenie swoich zmian do głównego repozytorium. To powoduje, że na dzień dzisiejszy Symfony2 jest najczęściej rozgałęzianym  i najchętniej oglądanym repozytorium PHP na Githubie, a liczba osób, które miały swój wkład w rozwój przekracza 250. Poza tym za frameworkiem stoi francuska firma Sensio Labs, wraz z założycielem i głównym programistą Fabienem Potencierem. Ten fakt zapewnia stabilność rozwoju, wysoką jakość dokumentacji oraz dostępność szkoleń i materiałów. Firma ta zorganizowała nawet zbiórkę pieniędzy wśród społeczności i zleciła audyt bezpieczeństwa organizacji zajmującej się tego typu działaniami .</p>
<p>Reasumując, trzeba przyznać, że Symfony2 zrewolucjonizowało świat frameworków PHP, a można się nawet pokusić o stwierdzenie, że programiści innych języków (Python, Ruby, Groovy) też z szacunkiem patrzą na to rozwiązanie. Symfony2 idealnie nadaje się do rozwoju aplikacji dedykowanych i na pewno można go polecić do większości projektów.</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/kiedy-i-dlaczego-tworzyc-oprogramowanie-pod-klucz-i-dlaczego-framework-symfony2-pasuje-tu-jak-ulal/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Symfony2 Showcase</title>
		<link>http://xlab.pl/symfony2-showcase/</link>
		<comments>http://xlab.pl/symfony2-showcase/#comments</comments>
		<pubDate>Mon, 31 Jan 2011 11:12:53 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[symfony2]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1740</guid>
		<description><![CDATA[Podczas pierwszego Piątku z XSolve w tym roku oraz pierwszego w nowej siedzibie, poprowadziłem prezentację na temat Symfony2, które wkrótce ujrzy światło dzienne. Symfony2 zostało przepisane od zera, porzucono kompatybilność wstecz, czego rezultatem jest nowoczesny framework, pozwalający szybko tworzyć bardzo złożone aplikacje. Nadąża on za najaktualniejszymi trendami w brażny, a autorzy chwalą się trzykrotną poprawą [...]]]></description>
			<content:encoded><![CDATA[<p>Podczas pierwszego Piątku z XSolve w tym roku oraz pierwszego w nowej siedzibie, poprowadziłem prezentację na temat Symfony2, które wkrótce ujrzy światło dzienne. Symfony2 zostało przepisane od zera, porzucono kompatybilność wstecz, czego rezultatem jest nowoczesny framework, pozwalający szybko tworzyć bardzo złożone aplikacje. Nadąża on za najaktualniejszymi trendami w brażny, a autorzy chwalą się trzykrotną poprawą wydajności i zmniejszeniem użycia pamięci o połowę.<br />
Aktualnie można przetestować Symfony2 pobierając wersję Preview Release 5 githuba. Ja już to zrobiłem, czego rezultatem jest poniższa prezentacja :-)</p>
<div style="width:645px" id="__ss_6761036"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/sznapka/symfony2-showcase" title="Symfony2 showcase">Symfony2 showcase</a></strong><object id="__sse6761036" width="645" height="540"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=symfony2showcase-110131050259-phpapp01&#038;stripped_title=symfony2-showcase&#038;userName=sznapka" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse6761036" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=symfony2showcase-110131050259-phpapp01&#038;stripped_title=symfony2-showcase&#038;userName=sznapka" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="645" height="540"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/sznapka">sznapka</a>.</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/symfony2-showcase/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Doctrine cache</title>
		<link>http://xlab.pl/doctrine-cache/</link>
		<comments>http://xlab.pl/doctrine-cache/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 22:38:33 +0000</pubDate>
		<dc:creator>Paweł Sołtysek</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[Optymalizacja]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1662</guid>
		<description><![CDATA[Użycie mechanizmu keszującego (ang. cache) jest powszechnie uznawane za jedną z najefektywniejszych metod optymalizacji. Niestety mimo licznych zastosowań często pomija się wykorzystanie tego mechanizmu w celu przechowywania wyników zapytań do baz danych. Tymczasem dzięki wbudowanemu w Doctrine wsparciu dla keszowania możemy łatwo przyspieszyć obsługę zapytań SQL w naszych aplikacjach. Konfiguracja Podstawowa konfiguracja polega na ustawieniu odpowiedniej [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Użycie mechanizmu keszującego (ang. <em>cache</em>) jest powszechnie uznawane za jedną z najefektywniejszych metod optymalizacji. Niestety mimo licznych zastosowań często pomija się wykorzystanie tego mechanizmu w celu przechowywania wyników zapytań do baz danych. Tymczasem dzięki wbudowanemu w <a target="_blank" href="http://www.doctrine-project.org/documentation/manual/1_2/en/caching">Doctrine wsparciu dla keszowania</a> możemy łatwo przyspieszyć obsługę zapytań SQL w naszych aplikacjach.</p>
<h2>Konfiguracja</h2>
<p style="text-align: justify;">Podstawowa konfiguracja polega na ustawieniu odpowiedniej klasy sterownika oraz strategii keszowania. Doctrine dostarcza klasy sterowników dla większości powszechnie używanych mechanizmów keszujących (<a target="_blank" href="http://pecl.php.net/package/APC">APC</a>, <a target="_blank" href="http://xcache.lighttpd.net/">XCache</a>, <a target="_blank" href="http://php.net/manual/pl/book.memcache.php">memcache</a>) oraz dwie strategie keszowania (keszowanie wyników procesu parsowania zapytań DQL oraz keszowanie wyników zapytań SQL). W zależności od potrzeb konfigurację możemy przeprowadzić globalnie (do wyboru mamy konfigurację managera lub połączeń) lub lokalnie z poziomu konkretnego zapytania.</p>
<p style="text-align: justify;">Pracując z symfony ustawienia globalne najlepiej zdefiniować poprzez dodanie metody <em>configureDoctrine()</em> klasy ProjectConfiguration. W zależności od wybranej strategii rejestrujemy sterownik poprzez ustawienie odpowiedniego atrybutu (<strong>Doctrine_Core::ATTR_QUERY_CACHE</strong> lub <strong>Doctrine_Core::ATTR_RESULT_CACHE</strong>). Dodatkowo poprzez ustawienie atrybutu<strong> Doctrine_Core::ATTR_*_CACHE_LIFESPAN</strong> możemy ustawić czas ważności rekordów.</p>
<p><script src="https://gist.github.com/760563.js?file=globalConfiguration1.php"></script> </p>
<p style="text-align: justify;">Powyższa konfiguracja obejmuje wszystkie połączenia nawiązywane przez Doctrine. Czasem może zaistnieć potrzeba ograniczenia konfiguracji do konkretnego połączenia (na przykład jedno z dwóch połączeń jest nawiązywane do bazy danych modyfikowanej przez inną aplikacje). W takiej sytuacji możemy użyć metody <em>configureDoctrineConnectionName()</em> zastępując człon <em>Name</em> nazwą naszego połączenia.</p>
<p><script src="https://gist.github.com/761844.js?file=globalConfiguration2.php"></script> </p>
<p style="text-align: justify;">W razie potrzeby konfigurację możemy przeprowadzić z poziomu konkretnego zapytania. Służą do tego metody <em>useQueryCache()</em> oraz <em>useResultCache()</em> (w dalszej części artykułu poznamy szersze zastosowanie tych metod). Konfiguracja lokalna nadpisuje konfigurację globalną.</p>
<p>    <script src="https://gist.github.com/761014.js?file=sampleQuery.php"></script></p>
<h2>Keszowanie w praktyce</h2>
<p style="text-align: justify;">Mając poprawnie skonfigurowany sterownik jesteśmy gotowi, aby zastosować mechanizm keszujący do naszych zapytań. Kolejne kroki zależą od poziomu konfiguracji oraz wybranej strategii keszownia. Dla przykładu, jeśli wybraliśmy konfigurację globalną  oraz keszowanie wyników zapytań, należy powiadomić Doctrine, które zapytania mają być keszowane (keszowanie wyników procesu parsowania DQL jest automatycznie zastosowane do wszystkich zapytań). Służy do tego wczesniej poznana metoda <em>useResultCache()</em>. Metoda ta jako pierwszy parametr przyjmuje instancję sterownika lub wartość logiczną, który informuje czy dane zapytanie ma być objęte mechanizmem keszowania. Drugi parametr określa czas ważności rekordu dla naszego zapytania, natomiast trzeci jest identyfikatorem tego rekordu.</p>
<p><script src="https://gist.github.com/761131.js?file=sampleQuery2.php"></script> </p>
<p style="text-align: justify;">W ten sposób wynik zapytania po pierwszym wykonaniu zostanie umieszczony w mechanizmie keszującym pod nazwą <em>sample_query</em> na okres jednej godziny. W ciągu tej godziny każde kolejne wykonanie zapytania będzie oddelegowane do mechanizmu keszującego.</p>
<p style="text-align: justify;">Przekazując do zapytania dodatkowe parametry, musimy zadbać, aby każdemu ich zestawowi odpowiadał inny ciąg identyfikujacy wynik zapytania. Doctrine automatycznie wygeneruje odpowiedni identyfikator jeśli nie podamy go jawnie. Takie rozwiązanie jest niewygodne jeśli mamy w perspektywie zarządzanie zawartością mechanizmu keszującego (musimy znać idnentyfikator). Więcej o zarządzaniu wynikami operacji keszowania można przeczytać w poniższym paragrafie.</p>
<p style="text-align: justify;">Metoda <em>useQueryCache()</em> poza tym, że nie przyjmuje trzeciego parametru (identyfikatora) działa analogicznie do <em>useResultCache()</em>. Szczegółowe informacje można znaleźć w <a target="_blank" href="http://www.doctrine-project.org/api/orm/1.2/doctrine/doctrine_query_abstract.html">dokumentacji API</a>.</p>
<h2>Co dalej?</h2>
<p style="text-align: justify;">Keszowanie wyników zapytań niesie ze sobą pewne niebezpieczeństwo &#8211; dane mogą szybko stać się nieaktualne. Wraz ze sterownikami otrzymujemy <a target="_blank" href="http://www.doctrine-project.org/api/orm/1.2/doctrine/doctrine_cache_driver.html#summary_method">zestaw metod</a> umożliwiających zarządzanie zawartością  mechanizmu keszującego. W połączeniu z <a target="_blank" href="http://www.doctrine-project.org/projects/orm/1.2/docs/manual/event-listeners/en#record-hooks">mechanizmem zdarzeń</a> możemy automatycznie usuwać skeszowane wyniki, zapewniając sobie w ten sposób aktualność danych. </p>
<p> <script src="https://gist.github.com/761229.js?file=Foo.class.php"></script></p>
<p style="text-align: justify;">Wolne od takich problemów jest keszowanie wyników parsowania DQL, które zarządzane jest przez Doctrine automatycznie.<br />
<h2>Podsumowanie</h2>
<p style="text-align: justify;">Pomimo oczywistych zalet, stosowanie mechanizmu keszującego powinno być dobrze przemyślane. O&nbsp;ile keszowanie sparsowanych zapytań DQL jest bezpieczne (jeśli stosujemy <em>prepared statements</em>) i&nbsp;zaleca się jego użycie w większości projektów, to wykorzystanie skeszowanych wyników zapytań powinno dotyczyć tylko wybranych przypadków, w których ma sens i faktycznie przyniesie nam korzyści. Nie należy zapominać tutaj o zasadzie, że nie wszystko co zostało opisane jako dobra metoda na polepszenie wydajności aplikacji, będzie dawało taki rezultat we wszystkich sytuacjach.</p>
<p style="text-align: justify;">
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/doctrine-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Konfiguracja projektu w symfony pod Test Driven Development</title>
		<link>http://xlab.pl/konfiguracja-projektu-w-symfony-pod-test-driven-development/</link>
		<comments>http://xlab.pl/konfiguracja-projektu-w-symfony-pod-test-driven-development/#comments</comments>
		<pubDate>Fri, 22 Oct 2010 14:53:20 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[lime]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[test driven development]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1578</guid>
		<description><![CDATA[W projektach symfony, korzystając z wbudowanego mechanizmu do testów jednostkowych (lime), można łatwo programować w oparciu o nurt Test Driven Development (TDD). Aby przygotować środowisko do TDD wystarczą trzy kroki.]]></description>
			<content:encoded><![CDATA[<p>W projektach symfony, korzystając z wbudowanego mechanizmu do testów jednostkowych (<em>lime</em>), można łatwo programować w oparciu o nurt Test Driven Development (TDD).</p>
<p>Aby przygotować środowisko do TDD wystarczą trzy kroki:</p>
<ol>
<li>Konfigurujemy połączenie do testowej bazy, którą będzie sqlite przechowywane w pamięci. W pliku config/databases.yml dopisujemy:<script src="https://gist.github.com/668117.js?file=databases.yml"></script></li>
<li>Tworzymy dane testowe, tzw. fixtures, które umieszczamy w folderze test/fixtures (należy go stworzyć ręcznie). Celowo nie umieszczamy danych w data/fixtures, gdyż tam powinny znaleźć się testowe dane, które mogą być użyte np. podczas prezentacji systemu. Jeśli chcemy zachować kolejność danych, możemy ponumerować pliki yml, np.   <script src="https://gist.github.com/668123.js?file=fixtures.lisst"></script></li>
<li>Tworzymy plik bootstrap dla testów opartych o bazę danych (test/bootstrap/model.php). Plik ten stworzy strukturę tabel na podstawie modelu i załaduje je do bazy oraz wprowadzi dane testowe (fixtures). &#8222;Dziedziczy&#8221; on pewne ustawienia z bootstrapu unit.php. Trzeba z niego korzystać zawsze, gdy przychodzi potrzeba odwoływania się w testach do bazy danych.<script src="https://gist.github.com/668127.js?file=model.php"></script></li>
</ol>
<p>Następnie uruchamiamy .<em>/symfony test:unit</em> i podziwiamy, jak nasze testy przechodzą w stu procentach :-)<br />
Powyższa konfiguracja była testowana dla <strong>symfony 1.4</strong> z <strong>doctrine 1.2</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/konfiguracja-projektu-w-symfony-pod-test-driven-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Uporządkowane wysyłanie e-maili w projekcie symfony</title>
		<link>http://xlab.pl/uporzadkowane-wysylanie-e-maili-w-projekcie-symfony/</link>
		<comments>http://xlab.pl/uporzadkowane-wysylanie-e-maili-w-projekcie-symfony/#comments</comments>
		<pubDate>Mon, 27 Sep 2010 07:13:08 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[e-mail]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1558</guid>
		<description><![CDATA[Niemal każdy projekt, z jakim miałem do czynienia, zawierał fragmenty, w których były wysyłane e-maile. Najczęściej czynność ta jest wykonywana w kontrolerze aplikacji (klasa actions), a treścią wiadomości jest wyrenderowany partial. To rozwiązanie jest dobre do czasu, gdy chcemy wysłać tego samego e-maila z dwóch różnych aplikacji (np. z frontend i z backend), czy z [...]]]></description>
			<content:encoded><![CDATA[<p>Niemal każdy projekt, z jakim miałem do czynienia, zawierał fragmenty, w których były wysyłane e-maile. Najczęściej czynność ta jest wykonywana w kontrolerze aplikacji (klasa <em>actions</em>), a treścią wiadomości jest wyrenderowany <em>partial</em>. To rozwiązanie jest dobre do czasu, gdy chcemy wysłać tego samego e-maila z dwóch różnych aplikacji (np. z frontend i z backend), czy z taska. Warto wtedy przyjrzeć się wbudowanemu systemowi do wysyłania emaili jaki oferuje symfony 1.4 (korzystającym z Switf Mailer).</p>
<p><span id="more-1558"></span></p>
<p>Aby nasze maile były uniwersalne i dostępne w każdej aplikacji tworzymy dla każdej wiadomości  osobną klasę dziedziczącą z <em>Swift_Message</em> i umieszczamy ją w głównym folderze lib</p>
<pre class="php" name="code">class MyTextMessage extends Swift_Message
{
  public function __construct($name, $address)
  {
    parent::__construct();
    $this-&gt;setFrom('foo@bar.com');
    $this-&gt;setTo($address);
    $this-&gt;setSubject('My text message');
    $body  = sprintf("Hi %s!\nHow are you?", $name);
    $this-&gt;setBody($body);
  }
}</pre>
<p>Taką wiadomość możemy łatwo wysłać w ten sposób (z akcji lub z taska):</p>
<pre class="php" name="code">$this-&gt;getMailer()-&gt;send(new MyTextMessage('Barack', 'barack@obama.com'));</pre>
<p>Jak widać metoda ta jest prosta i przyjemna. Trochę trudniej jest w przypadku e-maili, których zawartość jest w formacie HTML. Musimy wtedy użyć klasy <em>sfPHPView</em>, udostępnianej przez symfony</p>
<pre class="php" name="code">$templatesDir = sfConfig::get('sf_root_dir') . '/data/mailTemplates/';
$view = new sfPHPView(sfContext::getInstance(), null, null, null);
$view-&gt;setTemplate($templatesDir . 'MyMessageTemplate.php');
$view-&gt;getAttributeHolder()-&gt;add(array('name' =&gt; 'Barack'));
$view-&gt;setDecorator(false);
$body = $view-&gt;render();</pre>
<p>Szablon e-maila z pliku /data/mailTemplates/MyMessageTemplate.php może wyglądać następująco</p>
<pre class="html" name="code">&lt;h1&gt;Hi &lt;?php echo $name ?&gt;&lt;/h1&gt;
&lt;p&gt;How are you?&lt;/p&gt;</pre>
<p>Klasa do wysyłania e-maili w formie HTML będzie wyglądała następująco</p>
<pre class="php" name="code">class MyHtmlMessage extends Swift_Message
{
  public function __construct($name, $address)
  {
    parent::__construct();
    $this-&gt;setFrom('foo@bar.com');
    $this-&gt;setTo($address);
    $this-&gt;setSubject('My text message');
    $body = $this-&gt;_buildBody('Barack');
    $this-&gt;setBody($body);
  }

  protected function _buildBody($name)
  {
    $templatesDir = sfConfig::get('sf_root_dir') . '/data/mailTemplates/';
    $view = new sfPHPView(sfContext::getInstance(), null, null, null);
    $view-&gt;setTemplate($templatesDir . 'MyMessageTemplate.php');
    $view-&gt;getAttributeHolder()-&gt;add(array('name' =&gt; $name));
    $view-&gt;setDecorator(false);
    return $view-&gt;render();
  }
}</pre>
<p>Tak stworzony mechanizm można w łatwy sposób podpiąć pod eventy oferowane przez symfony, czyniąc go jeszcze bardziej elastycznym. Oczywiście w takim podejściu możemy wykorzystać dobrodziejstwa programowania obiektowego, tworząc klasy bazowe e-maili czy rozszerzać istniejące e-maile przy pomocy dziedziczenia.</p>
<p>Opis tej metody można również znaleźć w podręczniku symfony (<a href="http://www.symfony-project.org/gentle-introduction/1_4/en/11-Emails#chapter_11_email_messages_as_classes">Email Messages as Classes</a>).</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/uporzadkowane-wysylanie-e-maili-w-projekcie-symfony/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Symfony sfWidgetFormSelect z nieaktywnymi opcjami</title>
		<link>http://xlab.pl/symfony-sfwidgetformselect-z-nieaktywnymi-opcjami/</link>
		<comments>http://xlab.pl/symfony-sfwidgetformselect-z-nieaktywnymi-opcjami/#comments</comments>
		<pubDate>Tue, 25 May 2010 08:06:33 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sfWidgetFormSelect]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1157</guid>
		<description><![CDATA[sfWidgetFormSelect nie daje możliwości wyświetlania nieaktywnych opcji. Jest to rzadko wykorzystywana właściwość elementu select, ale czasami może się na prawdę przydać :-) Pomimo to, możemy uzyskać tą funkcjonalność poprzez stworzenie własnego widgetu, który dziedziczy z sfWidgetFormSelect. Kod klasy znajduję się poniżej. W tym przypadku nieaktywne opcje, to te które mają ujemne value. Można oczywiście ustawiać [...]]]></description>
			<content:encoded><![CDATA[<p><strong>sfWidgetFormSelect</strong> nie daje możliwości wyświetlania nieaktywnych opcji. Jest to rzadko wykorzystywana właściwość elementu select, ale czasami może się na prawdę przydać :-) Pomimo to, możemy uzyskać tą funkcjonalność poprzez stworzenie własnego widgetu, który dziedziczy z  <strong>sfWidgetFormSelect</strong>. Kod klasy znajduję się poniżej. W tym przypadku nieaktywne opcje, to te które mają ujemne <strong>value</strong>. Można oczywiście ustawiać inne opcje na nieaktywne, wystaczy odpowiednio zmodyfikować metodę <strong>disableCondition</strong>.<span id="more-1157"></span></p>
<pre class="php" name="code">
  class myWidgetFormSelect extends sfWidgetFormSelect
  {
    public function renderContentTag($tag, $content = null, $attributes = array())
    {
      if ($tag == 'option' &amp;&amp; isset($attributes['value'])
        &amp;&amp; $this-&gt;disableCondition($attributes['value'])
      ) {
        $attributes['disabled'] = 'disabled';
      }
      return parent::renderContentTag($tag, $content, $attributes);
    }   

    protected function disableCondition($value)
    {
      return (int)$value &lt; 0;
    }
  }
</pre>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/symfony-sfwidgetformselect-z-nieaktywnymi-opcjami/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Wykrywanie przeglądarek na urządzenia mobilne</title>
		<link>http://xlab.pl/wykrywanie-przegladarek-na-urzadzenia-mobilne/</link>
		<comments>http://xlab.pl/wykrywanie-przegladarek-na-urzadzenia-mobilne/#comments</comments>
		<pubDate>Tue, 18 May 2010 07:09:21 +0000</pubDate>
		<dc:creator>Wojtek Sznapka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[przeglądarki mobilne]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://xlab.pl/?p=1096</guid>
		<description><![CDATA[Często bywa  tak, że chcemy utworzyć mobilną wersję naszego serwisu. Cechuje się ona przeważnie skromniejszą szatą graficzną oraz umiarem w wykorzystaniu JavaScriptu. Aby wykryć przeglądarkę mobilną (używaną w telefonach komórkowych, smartphone lub palmtopach) wystarczy użyć poniższej klasy. W zaprezentowanym rozwiązaniu detekcja przeglądarki oparta jest o nagłówek HTTP_USER_AGENT lub o dodatkowe nagłówki wysyłane przez niektóre przeglądarki. [...]]]></description>
			<content:encoded><![CDATA[<p>Często bywa  tak, że chcemy utworzyć mobilną wersję naszego serwisu. Cechuje się ona przeważnie skromniejszą szatą graficzną oraz umiarem w wykorzystaniu JavaScriptu. Aby wykryć przeglądarkę mobilną (używaną w telefonach komórkowych, smartphone lub palmtopach) wystarczy użyć poniższej klasy. W zaprezentowanym rozwiązaniu detekcja przeglądarki oparta jest o nagłówek HTTP_USER_AGENT lub o dodatkowe nagłówki wysyłane przez niektóre przeglądarki.<span id="more-1096"></span></p>
<pre class="php" name="code">class MobileBrowserDetector
{
  private static $mobileAgentPrefixes = array(
    'w3c ','acs-','alav','alca','amoi','audi','avan','benq','bird','blac',
    'blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno',
    'ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-',
    'maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-',
    'newt','noki','oper','palm','pana','pant','phil','play','port','prox',
    'qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar',
    'sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-',
    'tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp',
    'wapr','webc','winw','winw','xda','xda-'
  );

  private static $mobileAgentParts = array(
    'up.browser', 'up.link', 'mmp', 'symbian', 'mobile safari',
    'opera mobi', 'android', 'smartphone', 'midp', 'wap', 'phone'
  );

  public static function isMobile($userAgent = '')
  {
    if (!strlen($userAgent)) {
      $userAgent = $_SERVER['HTTP_USER_AGENT'];
    }
    $isMobile = false;
    $isMobile |= in_array(strtolower(substr($userAgent, 0, 4)), self::$mobileAgentPrefixes);
    $isMobile |= preg_match(sprintf('#(%s)#i', implode('|', self::$mobileAgentParts)), $userAgent);
    $isMobile |= strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml') !== false;
    $isMobile |= isset($_SERVER['HTTP_PROFILE']);
    $isMobile |= isset($_SERVER['HTTP_X_WAP_PROFILE']);
    $isMobile |= isset($_SERVER['ALL_HTTP']) &amp;&amp; strpos(strtolower($_SERVER['ALL_HTTP']),'OperaMini') !== false;
    return (bool)$isMobile;
  }
}</pre>
<p>Możemy łatwo użyć tej klasy na przykład w projekcie symfony, podmieniając główny layout na layout mobilny (apps/APLIKACJA/templates/layout_mobile.php) w filtrze, w poniższy sposób.<br />
Zawartość apps/APLIKACJA/config/filters.yml</p>
<pre class="php" name="code">rendering: ~
security:
  class: sfGuardBasicSecurityFilter
mobile:
  class: MobileFilter
cache:     ~
common:    ~
execution: ~</pre>
<p>Klasa filtra lib/MobileFilter.class.php:</p>
<pre class="php" name="code">class MobileFilter extends sfFilter
{
  public function execute($filterChain)
  {
    if (MobileBrowserDetector::isMobile()) {
      $this-&gt;getContext()-&gt;getActionStack()-&gt;getFirstEntry()-&gt;getActionInstance()-&gt;setLayout('layout_mobile');
    }
    $filterChain-&gt;execute();
  }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/wykrywanie-przegladarek-na-urzadzenia-mobilne/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Piekło die();</title>
		<link>http://xlab.pl/pieklo-die/</link>
		<comments>http://xlab.pl/pieklo-die/#comments</comments>
		<pubDate>Sat, 28 Nov 2009 23:40:50 +0000</pubDate>
		<dc:creator>Krzysztof Wawoczny</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[debugowanie]]></category>
		<category><![CDATA[FirePHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=676</guid>
		<description><![CDATA[Natchnęło mnie dziś do pomyślenia jak uchronić się przed niebezpieczeństwami związanymi z stosowaniem die() w kodzie&#8230;tak wiem że była o tym parę razy mowa, że to zło w najczystszej postaci,  że najlepiej korzystać z xdebug, a najlepiej nigdy nie korzystać z die() w kodzie. Wszystko ma jednak swoje wady i zalety – o ile w [...]]]></description>
			<content:encoded><![CDATA[<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Natchnęło mnie dziś do pomyślenia jak uchronić się przed niebezpieczeństwami związanymi z stosowaniem die()  w kodzie&#8230;tak wiem że była o tym parę razy mowa, że to zło w najczystszej postaci,  że najlepiej korzystać z xdebug, a najlepiej nigdy nie korzystać z die() w kodzie.</p>
<p style="margin-bottom: 0cm">Wszystko ma jednak swoje wady i zalety – o ile w pracy xdebuga mogę sobie skonfigurować pod netbeansa to w domu nie korzystam z tego IDE (połączenie przez OpenVPN i dość częste skanowanie projektu jest nader uciążliwe). Dlatego zaczerpnąłem nieco mocy googla i po kilku testach przedstawiam: <strong> FirePHP</strong>.</p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm"><strong>FirePHP</strong> to całkiem zgrabny logger dla PHP wykorzystujący <strong>Firebuga</strong> – ku mojemu zdziwieniu jest to całkiem niezły plugin, do jego działania wystarczy mieć zainstalowaną wtyczkę Firebug z włączoną zakładką sieć i konsola. Instalując FirePHP trzeba jeszcze pamiętać o zaciągnięciu biblioteki FirePHPCore, którą rozpakowujemy do katalogu roota (w moim przypadku to katalog web w symfony).  FirePHP pokaże się jako niebieski robal (widoczny na poniższym zrzucie).</p>
<p style="margin-bottom: 0cm">Mając to wszystko jesteśmy niemal gotowi by bezpiecznie debugować skrypty. Do skryptu dołączamy:</p>
<pre class="php">require_once('FirePHPCore/FirePHP.class.php');</pre>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Teraz już poprzez wywołanie w postaci:</p>
<p style="margin-bottom: 0cm">
<pre class="php">$firephp = FirePHP::getInstance(true);
$firephp-&gt;log($var, 'nieobowiazkowy opis dla loga');</pre>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Zobaczymy w zakładce konsola Firebuga wartość zmiennej $var poprzedzoną opisem podanym jako drugi parametr funkcji log.<br />
Dla własnej wygody napisałem krótką funkcję ustawiającą podstawowe parametry debugowania:</p>
<p style="margin-bottom: 0cm">
<pre class="php">/**
* Function is used for debuging this scrypt, in prod environment change
* $firephp-&gt;setEnabled(true); to false.
*
* @param object $var
* @param string $desc[optional]
*/
public function keep($var, $desc = null)
{
  $options = array(
  'maxObjectDepth' =&gt; 10,
  'maxArrayDepth' =&gt; 20,
  'useNativeJsonEncode' =&gt; true,
  'includeLineNumbers' =&gt; true);

  $firephp = FirePHP::getInstance(true);
  $firephp-&gt;setEnabled(true);
  $firephp-&gt;setOptions($options);
  $firephp-&gt;log($var, $desc);
}</pre>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Ważne aby pamiętać iż dla własnego bezpieczeństwa należy w środowisku produkcyjnym ustawić setEnabled na false.</p>
<p style="margin-bottom: 0cm">Posłużę się Jobeetem, zatem moja funkcja index wygląda tak:</p>
<p style="margin-bottom: 0cm">
<pre class="php">public function executeIndex(sfWebRequest $request)
{
  $vars = 0;
  $this-&gt;categories = Doctrine::getTable('JobeetCategory')-&gt;getWithJobs();
  $this-&gt;keep($vars, "at line: ".__LINE__);
  $vars = 12;
  $this-&gt;keep($vars, "at line: ".__LINE__);
}</pre>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">drugi parametr funkcji keep służy mi jako informacja pozwalająca szybko odnaleźć miejsce z wywołaniem logera. Wynik logowania prezentuje się następująco:</p>
<p style="margin-bottom: 0cm;text-align: center"><a rel="attachment wp-att-677" href="http://www.xlab.pl/2009/11/pieklo-die/attachment/1/"><img class="size-medium wp-image-677 aligncenter" src="http://www.xlab.pl/wp-content/uploads/2009/11/1-300x172.jpg" alt="Debugowanie z użyciem FirePHP" width="300" height="172" /></a></p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Ciekawsze wyniki można uzyskać deklarując przykładową tablicę z danymi – najeżdżając kursorem myszy na wynik debugowania tablicy ukaże się okienko jak na poniższym zrzucie – możemy tutaj nieco dokładniej przyjrzeć się zwróconym wartościom.</p>
<p style="margin-bottom: 0cm;text-align: center"><a rel="attachment wp-att-688" href="http://www.xlab.pl/2009/11/pieklo-die/attachment/2/"><img class="size-medium wp-image-688 aligncenter" src="http://www.xlab.pl/wp-content/uploads/2009/11/2-300x172.jpg" alt="Debugowanie z FirePHP 2" width="300" height="172" /></a></p>
<p><!-- 		@page { size: 21cm 29.7cm; margin: 2cm } 		P { margin-bottom: 0.21cm } --></p>
<p style="margin-bottom: 0cm">Na koniec chciałbym zauważyć, że odświeżenie strony nie zawsze powoduje odświeżenie wyników debugowania, prowadzi to wręcz do ich utraty – należy wówczas kliknąć w Firebugu opcję Off a następnie On dzięki czemu zobaczymy aktualne dane.</p>
<p style="margin-bottom: 0cm">Zachęcam do korzystania z FirePHP i testowania jego pozostałych funkcjonalności.</p>
<p style="margin-bottom: 0cm"><a href="http://www.firephp.org/HQ/Use.htm">http://www.firephp.org/HQ/Use.htm</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/pieklo-die/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Połączenie sfGuardUser oraz sfGuardUserProfile</title>
		<link>http://xlab.pl/polaczenie-sfguarduser-oraz-sfguarduserprofile/</link>
		<comments>http://xlab.pl/polaczenie-sfguarduser-oraz-sfguarduserprofile/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 08:03:01 +0000</pubDate>
		<dc:creator>przemyslaw.piechota</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[admin generator]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sfGuardPlugin]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=162</guid>
		<description><![CDATA[Przy ostatnich pracach związanych z przeniesienie naszej aplikacji z symfony 1.0 na symfony 1.2 natknęliśmy się na kilka problemów z pluginem sfGuardPlugin, a dokładniej z połączeniem administracji sfGuardUser oraz sfGuardUserProfile. Chcieliśmy aby podczas listowania użytkowników, poprzez moduł sfGuardUser, można było wyświetlać nie tylko pola znajdujące się w tabeli sf_guard_user ale także wybrane pola z profilu [...]]]></description>
			<content:encoded><![CDATA[<p>Przy ostatnich pracach związanych z przeniesienie naszej aplikacji z symfony 1.0 na symfony 1.2 natknęliśmy się na kilka problemów z pluginem <a href="http://www.symfony-project.org/plugins/sfGuardPlugin" target="_blank">sfGuardPlugin</a>, a dokładniej z połączeniem administracji sfGuardUser oraz sfGuardUserProfile.<br />
Chcieliśmy aby podczas listowania użytkowników, poprzez moduł sfGuardUser, można było wyświetlać nie tylko pola znajdujące się w tabeli sf_guard_user ale także wybrane pola z profilu usera, a także mieć możliwość filtrowania po nich.</p>
<p>Na początek stworzymy sobie możliwość wyświetlania pól z profilu użytkownika podczas listowania. Otwieramy <em>generator.yml</em> z modułu sfGuardUser, który w oryginale wygląda tak:</p>
<pre>generator:
  class: sfPropelGenerator
  param:
    model_class:           sfGuardUser
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          sf_guard_user
    with_propel_route:     1

    config:
      fields:
        password_again: { label: "Password (again)" }

      list:
        title:   User list
        display: [=username, created_at, last_login]

      form:
        class: sfGuardUserAdminForm
        display:
          "NONE":                   [username, password, password_again]
          "Permissions and groups": [is_active, is_super_admin, sf_guard_user_group_list, sf_guard_user_permission_list]

      edit:
        title: Editing User "%%username%%"

      new:
        title: New User</pre>
<p>Interesuje nas dokładnie sekcja</p>
<pre>list:
  title:   User list
  display: [=username, created_at, last_login]</pre>
<p>Do wyświetlania informacji z pól z profilu użytkownika, będziemy korzystać z możliwości użycia własnych partiali.<br />
Nas interesuje np. imię, nazwisko, oraz adres e-mail użytkownika, dlatego wprowadzamy takie modyfikacje:</p>
<pre>list:
  title:   User list
  display: [=username, created_at, last_login, _first_name, _last_name, _email]</pre>
<p>Następnie tworzymy 3 pratiale w katalogu<em> sfGuardUser/templates/</em> :<br />
<em>_first_name.php</em></p>
<pre>&lt;?php echo $sf_guard_user-&gt;getProfile()-&gt;getFirstName(); ?&gt;</pre>
<p><em>_last_name.php</em></p>
<pre>&lt;?php echo $sf_guard_user-&gt;getProfile()-&gt;getLastName(); ?&gt;</pre>
<p><em>_email.php</em></p>
<pre>&lt;?php echo $sf_guard_user-&gt;getProfile()-&gt;getEmail(); ?&gt;</pre>
<p>Po tym</p>
<pre>symfony cc</pre>
<p>I po odświeżeniu strony z listowaniem użytkowników będziemy mieli także informacje o imieniu, nazwisku oraz adresie email.<br />
Wszytko pięknie tylko po takich modyfikacjach nie można sortować po dodanych polach. A to dlatego, że generator podczas tworzenia kodu naszych template&#8217;ów sprawdza czy pole które ma być wyświetlone istnieje w modelu.<br />
Dlatego musimy obejść to sprawdzanie poprzez zmodyfikowanie informacji o polach w listowaniu (<em>generator.yml</em>):</p>
<pre>list:
  title:   User list
  display: [=username, created_at, last_login, _first_name, _last_name, _email]
  fields:
    last_name:      { is_real: true }
    first_name:     { is_real: true }
    email:          { is_real: true }</pre>
<p>Teraz po wyczyszczeniu cache i odświeżeniu strony mamy już możliwość sortowania po polach z profilu.</p>
<p>Jeżeli przejdziecie teraz w środowisko dev zauważycie, że nagle zwiększyła się bardzo ilość zapytań do bazy.<br />
A to ze względu, że dla każdego użytkownika pobierany jest osobno jego profil.<br />
Należałoby to zoptymalizować, aby nie wywoływać niepotrzebnie tylu zapytań. Tutaj natknęliśmy się na kilka problemów, ponieważ klasa sfGuardUserPeer nie posiada metody doSelectJoinsfGuardUserProfile.<br />
Otwieramy plik <em>sfGuardUserPeer.php</em> i wprowadzamy pewne modfikacje:</p>
<pre>&lt;?php
class sfGuardUserPeer extends PluginsfGuardUserPeer
{
  public static function doSelectJoinsfGuardUserProfile(Criteria $c, $con = null, $join_behavior = Criteria::LEFT_JOIN)
  {

    foreach (sfMixer::getCallables('BasesfGuardUserPeer:doSelectJoin:doSelectJoin') as $callable)
    {
      call_user_func($callable, 'BasesfGuardUserPeer', $c, $con);
    }

    $c = clone $c;

    if ($c-&gt;getDbName() == Propel::getDefaultDB()) {
      $c-&gt;setDbName(self::DATABASE_NAME);
    }

    sfGuardUserPeer::addSelectColumns($c);
    $startcol = (sfGuardUserPeer::NUM_COLUMNS - sfGuardUserPeer::NUM_LAZY_LOAD_COLUMNS);
    sfGuardUserProfilePeer::addSelectColumns($c);

    $c-&gt;addJoin(array(sfGuardUserPeer::ID,), array(sfGuardUserProfilePeer::USER_ID,), $join_behavior);
    $stmt = BasePeer::doSelect($c, $con);
    $results = array();

    while ($row = $stmt-&gt;fetch(PDO::FETCH_NUM)) {
      $key1 = sfGuardUserPeer::getPrimaryKeyHashFromRow($row, 0);
      if (null !== ($obj1 = sfGuardUserPeer::getInstanceFromPool($key1))) {
      } else {

        $omClass = sfGuardUserPeer::getOMClass();

        $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1);
        $obj1 = new $cls();
        $obj1-&gt;hydrate($row);
        sfGuardUserPeer::addInstanceToPool($obj1, $key1);
      }
      $key2 = sfGuardUserProfilePeer::getPrimaryKeyHashFromRow($row, $startcol);
      if ($key2 !== null) {
        $obj2 = sfGuardUserProfilePeer::getInstanceFromPool($key2);
        if (!$obj2) {

          $omClass = sfGuardUserProfilePeer::getOMClass();

          $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1);
          $obj2 = new $cls();
          $obj2-&gt;hydrate($row, $startcol);
          sfGuardUserProfilePeer::addInstanceToPool($obj2, $key2);
        }
        $obj1-&gt;setProfile($obj2);

      }
      $results[] = $obj1;
    }
    $stmt-&gt;closeCursor();
    return $results;
  }
}</pre>
<p>A w pliku <em>sfGuardUser.php</em>, dodajemy funkcję setProfile :</p>
<pre>&lt;?php
class sfGuardUser extends PluginsfGuardUser
{
  public function setProfile( $profile )
  {
    $this-&gt;profile = $profile;
  }
}</pre>
<p>Na koniec musimy tylko zmienić peer_method wywoływaną przez generator do pobierania wyników.<br />
Otwieramy plik <em>sfGuardUser/lib/sfGuardUserGeneratorConfiguration.class.php</em> i dodajemy funkcję getPeerMethod:</p>
<pre>&lt;?php

/**
 * sfGuardUser module configuration.
 *
 * @package    sfGuardPlugin
 * @subpackage sfGuardUser
 */
class sfGuardUserGeneratorConfiguration extends BaseSfGuardUserGeneratorConfiguration
{
  public function getPeerMethod()
  {
    return 'doSelectJoinsfGuardUserProfile';
  }
}</pre>
<p>Teraz po wyczyszczeniu cache, odświeżeniu strony ilość zapytań do bazy powinna spaść.</p>
<p>Na dzisiaj to tyle. Zostało jeszcze opisanie jak filtrować po polach z profilu użytkownika ale to już innym razem.</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/polaczenie-sfguarduser-oraz-sfguarduserprofile/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Automatyczna inwigilacja kodu</title>
		<link>http://xlab.pl/automatyczna-inwigilacja-kodu/</link>
		<comments>http://xlab.pl/automatyczna-inwigilacja-kodu/#comments</comments>
		<pubDate>Tue, 24 Mar 2009 19:08:33 +0000</pubDate>
		<dc:creator>Marcin Dryka</dc:creator>
				<category><![CDATA[Programowanie PHP]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[codesniffer]]></category>
		<category><![CDATA[format]]></category>
		<category><![CDATA[formatter]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php_codesniffer]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.xlab.pl/?p=27</guid>
		<description><![CDATA[Każdy (dobry) programista chciałby, aby jego kod był najlepszy. Najważniejszą i podstawową rzeczą podczas pisania kodu, to prawidłowe nazywanie zmiennych, funkcji oraz klas. Tego jednak program nie jest w stanie sprawdzać (przynajmniej jak na razie nikt nie napisał, tak zaawansowanego analizatora kodu źródłowego). Możemy jednak zatrudnić parę skryptów aby utrzymać poprawne formatowanie kodu (tak, niektórzy [...]]]></description>
			<content:encoded><![CDATA[<p>Każdy (dobry) programista chciałby, aby jego kod był najlepszy. Najważniejszą i podstawową rzeczą podczas pisania kodu, to prawidłowe nazywanie zmiennych, funkcji oraz klas. Tego jednak program nie jest w stanie sprawdzać (przynajmniej jak na razie nikt nie napisał, tak zaawansowanego analizatora kodu źródłowego). Możemy jednak zatrudnić parę skryptów aby utrzymać poprawne formatowanie kodu (tak, niektórzy z tym także mają problemy).<br />
<span id="more-27"></span></p>
<p>Na pierwszy ogień testów poszedł <a href="http://www.waterproof.fr/products/phpCodeBeautifier/">phpCodeBeautifier</a>. Szybko jednak okazało się, że klamra otwierająca zawartość klasy musi znajdować się w tej samej linii co jej deklaracja. Istnieje opcja:</p>
<p><code><strong>--space-before-start-angle-bracket</strong></code></p>
<p>która pozwala wymusić jeden znak spacji przed &#8222;{&#8222;. Czemu autorzy nie pomyśleli o</p>
<p><code><strong>--newline-before-start-angle-bracket</strong></code></p>
<p>? Cieżko powiedzieć. Kod jest zamknięty, więc się tego nie dopisze samemu.</p>
<p>Istnieje jednak <a href="http://pear.php.net/package/PHP_CodeSniffer">PHP_CodeSniffer</a> (phpcs), który połączony z odpowiednim standardem sprawdzi składnie pliku.</p>
<p>Zainstalujmy &#8222;to&#8221; wszystko&#8230; Na początek instalacja samego phpcs:</p>
<pre name="code" class="bash">apt-get install php-codesniffer</pre>
<p>Aby aplikacja mogła sprawdzać <a href="http://trac.symfony-project.org/wiki/HowToContributeToSymfony#CodingStandards">formatowanie zgodne z symfony</a> należy jeszcze doinstalować standard symfony. Można go znaleźć w repozytorium svn pod adresem http://subversion.assembla.com/svn/phpsymfony/Symfony%20Code%20sniffer%20standards/Symfony/.</p>
<pre name="code" class="bash"> cd /usr/share/php/PHP/CodeSniffer/Standards &amp;&amp; sudo svn co http://subversion.assembla.com/svn/phpsymfony/Symfony%20Code%20sniffer%20standards/ .</pre>
<p>Do przetestowania programu stwórzymy plik:</p>
<pre name="code" class="bash">vim ~/test.php</pre>
<p>z odpowiednią zawartością:</p>
<pre name="code" class="php">&lt;?php
class TestKlasa
{
  public function testFunction()
  {
  }
}</pre>
<p>Uf, nareszcie możemy sprawdzić czy nasz kod jest poprawny:</p>
<pre name="code" class="bash">phpcs --standard=Symfony ~/test.php</pre>
<p>W wyniku czego otrzymamy:</p>
<pre name="code" class="bash">FILE: ~/test.php
--------------------------------------------------------------------------------
FOUND 3 ERROR(S) AND 0 WARNING(S) AFFECTING 2 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
2 | ERROR | Missing class doc comment
4 | ERROR | Missing function doc comment
--------------------------------------------------------------------------------</pre>
<p>Działa! To znaczy są błędy, ale o to właśnie nam chodziło &#8211; aby je wykrywać. Poprawmy więc zawartość pliku:</p>
<pre name="code" class="php">&lt;?php
/**
* Test file for PHP_CodeSniffer
*
* PHP versions 4 and 5
*
* @category Tests
* @package  XX
* @author   Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
* @license  http://www.opensource.org/licenses/lgpl-license.php LGPL
* @version  SVN: 1
* @filesource
*/

/**
* TestKlasa
*
* @package XX
* @author  Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
* @version Release: 1
*/
class TestKlasa
{
  /**
  * testFunction
  *
  * @author Marcin Dryka &lt;marcin.dryka@xsolve.pl&gt;
  * @access public
  * @return void
  */
  public function testFunction()
  {
  }
}</pre>
<p>Przy tak wyglądającym pliku wynik działania phpcs jest pusty, brak błędów oznacza poprawnie sformatowany plik. Teraz potrzebna jest tylko silna wola, aby używać skryptu do sprawdzania wszystkich plików. Przy starszych aplikacjach będzie to dość kłopotliwe. Zdarzają się jednak pliki pisane od tak zwanego zera. Gdzie możemy w pełni stosować konwencje ustalone przez autorów symfony (i nie tylko).</p>
]]></content:encoded>
			<wfw:commentRss>http://xlab.pl/automatyczna-inwigilacja-kodu/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

