<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Rafał Trójniak Toolblog - Artykuły</title>
    <description>linux user &amp; admin , web developer, student</description>
    <pubDate>Sun, 10 Apr 2011 21:48:36 +0200</pubDate>
    <generator>Zend_Feed_Writer 1.11.3 (http://framework.zend.com)</generator>
    <link>http://trojniak.net</link>
    <author>rafal@trojniak.net (Rafał Trójniak)</author>
    <dc:creator>Rafał Trójniak</dc:creator>
    <atom:link rel="self" type="application/rss+xml" href="http://trojniak.net/Articles-rss"/>
    <atom:link rel="self" type="application/atom+xml" href="http://trojniak.net/sitemap.xml"/>
    <item>
      <title>DbDeploy - wersjonowanie schematu bazy danych</title>
      <description><![CDATA[<p>
Większość projektów z jakimi mam do czynienia wykorzystuje bazę danych.
Baza zazwyczaj jest SQLowa, i rozwija się wraz z projektem.
Dodawane są nowe tabele, modyfikowane i dodawane pola w tabelach, powstają klucze, procedury składowane i wiele innych.
</p>
<p>
Nadchodzi jednak etap, kiedy w projekcie pojawia się gałąź produkcyjna lub testowa.
Aplikacja ma teraz instancje w środowisku testowym, i/lub produkcyjnym.
Jak od tego momentu zapanować nad odpowiednim wrażaniem zmian na wersję produkcyjną ?
Jak panować nad zbiorem poprawek, i ich wdrażaniu w odpowiedniej kolejności ?
Jeszcze niedawno zadowalało mnie proste zrzucanie struktury, i wrzucanie jej na wersję produkcyjną.
Powstają jednak problemy z ponownym wrzucaniem danych, czasem niedostępności aplikacji i nie tylko.
</p>]]></description>
      <pubDate>Sun, 10 Apr 2011 21:48:36 +0200</pubDate>
      <link>http://trojniak.net/Article/dbdeploy</link>
      <guid>http://trojniak.net/Article/dbdeploy</guid>
      <content:encoded><![CDATA[<h2 id="toc_1.1">Wymagania</h2>

<p>
Problem utrzymywania aktualnej wersji jest już szeroko znany przy utrzymywaniu kodu.
Powstało więc wiele systemów kontroli wersji (CVS,SVN,Git,Mercurial ...).
</p>

<p>
Struktura bazy danych to jednak nie do końca to samo.
W bazie danych nie mamy kodu który w danej wersji jest zawsze taki sam.
Jest coś więcej. Są dane.
Problem polega więc nad tym, aby przy aktualizacji schematu bazy danych do najnowszej wersji nie "zepsuć" bazy.
Na czym więc nam dodatkowo zależy :
</p>
<ul>
<li>
Aby schemat otrzymał nowe elementy(pola, tabele, klucze, triggery) z aktualnej wersji
</li>
<li>
Aby nie utracić danych
</li>
<li>
Aby dane nadal były spójne
</li>
</ul>

<p>
Dodatkowo, na samym środowisku docelowym wykonywane są czasami jakieś dodatkowe zmiany.
Dodawane są klucze, czasami jakieś małe zmiany w polach.
Zależy nam więc jak zwykle na zmniejszeniu liczby zależności przy wprowadzaniu zmian
Zależy nam też na uodpornieniu procesu aktualizacji na błędy związane z rozbieżnościami oczekiwanego i otrzymanego na początku schematu.
</p>

<h2 id="toc_1.2">Podejście chałupnicze</h2>

<p>
Przy małej ilości zmian lub rzadkich wdrożeniach na wersję produkcyjną możemy to załatwić ręcznie.
</p>
<ul>
<li>
Możemy to zrobić porównując ręcznie schemat z bazy z tym z pliku schematu
</li>
<li>
Możemy przy zmianach tworzyć proste pliki tworzące wykonujące zmiany i podsyłać wraz z kodem
</li>
<li>
Możemy zrzuć dane do pliku, wyczyścić bazę, wrzucić nowy schemat i dane z pliku
</li>
</ul>

<p>
Wszystkie metody powyższe dają efekty.
Są jednak wrażliwe na błędy, i często wymagają odłączenia aplikacji od bazy.
</p>

<h2 id="toc_1.3">Opis narzędzia</h2>

<p>
Narzędzie, którego od nie dawna używam, to <a href="http://dbdeploy.com/">DbDeploy</a>.
Pozwala ono na zarządzanie schematem bazy danych w oparciu o zbiór skryptów zmieniających ją.
Narzędzie napisane jest w Javie, można podpiąć je do narzędzia ANT.
</p>

<p>
Skrypty te numerowane są na podstawie ich nazwy.
3 pierwsze znaki oznaczają numer, a zarazem kolejność wprowadzania ich do bazy.
Są to w zasadzie zwykłe skrypty SQL, jednak przy pomocy odpowiednich komentarzy mamy możliwość tworzenia skryptów cofania zmian.
</p>

<p>
Narzędzia można używać jako target do narzędzia <em>ant</em>, jak i jako narzędzie <em>command line</em>
</p>

<h2 id="toc_1.4">Workflow</h2>

<p>
Przy <a href="http://dbdeploy.com/">DbDeploy</a> przyjmuję następujący tryb pracy :
</p>
<ul>
<li>
Wykonanie poprawki do bazy w języku SQL (nie koniecznie ręcznie, z pomocą mogą przyjść mysql-workbench lub inne narzędzia okienkowe)
</li>
<li>
Zapisanie jej w katalogu ze skryptami używanego przez DbDeploy
</li>
<li>
Nałożenie poprawki na bazę danych przy pomocy DbDeploy
</li>
</ul>

<p>
Należy tutaj podać następujące założenia :
</p>
<ul>
<li>
Po umieszczeniu pliku poprawki i nałożeniu jej nie powinno się jej zmieniać lub usuwać
</li>
<li>
Kolejne zmiany to koniecznie kolejne pliki z poprawkami
</li>
</ul>

<p>
Cały katalog wraz z zbiorem skryptów, samym DbDepoy, sterownikiem bazy danych i konfiguracją (build.xml) trzymam pod kontrolą bazy danych.
Pozwala to na jednoczesne aktualizowanie kodu i schematu na serwerach docelowych.
Przy aktualizacji tych repozytoriów natychmiast można zauważyć pojawiające się pliki z poprawkami do bazy danych.
Uruchomienie <em>ant</em> w odpowiednim katalogu, i baza jest już aktualna.
</p>

<h2 id="toc_1.5">Przykładowa zmiana</h2>
<p>
Załóżmy, że w bazie danych przechowujemy tabelę z użytkownikami, której struktura zapisana jest w pliku 001-base.sql
</p>
<pre name="code" class="sql">
CREATE  TABLE IF NOT EXISTS User (
	idUser INT NOT NULL AUTO_INCREMENT ,
	email TINYTEXT NOT NULL ,
	pasword CHAR(40) NOT NULL ,
	salt CHAR(23) NOT NULL ,
	meta TEXT NOT NULL ,
	created DATETIME NOT NULL ,
	lastLogin DATETIME NOT NULL ,
	PRIMARY KEY (idUser) )
	ENGINE = InnoDB|
</pre>
<p>
Aby rozpocząć pracę, uaktualniamy plik build.xml o dane do bazy danych, wykonujemy <em>ant clean</em> w celu utworzenia tabeli do przechowywania stanu schematu.
</p>
<pre>
ert16@mert dbdeploy&gt; ant clean
Buildfile: /home/ert16/dbdeploy/build.xml

create-changelog-table:
		[sql] Executing resource:
 /home/ert16/dbdeploy/lib/createSchemaVersionTable.mysql.sql
		[sql] 2 of 2 SQL statements executed successfully

clean:

BUILD SUCCESSFUL
Total time: 5 seconds
</pre>

<p>
Następnie ustawiamy aktualną wersję bazy danych.
</p>

<p>
ert16@mert dbdeploy&gt; ant
Buildfile: /home/ert16/dbdeploy/build.xml
</p>
<pre>
update-database:
 [dbdeploy] dbdeploy 3.0M3
 [dbdeploy] Reading change scripts from directory /home/ert16/dbdeploy...
 [dbdeploy] Changes currently applied to database:
 [dbdeploy]   (none)
 [dbdeploy] Scripts available:
 [dbdeploy]   1
 [dbdeploy] To be applied:
 [dbdeploy]   1
 [dbdeploy] Applying #1: 001-base.sql...
 [dbdeploy]  -&gt; statement 1 of 11...
 [dbdeploy]  -&gt; statement 2 of 11...
 [dbdeploy]  -&gt; statement 3 of 11...
 [dbdeploy]  -&gt; statement 4 of 11...
 [dbdeploy]  -&gt; statement 5 of 11...
 [dbdeploy]  -&gt; statement 6 of 11...
 [dbdeploy]  -&gt; statement 7 of 11...
 [dbdeploy]  -&gt; statement 8 of 11...
 [dbdeploy]  -&gt; statement 9 of 11...
 [dbdeploy]  -&gt; statement 10 of 11...
 [dbdeploy]  -&gt; statement 11 of 11...

default:

BUILD SUCCESSFUL
Total time: 3 seconds
</pre>

<p>
Chciałbym do tabeli dołożyć pole emailHash, które będzie skrótem <em>sha1</em> pola email.
Realizacja skrótu będzie wykonywana na poziomie aplikacji piszącej do bazy danych.
Tworzę więc następującą poprawkę :
</p>
<pre name="code" class="sql">
ALTER TABLE User 
	ADD COLUMN emailHash CHAR(32)
		CHARACTER SET 'ascii'
		COLLATE 'ascii_general_ci'
		NOT  NULL AFTER lastLogin ,
	ADD UNIQUE INDEX emailHash_UNIQUE (emailHash ASC) |

UPDATE User 
	SET emailHash=md5(email)|
</pre>

<p>
Umieszczam ją w katalogu dbDeploy pod nazwą 002-emailHashField.sql i wykonuję <em>ant</em>
</p>
<pre>
ert16@mert dbdeploy&gt; ant
Buildfile: /home/ert16/dbdeploy/build.xml

update-database:
 [dbdeploy] dbdeploy 3.0M3
 [dbdeploy] Reading change scripts from directory /home/ert16/dbdeploy...
 [dbdeploy] Changes currently applied to database:
 [dbdeploy]   1
 [dbdeploy] Scripts available:
 [dbdeploy]   1, 3
 [dbdeploy] To be applied:
 [dbdeploy]   3
 [dbdeploy] Applying #3: 002-emailHashField.sql...

default:

BUILD SUCCESSFUL
Total time: 3 seconds
</pre>


<p>
Po pewnym czasie, przypominam sobie jednak, że md5 jest już przestarzałe, i warto by było, aby zmienić tą sumę na sha1.
Co więc należy zrobić ? Oczywiście, kolejna poprawka.
</p>
<pre name="code" class="sql">
ALTER TABLE User 
	CHANGE COLUMN emailHash emailHash CHAR(40)
		CHARACTER SET 'ascii'
		COLLATE 'ascii_general_ci'
		NOT  NULL |
UPDATE User SET emailHash=sha1(email)|
</pre>
<p>
Po zapisaniu jako 003-emailhashField-fix.sql i wykonaniu <em>ant</em> baza schemat jest już aktualny.
</p>
<pre>
ert16@mert dbdeploy&gt; ant
Buildfile: /home/ert16/dbdeploy/build.xml

update-database:
 [dbdeploy] dbdeploy 3.0M3
 [dbdeploy] Reading change scripts from directory /home/ert16/dbdeploy...
 [dbdeploy] Changes currently applied to database:
 [dbdeploy]   1, 3
 [dbdeploy] Scripts available:
 [dbdeploy]   1, 3, 4
 [dbdeploy] To be applied:
 [dbdeploy]   4
 [dbdeploy] Applying #4: 003-emailhashField-fix.sql...
 [dbdeploy]  -&gt; statement 1 of 2...
 [dbdeploy]  -&gt; statement 2 of 2...

default:

BUILD SUCCESSFUL
Total time: 4 seconds
</pre>


<h2 id="toc_1.6">Napotkane problemy</h2>

<p>
Dokumentacji do tego narzędzia nie oceniam dobrze. Czasami jest mi ciężko się w niej odnaleźć.
</p>

<p>
Konfiguracja DbDeploy jest dość problematyczna.
Dołączony skrypt build.xml był dla mnie mało przeźroczysty, co zmusiło mnie do jego przebudowania.
Chodziło mi głównie o wyciągnięcie parametrów dostępu do bazy danych na początek pliku i usunięcie niepotrzebnych celów.
Do działania wymagana jest również konfiguracja sterownika do bazy danych. Trzeba umieścić ją w odpowiednim miejscu i zmienić konfigurację.
</p>

<p>
Miałem również pewne problemy przy umieszczaniu w bazie triggerów.
Zmusiło mnie to do zmiany parametru <em>delimiter</em> co widać w powyższych skryptach.
</p>

<h2 id="toc_1.7">Przydatne materiały</h2>
<ul>
<li>
Mój działający dla MySQL setup DbDeploy <a href="http://svn.trojniak.net/czytnikrss/trunk/docs/dbdeploy/">http://svn.trojniak.net/czytnikrss/trunk/docs/dbdeploy/</a>
</li>
<li>
Inne prezentacje <a href="http://www.slideshare.net/harrieverveer/database-version-control-without-pain-the-dpc-version">http://www.slideshare.net/harrieverveer/database-version-control-without-pain-the-dpc-version</a>
</li>
<li>
Strona projektu <a href="http://dbdeploy.com/">http://dbdeploy.com/</a>
</li>
</ul>
<script >
dp.SyntaxHighlighter.HighlightAll('code');
</script>]]></content:encoded>
    </item>
    <item>
      <title>Układanka Zend_Form</title>
      <description><![CDATA[<p>Przy pomocy Zend_Form i klas towarzyszących jej w <a href="http://zendframework.com/">frameworku Zend</a> mamy możliwość kompleksowego obsłużenia formularzy. Te klasy pozwalają na wyświetlenie formularza, odfiltrowanie pól, wyczyszczenie i weryfikację poprawności danych i wiele innych.</p>

<p>W artykule przedstawiam mój sposób na zrobienie tego w jasny, poukładany sposób.</p>]]></description>
      <pubDate>Thu, 24 Feb 2011 21:01:22 +0100</pubDate>
      <link>http://trojniak.net/Article/ukladanka-zend_form</link>
      <guid>http://trojniak.net/Article/ukladanka-zend_form</guid>
      <content:encoded><![CDATA[<p>
Artykuł będę bazował na przykładzie prostego skryptu z artykułami.
</p>

<p>
Na początek same formularze. Dla przykładowego projektu typu CRUD(Create, Read, Update, Delete) zazwyczaj wystarczą 3 formularze : Dodania, Edycji i potwierdzenia usunięcia. Ja Zazwyczaj tworzę więc 3 formularze : 
</p>
<pre>
zf create form AreYouSure
zf create form PageCreate
zf create form PageEdit

</pre>

<p>
Zend_Tool wygenerował nam 3 szkielety formularzy:
</p>

<pre name="code" class="php">
class Application_Form_AreYouSure extends Zend_Form
{
    public function init()
    {
        /* Form Elements &amp; Other Definitions Here ... */
    }
}

class Application_Form_PageCreate extends Zend_Form
{
    public function init()
    {
        /* Form Elements &amp; Other Definitions Here ... */
    }
}

class Application_Form_PageEdit extends Zend_Form
{
    public function init()
    {
        /* Form Elements &amp; Other Definitions Here ... */
    }
}
</pre>

<h2 id="toc_0.1">Pola w formularzach</h2>

<p>
Typy pól w formularza zazwyczaj odzwierciedlają bazę danych. To jak przechowujemy dane odzwierciedlone powinno być w sposobie ich wprowadzania. Możemy więc zdefiniować przykładowe pola :
</p>
<ul>
<li>
Title - tytuł wpisu
</li>
<li>
Head - nagłówek wpisu
</li>
<li>
Body - Treść wpisu
</li>
<li>
Type - typ wpisu
</li>

<li>
Email - Adres autora
</li>
</ul>

<p>
Ponieważ pola wykorzystywane są w obydwóch z formularzy (Create i Edit) umieszczamy je w osobnych klasach. 
Ja zazwyczaj w tym celu tworzę dodatkowy katalog form/Element . Umieszczam w nich klasy dla każdego z pól.
</p>

<p>
Klasy te dziedziczę po klasach z biblioteki. Przykładowymi klasami przydatnymi do dziedziczenia są Text, Textarea, Select oznaczających typowe elementy formularzy znane z HTMLa.
</p>

<pre name="code" class="php">
class Application_Form_Element_Body extends Zend_Form_Element_Textarea
{
    public function init()
    {
    }
}

class Application_Form_Element_Email extends Zend_Form_Element_Text
{
    public function init()
    {
    }
}

class Application_Form_Element_Head extends Zend_Form_Element_Textarea
{
    public function init()
    {
    }
}

class Application_Form_Element_Title extends Zend_Form_Element_Text
{
    public function init()
    {
    }
}

class Application_Form_Element_Type extends Zend_Form_Element_Select
{
    public function init()
    {
    }
}
</pre>

<p>
Jak już mamy elementy formularzy, umieszczamy je na swoim miejscu.
Każde z pól dodajemy do formularza w funkcji init. Do konstruktora obiektu pola przekazujemy jego indeks w formularzu.
To po tym indeksie będziemy pobierać wartość danego pola z formularza.
</p>

<pre name="code" class="php">
class Application_Form_AreYouSure extends Zend_Form
{

	public function init()
	{
		$this-&gt;addElement(new Zend_Form_Element_Submit('yes'));
		$this-&gt;addElement(new Zend_Form_Element_Submit('no'));
	}

}

class Application_Form_PageCreate extends Zend_Form
{

	public function init()
	{
		$this-&gt;addElement(new Application_Form_Element_Title ('title'));
		$this-&gt;addElement(new Application_Form_Element_Head ('head'));
		$this-&gt;addElement(new Application_Form_Element_Body ('body'));
		$this-&gt;addElement(new Application_Form_Element_Type ('type'));
		$this-&gt;addElement(new Application_Form_Element_Email ('email'));
		$this-&gt;addElement(new Zend_Form_Element_Submit('create'));
	}

}

class Application_Form_PageEdit extends Zend_Form
{

	public function init()
	{
		$this-&gt;addElement(new Application_Form_Element_Title ('title'));
		$this-&gt;addElement(new Application_Form_Element_Head ('head'));
		$this-&gt;addElement(new Application_Form_Element_Body ('body'));
		$this-&gt;addElement(new Application_Form_Element_Type ('type'));
		$this-&gt;addElement(new Application_Form_Element_Email ('email'));
		$this-&gt;addElement(new Zend_Form_Element_Submit('save'));
	}
}


</pre>

<h2 id="toc_0.2">Dekoratory</h2>

<p>
Kolejnym krokiem jest praca nad samą prezentacją formularza użytkownikowi. W tym celu używam domyślny zestaw dekoratorów odpowiadających za wygenerowanie HTML. Przydatnych jest przy tym kilka funkcji, które pozwalają na ustawienie etykiety pola (setLabel ) i opisu ( setDescription) . Przykładowo klasa elementu Head będzie teraz wyglądał następująco :
</p>
<pre name="code" class="php">
class Application_Form_Element_Head extends Zend_Form_Element_Textarea
{
    public function init()
    {
			$this
				-&gt;setLabel('Nagłówek')
				-&gt;setDescription('Ta część artykułu pojawiać się będzie na stronie głównej, oraz w widoku detalu artykułu')
				;
    }
}
</pre>

<h2 id="toc_0.3">Filtry i Validatory</h2>

<p>
Po odebraniu danych możemy przepuścić je przez różne filtry.
Przydaje się tutaj głównie Zend_Filter_StringTrim. Dzięki niemu w kluczowych polach nie pojawiają się zbędne spacje na początku i końcu.
</p>

<p>
Sprawdzanie poprawności wprowadzonych danych jest bardzo ważną rzeczą. Pozwala to na zabezpieczenie się przed atakami i upewnienie, że użytkownik wpisał to, co powinien. Zend daje nam do tego szeroką gamę validatorów, które musimy tylko przypiąć i ustawić zgodnie z nasza bazą danych.
Często zapomnianą rzeczą do sprawdzenia jest długość pól. Ponieważ w bazie danych większość pól ma swoje ograniczenia warto zablokować ich przekraczanie.
</p>

<p>
Uzupełniona klasa elementu Title będzie wyglądała tak :
</p>
<pre name="code" class="php">
class Application_Form_Element_Title extends Zend_Form_Element_Text
{
    public function init()
    {
			$this
				-&gt;setLabel('Tytuł')
				-&gt;addFilter('StringTrim')
				-&gt;addValidator('StringLength', false, array('max'=&gt;255) )
				;
    }
}

</pre>


<h2 id="toc_0.4">Użycie</h2>
<p>
Tak stworzone formularze bardzo pomagają w pracy z danymi otrzymywanymi od użytkownika. 
Głównymi funkcjami przydatnymi do użytku są :
</p>
<ul>
<li>
isValid($data) - ustawia wartości pól i sprawdza, czy dane spełniają warunki walidatorów.
</li>
<li>
getValues() - pobiera wartość wszystkich pól po przefiltrowaniu w postaci tablicy
</li>
<li>
getValue($name) - pobiera wartość z pola o danym indeksie

</li>
<li>
setValues($arr) - ustawia wartości pól na podstawie tablicy
</li>
<li>
setValue($index,$arr) - ustawia wartości pola
</li>
<li>
__toString() - Pozwala na wyświetlenie formularze przez użycie echo $formularz
</li>
</ul>

<h2 id="toc_0.5">Podsumowanie</h2>
<p>
Formularze w Zend pozwalają na pobranie, przetworzenie, sprawdzenie danych oraz wyświetlenie wypełnionego formularza.
Praca z formularzami jest prosta, oraz wydajna. Kod formularza może być wykorzystywany wielokrotnie na wielu poziomach, dzięki wydzieleniu klas jego elementów do osobnych obiektów.
</p>
<script >
dp.SyntaxHighlighter.HighlightAll('code');
</script>]]></content:encoded>
    </item>
    <item>
      <title>Hello World!</title>
      <description><![CDATA[<p>Zaczynam więc bloga, ToolBloga. Co z tego wyjdzie ? Zobaczymy.</p><p>Na blogu postaram się pokazać projekty w kt&oacute;rych brałem/biorę udział, narzędzia kt&oacute;re znam/poznaję i nie tylko.</p>]]></description>
      <pubDate>Wed, 16 Feb 2011 23:19:05 +0100</pubDate>
      <link>http://trojniak.net/Article/hello-world</link>
      <guid>http://trojniak.net/Article/hello-world</guid>
      <content:encoded><![CDATA[<p>Cały blok stoi na autorskim projekcie  , kt&oacute;ry <acronym title="jak w kodzie posprzątam">niebawem</acronym> wypuszczę jako Open Source. </p><p>Wsparcie dla komentarzy (żebyście mogli się na artykułach wyżywać) w planie. Do tego czasu zapraszam do pisania do mnie.</p><p>Skrypt wymaga r&oacute;wnież innego dopracowania, gł&oacute;wnie stylizacji i małych poprawek. Myślę jednak, że nadszedł już czas aby umieścić go pod tym adresem.</p><p>A o czym dokładnie chciałbym pisać ?</p><p><h2>Narzędzia</h2></p><p>Bo narzędzia są tym, co pozwala nam się rozwijać. Bez narzędzi nadal jedlibyśmy owoce, a spalibyśmy w jaskiniach. Przez poznawanie nowych narzędzi możemy pracować szybciej, i tworzyć lepsze produkty.</p><p>Ja skupiać się będę gł&oacute;wnie na narzędziach wykorzystywanych w administracji , tworzeniu stron internetowych przy użyciu języka PHP , og&oacute;lnej pracy z systemami z rodziny Linux. Zapraszam do obserwowania tag&oacute;w kt&oacute;re dotyczą tych działek.</p><p><h2>Ciekawe rozwiązania</h2></p><p>W świecie oprogramowania wiele rzeczy można wykonać na wiele sposob&oacute;w. Jedną operację można wykonać na wiele sposob&oacute;w. Niekt&oacute;re są lepsze w jednych sytuacja, inne w innych.</p><p><h2>Wykonywane projekty</h2></p><p>A może kt&oacute;ryś z wykonywanych projekt&oacute;w Ci się przyda ? Może Cię do czegoś zainspiruje ?</p>]]></content:encoded>
    </item>
  </channel>
</rss>
