Każdy kto używał jQuery i tworzył pod niego pluginy to zdążył już zapoznać się z genialną metodą $.extends służącą w głównej mierze do łączenia kilku obiektów z nadpisywaniem istniejących już metod / własności.
Najczęstszym wykorzystaniem niniejszej metody jest łączenie dwóch obiektów:
- z domyślnymi parametrami danego obiektu,
- z ustawieniami zdefiniowanymi przez użytkownika.
Najlepiej i najłatwiej będzie jednak posłużyć się przykładem:
1 2 3 4 5 6 7 8 9 10 11 12 | var pluginOptions = { validate: false, limit: 5, name: "foo" }; var userOptions = { validate: true, name: "bar" }; jQuery.extend(pluginOptions, userOptions); |
Co tutaj się dzieje? A więc najpierw ustawiamy domyślne parametry pluginu – własności konfiguracyjne, które później podstawiamy w odpowiednich miejscach w tworzonej klasie.
Następnie pobieramy obiekt z własnościami webdevelopera wykorzystującego nasz plugin. Obiekt ten nadpisuje domyślne ustawienia i teraz w pluginie są wykorzystywane ustawienia przekazane przez developera.
To tyle. Choć metoda $.extend() ma więcej możliwości, skupmy się na wyżej opisanych.
Przenosimy $.extends do PHP
Metoda tak bardzo mi się spodobała, że postanowiłem wykorzystywać niniejszą funkcjonalność w PHP.
Nie widziałem wbudowanej funkcji mającej podobne działanie (no może poza array_merge, ale jest drobna różnica w działaniu), więc stworzyłem własny kod, który prezentuje poniżej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * Array Extends * merge the contents of two array together into the first array * * @param array default options * @param array user options * * @return array new options **/ function arrayExtends($default, $options) { foreach ($options as $key => $value) { if (is_array($value)) { $default[$key] = arrayExtends($default[$key], $value); } else { $default[$key] = $value; } } return $default; } |
W powyższym kodzie wykorzystałem rekurencję aby podmiana wartości w tablicach działała także w zagnieżdżonych tablicach.
Poza tym, istotną różnicą między PHP a JavaScript jest typ danych, na których operujemy. W jQuery bowiem do metody trafiały obiekty, natomiast w PHP operujemy na tablicach asocjacyjnych.
Praktyczny przykład użycia
I teraz czas na krótki przykład z życia wzięty:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // default options $defaults = array( 'font-size' => 15, 'color' => array(0, 0, 0), 'pos-x' => 0, 'pos-y' => 20 ); // user options $options = array( 'font-size' => 25, 'color' => array(255, 255, 255) ); $o = arrayExtends($defaults, $options); |
W wyniku powyższej fuzji dwóch tablic powstanie następująca tablica wynikowa:
1 2 3 4 5 6 | $o = array( 'font-size' => 25, 'color' => array(255, 255, 255), 'pos-x' => 0, 'pos-y' => 20 ); |
Uzasadnienie
I to by było na tyle. Ten sposób jest bardzo prosty w użyciu oraz przyjemny w późniejszym czytaniu kodu, przynajmniej tak mi się wydaje.
Jeśli masz inne sposoby lub przemyślenia odnośnie tworzenia domyślnych ustawień dla klasy wraz z późniejszym ich nadpisywaniem/wykorzystaniem to z chęcią poczytam o tym w komentarzach :-)
http://pl.php.net/array_merge
Ten kod Ci się uruchamia? Bo jak dla mnie, to extends jest na liście reserved words w PHP. I mi się nie uruchamia. A zrobiłem porównanie z array_merge, i Twój kod rzeczywiście działa lepiej dla zagnieżdżonych struktur :)
@activ: fajnie, że przeczytałeś wpis.
@singles: faktycznie, dzięki za zwrócenie uwagi :-) U siebie mam tą funkcję zapisaną jako metodę o trochę innej nazwie, przy pisaniu bloga nie chciałem komplikować niepotrzebnie sprawy i zmieniłem – już poprawione.
Przydało by się aby przykład pokazywał tą „drobną różnicę w działaniu” pomiędzy Twoją funkcją a array_merge. Wiem o co chodzi oczywiście, ale przykład tego nie obrazuje.
Mało tego przy założeniach z przykładu równie dobrze można użyć:
jak i
i obydwa sposoby będą wydajniejsze od Twojej funkcji i wydaje mi się że bardziej czytelne (Przynajmniej w projektach o dużej ilości kodu).
@Kamil Brenk
Hmm… a po co robić coś, co już jest i działa :)
2
print_r($p);
I masz ten sam wynik co w przypadku Twojej funkcji.
Pozdrawiam!
@Jacek Smolak
Nie do końca działa :) Kamilowi chodziło o coś innego tylko posłużył się trochę słabym przykładem (który nie do końca obrazuje przyświecającą jago funkcji ideę).
Aby szybko zobrazować o co chodzi zmień sobie tablicę $options na np. taką:
2
3
4
'font-size' => 25,
'color' => array(255)
);
@Michał Śliwka
Tak, w tym przypadku tak. Bazowałem na danych w przykładzie.
Pozdrawiam!
Oj bo chyba nie :)
Odpowiednij $.extends w prototype.js Object.extend()
I na koniec, moja wersja PHP’owego odpowiednika
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
$source = func_get_args();
for($i = 1; $i < func_num_args(); $i++) {
foreach($source[$i] as $key => $srcValue) {
if(is_object($target)) {
$trgValue = &$target->$key;
}
else {
$trgValue = &$target[$key];
}
if(is_array($srcValue)) {
$trgValue = merge($trgValue, $srcValue);
}
else {
$trgValue = $srcValue;
}
}
}
return $target;
}
$targets = (object) array(
'font-size' => 15,
'color' => array(0, 0, 0),
'pos-x' => 0,
'pos-y' => 20
);
// user options
$source = (object) array(
'font-size' => 25,
'color' => array(255, 255, 255)
);
// more user options
$moreOptions = (object) array(
'color' => array(128),
'pos-x' => 20,
'pos-y' => 20
);
$o = merge($targets, $source, $moreOptions);
/*
stdClass Object
(
[font-size] => 25
[color] => Array
(
[0] => 128
[1] => 255
[2] => 255
)
[pos-x] => 20
[pos-y] => 20
)
*/
Eh, na potrzeby bloga chciałem zminimalizować kod i wywalić niepotrzebne fajerwerki i doszedłem do tego stopnia, że śmiało można dać:
Oczywiście nie o takie działanie funkcji mi chodziło i już wyjaśniam zasadę działania oryginału. Mamy taką tablicę domyślnych wartości:
2
3
4
'table' => 'teksty',
'columns' => array('tytul', 'tekst', 'status'),
);
Następnie przekazujemy takie dane do nadpisania:
2
3
4
'table' => 'teksty',
'columns' => array('meta_keywords', 'meta_description'),
);
W wyniku dodawania tablic uzyskalibyśmy następującą tablicę:
2
3
4
'table' => 'teksty',
'columns' => array('meta_keywords', 'meta_description', 'status'),
);
Co nie jest prawidłowym wynikiem w przypadku, gdy później pobieramy dane z ‚columns’ i robimy coś z nimi (np. wyświetlamy z bazy takowe kolumny).
Było jeszcze kilka opcji, na które nie zwróciłem uwagi we wpisie – bardziej chodziło mi o samą ideę dostarczania i pobierania danych konfiguracyjnych klasy :-)
@Michał: że też musisz czytać komentarze do funkcji :P poprawione.
Dla mnie to trochę mało intuicyjne, gdyż pozostaje mały problem – co w wypadku, gdy chcemy nadpisać któryś parametr, bez łączenia subtablic?
@eRIZ: racja racja, nie jest to zbyt bogata funkcja, bardziej chodziło mi o samą ideę konfigurowania klasy, coś na wzór jquery.extends (którą to konstrukcję bardzo lubię wykorzystywać).
@eRiz – dla pojedynczej wartości nie ma sens używać tej funkcji, dla większej ilości – już tak.
Późno już, ale czy tak nie działa array_replace_recursive()?
A działa, pod warunkiem że pracujesz na 5.3.