<?php append_to_slot('javascript'); ?>
$(function() {
$("#date_of_birth").datepicker();
});
<?php end_append_to_slot(); ?>
2012-09-05
użycie JQuery datepicker w symfony
Kilka dni temu na stackOverflow ktoś pałował się z użyciem w symfony sfWidgetFormDateJQueryUI niestety bez rezultatu... rozwiązaniem (jak się okazało skutecznym), które zaproponowałem było zastosowanie standardowego widgeta textowego z symfony (z id="date_of_birth") i dodanie w templatce widoku do slotu javascript kodu jQuery:
2012-08-21
Follow the white RabbitMQ - czyli rozproszone przetwarzanie zadań
Często gdy przetwarzanie pewnych zadań w PHP po akcji użytkownika zajmuje cenny czas i zmusza do oczekiwania na ponowne wyrenderowanie widoku, warto wydelegować je do zewnętrznego workera. Typowym przykładem jest np. wysyłka wiadomości e-mail po zarejestrowaniu użytkownika, czy generowanie plików pdf. Można w tym celu posłużyć się taskami wywoływanymi z CRONa, ale zwłaszcza w systemach o większej skali, to rozwiązanie nie jest wystarczające.
Zacznijmy od instalacji króliczka, która na moim developerskim ubuntu była bardzo prosta:
w php.ini uzupełniamy:
UWAGA użytkownicy DEBIANA! Powyższe kroki były wystarczjące gdy instalowałem amqp pod Ubuntu, jednak podczas próby powtórzenia ich pod debianem squeeze wystąpiły problemy z instalację z pecl'a (komunikat "configure: error: Please reinstall the librabbit-mq distribution")
Rozwiązaniem jest wykonanie:
git clone git://github.com/alanxz/rabbitmq-c.git
cd rabbitmq-c
git submodule init
git submodule update
autoreconf -i
./configure
make
sudo make install
Naturalnie użytkownicy debiana squeeze mają do dyspozycji mega starą wersję servera rabbitmq, z którą nie będzie chciał gadać AMQP. Dlatego konieczne będzie wykonanie dodatkowych kroków:
Jeśli już zainstalowałeś starego rabbita - usuń go, aby uniknąć kolejnych przykrości :)
sudo apt-get remove rabbitmq-server --purge
następnie update i instalacja backportu:
sudo apt-get update
sudo apt-get -t squeeze-backports install rabbitmq-server
Teraz należy skonfigurować RabbitMQ. Służy do tego konsolowy narząd rabbitmqctl, który dostajemy w paczce z królikiem. Po szczegóły odsyłam do dokumentacji rabbita, która w odróżnieniu od tej z PHP.net jest bardzo dobra i szczegółowa.
Dodajemy usera i host wirtualny, oraz ustawiamy uprawnienia:
sudo rabbitmqctl add_user butterfly butterfly
Warto jeszcze (przynajmniej na początku) włączyć logowanie dla naszego hosta i na jednej z konsol odpalić podgląd logu:
Jesteśmy już gotowi do napisania klasy PHP, która będzie się komunikowała protokołem AMQP z serverem RabbitMQ:
Godnym polecenia systemem, który rozwiąże problem jest RabbitMQ.
Integracja z PHP okazuje się nie być tak banalna jak by się to mogło wydawać (a to głównie z uwagi na delikatnie mówiąc kulejącą dokumentację biblioteki AMQP).
Zacznijmy od instalacji króliczka, która na moim developerskim ubuntu była bardzo prosta:
sudo apt-get install rabbitmq-server
sudo pecl install amqp
w php.ini uzupełniamy:
extension=ampq.so
UWAGA użytkownicy DEBIANA! Powyższe kroki były wystarczjące gdy instalowałem amqp pod Ubuntu, jednak podczas próby powtórzenia ich pod debianem squeeze wystąpiły problemy z instalację z pecl'a (komunikat "configure: error: Please reinstall the librabbit-mq distribution")
Rozwiązaniem jest wykonanie:
git clone git://github.com/alanxz/rabbitmq-c.git
cd rabbitmq-c
git submodule init
git submodule update
autoreconf -i
./configure
make
sudo make install
Naturalnie użytkownicy debiana squeeze mają do dyspozycji mega starą wersję servera rabbitmq, z którą nie będzie chciał gadać AMQP. Dlatego konieczne będzie wykonanie dodatkowych kroków:
Jeśli już zainstalowałeś starego rabbita - usuń go, aby uniknąć kolejnych przykrości :)
sudo apt-get remove rabbitmq-server --purge
następnie dodaj do /etc/apr/sources.list linijkę:
deb http://backports.debian.org/debian-backports squeeze-backports main
następnie update i instalacja backportu:
sudo apt-get update
sudo apt-get -t squeeze-backports install rabbitmq-server
Teraz należy skonfigurować RabbitMQ. Służy do tego konsolowy narząd rabbitmqctl, który dostajemy w paczce z królikiem. Po szczegóły odsyłam do dokumentacji rabbita, która w odróżnieniu od tej z PHP.net jest bardzo dobra i szczegółowa.
Dodajemy usera i host wirtualny, oraz ustawiamy uprawnienia:
sudo rabbitmqctl add_user butterfly butterfly
sudo rabbitmqctl add_vhost butterfly
sudo rabbitmqctl set_permissions -p butterfly butterfly ".*" ".*" ".*"
Warto jeszcze (przynajmniej na początku) włączyć logowanie dla naszego hosta i na jednej z konsol odpalić podgląd logu:
sudo rabbitmqctl trace_on -p butterfly
tail -f /var/log/rabbitmq/rabbit@Butterfly.log
Jesteśmy już gotowi do napisania klasy PHP, która będzie się komunikowała protokołem AMQP z serverem RabbitMQ:
/**
* Class for AMQP Connections
*
* @author gmotyl
*/
class AMQPConnector {
//Broker login credentials
protected $login = "butterfly"
protected $password = "butterfly";
protected $vhost = "butterfly";
//amqp connection variables
private $amqpConnection;
private $queue;
private $exchange;
private $routingKey;
/**
* Initializes amqpConnection and sets up queue and exchange
*
* @param string $exchangeName
* @param string $routingKey
* @param string $queueName
*/
public function __construct($exchangeName, $routingKey, $queueName)
{
$this->amqpConnection = $this->connect();
$channel = new AMQPChannel($this->amqpConnection);
$this->exchange = new AMQPExchange($channel);
$this->exchange->setName($exchangeName);
$this->exchange->setType(AMQP_EX_TYPE_DIRECT);
$this->queue = new AMQPQueue($channel);
$this->queue->setName($queueName);
$this->queue->declare();
$this->queue->bind($exchangeName, $routingKey);
$this->routingKey = $routingKey;
}
public function __destruct()
{
if(!$this->amqpConnection->disconnect()) {
throw new Exception("Could not disconnect !");
}
}
/**
* Connects to broker
*
* @return \AMQPConnection
*/
protected function connect()
{
$amqpConnection = new AMQPConnection();
$amqpConnection->setLogin($this->login);
$amqpConnection->setPassword($this->password);
$amqpConnection->setVhost($this->vhost);
$amqpConnection->connect();
if(!$amqpConnection->isConnected()) {
die("Cannot connect to the broker, exiting !");
}
return $amqpConnection;
}
/**
* Returns Queue object
*
* @return \AMQPQueue
*/
public function receiveQueue()
{
return $this->queue;
}
/**
* Publish a message to the exchange
* Returns TRUE on success or FALSE on failure.
*
* @param string $text
* @return bool
*/
public function sendMessage($text)
{
return $this->exchange->publish($text, $this->routingKey);;
}
}
2012-08-20
Backone.js dla bezkręgowców cz.4 - Bindowanie zdażeń
Pora na rozszerzenie klasy widoku o kod, który doda stworzone zadania do listy w HTMLu. W tym celu piszemy metodę insertTask, która za pomocą prostego jQuerowego kodu doda nowy element <li> do listy zadań. Zostało już tylko podbindowanie tej metody pod zdarzenie add kolekcji. Od tej pory zawsze gdy kolekcjia zwiększy się o kolejny model - jego reprezentacja zostanie zwizualizowana w HTMLu przez naszą klasę widoku:
To było bardzo podstawowe wprowadzenie do framworka backbone.js, jeśli chcesz lepiej poznać tą technologię - zachęcam do lektury dokumentacji na backbonejs.org
Z doświadczenia wiem, że framework ten bardzo upraszcza bardziej skomplikowane projekty. Szczególnie jest to widoczne, podczas implementacji części funkcjonalności, która (przynajmniej częściowo) wpisuje się w definicję single-page application, gdzie kodu JSowego może być dość dużo.
Wszystkie kody z tego tutka są dostępne publicznie w repo bitBucket:
$(function() {
TaskEditView = Backbone.View.extend ({
initialize: function() {
console.log('Initializing task list view');
this.collection.bind('add', this.insertTask);
},
(...)
insertTask: function(model) {
var item = $('<li>')
.html(model.get('name'));
$('#taskList').append(item);
}
});
To było bardzo podstawowe wprowadzenie do framworka backbone.js, jeśli chcesz lepiej poznać tą technologię - zachęcam do lektury dokumentacji na backbonejs.org
Z doświadczenia wiem, że framework ten bardzo upraszcza bardziej skomplikowane projekty. Szczególnie jest to widoczne, podczas implementacji części funkcjonalności, która (przynajmniej częściowo) wpisuje się w definicję single-page application, gdzie kodu JSowego może być dość dużo.
Wszystkie kody z tego tutka są dostępne publicznie w repo bitBucket:
git clone https://bitbucket.org/gmotyl/backbone_tutorial.git
git clone git@bitbucket.org:gmotyl/backbone_tutorial.git
2012-08-15
Backone.js dla bezkręgowców cz.3 - Kolekcja
Kolekcja w Backbone to zwyczajnie lista modeli. W naszym przykładzie potrzebujemy modelu Task reprezentującego pojedyńcze zadanie z listy zadań. Kolekcja TaskCollection będzie zawierała listę zadań:
Na końcu metody showPrompt dodaliśmy wywołanie createNewTask, która uzupełni kolekcję. Służy do tego metoda add kolekcji, jako parametr podajemy model. W tym celu stworzyliśmy instancję klasy Task, wstrzykując w nią podane przez użytkownika zadanie. Na końcu metody w celach szkoleniowych zapisujemy w konsoli Jsonową reprezentację kolekcji.
Teraz gdy podamy dwa kolejne zadania jako "Napisać kolejny odcinek tutka" i "Skosić trawnik", na konsoli wyświetli się :
//Model
var Task = Backbone.Model.extend({
initialize: function(){
console.log("New Task initialized");
}
});
//Kolekcja
var TaskCollection = Backbone.Collection.extend({
model: Task
});
Naturalnie musimy teraz zainicjować kolekcję:
W kolejnym kroku zmodyfikujemy zadeklarowaną w poprzednim tutku instancję widoku, wstrzykując do niej nasz obiekt kolekcji zadań:
//Instancja Kolekcji var tasks = new TaskCollection();
W kolejnym kroku zmodyfikujemy zadeklarowaną w poprzednim tutku instancję widoku, wstrzykując do niej nasz obiekt kolekcji zadań:
//Instancja widoku
taskedit = new TaskEditView({
el: 'body',
collection: tasks
});
Teraz gdy mamy do dyspozycji w widoku obiekt kolekcji, możemy już nią swobodnie manipulować. Na początek zadbamy o uzupełnianie kolekcji o nowe zadania:
//klasa widoku listy zadań
$(function() {
TaskEditView = Backbone.View.extend ({
initialize: function() {
console.log('Initializing task list view');
},
events: {
"click #addTask" : "showPrompt"
},
showPrompt: function() {
var taskName = prompt("Co chcesz dziś zrobić?");
this.createNewTask(taskName);
},
createNewTask: function(taskName) {
var taskItem = new Task({
name: taskName
});
this.collection.add(taskItem);
console.log(this.collection.toJSON());
}
});
Na końcu metody showPrompt dodaliśmy wywołanie createNewTask, która uzupełni kolekcję. Służy do tego metoda add kolekcji, jako parametr podajemy model. W tym celu stworzyliśmy instancję klasy Task, wstrzykując w nią podane przez użytkownika zadanie. Na końcu metody w celach szkoleniowych zapisujemy w konsoli Jsonową reprezentację kolekcji.
Teraz gdy podamy dwa kolejne zadania jako "Napisać kolejny odcinek tutka" i "Skosić trawnik", na konsoli wyświetli się :
Initializing task list view
New Task initialized
[Object { name="Napisać kolejny odcinek tutka"}]
New Task initialized
[Object { name="Napisać kolejny odcinek tutka"}, Object { name="Skosić trawnik"}]
cdn...Wszystkie kody z tego tutka są dostępne publicznie w repo bitBucket:
git clone https://bitbucket.org/gmotyl/backbone_tutorial.git
git clone git@bitbucket.org:gmotyl/backbone_tutorial.git
2012-08-14
Backone.js dla bezkręgowców cz.2 - Widok
Dziś na tapetę weźmiemy przykład prostej klasy widoku backbone.js
Dla uproszczenia wszystkie kody zapiszemy w jednym pliku, naturalnie przy poważnych projektach będziemy chcieli mieć każdą klasę w osobnym pliku - co znacznie ułatwi "ogarnięcie" całości.
Stwórzmy więc gdzieś na serwerze index.html:
<!DOCTYPE html>
<html>
<head>
<title>Lista zadań</title>
<meta charset="utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/underscore.js/1.3.1/underscore-min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
</head>
<body>
<button id="addTask">Dodaj zadanie</button>
<ul id="taskList">
</ul>
<script type="text/javascript">
//klasa widoku listy zadań będzie tutaj
</script>
</body>
</html>
Zacznijmy od napisania szkieletu widoku:
Jeśli czytasz uważnie, zauważysz w drugiej linii powyższego listingu konstrukcję $(function() {}); - jest to odpowiednik oklepanego $(document).ready(function () {}); Efekt jest dokładnie ten sam, $() sprawi, że jQuery wewnętrznie wywoła za nas czerwony kod, użyliśmy jednak znacznie mniej znaków i tym samym przyczyniliśmy się do ochrony środowiska naturalnego :)
Nasza klasa widoku (nie mylić z nk.pl! :) ) składa się z funkcji initialize która zconsolloguje komunikat podczas instancjonowania obiektu (dla celów szkoleniowych oczywiście). Dalej zapinamy eventa na kliknięcie elementu o id #addTask (jQuerowe selectory działają w backbone). W efekcie po kliknięciu guzika powinna się wyświetlić nasza metoda showPrompt, która na razie jeszcze nic nie robi poza pokazaniem okna inputu, ale przecież nie od razu Rzym zbudowano :)
Naturalnie musimy stworzyć instancję klasy (linia 17), przekazujemy jako parametr el, 'body' - dzięki temu metoda events widoku będzie słuchała na całym body.
W backbone istnieje kilka specjalnych opcji które możemy przekazać do klasy widoku - są one dostępne bezpośrednio jako zmienne (np. w tym wypadku możemy wewnątrz dowolnej metody widoku odwołać się do elementu poprzez this.el lub skrótem do jQerowego obiektu this.$el.
Pozostałe specjalne opcje dostępne bezpośrednio w widoku to: model, collection, id, className, tagName i attributes - o większości z nich wspomnę napewno w kolejnych częściach tutka.
Backbone nie ogranicza nas (na szczęście) jedynie do wymienionych opcji, instancjonując widok możemy przekazać dowolne zmienne, tablice, obiekty itd. Będą one wówczas dosŧepne w widoku przez this.options.nazwanaszejopcji.
W kolejnym odcinku rozbudujemy klasę widoku i połączymy ją z modelem.
//klasa widoku listy zadań
$(function() {
TaskEditView = Backbone.View.extend ({
initialize: function() {
console.log('Initializing task list view');
},
events: {
"click #addTask" : "showPrompt"
},
showPrompt: function() {
var taskName = prompt("Co chcesz dziś zrobić?");
}
});
taskedit = new TaskEditView({
el: 'body'
});
});
Jeśli czytasz uważnie, zauważysz w drugiej linii powyższego listingu konstrukcję $(function() {}); - jest to odpowiednik oklepanego $(document).ready(function () {}); Efekt jest dokładnie ten sam, $() sprawi, że jQuery wewnętrznie wywoła za nas czerwony kod, użyliśmy jednak znacznie mniej znaków i tym samym przyczyniliśmy się do ochrony środowiska naturalnego :)
Nasza klasa widoku (nie mylić z nk.pl! :) ) składa się z funkcji initialize która zconsolloguje komunikat podczas instancjonowania obiektu (dla celów szkoleniowych oczywiście). Dalej zapinamy eventa na kliknięcie elementu o id #addTask (jQuerowe selectory działają w backbone). W efekcie po kliknięciu guzika powinna się wyświetlić nasza metoda showPrompt, która na razie jeszcze nic nie robi poza pokazaniem okna inputu, ale przecież nie od razu Rzym zbudowano :)
Naturalnie musimy stworzyć instancję klasy (linia 17), przekazujemy jako parametr el, 'body' - dzięki temu metoda events widoku będzie słuchała na całym body.
W backbone istnieje kilka specjalnych opcji które możemy przekazać do klasy widoku - są one dostępne bezpośrednio jako zmienne (np. w tym wypadku możemy wewnątrz dowolnej metody widoku odwołać się do elementu poprzez this.el lub skrótem do jQerowego obiektu this.$el.
Pozostałe specjalne opcje dostępne bezpośrednio w widoku to: model, collection, id, className, tagName i attributes - o większości z nich wspomnę napewno w kolejnych częściach tutka.
Backbone nie ogranicza nas (na szczęście) jedynie do wymienionych opcji, instancjonując widok możemy przekazać dowolne zmienne, tablice, obiekty itd. Będą one wówczas dosŧepne w widoku przez this.options.nazwanaszejopcji.
W kolejnym odcinku rozbudujemy klasę widoku i połączymy ją z modelem.
2012-08-13
Last minute commit
Planowałem dziś napisać kolejny odcinek tutka z backbone.js... Wracam wlasnie z pracy i pisze ten wpis na fonie...
Zostałem dziś dłużej, właśnie po to by dokończyć kod w wymienionym framrworku. Po 10h pisania czuję, że mam już powyżej uszu dzej-esa, a nie da się napisać o backbone nie dotykając JS. Pocieszam się tylko tym, że bez backbone moje dzisiejsze zadanie byłoby o wiele bardziej pracochłonne... i pewnie zastała by mnie ciemna noc w pracy.
Miałem dziś wyjść punktualnie
i do domu... A tu nagle coś wybuchło...
Przy okazji naprawy błędu zrobiłem wspólnie z moim szefem refactoring backbonowego kodu i udało nam się skurczyć główną klasę o ponad 100 linii! A więc jednak warto było poświęcić te dodatkowe dwie godziny :)
Zostałem dziś dłużej, właśnie po to by dokończyć kod w wymienionym framrworku. Po 10h pisania czuję, że mam już powyżej uszu dzej-esa, a nie da się napisać o backbone nie dotykając JS. Pocieszam się tylko tym, że bez backbone moje dzisiejsze zadanie byłoby o wiele bardziej pracochłonne... i pewnie zastała by mnie ciemna noc w pracy.
Miałem dziś wyjść punktualnie
git push
i do domu... A tu nagle coś wybuchło...
Przy okazji naprawy błędu zrobiłem wspólnie z moim szefem refactoring backbonowego kodu i udało nam się skurczyć główną klasę o ponad 100 linii! A więc jednak warto było poświęcić te dodatkowe dwie godziny :)
posted from Bloggeroid
2012-08-10
Backone.js dla bezkręgowców cz.1
Ten post będzie początkiem krótkiego mini tutoriala wprowadzającego do JSowego frameworka Backbone.js
W ostatniej linii powyższego kodu instancjonujemy model MyModel, spowoduje to automatyczne wywołanie funkcji initialize i na konsoli pojawi się "Hello world!"
cdn...
Wiem, że podobnych samouczków mnoży się nad każdym frameworkiem jak nie przymierzając much nad... ehmm... :) jednak ponieważ tak się składa, że ostatnio dość dużo w backbonie pisałem, to postanowiłem mimo wszystko dodać coś od siebie w temacie... i dla odmiany po polsku.
Tutaj mała dygresja : większość dobrych tutoriali jest w sieci po angielsku, polecam ich lekturę tym, którzy chcą naprawdę dobrze poznać nowe technologie (nota bene: trudno sobie wyobrazić programistę nie władającego językiem Shakespeare'a). Lecz zgodzi się chyba ze mną każdy polak, że miło czasem poczytać coś o technologii po polsku ;)
Ok wracając do meritum: Naukę Backobe.js proponuję zacząć (po przeczytaniu dokumentacji) od stworzenia pierwszego modelu:
Najpierw upewnijmy się że w hedzie strony dołączyliśmy biblioteki, backbone zależy tylko od underscore a więc:
Najpierw upewnijmy się że w hedzie strony dołączyliśmy biblioteki, backbone zależy tylko od underscore a więc:
Teraz można już zadeklarować pierwszy model:
MyModel = Backbone.Model.extend({
initialize: function(){
console.log("Hello world!");
}
});
var modelInstance = new MyModel;
W ostatniej linii powyższego kodu instancjonujemy model MyModel, spowoduje to automatyczne wywołanie funkcji initialize i na konsoli pojawi się "Hello world!"
cdn...
Subskrybuj:
Posty (Atom)