Dziękuję za odzew
Miło wiedzieć, że mój opis Allegro WebAPI na coś się komuś przydał i momo, że moja strona w głównym założeniu nie ma być blogiem, to uważem, że naprawdę brakuje materiałów opisujących tą usługę. Ponieważ po poprzednich dwóch częściach dostałem kilka maili z proźbami o pomoc i objaśnienia sądzę, że dobrze będzie to zrobić publicznie, bo innym też się może przydać, a jak już pisałem w sieci ze świecą szukać jakiejkolwiek pomocy.
Dwa problemy, o które zagadnął mnie 333kylix999 to wyszukiwanie z użyciem doSearch() i pobieranie listy kategorii.
Synchronizacja kategorii
Jako iż ten problem jest dużo łatwiejszy do omówienia to pójdzie na początek. Przedstawię jedynie sposób ściągania listy kategorii - ich zapis do bazy danych to raczej kwestia indywidualna i nie powinno to zresztą sprawić żadnego kłopotu gdy już będziemy mieli dane. Do pobierania kategorii służy wywołanie doGetCatsData() oraz jego inna wersja - doGetCatsDataLimit() (ponieważ kategorii jest bardzo dużo nieraz może się okazać, że trzeba je pobierać częściami). Należy pamiętać, że każdy serwis/kraj ma inne kategorie, dlatego jako parametry do wspomnianych wywołań przekazujemy kraj, który nas interesuje, swój klucz WebAPI, oraz klucz wersji danego serwisu. W odpowiedzi dostajemy listę kategorii w postacii tablicy obiektów. Należy pamiętać tutaj o wspomnianym w poprzednich częściach mankamencie WebAPI - nazewnictwie pól, których to nazwy zawierają myślnik (traktowany przez języki programowania jako minus). Kategorie oczywiście są zagnieżdżane, a nasza lista, którą otrzymamy posiada informacje o kategoriach nadrzędnych. Przyda się więc wygenerować drzewo kategorii. Na początku przedstawie klasę w której będę zamykał informacje dotyczące danej kategorii, oraz ich listy:
/** * @link http://wrzasq.pl/ * @author Wrzasq <wrzasq@gmail.com> * @copyright 2008 (C) by Wrzasq * @license http://www.opensource.org/licenses/bsd-license.php */ class WebAPICategory { /** * ID kategorii. * * @var int */ protected $id; /** * Nazwa kategorii. * * @var string */ protected $name; /** * Kategorie wewnątrz tej kategorii. * * @var array */ protected $children = array(); /** * Tworzy nową kategorię. * * @param int $id ID kategorii. * @param string $name Nazwa kategorii. */ public function __construct($id, $name) { $this->id = $id; $this->name = $name; } /** * Zwraca ID kategorii. * * @return int ID. */ public function getId() { return $this->id; } /** * Zwraca nazwę kategorii. * * @return string Nazwa. */ public function getName() { return $this->name; } /** * Dodaje podkategorię. * * @param WebAPICategory $child Podkategoria. */ public function appendChild(WebAPICategory $child) { $this->children[] = $child; } /** * Zwraca listę podkategorii. * * @return array Podkategorie. */ public function getChildren() { return $this->children; } } /** * @link http://wrzasq.pl/ * @author Wrzasq <wrzasq@gmail.com> * @copyright 2008 (C) by Wrzasq * @license http://www.opensource.org/licenses/bsd-license.php */ class WebAPICategories { /** * Lista kategorii. * * @var array */ protected static $categories = array(); /** * Dodaje kategorię do listy. * * @param WebAPICategory $category Kategoria do dodania. */ public static function addCategory(WebAPICategory $category) { self::$categories[ $category->getId() ] = $category; } /** * Zwraca kategorię o podanym ID. * * @param int $id ID kategorii. * @return WebAPICategory Żądana kategoria. * @todo Jest to tylko poglądowy przykład i zakładam, że w WebAPI kategorie są posortowane, jednak w biznesowych implementacjach potrzebne będzie sprawdzanie, czy żądana kategoria istnieje. */ public static function getCategory($id) { return self::$categories[$id]; } } // dodaje pierwszą - główną kategorię WebAPICategories::addCategory( new WebAPICategory(0, '') );
Teraz wystarczy wczytać listę kategorii, którą otrzymamy w odpowiedzi od Allegro:
$categories = $client->doGetCatsData(WebAPISoapClient::COUNTRY_PL, $version['ver-key'], $config['apiKey']); foreach($categories['cats-list'] as $entry) { // tworzymy nasz wrapper na dane o kategorii $category = new WebAPICategory($entry->{'cat-id'}, $entry->{'cat-name'}); // pobieramy kategorię nadrzędną z listy $parent = WebAPICategories::getCategory($entry->{'cat-parent'}); // dowiązujemy podkategorię $parent->appendChild($category); // dodajemy nową kategorię do listy WebAPICategories::addCategory($category); }
Obiekt kategorii przesyłany w odpowiedzi posiada też pole zaznaczające jego kolejność w liście podkategorii $category->{'cat-position'} jednak do samego mechanizmu drzewa kategorii nie jest to niezbędne, a jego obsługa również nie powinna być zbyt trudna, więc nie chcę tutaj dodawać zbędnych rzeczy. Tak przetworzoną listę kategorii można łatwo przejrzeć zaczynając od kategorii głównej, czyli od WebAPICategories::getCategory(0):
// wypisujemy rekurencyjnie nasze drzewo kategorii function printTree(WebAPICategory $category, $prefix = '') { foreach( $category->getChildren() as $child) { echo $prefix, $child->getId(), ': ', $child->getName(), "\n"; printTree($child, $prefix . ' '); } } printTree( WebAPICategories::getCategory(0) );
Oprócz tego należy pamiętać, że wiele kategorii posiada dodatkowe pola - dlatego jeśli zależy nam na pełnej obsłudze opcji kategorii warto zaznajomić się również z wywołaniem doGetSellFormFieldsExt(), które zwraca listę pól formularza wraz z informacjami o kategorii, której się tyczą.
Wyszukiwarka Allegro
Drugim problemem, który miałem omówić jest wyszukiwanie. Do tego służy wywołanie doSearch(). Jest ono dość proste w działaniu - wysyłamy obiekt formularza z wartościami pól poszczególnych kryteriów, a otrzymujemy listę pasujących aukcji. Aby wywołać doSearch() musimy się najpierw zalogować. Dodatkowo należy pamiętać, że rezultaty wyszukiwania są zwracane po 50 rekordów i nie mamy możliwości zmian wielkości tej tablicy. Obiekt formularza składa się z następujących pól:
- search-string (tekstowe)
- Wyszukiwana fraza.
- search-options (liczbowe)
- Opcje wyszukiwania (domyślna wartośc 0).
- search-order (liczbowe)
- Kryterium sortowania (domyślna wartość 1).
- search-order-type (liczbowe)
- Kierunek sortowania - 0 rosnąco, 1 malejąco (domyślna wartość 0).
- search-country (liczbowe)
- Kraj z którego wyniki mają być uwzględnione (domyślna wartość 0).
- search-category (liczbowe)
- ID kategorii w której chcemy wyszukiwać.
- search-offset (liczbowe)
- Strona wyników (wybiera winyki z zakresu strona * 50 do (strona + 1) * 50 - 1).
- search-city (tekstowe)
- Miasto w którym chcemy szukać.
- search-state (liczbowe)
- Numer województwa.
- search-price-from (zmiennoprzecinkowe)
- Dolny zakres ceny.
- search-price-to (zmiennoprzecinkowe)
- Górny zakres ceny.
Do opcji po raz kolejny przyda nam się kilka nowych wartości stałych w naszej klasie - po pierwsze opcje wyszukiwania (jak zwykle wypadkową flag otrzymujemy przez ich binarne sumowanie):
- Flaga 1
- Wyszukiwanie któregokolwiek z wyrazów szukanej frazy (domyślnie wyszukiwane są wszystkie wyrazy).
- Flaga 2
- Wyszukiwanie także w opisach przedmiotów (domyślnie tylko w nazwach aukcji).
- Flaga 4
- Tylko nowe aukcje.
- Flaga 8
- Tylko aukcje typu "Kup Teraz".
- Flaga 16
- Uwzględnianie także już zakończonych aukcji.
- Flaga 32
- Wyszukiwanie w mieście użytkownika aktualnie zalogowanego (właściciela klucza sesji).
- Flaga 64
- Wyszukiwanie w województwie użytkownika aktualnie zalogowanego (właściciela klucza sesji).
Oraz numery pól do sortowania według:
- 1
- Czasu zakończenia aukcji.
- 2
- Ilości ofert.
- 4
- Ceny.
- 8
- Nazwy.
Tak więc obecnie klasa główna przedstawia się tak:
/** * @see http://www.php.net/manual/en/book.soap.php * @link http://wrzasq.pl/ * @author Wrzasq <wrzasq@gmail.com> * @copyright 2008 (C) by Wrzasq * @license http://www.opensource.org/licenses/bsd-license.php */ class WebAPISoapClient extends SoapClient { /** * Kraj - Polska. */ const COUNTRY_PL = 1; /** * Logowanie do serwisu testwebapi.pl. */ const COUNTRY_TESTWEBAPI = 228; /** * Zapytanie o wersję Allegro WebAPI. */ const QUERY_ALLEGROWEBAPI = 1; /** * Czas trwania - 3 dni. */ const LIFETIME_3DAYS = 0; /** * Czas trwania - 5 dni. */ const LIFETIME_5DAYS = 1; /** * Czas trwania - 7 dni. */ const LIFETIME_7DAYS = 2; /** * Czas trwania - 10 dni. */ const LIFETIME_10DAYS = 3; /** * Czas trwania - 14 dni. */ const LIFETIME_14DAYS = 4; /** * Koszty pokrywa sprzedający. */ const TRANSPORT_COST_SELLER = 0; /** * Koszty pokrywa kupujący. */ const TRANSPORT_COST_BUYER = 1; /** * Przesyłka pocztowa (polecona/paczka). */ const TRANSPORT_OPTION_POST = 1; /** * Przesyłka pocztowa priorytetowa (polecona/paczka). */ const TRANSPORT_OPTION_POSTPRIORITY = 2; /** * Przesyłka kurierska. */ const TRANSPORT_OPTION_COURIER = 4; /** * Odbiór osobisty. */ const TRANSPORT_OPTION_PERSONAL = 8; /** * Inne. */ const TRANSPORT_OPTION_OTHER = 16; /** * Zgadzam się na przesłanie przedmiotu za granicę. */ const TRANSPORT_OPTION_ABROAD = 32; /** * Przedpłata. */ const PAYMENT_OPTION_PREPAID = 1; /** * Pobranie. */ const PAYMENT_OPTION_POSTPAID = 2; /** * Płatności Allegro. */ const PAYMENT_OPTION_ALLEGRO = 4; /** * Bezpieczne Płatności Allegro z Escrow. */ const PAYMENT_OPTION_ESCROW = 8; /** * Bezpieczne Płatności Allegro z Escrow. */ const PAYMENT_OPTION_OTHER = 16; /** * Pogrubienie. */ const OPTION_BOLD = 1; /** * Miniaturka. */ const OPTION_THUMB = 2; /** * Podświetlenie. */ const OPTION_HIGHLIGHT = 4; /** * Wyróżnienie. */ const OPTION_PREMIUM = 8; /** * Strona kategorii. */ const OPTION_CATEGORY = 16; /** * Strona główna. */ const OPTION_HOME = 32; /** * Wyszykiwanie któregokolwiek wyrazu. */ const SEARCH_OPTION_ANY = 1; /** * Wyszukuj w opisach. */ const SEARCH_OPTION_DESCRIPTION = 2; /** * Tylko nowe aukcje. */ const SEARCH_OPTION_NEWONLY = 4; /** * Tylko aukcje "Kup Teraz". */ const SEARCH_OPTION_BUYNOWONLY = 8; /** * Uwzględniaj także zakończone aukcje. */ const SEARCH_OPTION_CLOSEDTOO = 16; /** * Wyszukuj aukcji z miasta użytkownika. */ const SEARCH_OPTION_SESSIONCITY = 32; /** * Wyszukuj aukcji z województwa użytkownika. */ const SEARCH_OPTION_SESSIONSTATE = 64; /** * Sortowanie według daty zakończenia. */ const ORDERBY_ENDING = 1; /** * Sortowanie według ilości ofert. */ const ORDERBY_BIDS = 2; /** * Sortowanie według ceny. */ const ORDERBY_PRICE = 4; /** * Sortowanie według nazwy. */ const ORDERBY_NAME = 8; /** * Sortowanie rosnąco. */ const ORDER_ASC = 0; /** * Sortowanie malejąco. */ const ORDER_DESC = 1; /** * Automatycznie tworzy klienta dla interfejsu Allegro WebAPI. */ public function __construct() { parent::__construct('http://webapi.allegro.pl/uploader.php?wsdl'); } /** * Redukuje obraz do wielkości nadającej się do przesyłu. * * @param string $url URL obrazka (lokalne, albo sieciowe). * @return string Binarna zawartość obrazka w formacie JPEG. */ public static function resize($url) { $image = file_get_contents($url); // właśnie tutaj używamy Base64 ręcznie, ale nigdzie indziej! while( strlen( base64_encode($image) ) > 200000) { $temp = imagecreatefromstring($image); $x = ceil(0.9 * imagesx($temp) ); $y = ceil(0.9 * imagesy($temp) ); $image = imagecreatetruecolor($x, $y); imagecopyresized($image, $temp, 0, 0, 0, 0, $x, $y, imagesx($temp), imagesy($temp) ); imagejpeg($image, 'temp.jpg', 75); $image = file_get_contents('temp.jpg'); unlink('temp.jpg'); } return $image; } }
Odpowiedź na nasze zapytanie zawierać będzie trzy pola:
- search-count (liczbowe)
- Ilość znalezionych przedmiotów (wszystkich, nie tylko na aktualnej stronie).
- search-count-featured (liczbowe)
- Ilość przedmiotów wyróżnionych (przedmioty wyróżnione są zwracane przed pozostałymi).
- search-array (tablicowe)
- To pole zawiera tablicę obiektów znalezionych aukcji.
Teraz wystarczy stworzyć obiekt formularza, przypisać wartości jego polom i wyślać wywołaniem doSearch(). Przedtem oczywiście się zalogować. Używając przykładu z części pierwszej nasz kod wyglądałby mniej więcej tak (przykładowo szukamy aukcji zawietających wyrazy GeForce lub Radeon w naszym mieście do 300 PLN, wyniki posortujemy według cen):
// właściwe logowanie do serwisu $session = $client->doLogin($config['login'], $config['password'], $country, $config['apiKey'], $version['ver-key']); // generujemy formularz $form = new StdClass(); $form->{'search-string'} = 'GeForce Radeon'; $form->{'search-options'} = WebAPISoapClient::SEARCH_OPTION_ANY | WebAPISoapClient::SEARCH_OPTION_SESSIONCITY; $form->{'search-order'} = WebAPISoapClient::ORDERBY_PRICE; $form->{'search-order-type'} = WebAPISoapClient::ORDER_ASC; $form->{'search-country'} = 0; // 0 oznacza, że wyszukujemy w całym serwisie - ID kategorii możemy pobrać z poprzedniego omówienia problemu importu kategorii $form->{'search-category'} = 0; $form->{'search-city'} = ''; $form->{'search-state'} = 0; $form->{'search-price-from'} = 1; $form->{'search-price-to'} = 300; // strona wyników liczona od 0 $form->{'search-offset'} = 0; echo '<table> <thead> <tr> <td>ID</td> <td>Nazwa</td> <td>Cena</td> </tr> </thead> <tbody>'; // listujemy wszystkie wyniki do { // wysyłamy formularz $search = $client->doSearch($session['session-handle-part'], $form); $form->{'search-offset'}++; // listujemy aktualną stronę foreach($search['search-array'] as $auction) { echo ' <tr> <td>', $auction->{'s-it-id'}, '</td> <td><a href="http://allegro.pl/item', $auction->{'s-it-id'}, '.html">'; // sprawdza, czy aukcja ma miniaturkę do wyświetlenia if( !empty($auction->{'s-it-thumb-url'}) ) { echo '<img src="', $auction->{'s-it-thumb-url'}, '" alt="thumbnail" />'; } echo htmlspecialchars($auction->{'s-it-name'}), '</a></td> <td>'; // cena aukcyjna if($auction->{'s-it-price'} > 0) { echo htmlspecialchars($auction->{'s-it-price'}, 2, ',', ' '); } // cena "Kup Teraz" else { echo htmlspecialchars($auction->{'s-it-buy-now-price'}, 2, ',', ' '); } echo '</td> </tr>'; } } // sprawdzamy czy są następne strony while( $form->{'search-offset'} * 50 < $search['search-count']); echo ' </tbody> </table>';
Dokładny opis struktury obiektu pojedynczej aukcji znajduje się tutaj. Nie sposób opisać w prostym przykładzie wszystkie jego pola, a na szczęście ich zastosowanie jest raczej dośc oczywiste.
Tagi: php, kod, soap, allegro, webapi, tutorial.
Aby pisać komentarze musisz być zalogowany.
333kylix999 (sobota, 27 wrzesień 2008 - 20:46:34)
Re: Allegro WebAPI tutorial vol. 3
Ta wiedza jest bezcenna, dziekujemy mistrzu. Jak patrze na allegro to przypomina mi jakis bazar jak stadion dziesieciolecia w warszawie, coz stadion ten juz rozbieraja... pozdrawiam Kuba
Arek (czwartek, 14 lipiec 2011 - 21:41:51)
Re: Allegro WebAPI tutorial vol. 3
witam mam pytanie odnośnie fragmentu $parent = WebAPICategories::getCategory($entry->{'cat-parent'}); $parent->appendChild($category); na testowum api działał ale był problem bo wyświetlał wszystkie kategorie natomiast po podpięciu się pod allegro wywala błąd Fatal error: Call to a member function appendChild() on a non-object in /home/fdg/public_html/allegro.php on line 230 z czego może to wynikać?
aleksy (sobota, 16 lipiec 2011 - 12:55:58)
Re: Allegro WebAPI tutorial vol. 3
Witam, Po pierwsze - dzięki za rzetelne opracowanie tematu. Po drugie - mam pytanie :-) Czy w swojej aplikacji mogę polegać na identyfikatorach kategorii? Szerzej - w bazie danych aplikacji trzymam informacje o przedmiotach z aukcji, wraz z numerem kategorii. Np kategoria Motoryzacja ma ID == 3. Czy mogę założyć, że po uaktualnieniu drzewa kategorii Motoryzacja nadal będzie miała ID == 3? Dzięki, pozdrawiam Aleksy