2013-08-22

Iterator Modelu w CakePHP

Zadziwiające jak często stajemy przed problemem przeiterowania przez wszystkie rekordy jakiegoś modelu (np. przy generowaniu raportów). Najefektywniejszym rozwiązaniem jest zastosowanie interfejsu iteratora. Uniwersalny przykładem dla CakePHP może być ModelIterator. Klasę umieszczam w katalogu Model/Iterator
 
/**
 * Iterates Model
 *
 * @author gmotyl
 */
class ModelIterator implements Iterator{
  
  protected $key;
  protected $index;
  protected $count;
  protected $conditions;
  protected $Model;

  /**
   * Class constructor
   * 
   * @param array $conditions CakePHP find conditions
   */
  public function __construct($modelName, $conditions = NULL) {
    App::import('Model', $modelName);
    
    $this->Model = new $modelName;
    $this->conditions = $conditions;
    $this->count = $this->Model->find('count', array('conditions' => $conditions));
  }

  /**
   * Returns current row
   * 
   * @return array
   */
  public function current() {
    return $this->Model->find('first', array('conditions' => $this->conditions, 'page' => $this->index));
  }  
  
  /**
   * Increments row
   */
  public function next() {
    $this->index++;
  }
  
  /**
   * Returns index
   * 
   * @return int
   */
  public function key() {
    return $this->index;
  } 
 
  /**
   * Rewind to first index
   */
  public function rewind() {
    $this->index = 1;
  }
  
  /**
   * Returns FALSE if iteration is over
   */
  public function valid() {
    return ($this->count >= $this->index);
  }
}
Dodatkowo wygodnie będzie zaimplementować interfejs IteratorAggregate w AppModel (po którym dziedziczą wszystkie Modele w CakePHP:


App::uses('ModelIterator', 'Model/Iterator');

class AppModel extends Model implements IteratorAggregate{

(...)

  public function getIterator() {
    return new ModelIterator($this->name);
  }

(...)
Dzięki temu możemy w dowolnym miejscu użyć kostrukcji foreach, która przeiteruje się przez wszystkie elementy Modelu bez konieczności jawnego instancjonowania iteratora np. w kontrolerze UsersController :


    foreach ($this->User as $user) {
      debug($user);
    }   

2013-04-25

require.js i CakePHP

Jak w prosty sposób zaimplementować require.js w CakePHP?

Po ściągnięciu require.js wraz ze wszystkimi jego zależnościami do app/webroot/js

wrzucamy do layoutu kod:

<?php
  echo $this->Html->script( 
            'require', array(
            'data-main' => $this->Html->assetUrl('main', array(
            'pathPrefix' => JS_URL,
            'ext' => '.js'
          ))
          ));
?>

następnie w pliku app/webroot/js/main.js możemy includować poszczególne biblioteki np:

require(
  [
    "jquery/jquery-ui-1.8.23.custom",
    'jquery-ui-timepicker-addon',
    'underscore/underscore-min',
    'backbone/backbone-min',
    'bootstrap.min'
  ], 
  function($) {
    requireReadyFunction();
  }
);

2013-03-06

Testy jednostkowe - Instalacja PHPUnit

Zanim ochoczo zabiorę się do instalacji PHPUnita najpierw warto przypomnieć: po co to komu? :) Testy jednostkowe (ang. unit test) służą do częstego weryfikowania poprawności działania pojedynczych elementów programu takich jak np. obiekty. Przykładowo gdy piszemy klasę modelu korzystając z jakiegoś frameworka PHP, warto napisać dla niej Unit testy. Dzięki temu po każdej zmianie kodu aplikacji, możemy zyskać pewność, że kod działa poprawnie bez żmudnego manualnego testowania. Tak więc poddajemy fragment programu testowi, który wykonuje go i porównuje wynik (np. zwrócone wartości, stan obiektu, wyrzucone wyjątki) z wynikami oczekiwanymi. 
Co nam to daje, poza wysiłkiem implementacji test casów? Otóż zyskujemy możliwość błyskawicznego wychwycenia i poprawienia błędu - zanim jeszcze zostanie wprowadzony na środowisko produkcyjne.

OK to tyle jeśli chodzi o teorię - pora na praktykę. A w praktyce... cóż PHPUnit żądzi i basta :) Jest to swego rodzaju złoty standard w temacie testowania kodu PHP. Ale jak go zainstalować?

Zwyczajnie :) PEARem:

sudo pear config-set auto_discover 1
sudo pear install pear.phpunit.de/PHPUnit

W idealnym przypadku wystarczy następnie zainstalować PHPUnit
sudo apt-get install phpunit

i już można rozpocząć testy...
... niestety wielu użytkowników ubuntu napotka tu pierwsze schody, mi też się to przytrafiło. Po napisaniu przykładowego testu zabrałem się ochoczo do uruchomienia testu... lecz próba uruchomienia z terminala phpunit powodowała tylko komunikat:

PHP Fatal error: Call to undefined method PHP_CodeCoverage_Filter::getInstance() in /usr/bin/phpunit on line 39


Okazuje się, że niektóre wersje Ubuntu zawierają błąd na szczęście łatwo go samodzielnie naprawić uruchamiając po kolei komendy:

sudo pear config-set bin_dir /usr/bin
sudo pear config-set doc_dir /usr/share/php/doc
sudo pear config-set php_dir /usr/share/php
sudo pear config-set cfg_dir /usr/share/php/cfg 
sudo pear config-set data_dir /usr/share/php/data
sudo pear config-set test_dir /usr/share/php/test
sudo pear uninstall phpunit/PHPUnit
sudo pear channel-discover pear.phpunit.de
sudo pear install --alldeps phpunit/PHPUnit

2013-01-08

Odroid U2 - juz tydzień "zabawy"

Po tygodniu obcowania z odroidem U2 muszę stwierdzić - fajna zabawka ale raczej dla dużych dzieci... i to najlepiej z backgroundem informatycznym i ogromną dozą cierpliwości :) Jest to produkt developerski i jako taki nie pozbawiony błędów, nadal rozwijany. Nowe wersje softu wydawane są przez producenta co kilka dni. Na szczęście support na forum społeczności jest bardzo sprawny i nikt nie jest pozostawiany bez pomocy. Ja w pierwszym tygodniu użytkowania skupiłem się na konfiguracji androida - gdyż dla mnie Odroid ma pełnić funkcję HTPC oraz konsoli do gier. Z linuxem poczekam jeszcze parę dni, gdy większość bugów zostanie rozwiązanych. Pewną bolączką wydawanych nowych wersji softu Hardkernela jest konieczność flushowania całej karty pamięci i co za tym idzie utrata wszystkich ustawień i aplikacji. Gdy się to robi raz czy kilka razy dziennie - przestaje być już zabawne... dlatego cierpliwie poczekam.

Cierpliwością muszą się również wykazać domownicy użytkownika Odroida... Mam w domu jeden TV z wejsciem HDMI (stary monitor DVI się nie nadaje), dlatego podziwiam moją rodzinę, która cierpliwie znosiła moje klikanie bezprzewodową klawiaturą, ciągle restarty, gorączkowe ściąganie ROMu, przekładanie kart eMMC z odroida do czytnika, flash, itd. itd....
Przez ten tydzien przetestowałem pierwszą nieudaną wersję ICS, nstępnie JB w wersji od hardkernela i Cyanogenmod dla Androida. Ostatecznie zostałem przy najnowszym ICS gdzie jest zdecydowanie mniej bugów...

W obecnej chwili mam dwa zasadnicze problemy do rozwiązania. Odroid nie widzi mojego dysku USB 2.5 cala 1 TB Verbatim, problem z emulacją dotyku za pomocą myszki.

Trzeci problem z nie działającym padem bezprzewodowym XBOX360 rozwiązałem wpisując w terminal emulator:

su
mount -o remount,rw /system

Ponieważ niestety komenda cp nie działa... użyłem cat :)
cat /system/usr/keylayut/Vendor_045e_Product_028e.kl > Vendor_045e_Product_0719.k
chmod 644 /system/usr/keylayout/Vendor_045e_Product_0719.kl
reboot
Rozwiązanie to opiera się na tym, że w androidzie jest profil dla pada przewodowego - brakuje jedynie dla bezprzewodowego. Proste skopiowanie profilu pod inną nazwą rozwiązało problem.

2013-01-02

Odroid U2

W ubiegły poniedziałek nareszcie dotarł z korei mój developerski egzemplarz odroid U2. Wspaniały początek nowego roku :)
Ponieważ zamierzam używać go również jako HTPC z zasilanym z USB dyskiem 1TB, zamówiłem już wcześniej w polsce silniejszy 3A zasilacz (koreańczycy oferują standardowo 2A - a to prowadzi do niedoboru mocy w przypadku dużego obiążenia). Z duszą na ramieniu dokonałem pierwszego odpalenia... a tu nic, nie reaguje. Na szczęście po dociśnięciu wszystkich styków zasilacza wkońcu odpalił... uff :)

System (Android, ale można zaintalować również arm'ową wersję ubuntu) odpalił się dosłownie w kilka sekund. 4rdzeniowy procek samsunga w połączeniu z ultra szybką kartą eMMC robią swoje :)
Do szczęścia brakowało mi jeszcze aplikacji googla (głównie play store), których koreańczycy nie mogli dołączyć do dystrybucji ze względu na licencję. Na szczęście instalacja jest trywialna:

1. w pierwszym kroku należy się upewnić że SDK androida jest zainstalowane w systemie.
2. teraz pora ściągnąć sterownik odroida dla windows z forum społeczności hardkernela i można już podłączyć PC do portu micro USB maszynki
4. teraz z konsoli, gdzie rozpakoaliśmy archiwum należy wykonać polecenia:
adb remount
adb push system /system
adb reboot

Po zrestartowaniu odroid ma już dostęp do sklepu googla i możemy zacząć zabawę.... ,która niestety szybko się kończy... image androida aktualnie zawiera jakiś błąd i po kilkudziesięciu minutach maszyna się zawiesi, po kolejnym restarcie już nie wstaje. Po zflashowaniu karty "świerzym" obrazem,  system sie uruchamia ale ponownie się szybko zawiesi. Producent zna problem i aktualnie pracuje nad rozwiązaniem. Tymczasem więc "pobawię" się linuxem - który ma niestety jedną wadę - brak sprzętowej akceleracji dla procesora graficznego Mali (na Androidzie śmiga jak burza)... więc gdy np. chcemy obejrzeć film, całość obliczeń bierze na klatę 4 rdzeniowy proc... no ale przynajmniej jest stabliny :)