Archiwum kategorii ‘Sekrety Webmasterów’

Co się dzieje z tymi marginesami?

piątek, 25 Marzec 2011

Collapsing margins jest zjawiskiem zapadania się (można też spotkać określenie „załamywania”) marginesów pionowych elementów sąsiadujących lub zagnieżdżonych. Zaprezentuję to na przykładzie.

<div id="kontener">
	<p>Hello</p>
</div>

Tworzę blok div o identyfikatorze kontener, a w nim umieszczam akapit. Chcę by akapit miał zielone tło i był odsunięty od krawędzi bloku div o 20 pikseli. Natomiast blok div ma mieć tło zielone. Tworzę więc następujące style.

#kontener
{
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 20px; background-color: #00ff00;
}

To, co otrzymuję nie do końca spełnia moje oczekiwania.

Hello

O ile poziome marginesy zachowały się zgodnie z oczekiwaniami, to pionowe przeszły z akapitu do bloku div.

Problem dla konkretnego marginesu nie wystąpi, jeżeli przed lub po akapicie umieszczę jakąś zawartość.

Definicja stylu pozostaje bez zmian

<div id="kontener">
	Napis przed akapitem
	<p>Hello</p>
</div>

lub

<div id="kontener">
	<p>Hello</p>
	Napis po akapicie
</div>

Da mi odpowiednio

Napis przed akapitem

Hello

lub

Hello

Napis po akapicie

Margines pojawił się automatycznie od strony, z której umieściłem napis. Gdybym umieścił go po obu stronach, marginesy pojawiłyby się powyżej i poniżej akapitu. Co mam jednak robić jeżeli nie chcę umieszczać żadnej zawartości? W takiej sytuacji należy po prostu zmienić definicję styli.

<div id="kontener">
	<p>Hello</p>
</div>

Kod html pozostaje niezmieniony.

#kontener
{
	padding: 20px 0;
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 20px; background-color: #00ff00;
}

Do definicji styli elementu #kontener dodaję 20 pikseli dopełnienia górnego i dolnego. Daje to następujący efekt.

Hello

Tym razem odstępy są zbyt duże. Margines akapitu został oddzielony właściwością padding elementu div i tym samym został przez przeglądarkę uwzględniony. To co widzimy na stronie to suma marginesu akapitu i dopełnienia elementu div. Aby poprawić ostateczny efekt należy pozbyć się pionowych marginesów akapitu.

#kontener
{
	padding: 20px 0;
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 0 20px; background-color: #00ff00;
}

Wstawiam zero jako pierwszą wartość właściwości padding akapitu, czyli pionowe marginesy zostają wyzerowane przy niezmienionym stanie marginesów poziomych. Tym razem efekt jest następujący.

Hello

Co jednak, gdy będę chciał umieścić wewnątrz bloku div kilka akapitów tak, by pomiędzy nimi uzyskać 20 pikseli przerwy?

<div id="kontener">
	<p>Hello</p>
	<p>Hello</p>
	<p>Hello</p>
</div>

Hello

Hello

Hello

Dodam dla każdego akapitu marginesy pionowe: górny i dolny o wartości 10 pikseli. To powinno nam dać w sumie 20 pikseli przerwy. Aby odległość pomiędzy pierwszym akapitem, a górną krawędzią elementu div oraz pomiędzy ostatnim akapitem i dolną krawędzią elementu div nie została zwiększona do 30 pikseli. Zmniejszę wartość pionowego dopełnienia do 10 pikseli.

#kontener
{
	padding: 10px 0;
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 10px 20px; background-color: #00ff00;
}

Hello

Hello

Hello

Ponownie efekt nie jest zgodny z oczekiwanym. Tym razem marginesy pomiędzy akapitami zostały zredukowane do jednego marginesu. Gdyby marginesy akapitów górny i dolny nie były sobie równe. Odległość pomiędzy akapitami zostałaby zredukowana do większego z marginesów.

Aby marginesy były takie, jakie być powinny, wyzeruję jeden z marginesów akapitów, a drugi ustawię na 20 pikseli. Nie ma większego znaczenia, czy wyzeruję górny, a zwiększę dolny, czy odwrotnie. Będę tylko musiał pamiętać o zmianie odpowiedniego dopełnienia bloku div.

#kontener
{
	padding: 1px 0 20px;
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 20px 20px 0; background-color: #00ff00;
}

Każdy akapit ma margines górny równy 20 pikseli, marginesy poziome równe 20 pikseli i margines dolny równy 0. Natomiast dopełnienia elementu div zmieniły się odwrotnie. To dopełnienie górne zostało zredukowane wprawdzie do 1 piksela, a nie do 0, ale wyzerowanie tej wartości doprowadziłoby do zapadnięcia się marginesu górnego pierwszego akapitu w marginesie górnym bloku div, co obrazuje pierwszy przykład tej lekcji. Jeżeli nie potrzebujemy dokładności co do piksela, to rozwiązanie jest zupełnie zadowalające. Zresztą wystarczy spojrzeć poniżej.

Hello

Hello

Hello

Jeżeli jednak ta dokładność jest potrzebna, należy dodać jeszcze jedną właściwość w definicji stylu.

#kontener
{
	padding: 1px 0 20px;
	background-color: #ffff00;
}
 
#kontener p
{
	margin: 20px 20px 0; background-color: #00ff00;
}
 
#kontener p:first-child
{
	margin-top: 19px;
}

Selektor #kontener p:first-child wskazuje tylko na pierwszy akapit zawarty w elemencie #kontener i tylko dla niego zmniejsza górny margines do 19 pikseli, co łącznie z górnym dopełnieniem daje wartość 20. Niestety to rozwiązanie nie działa w przeglądarce IE nawet w wersji 8. Problem ten można ominąć nadając pierwszemu akapitowi dowolny identyfikator np: id=”pierwszy”. Wówczas selektor powinien wyglądać: #kontener p#pierwszy. Jeżeli takich fragmentów kodu na stronie mamy kilka, zamiast identyfikatora używamy klasy.

Hello

Hello

Hello

Prezentowany przypadek jest dosyć prosty, w praktyce można natknąć się na bardzo różne układy elementów i różne też będą zaplanowane efekty. Z tego względu powyższych rozwiązań nie należy bezmyślnie kopiować. Mają one jedynie wskazać ogólny kierunek szukania rozwiązań.

Jeżeli ktoś z czytelników spotkał się w swojej pracy z szczególnie interesującym problemem związanym z collapsing margins, to proszę o przysłanie kodu z opisem oczekiwanego rezultatu na adres trener@serwan.pl. Rozwiązanie 2-3 ciekawszych przykładów zaprezentuję w jednej z najbliższych lekcji. Zachęcam również do pozostawiania komentarzy poniżej.



Obejrzyj pierwszą lekcję…

środa, 2 Marzec 2011

Poniżej prezentuję pierwszą lekcję szkolenia „Bazy danych w aplikacjach internetowych”, oraz krótki słowniczek. Gdyby jeszcze jakieś terminy wymagały wytłumaczenia, proszę zgłaszać. Postaram się w miarę szybko uzupełnić słowniczek.

Dla większej wygody można powiększyć nagranie klikając ikonkę w prawym dolnym rogu filmu. Klip wymaga wtyczki Adobe Flash Player.

Słowniczek

rekord
wiersz tabeli, pojedynczy wpis do bazy o ustalonej strukturze
pole
najmniejsza część rekordu przechowująca daną określonego typu
klucz potencjalny
złożenia pól lub pojedyncze pola, których wartość jednoznacznie identyfikuje rekord tabeli
klucz podstawowy
może być też nazywany kluczem głównym. Jest to jeden z kluczy potencjalnych. Jeżeli tabela zawiera tylko jeden klucz potencjalny, automatycznie jest on kluczem podstawowym
klucz obcy
pole lub pola zawierające klucz podstawowy z innej tabeli
relacja
powiązanie rekordów z różnych tabel wykorzystujące klucze główne i obce tworzące logiczną całość. Występują trzy rodzaje relacji:

  1. relacja jeden do jednego

    jednemu rekordowi z pierwszej tabeli odpowiada tylko jeden rekord z tabeli drugiej

  2. relacja jeden do wielu

    jednemu rekordowi z pierwszej tabeli odpowiada jeden lub wiele rekordów z tabeli drugiej

  3. relacja wiele do wielu

    jednemu lub wielu rekordom z pierwszej tabeli odpowiada jeden lub wiele rekordów z tabeli drugiej. Tę relację w praktyce realizuje się poprzez dodanie dodatkowej tabeli, z którą realizowane są dwie relacje jeden do wielu

indeks
jest to dodatkowo utworzona struktura służąca zoptymalizowaniu operacji dotyczących danej kolumny. W uproszczeniu można powiedzieć, że jest to kopia kolumny odpowiednio posortowana.

Zapraszam na szkolenie Bazy danych w aplikacjach internetowych



Po co mi bazy danych.

wtorek, 22 Luty 2011

Wykorzystanie baz danych znacznie rozszerza możliwości programów. Programy tworzone w PHP często potrzebują jakiejś metody do przechowywania i przetwarzania danych. Najprostszym i najwcześniej wykorzystywanym sposobem było zapisywanie danych do plików tekstowych. Niestety możliwości wykorzystania takich zbiorów danych są mocno ograniczone, a implementacja tego rozwiązania rodziła wiele problemów związanych choćby z jednoczesnym dostępem do danych przez wielu użytkowników. Na szczęście większość kont hostingowych, nawet tych darmowych, jest wyposażonych w jakąś bazę danych. Najczęściej jest to MySql.

Jakie są zalety stosowania baz danych?

  1. każdą daną wystarczy wpisać jeden raz by była dostępna we wszystkich wymaganych miejscach
  2. łatwość aktualizacji danych
  3. umożliwienie jednoczesnego dostępu do danych dla wielu użytkowników
  4. możliwość ograniczania dostępu do danych dla różnych grup użytkowników
  5. uniwersalny język do przetwarzania danych (SQL)
  6. eliminacja błędów spójności bazy danych

Co to jest baza danych?

Baza danych to zbiór uporządkowanych danych z pewnej dziedziny. Dane są wzajemnie ze sobą powiązane związkami logicznymi pozwalającymi na wybranie zestawu danych pod odpowiednim do potrzeb kontem.

W dalszej części będę skupiał się na bazie MySql głównie ze względu na jej dużą popularność i ogólną dostępność. W zasadzie wszystkie darmowe skrypty wykorzystujące bazy danych (fora, blogi, CMS-y itp.) są oparte na MySql-u, a jeżeli skrypt jest dostosowany do różnych baz danych, to opcja z MySql jest najlepiej udokumentowana.

MySql jest relacyjną bazą danych. Dane w tym modelu przechowywane są w tabelach zwanych fachowo relacjami. Od razu zaznaczę, że fachowa terminologia nie jest zbyt popularna i pisząc o bazach danych będę używał raczej określeń powszechnie stosowanych, a terminy fachowe będę umieszczał w nawiasach. Tabele składają się z wierszy – rekordów (krotek – encji) – najmniejszych zbiorów danych dotyczących jakiegoś przedmiotu i kolumn (atrybutów) definiujących najmniejsze porcje danych. Np. tabela samochody będzie składała się z rekordów opisujących konkretne auta, a w kolumnach będą zdefiniowane cechy tych samochodów np. marka, model, rok produkcji, kolor itd. Poniżej prezentuję taką przykładową tabelkę.

marka model rok_produkcji kolor
Seat Ibiza 2001 czerwony
Volkswagen Golf 2003 niebieski
Peugeot 307 2003 srebrny
Opel Astra 2007 czarny
Ford Focus 2006 żółty

Rekordy z poszczególnych tabel mogą być powiązane relacjami (związkami encji). Wyróżniamy trzy rodzaje relacji: jeden do jednego, jeden do wielu i wiele do wielu. Relacja jeden do jednego zachodzi w sytuacji, gdy jednemu rekordowi z pierwszej tabeli odpowiada jeden rekord z drugiej tabeli. Relacja jeden do wielu zachodzi, gdy jednemu rekordowi z pierwszej tabeli odpowiada jeden lub więcej rekordów z drugiej tabeli. Relacja wiele do wielu zachodzi, gdy jednemu lub kilku rekordom z pierwszej tabeli odpowiada jeden lub więcej rekordów z drugiej tabeli. W praktyce ten ostatni typ relacji jest zastępowany dwoma relacjami jeden do wielu z dodatkową tabelą.

Relacja pomiędzy tabelami tworzone są dzięki kluczom – specjalnym polom identyfikującym rekordy w tabeli. Występują klucze główne zwane też podstawowymi i obce. Klucz podstawowy jest unikalny w ramach jednej tabeli. Zapisanie wartości klucza głównego w innej tabeli tworzy klucz obcy.



Ustalanie pozycji elementów w CSS cz. 3.

czwartek, 3 Luty 2011

Pozycjonowanie fixed

Element w pierwotnym położeniu. Pozostaje w miejscu, w którym był po wczytaniu się strony.
Element dodatkowy

Ze względu na specyfikę pozycjonowania fixed pierwszy przykład umieściłem powyżej, tak by był widoczny od razu po załadowaniu strony w większości przeglądarek. Kod tego przykładu znajdziesz nieco niżej.

W pozycjonowaniu fixed określamy pozycje elementu względem krawędzi okna przeglądarki. Elementy występujące po elemencie pozycjonowanym zajmują miejsce tego elementu. Pozycje tradycyjnie ustala się właściwościami left, right, top i bottom. Pozycjonowanie fixed jest określane właściwością position: fixed;. Elementy pozycjonowane w ten sposób umieściłem w czerwonym obramowaniu. Dla większej jasności po elemencie pozycjonowanym umieściłem element dodatkowy (zielone tło), a całość umieszczam w elemencie z szaro-niebieską ramką.

<div class="ramka">
	<div class="border" id="fixed1">Element w pierwotnym położeniu</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#fixed1
{
	position: fixed;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}


<div class="ramka">
	<div class="border" id="fixed2">
	Element z właściwościami top: 90px; right: 40px; - odsunięty od górnej i prawej krawędzi okna przeglądarki o 40 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#fixed2
{
	position: fixed;
	top: 90px;
	right: 40px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwościami top: 90px i right: 40px; – odsunięty od górnej 90 pikseli i prawej krawędzi okna przeglądarki o 40 pikseli
Element dodatkowy


Pozycjonowanie statyczne

Ostatnim typem pozycjonowania jest pozycjonowanie statyczne. Oznaczane jest właściwością position: static;. Jest to domyślne pozycjonowanie. Mają je wszystkie elementy na stronie, którym nie zmieniono właściwości position. Czasami zachodzi konieczność jawnego użycia pozycjonowania statycznego. Przyjrzyjmy się przykładowi poniżej. Jeden z bloków div jest pozycjonowany względnie (position: relative). Wstawiony do jego wnętrza kolejny blok div automatycznie ma tę samą właściwość.

<div class="ramka" id="relative">
	<div class="border">
		Element z właściwościami left: 40px; i top: -50px - pozycjonowany względnie.
		<div class="border">
			Element ma to samo pozycjonowanie, co element nadrzędny.
		</div>
	</div>
	<div style="margin: 8px; padding: 12px; background-color: #33ff99; color: #ffffff; font-weight: bold;">
		Element dodatkowy</div>
	</div>
#relative div
{
	position: relative;
	left: 40px;
	top: -50px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwościami left: 40px; i top: -50px – pozycjonowany względnie.
Element przejmuje pozycjonowanie elementu nadrzędnego.
Element dodatkowy


Jeżeli chcielibyśmy usunąć pozycjonowanie dla wewnętrznego bloku, musimy użyć position: static

<div class="ramka" id="relative">
	<div class="border">
		Element z właściwościami left: 40px; i top: -50px - pozycjonowany względnie.
		<div class="border" id="static">
			Element z usuniętym pozycjonowaniem dzięki właściwości <em>position: static</em>.
		</div>
	</div>
	<div style="margin: 8px; padding: 12px; background-color: #33ff99; color: #ffffff; font-weight: bold;">
		Element dodatkowy</div>
	</div>
#relative div
{
	position: relative;
	left: 40px;
	top: -50px;
}
#static
{
	position: static;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwościami left: 40px; i top: -50px – pozycjonowany względnie.
Element z usuniętym pozycjonowaniem dzięki właściwości position: static.
Element dodatkowy

Zachęcam do pozostawieni swoich uwag i komentarzy poniżej.



Ustalanie pozycji elementów w CSS cz. 2.

wtorek, 11 Styczeń 2011

Pozycjonowanie absolutne

W pozycjonowaniu absolutnym określamy pozycje elementu względem kontenera. Kontenerem zazwyczaj jest element body, może też być nim inny element z właściwością position ustawioną na relative, absolute lub fixed. Elementy występujące po elemencie pozycjonowanym zajmują miejsce tego elementu. Pozycje tradycyjnie ustala się właściwościami left, right, top i bottom. W przypadku gdy ustalamy właściwość top, a kontenerem jest element body, pozycja jest liczona od dolnej krawędzi części widocznej po załadowaniu strony. Samo pozycjonowanie absolutne jest określane właściwością position: absolute;. Poniżej przedstawiłem element pozycjonowany absolutnie w czerwonym obramowaniu. Dla większej jasności po elemencie pozycjonowanym umieściłem element dodatkowy (zielone tło), a całość umieszczam w elemencie z szaro-niebieską ramką.

<div class="ramka">
	<div class="border" id="absolute1">Element w pierwotnym położeniu</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute1
{
	position: absolute;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element w pierwotnym położeniu
Element dodatkowy


<div class="ramka">
	<div class="border" id="absolute2">
	Element z właściwością left: 40px; - odsunięty od lewej krawędzi kontenera o 40 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute2
{
	position: absolute;
	left: 40px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwością left: 40px; – odsunięty od lewej krawędzi kontenera o 40 pikseli
Element dodatkowy


<div class="ramka">
	<div class="border" id="absolute3">
	Element z właściwością right: 40px; - odsunięty od prawej krawędzi kontenera o 40 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute3
{
	position: absolute;
	right: 40px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwością right: 40px; – odsunięty od prawej krawędzi kontenera o 40 pikseli
Element dodatkowy


<div class="ramka">
	<div class="border" id="absolute4">
	Element z właściwością bottom: 10px; - odsunięty od dolnej krawędzi kontenera o 10 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute4
{
	position: absolute;
	bottom: 10px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwością bottom: 10px; – odsunięty od dolnej krawędzi kontenera o 10 pikseli
Element dodatkowy


<div class="ramka">
	<div class="border" id="absolute5">
	Element z właściwością top: 3500px; - odsunięty od górnej krawędzi kontenera o 3500 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute5
{
	position: absolute;
	top: 3500px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwością top: 3500px; – odsunięty od górnej krawędzi kontenera o 3500 pikseli
Element dodatkowy


<div class="ramka">
	<div class="border" id="absolute6">
	Element z właściwościami left: 40px; i top: 4500px - odsunięty od lewej krawędzi kontenera o 40 pikseli oraz od górnej krawędzi o 4500 pikseli
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute6
{
	position: absolute;
	left: 40px;
	top: 4500px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwościami left: 40px; i top: 4500px – odsunięty od lewej krawędzi kontenera o 40 pikseli oraz od górnej krawędzi o 4500 pikseli
Element dodatkowy


<div class="ramka" id="relative">
	<div class="border" id="absolute7">
	Element z właściwościami left: 40px; i top: 50px - odsunięty od lewej krawędzi kontenera o 40 pikseli oraz od górnej krawędzi o 50 pikseli. W tym przypadku kontenerem jest element o identyfikatorze relative.
	</div>
	<div class="dodatkowy">Element dodatkowy</div>
</div>
#absolute6
{
	position: absolute;
	left: 40px;
	top: 50px;
}
#relative
{
	position: relative;
	left: -30px;
	top: 10px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
	background-color: #ffffff;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
.dodatkowy
{
	margin: 8px;
	padding: 12px;
	background-color: #33ff99;
	color: #ffffff;
	font-weight: bold;
}
Element z właściwościami left: 40px; i top: 50px – odsunięty od lewej krawędzi kontenera o 40 pikseli oraz od górnej krawędzi o 50 pikseli. W tym przypadku kontenerem jest element o identyfikatorze relative.
Element dodatkowy

Zachęcam do pozostawieni swoich uwag i komentarzy poniżej.



Ustalanie pozycji elementów w CSS cz. 1.

poniedziałek, 20 Grudzień 2010

Pozycjonowanie względne

W pozycjonowaniu względnym możemy przesuwać element względem pierwotnego położenia. Służą do tego właściwości left, right, top i bottom. Samo pozycjonowanie względne jest określane właściwością position: relative;. Poniżej przedstawiłem element pozycjonowany względnie w czerwonym obramowaniu. Dla większej jasności elementy pozycjonowane umieszczam w elemencie z szaro-niebieską ramką.

<div class="ramka">
	<div class="border" id="relative1">Element w pierwotnym położeniu</div>
</div>
#relative1
{
	position: relative;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element w pierwotnym położeniu


<div class="ramka">
	<div class="border" id="relative2">
	Element z właściwością left: 40px; - odsunięty od lewej krawędzi pierwotnego położenia o 40 pikseli
	</div>
</div>
#relative2
{
	position: relative;
	left: 40px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element z właściwością left: 40px; – odsunięty od lewej krawędzi pierwotnego położenia o 40 pikseli


<div class="ramka">
	<div class="border" id="relative3">
	Element z właściwością right: 40px; - odsunięty od prawej krawędzi pierwotnego położenia o 40 pikseli
	</div>
</div>
#relative3
{
	position: relative;
	right: 40px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element z właściwością right: 40px; – odsunięty od prawej krawędzi pierwotnego położenia o 40 pikseli


<div class="ramka">
	<div class="border" id="relative4">
	Element z właściwością bottom: 10px; - odsunięty od dolnej krawędzi pierwotnego położenia o 10 pikseli
	</div>
</div>
#relative4
{
	position: relative;
	bottom: 10px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element z właściwością bottom: 10px; – odsunięty od dolnej krawędzi pierwotnego położenia o 10 pikseli


<div class="ramka">
	<div class="border" id="relative5">
	Element z właściwością top: 10px; - odsunięty od górnej krawędzi pierwotnego położenia o 10 pikseli
	</div>
</div>
#relative5
{
	position: relative;
	top: 10px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element z właściwością top: 10px; – odsunięty od górnej krawędzi pierwotnego położenia o 10 pikseli


<div class="ramka">
	<div class="border" id="relative6">
	Element z właściwościami left: 40px; i top: 15px - odsunięty od lewej krawędzi pierwotnego 
położenia o 40 pikseli oraz od górnej krawędzi o 15 pikseli
	</div>
</div>
#relative6
{
	position: relative;
	left: 40px;
	top: 15px;
}
.border
{
	margin: 15px;
	border: 2px solid #b80400;
	padding: 10px;
}
.ramka
{
	width: 350px; 
	border: 1px solid #9999cc;
}
Element z właściwościami left: 40px; i top: 15px – odsunięty od lewej krawędzi pierwotnego położenia o 40 pikseli oraz od górnej krawędzi o 15 pikseli

Zachęcam do pozostawieni swoich uwag i komentarzy poniżej.



Metoda, która nie istnieje…

poniedziałek, 13 Grudzień 2010

Kilka lat temu, gdy nie znałem jeszcze wszystkich tajników programowania obiektowego, zlecono mi modyfikację pewnego skryptu napisanego właśnie obiektowo. Klasy, obiekty i metody nie były dla mnie zupełną nowością, więc zabrałem się do pracy z nastawieniem, że szybko wykonam zlecenie, wezmę kasę i… Już teraz nie pamiętam na co chciałem wydać te pieniądze ;) .

Zlecenie wydawało się dziecinnie proste. Przynajmniej do czasu, za nim nie wgłębiłem się w kod. Skrypt był podobny do tego poniżej: na początku definicje kilku klas, a później program, który ich używał. Klasy nie wzbudziły moich podejrzeń. Za to później trafiłem, na coś, czego zupełnie nie mogłem zrozumieć.

W poniższym kodzie, symulującym kilka kolejnych zakupów i sprzedaży złota przy zmieniającym się kursie, umieściłem coś podobnego. Chodzi mi o metody kup() i sprzedaj() wywoływane na obiekcie $zloto klasy Gold. Tyle że w klasie Gold nie ma definicji metod kup() i sprzedaj(). Klasa Gold nie rozszerza też żadnej innej klasy, po której mogłaby te metody odziedziczyć. O co więc chodzi?

Najdziwniejsze było to, że ten kod działał. Nie zgłaszał żadnych błędów. W pewnym momencie zacząłem podejrzewać mój komputer o magię i jak się okazało, w pewnym sensie trafiłem. Rozwiązaniem mojej zagadki była metoda _call() należąca do grupy tzw. metod magicznych, które odpowiadają za właśnie takie „niezwykłe” zachowanie programu.

Zajmijmy się jednak poniższym przykładem.

<?php
class Gold
{
	const NAZWA='zloto';
	private $cena=128.4;
 
	public function kurs()
	{
		return $this->cena;
	}
 
	public function __call($n, $p)
	{
		$this->zmien_kurs();
		return array('koszt' => $p[0]*$this->cena, 'towar' => array('nazwa' => self::NAZWA, 'ilosc' => $p[0]));
	}
 
	private function zmien_kurs()
	{
		$zmiana=rand(1, 200)/100;
 
		switch(rand(1, 2))
		{
			case '1': $this->cena+=$zmiana;
			break;
			case '2': $this->cena-=$zmiana;
			break;
		}
	}
}
 
class Portfel
{
	private $kasa=0;
	private $inwestycje=array();
 
	public function __construct($kasa)
	{
		$this->kasa=$kasa;
	}
 
	public function sprawdz()
	{
		return array_merge(array('pieniadze' => $this->kasa), $this->inwestycje);
	}
 
	public function transakcja($inwestycja)
	{
		$this->kasa-=$inwestycja['koszt'];
		$this->inwestycje[$inwestycja['towar']['nazwa']]+=$inwestycja['towar']['ilosc'];
	}
}
 
$portfel= new Portfel(10000);
$zloto= new Gold;
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
 
//kupuję 24 sztabki złota
$portfel->transakcja($zloto->kup(24));
echo '<br />Aktualny kurs: ';
echo $zloto->kurs();
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
 
//sprzedaję 32 sztabki złota
$portfel->transakcja($zloto->sprzedaj(-32));
echo '<br />Aktualny kurs: ';
echo $zloto->kurs();
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
 
$portfel->transakcja($zloto->kup(53));
echo '<br />Aktualny kurs: ';
echo $zloto->kurs();
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
 
$portfel->transakcja($zloto->sprzedaj(-20));
echo '<br />Aktualny kurs: ';
echo $zloto->kurs();
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
 
$portfel->transakcja($zloto->sprzedaj(-25));
echo '<br />Aktualny kurs: ';
echo $zloto->kurs();
 
echo '<br />Sprawdzenie:<br />';
foreach($portfel->sprawdz() as $key => $value)
{
	echo $key.': '.$value.'<br />';
}
?>

Metody sprzedaj() lub kup() są wywoływane na obiekcie $zloto z przekazanym do nich argumentem typu int. Dla metody kup() argument ma zawsze wartość dodatnią i oznacza ilość kupionego towaru (dodatnia wartość oznacza, że zyskaliśmy tyle sztuk tego towaru), natomiast dla metody sprzedaj() argument ma wartość ujemną (pozbywamy się towaru). Wynik działania tych metod przekazywany jest do metody transakcja() wywołanej na obiekcie $portfel klasy Portfel.

Ponieważ metody sprzedaj() i kup() nie zostały zdefiniowane w klasie Gold, obsługą tych wywołań zajmie się metoda magiczna __call() Metoda przyjmuje dwa argumenty $n i $p. W pierwszym argumencie, w tym akurat przypadku nieużywanym, jest dostępna nazwa wywoływanej metody. Natomiast drugi argument jest tablicą argumentów, z jakimi metoda została wywołana. Metody sprzedaj() i kup() są wywoływane tylko z jednym argumentem więc $p jest tablicą jednoelementową. Wywołując metody sprzedaj() lub kup() robimy praktycznie to samo – zmieniamy ilość pieniędzy i złota w portfelu. To czy ta zmiana jest na plus czy na minus zależy od wartości argumentu, więc nazwy metod są zbędne.

Na dobrą sprawę moglibyśmy metodę __call() zastąpić np. metodą kupno_sprzedaz(), którą wywoływalibyśmy zupełnie jawnie w kodzie i nie byłoby tego całego zamieszania. Jednak dzięki użyciu metod sprzedaj() i kup() kod jest bardziej intuicyjny (pod warunkiem, że zna się metody magiczne). Poza tym dzięki różnym nazwom moglibyśmy zmodyfikować metodę __call() w taki sposób, by przy transakcji sprzedaży doliczana byłaby marża, a przy kupnie nie. Wystarczyłoby sprawdzić wówczas wartość argumentu $n. Zresztą możliwości jest dużo więcej, a __call() to tylko jedna z metod magicznych. Warto znać je wszystkie.

Pisząc ten artykuł postawiłem sobie zadanie przybliżenia pojęcia metod magicznych na przykładzie metody __call(). Gdyby jednak inne elementy przykładowego programu były dla Ciebie nie do końca zrozumiałe, napisz o tym w komentarzu poniżej. Chętnie wszystko wyjaśnię.



Interfejs czy abstrakcja?

środa, 24 Listopad 2010

Wielu programistów stawiających pierwsze kroki w programowaniu obiektowym ma problem z decyzją, kiedy stosować dziedziczenie po klasie abstrakcyjnej, a kiedy implementację interfejsu. Dodatkowo dużo zamieszania wprowadza często powtarzane twierdzenie, że mechanizm implementacji interfejsów jest receptą na brak możliwości bezpośredniego dziedziczenia po kilku klasach jednocześnie.

Załóżmy, że mamy dwie klasy abstrakcyjne:

abstract class Uzytkownik
{
  protected $imie='';
  protected $nazwisko='';
 
  public abstract function zapisz_imie($imie)
  {
    $this->imie=$imie;
  }
 
  public abstract function zapisz_nazwisko($nazwisko)
  {
    $this->nazwisko=$nazwisko;
  }
}
 
abstract class Uprawnienia
{
  protected $poziom=0;
  protected $zakres=array();
 
  public abstract function ustaw_poziom($poziom)
  {
    $this->poziom=$poziom;
  }
 
  public abstract function dodaj_zakres($zakres)
  {
    $this->zakres[]=$zakres;
  }
 
  public abstract function sprawdz_dostep($zakres)
  {
    return in_array($zakres, $this->zakres);
  }
}

Nie możemy utworzyć klas Administrator, Kierownik, Handlowiec itp. dziedziczących po tych klasach. Zapis: „class Admin extends Uzytkownik, Uprawnienia” spowoduje przerwanie programu i wyświetlenie odpowiedniego komunikatu błędu. Próba rozwiązania tego problemu przy pomocy interfejsów wyglądałaby tak:

interface Uzytkownik
{
  public function zapisz_imie($imie);
  public function zapisz_nazwisko($nazwisko);
}
 
interface Uprawnienia
{
  public function ustaw_poziom($poziom);
  public function dodaj_zakres($zakres);
  public function sprawdz_dostep($zakres);
}
 
class Administrator implements Uzytkownik, Uprawnienia
{
  protected $imie='';
  protected $nazwisko='';
  protected $poziom=0;
  protected $zakres=array();
 
  public function zapisz_imie($imie)
  {
    $this->imie=$imie;
  }
 
  public function zapisz_nazwisko($nazwisko)
  {
    $this->nazwisko=$nazwisko;
  }
 
  public function ustaw_poziom($poziom)
  {
    $this->poziom=$poziom;
  }
 
  public function dodaj_zakres($zakres)
  {
    $this->zakres[]=$zakres;
  }
 
  public function sprawdz_dostep($zakres)
  {
    return in_array($zakres, $this->zakres);
  }
}
 
class Kierownik implements Uzytkownik, Uprawnienia
{
  protected $imie='';
  protected $nazwisko='';
  protected $poziom=0;
  protected $zakres=array();
 
  public function zapisz_imie($imie)
  {
    $this->imie=$imie;
  }
 
  public function zapisz_nazwisko($nazwisko)
  {
    $this->nazwisko=$nazwisko;
  }
 
  public function ustaw_poziom($poziom)
  {
    $this->poziom=$poziom;
  }
 
  public function dodaj_zakres($zakres)
  {
    $this->zakres[]=$zakres;
  }
 
  public function sprawdz_dostep($zakres)
  {
    return in_array($zakres, $this->zakres);
  }
}

To co od razu rzuca się w oczy, to identyczne metody w klasach Administarator i Kierownik. Taki zapis jest rozwiązaniem fatalnym i niwelującym większość zalet programowania obiektowego. Na tym przykładzie wyraźnie widać, że interfejsy nie zastąpią dziedziczenia. Zaś na dziedziczenie bezpośrednio po kilku klasach programistom PHP przyjdzie poczekać pewnie jeszcze kilka lat. Są sposoby symulowania takiego dziedziczenia już dzisiaj przy pomocy metod magicznych. Jeden z takich sposobów omawiam w szkoleniu Programowanie Obiektowe w PHP. Nie to jednak jest tematem tego artykułu.

Na podstawie powyższych przykładów mogę stwierdzić, jakie są korzyści z dziedziczenia.

  • Metody zdefiniowane w jednej klasie mogę użyć w innych klasach.
  • Mogę podzielić kod całego elementu na małe części (klasy) zawierające dane i funkcje związane z konkretną częścią systemu lub aspektem działania systemu.

A jakie są zalety interfejsów? Wyjaśnię to na przykładzie poniżej.

interface Cache
{
  public function zapisz($klucz, $dane);
  public function odczytaj($klucz);
}
 
class Pliki implements Cache
{
  public function zapisz($klucz, $dane)
  {
    //zapis danych w plikach na dysku serwera
    //...
  }
 
  public function odczytaj($klucz)
  {
    //odczyt danych z plików serwera
    //...
  }
}
 
class Baza_danych implements Cache
{
  public function zapisz($klucz, $dane)
  {
    //zapis danych w bazie danych
    //...
  }
 
  public function odczytaj($klucz)
  {
    //odczyt danych z bazy danych
    //...
  }
}

Dzięki zastosowaniu interfejsu możemy tworzyć program, a na samym końcu zdecydować czy będziemy korzystać z cachowania w plikach czy bazie danych. Implementacja tego samego interfejsu przez klasy Pliki i Baza_danych wymusza na nich posiadanie metod o takich samych nazwach i tej samej liczbie argumentów. Korzyści prezentuję na poniższym przykładzie

//$cache = new Pliki;
$cache = new Baza_danych;
 
$cache->zapisz('czas', '11:20');
//...
$czas=$cache->odczytaj('czas');

Możemy dowolnie wybrać jakiej klasy ma być obiekt $cache bez wpływu na dalszy kod. Ponadto możemy zlecić innemu programiście napisanie klasy, która będzie implementowała interfejs Cache i ona również będzie pasowała do napisanego wcześniej kodu.



Zabezpieczony: Programowanie obiektowe w PHP – lekcje 8-9

środa, 17 Listopad 2010

Ten wpis jest zabezpieczony hasłem. Aby go zobaczyć, proszę wprowadzić poniżej swoje hasło:



Zabezpieczony: Programowanie obiektowe w PHP – lekcje 6 i 7

wtorek, 16 Listopad 2010

Ten wpis jest zabezpieczony hasłem. Aby go zobaczyć, proszę wprowadzić poniżej swoje hasło: