Although I’d normally write this article in Romanian I expect a wider audience for it, so I will post it in English.

Snippet

If you don’t want to read the entire article here are some shortcuts:

Dance-Tunes.com

Dance Tunes Logo

Dance-Tunes is a music online store, located in the Netherlands, where one can buy their favorite … dance tunes, of course. Organised in eight main genres (Downtempo, Dance, Techno/Minimal, Hardcore, Breaks, Electro/House, Hardstyle/Jump and Trance) Dance-Tunes has an extensive database of tunes, artists and labels.

The Public API

On the 22nd of February 2010, Dance-Tunes released their 1.0 version of the API. The main purpose of the API is to help create rich internet applications that are based on the information of tunes, artists, labels or charts. Developers are encouraged to use the public Dance-Tunes API to get creative and use the extensive library of labels, artists and tracks in their sites or applications.

Dance-Tunes has created a special page for developers where one can find all information about the public API. Each individual API call is explained and examples are given to show the correct structure of the calls and response. One can request an API key by logging in using the Dance-Tunes account details. The API key will be sent to the developer’s email address. One can download the API documentation in pdf format for printing and off-line viewing.

The PHP Client

The Dance-Tunes PHP API Client that I am releasing today, was written with intention of integrating into a Zend Framework application. As of this it follows the PHP File Formatting, Naming Conventions and Coding Style recommended by the Zend Framework Team.

It uses the Zend_Http and Zend_Exception packages from the Zend Framework classes. It can be used either with an entire Zend Framework application or as is. The dependencies can be load by themselves (just the 2 packages), or the Dancetunes class can be modified in order to be used in a simple PHP project. One can replace the use of Zend_Http package with the use of the cURL library and the Zend_Exception with the PHP base Exception.

Usage

Each of the documented function in the Dance-Tunes API has a corespondent wrapper method in the Dancetunes class that accepts the exact amount of parameters that the API requires, except the command and the session(params cmd and session) which are built in. For each session key, as the documentation specifies, a timestamp is updated on each request, and the session key is deleted if no request is performed for 6 hours. So, as a best practice, it is recommended to cache the session key locally for immediate access to the resources on other requests.

$dancetunes = new Dancetunes(DT_API_KEY);

if (!($sessionKey = $cache->load('sessionkey')))
{
    $dancetunes->setUserName(DT_USERNAME);
    $dancetunes->setPassword(DT_PASSWORD);

    $sessionKey = $dancetunes->authenticate();
    $cache->save($sessionKey, 'sessionkey');
}
$dancetunes->setSessionKey($sessionKey);

/* Get the first 10 releases */
$releases = $dancetunes->getNewReleases(11, 10, 1, 10);

/* Get a preview */
$audio = $dancetunes->getPreview( 123456 );
/* saving the audio content to disc */
file_put_contents('123456-tune-preview.mp3', $audio);

Responses

Dance-Tunes API will respond either with an XML string or with binary data (for artwork and preview). For the case of XML response I have created a Dancetunes_Vo class that takes a XML string and converts it into an array. Also for each response type I have created an individual class that extends the Dancetunes_Vo class and implements some particular methods for a specific result. For example the Dancetunes_NewReleases class will be the result of the Dancetunes::getNewReleases method and has a method for returning just the tunes info from the response, and another one to return the number of total possible tunes that can be returned on a specific request.

class Dancetunes_NewReleases extends Dancetunes_Vo {

    public function getTracks() {
        return $this->_array ['new_releases'] ['tune'];
    }

    public function getCount() {
        return $this->_array ['new_releases'] ['total_count'];
    }
}

These classes can be modified or extended in order to deal in a specific way with the XML response.


License

The package is released as an open source project under The New BSD License.

Copyright (c) 2010, George Enciu, ColorSoft System SRL, Romania

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the ColorSoft System SRL nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Change Log

2010-04-06
  • Version 0.1 BETA released
2010-05-15
  • Added a version written in simple PHP
  • Still in 0.1 Beta

Download

All the code and download files can be found at the Google Code Project

“Best practice”

Vom începe scrierea unei serii de articole menite să promoveze diverse practici de programare sau utilizare corectă a limbajului PHP, MySQL, Javascript sau a framework-ului de la Zend. Ne vom adresa prin acestea în special începătorilor şi vom încerca să arătăm consecintele unei folosiri incorecte a diverselor facilităţi oferite de limbajele de mai sus.

Paginare datelor este un mijloc eficient de a reduce datele raportate unui utilizator la o secţiune de interes local în ideea de a economisi timp de execuţie, spaţiu de afişare şi se a crea nişte raportări prietenoase. Exemplul cel mai simplu în reprezintă afişarea unor produse dintr-un magazin online pe … pagini, lucru care are avantajul că pe de partea clientului nu încarcă excesiv pagina utilizatorului (un magazin poate avea câteva sute de produse, poate chiar mii) iar pe partea serverului nu solicită nici baza de date dar nici procesările făcute după ce un set de date a fost returnat aplicaţiei, toate acestea făcându-se asupra unui set restrans de date.

Introdus in versiunea 1.6 a Zend Framework, modulul Zend_Paginator, aşteptat încă din versiunea 0.9, se vrea a fi o componentă flexibilă pentru paginarea colecţiilor de date şi prezentarea lor către utilizatori. Principalele avantaje ale acestui modul sunt posibilitatea de a pagina orice set de date, nu doar cele provenite din baza de date, aduce si prelucreaza doar setul de date care urmează a fi afişat nu asupra tuturor datelor, plus că fiind felxibil nu limitează programatorii doar la folosirea lui şi într-un anume mod, dar oferă totodată posibilitatea de folosirea a lui din alte componente ale framework-ului precum Zend_View sau Zend_Db.

Folosirea paginatorului este relativ simplă, şi se foloseşte de ideea că pentru a putea pagina un set de date, în orice format ar fi el, este nevoie de un adaptro specific pentru a putea interacţiona cu acele date. Astfel, pentru a putea pagina un vector, se poate folosi un array simplu din PHP, pentru a pagina un set de date returnate în urma unei interogări a bazei de date se foloseşte o instanţă a Zend_Db_Select, iar pentru a pagina un iterator se va folosi o instanşă a acestei clase din SPL. Pentru a începe folosirea paginatorului se poate instanţia acesta folosind adaptorul dorit, sau se poate folosi metoda statică factory():


$array = array(‘Luni’, ‘Marti’, ‘Miercuri’, ‘Joi’, ‘Vineri’, ‘Sambata’, ‘Duminica’);
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($array));
$paginator = Zend_Paginator::factory($array);

În acest moment paginatorul are nevoie de o referinţă pentru a putea determnia şi prelucra secţiunea care va fi afişată utilizatorului, şi anume pagina, informaţie care de regula se stochează într-un parametru al URL-ului(implicit va fi considerată pagina 1). Totodată se poate specifica şi numărul de elemente care vor apărea intr-o pagină, acest număr este implicit 10.


$paginator->setCurrentPageNumber($this->_request->getParam(‘page’));
$paginator->setItemCountPerPage(20);

În final paginatorul se atribuie unei variabile din partea vizuală a aplicaţiei …


$this->view->paginator = $paginator;

iar apoi va fi afişată


paginator as $element): ?>


Daca dorim paginarea unor date provenite din baza de date, cu siguranţă că va fi mai eficient ca interogarea făcută către baza de date să returneze doar acele date din pagina care ne interesează. Pentru acest lucru vom instanţia paginatorul folosind un obiect de tipul Zend_Db_Select, care va conţine o instrucţiune select personalizată pentru nevoile noastre.


class My_Model
{
public static function getCategoryList($category_id, $page = 1)
{
$databaseAdaptor = Zend_Registry::get('db');
try
{
$select = new Zend_Db_Select($databaseAdaptor);
/*$select = $databaseAdaptor->select();*/
$select->from('products', array('id', 'name', 'price'))
->where('category_id = ?', $category_id)
->order('name asc')
;

$paginator = Zend_Paginator::factory($select);
$paginator->setCurrentPageNumber($page);
$paginator->setItemCountPerPage(10);

return $paginator;

}
catch (Zend_Exception $ex)
{
Zend_Debug::d ump($ex);
return null;
}
}
}

Am creat asadar un model care va returna din baza de date toate produsele dintr-o anumită categorie, sortate după nume, folosind un obiect Zend_Db_Select instanţiat folosind un adaptor pentru baza de date, care a fost preluat din registru. Rezultatul unei astefel de abordări poate fi procesat într-un controller după cum urmează:


class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
Zend_Loader::loadClass('My_Model');
$this->view->paginator = My_Model::getNewsletterList($this->_request->getParam('category_id'), $this->_request->getParam('page'));
}
}

Un alt aspect important al paginării este capacitatea acestei componente de a genera un nagivator printre “paginile” astfel determinate ale paginării, deci în ultimă instanţă posibilitatea de a avea acces secvenţial la tot setul de informaţii. Zend_Paginator furnizeată unul chiar interesant, datorită facilităţilor ce le oferă.


echo $this->paginationControl($this->paginator, 'Sliding', 'pagination_control.phtml');

Deşi ultimii doi parametri sunt opţionali, iar controlul ar putea funcţiona perfect fără aceste două informaţii, aceştia pot influenţa foarte mult modul în care acest index al navigării este construit. Ele se referă la, şi influenţează modul în care navigatorul generat va arăta şi comporta. Vom considera, pentru exemplificare, că navigatorul generat de Google la o căutare ar fi generat chiar de către Zend_Paginator.

Google Slider

Cel de-al doilea parametru controlează ce se întamplă daca acţionăm linkul “Precedentă” sau “Următoarele” şi poate avea urmatoarele valori

  • All caz în care va afişa toate paginile existente, lucru util pentru momentele când numărul paginilor rezultate este relativ scăzut
  • Elastic caz în care paginile se lărgesc şi se contractă în funcţie de poziţia în paginare şi numărul de pagini – gen Google
  • Jumping caz în care pe măsură ce utilizatorul trece de la o pagină la alta, pagina curentă se muta catre finalul unui interval de pagini, la finalul caruia va continua cu un interval nou
  • Sliding caz în care pagina curentă va fi afişată în centrul intervalului de pagini afişat, sau cel mai aproape posibil (gen Yahoo!); acesta este valoarea implicită

Cu toate acestea însă programatorul nu este restricţionat la acest set de posibilităţi, oferindui-se posibilitatea de a-si crea singur porpiul paginator. Pentru acest lucru sunt disponibile o serie de metode predefinite:

  1. first() — numărul primei pagini
  2. firstItemNumber() — numărul primei înregistrari din pagină în setul general de date
  3. firstPageInRange() — numărul primei pagini din setul curent
  4. current() — numărul paginii curente
  5. currentItemCount() — numărul de elemente afişate pe pagina curentă
  6. last() — numărul ultimei pagini
  7. lastItemNumber() — numărul ultimei înregistrari din pagină în setul general de date
  8. lastPageInRange() — numărul ultimei pagini din setul curent
  9. next() — numărul paginii următoare
  10. pageCount() — numărul total de pagini
  11. pagesInRange() — paginile aflate în setul de pagini
  12. previous() — numărul paginii precedente
  13. totalItemCount() — numărul total de elemente

Prin setul curent de pagini, ne-am referit la paginile vizibile la o afişare.