Tworzenie i modyfikacja bazy danych z poziomu kodu PHP – Laravel Schema Builder
|Przedstawiam kompletny kurs korzystania z narzędzia dostarczonego wraz z Laravel Framework o nazwie Schema Builder. Dzięki niemu można w prostu i skuteczny sposób tworzyć i modyfikować strukturę naszej bazy danych wprost w poziomu kodu PHP.
Na wstępnie można by zadać pytanie: po co zajmować się definiowaniem bazy danych z poziomu kodu PHP ? Może w chwili obecnej nie wydaj się to przydatne ale wraz z następnym wpisem na temat migracji postaram się udowodnić mega użyteczność tego narzędzia oraz zachęcić Was do skorzystania z niego. Dodatkowo zaletą jest również fakt, że tworzony w ten sposób kod zadziała w czterech różnych systemach baz danych: MySQL, Postgres, SQLite oraz SQL Server. Nie potrzebujemy znać do tego szczegółowej składni tych systemów.
Cały niżej przedstawiony kod pisany i testowany był w Laravel 4.2. Przed przystąpieniem do modyfikacji należ skonfigurować wcześniej połączenie z bazą danych (domyślnie w pliku app/config/database.php).
Dla celów testowych możemy utworzyć sobie osobnego routa który będzie wykonywał nasz kod pod odwiedzeniu konkretnego adresu, przykładowo:
Route::get('schema', function () { Schema::create('customers', function ($table) { $table->increments('id'); }); });
Sam routing w Laravelu jest na tyle ciekawy, że zasługuje na osobny wpis. Na chwilę obecną wystarczyć odwiedzić adres /schema aby uruchomić nasz Schema Builder.
Operacje na samych tabelach
Kod buildera jest na tyle intuicyjny i samo opisujący się, że postanowiłem najpierw go przedstawić (fragmenty) a następnie krótko opisać:
Route::get('schema', function () { Schema::create('customers', function ($table) { $table->increments('id'); }); if (Schema::hasTable('customers')) { Schema::rename('customers', 'clients'); Schema::table('clients', function ($table) { $table->string('email'); }); } Schema::drop('clients'); Schem<code>a::dropIfExists('clients'); });
Metoda Schema::create pozwala na utworzenie nowej tabeli. Przyjmuje ona dwa argumenty: pierwszym jest nazwa tabeli a drugim anonimowa funkcja która przyjmuje jako parametr obiekt typu Blueprint którym (jak przedstawię poniżej) posłużymy się do dalszej definicji naszej tworzonej/modyfikowanej tabeli. Gdy dana tabela już istnieje i chcemy tylko zmodyfikować jej właściwości lub kolumny możemy wykorzystać Schema::table – metoda ta przyjmuje takie same argumenty jak Schema::create.
Kolejne metody opisują się wręcz same z definicji. Mamy Schema::rename do zmieniania nazwy tabeli, Schema::drop do usuwania istniejącej tabeli oraz jej bardziej bezpieczniejszą odpowiedniczkę (brzmi zabójczo :)) Schema::dropIfExists która najpierw sprawdzi czy dana tabela istnieje i dopiero wtedy trwale ją usunie. Dodatkowo możemy osobno sprawdzić czy dana tabela istnieje przy pomoc Schema::hasTable.
Operacje na kolumnach
Obiekt klasy Blueprint który otrzymujemy w naszej anonimowej funkcji (w metodach Schema::create oraz Schema::table) posiada szereg przydatnych metod, które posłużą nam do opisu definicji tabeli.
Dodawanie kolumn
Aby dodać nową kolumną wystarczyć posłużyć się odpowiednią metodą klasy Blueprint. Całość kodu umieszczamy w funkcji którą przekazujemy jak drugi parametr do Schema::create oraz Schema::table. Większość poniższych metod tej klasy jako pierwszy parametr przyjmuje nazwę tworzonej kolumny. Przykład:
Schema::create('users', function ($table) { $table->string('email'); });
W ten sposób utworzona została kolumna dla tabeli users typu VARCHAR o nazwie email. Poniżej lista możliwych metod:
Metoda | Opis |
---|---|
$table->bigIncrements(’id’); | tak samo jak increments ale używa BIGINT |
$table->bigInteger(’points’); | pole typu BIGINT |
$table->binary(’image’); | odpowiednik pola typu BLOB |
$table->boolean(’active’); | odpowiednik pola typu BOOL |
$table->char(’firstname’, 4); | odpowiednik pola typu CHAR o zadanej długości |
$table->date(’created_at’); | odpowiednik pola typu DATE |
$table->dateTime(’created_at’); | odpowiednik pola typu DATETIME |
$table->decimal(’column’, 5, 2); | odpowiednik pola typu DECIMAL z odpowiednimi parametrami |
$table->double(’column’, 15, 8); | odpowiednik pola typu DOUBLE z odpowiednimi parametrami |
$table->enum(’title’, array(’Mr.’, 'Mrs.’)); | odpowiednik pola typu ENUM |
$table->float(’amount’); | odpowiednik pola typu FLOAT |
$table->increments(’id’); | odpowiednik pola typu INTEGER wraz z opcją auto inkrementacji oraz kluczem podstawowym |
$table->integer(’votes’); | odpowiednik pola typu INTEGER |
$table->longText(’content’); | odpowiednik pola typu LONGTEXT |
$table->mediumInteger(’score’); | odpowiednik pola typu MEDIUMINT |
$table->mediumText(’description’); | odpowiednik pola typu MEDIUMTEXT |
$table->morphs(’relation’); | utworzy dwie kolumny: relation_id (INTEGER) oraz relation_type (STRING) |
$table->nullableTimestamps(); | tak samo jak timestamps ale pozwala na NULL’e |
$table->smallInteger(’points’); | odpowiednik pola typu SMALLINT |
$table->tinyInteger(’type’); | odpowiednik pola typu TINYINT |
$table->softDeletes(); | dodaje kolumnę deleted_at w celu „miękkiego usuwania” |
$table->string(’email’, 100); | odpowiednik pola typu VARCHAR o zadanej długości (długość jest opcjonalna) |
$table->text(’content’); | odpowiednik pola typu TEXT |
$table->time(’start’); | odpowiednik pola typu TIME |
$table->timestamp(’updated_at’); | odpowiednik pola typu TIMESTAMP |
$table->timestamps(); | dodaje dwie kolumny typy TIMESTAMP created_at oraz updated_at |
$table->rememberToken(); | specjalna metoda dla klasy Auth, dodaje pole remember_token typu VARCHAR |
Dodatkowo każda z tych metod implementuje fluent interface który pozwala na bezpośrednie użycie trzech kolejnych metod:
Metoda | Opis |
---|---|
->nullable() | kolumna może mieć wartość NULL |
->default($value) | definicja wartości domyślne dla kolumny |
->unsigned() | kolumny typu INT są UNSIGNED (nieujemne) |
Przykładowo chcemy utworzyć kolumnę title która domyślnie zawierać będzie string 'Mr.’ oraz będzie mogła mieć wartość typu NULL:
Schema::table('clients', function ($table) { $table->string('title')->nullable()->default('Mr.'); });
Modyfikacja kolumn
Schema::table('clients', function ($table) { $table->renameColumn('login', 'username'); $table->dropColumn('username'); $table->dropColumn(array('firstname', 'lastname')); });
Metoda renameColumn zmienia nazwę kolumny (pierwszy parametr to kolumna do zmiany, drugi to nowa nazwa). Tutaj uwaga: zmiana nazwy nie jest wspierana dla kolumn typu enum. Z kolei dropColumn pozwala na usunięcie wybranej kolumny lub kolumn (przekazujemy wtedy tablicę). Niestety aby sprawdzić czy dana kolumna istnieje musimy posłużyć się metodą Schema::hasColumn, gdzie pierwszy parametr to tabela a drugi kolumna:
Schema::hasColumn('clients', 'username');
Indeksy
Dodawanie indeksu
Indeks możemy określić przy pomocy wspomnianego fluent interface lub oddzielnie dla tabeli (wtedy jako parametr podajemy nazwę kolumny). Poniżej przedstawiam oba sposoby:
Schema::table('clients', function ($table) { // dla nowej kolumny $table->string('email')->unique(); // dla kolumny już istniejącej $table->unique('email'); });
Do dyspozycją są następujące indeksy:
Metoda | Opis |
---|---|
$table->primary(’id’); | dodanie indeksu primary (podstawowego) |
$table->primary(array(’first’, 'last’)); | dodanie indeksu primary stworzonego z więcej niż jednej kolumny |
$table->unique(’email’); | dodanie indeksu unikalnego |
$table->index(’state’); | dodanie indeksu zwykłego |
Usuwanie indeksu
Tutaj sprawa jest trochę trudniejsza. Do dyspozycji mam trzy metody: dropPrimary, dropUnique oraz dropIndex. Każda z tych metod usuwa odpowiadający jej typ indeksu. Jako parametr przyjmuje specjalnie utworzony klucz. Do jego budowy należy użyć nazwę tabeli, nazwę kolumny oraz typ indeksu. Całość łączymy znakiem „_”. Dla przykładu usunięcie indeksu primary z kolumny id w tabeli users:
Schema::table('users', function ($table) { $table->dropPrimary('users_id_primary'); });
Klucze obce
Schema Builder wspiera również definiowanie kluczy obcych w tabeli. Każdy klucz obcy który jest typu integer musi być unsigned. Dodatkowo możemy zdefiniować opcję ON DELETE oraz ON UPDATE. Poniżej przykład wraz z metodą która pozwala taki klucz usunąć:
Schema::table('orders', function ($table) { $table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade'); $table->dropForeign('orders_user_id_foreign'); });
W ten sposób kolumna user_id w tabeli orders stała się kluczem obcym i odwołuję się do tabeli users po kolumnie id. Dodatkowo w przypadku usunięcia rekordu z tabeli users automatycznie usunięte zostaną jego rekordy zależne w tabeli orders (ON DELETE CASCADE).
Dodatkowe metody i właściwości
Inne wyżej nie wspomniane metody i właściwości klasy Blueprint
Schema::create('orders', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->timestamps(); $table->softDeletes(); }); Schema::table('orders', function ($table) { $table->dropTimestamps(); $table->dropSoftDeletes(); });
W pierwszej definicji widzimy wykorzystanie atrybutu $table->engine do zmiany silnika przechowywania danych. Dodatkowo utworzone zostają kolumny przy pomocy timestamps oraz softDeletes. Następnie skorzystałem z dwóch metod których nie opisałem wcześniej dropTimestamps oraz dropSoftDeletes, które usuwają wcześniej utworzone kolumny ich odpowiednikami.
Mam nadzieję, że ten sposób budowania bazy danych przypadnie Wam do gustu. Ja osobiście się w tym zakochałem 🙂 W następnym wpisie skupię się nad migracjami które stanowią mega użyteczne opakowanie do dzisiejszego zbioru metod. Zobaczycie wtedy że ten wpis nabierze większego sensu. Z taką wiedzą tworzenie i przenoszenie bazy danych stanie się przyjemnością nawet w systemach kontroli wersji (np. GIT). Po drodze mam jeszcze w planach wpis gościnny o którym na pewno będę informował Was na Facebook’owym profilu oraz moim Twitter’że. Dziękuję za poświęcony czas oraz zachęcam do komentowania wpisu.
Zdjęcie z wpisu: Flickr na licencji Creative Commons