Autor wpisu: piotrooo89, dodany: 30.01.2010 22:15, tagi: javascript, sql, jquery
W pierwszej części poradnika http://piotrooo.com/2010/01/kalendarz-google-w-jquery-cz-1/ pokazałem jak generować kalendarz, jak dodawać nowe zdarzenia i wyświetlać je w kalendarzu czas na pozostałą obsługę zdarzeń mianowicie zmiana rozmiaru zdarzenia (czyli zmiana czasu), edycja i usuwanie. Zapraszam do lektury.
1. Zmiana godzin zlecenia – czyli rozszerzamy i przenosimy zdarzenia
To zmian rozmiarów (czasów, aczkolwiek dla uproszczenia będę używał słów zmian rozmiarów) zdarzenia odpowiedzialna jest metoda eventResize. A do zmiany całych dni za pomocą Drag ‘n Drop odpowiedzialna jest funkcja eventDrop. Oczywiście do zmiany będą nam potrzebne dane które przechwycimy w pliku kalnedarz_do.php. Obsłużenie tych metod po strobie jQuery wygląda następująco:
- eventDrop : function(calEvent, $event) {
- $.ajax({
- type : "GET",
- url: "kalendarz_do.php?co=resize&id="+calEvent.id+"",
- data: ({start: new Date(calEvent.start)/1000, end: new Date(calEvent.end)/1000})
- });
- },
- eventResize : function(calEvent, $event) {
- $.ajax({
- type : "GET",
- url: "kalendarz_do.php?co=resize&id="+calEvent.id+"",
- data: ({start: new Date(calEvent.start)/1000, end: new Date(calEvent.end)/1000})
- });
I teraz obsługa tych zdarzeń po stronie serwera aby w naszej bazie coś się zmieniło. Jak pewnie zauważyliście przekazuje również ID tego zdarzenia aby można było jednoznacznie określić co ma być edytowane, plik kalendarz_do.php:
- if ($_GET['co']=='resize')
- {
- $baza->quick_sel("UPDATE kalendarz SET start=('1970-01-01 00:00'::timestamp + interval '{$_GET['start']}')::timestamp at time zone 'GMT',
- koniec=('1970-01-01 00:00'::timestamp + interval '{$_GET['end']}')::timestamp at time zone 'GMT'
- WHERE id={$_GET['id']}");
- }
I teraz po zmianie rozmiaru (czasu), przeciągnięciu i upuszczeniu na inny dzień możemy zaobserwować zmiany.
2. Edytowanie i usuwanie zdarzeń
Umiemy już dodać, wyświetlić, zmienić dane to teraz czas na edycję i usuwanie. Można powiedzieć że jest to najbardziej skomplikowana część naszego kalendarza. Aby zrealizować tą funkcjonalność należy obsłużyć metodę eventClick. Obsługa tej metody wygląda następująco:
Autor wpisu: Piotr Śliwa, dodany: 30.01.2010 13:28, tagi: php
Dziś pora na trzeci artykuł z serii "wzorce w praktyce". Tym razem postanowiłem omówić praktyczne zastosowanie mniej znanego i stosowanego (w programowaniu w php) wzorca.
Każdy z Was zapewne wie co to jest transakcja bazodanowa. Jeśli jednak nie, to przypomnę, że transakcja w systemach baz danych polega na tym, że określony zbiór zapytań wykona się poprawnie w całości lub wogóle żadne zapytanie nie zostanie wykonane (de facto "nie wykona się" jest błędnym określeniem, raczej "nie zostanie zatwierdzone"). Więcej na ten temat znajdziecie na wikipedii. Transakcja jest implementacją wzorca projektowego koordynator, a raczej rozbudowanej specjalizacji tego wzorca o nazwie "Zatwierdzanie trójfazowe" (Three-Phase Commit) - właśnie tą odmianę Koordynatora będe omawiał.
Wyjątkowo w skrócie opiszę ideę tego wzorca, gdyż jest on mniej powszechny niż dwa poprzednie wzorce opisywane przeze mnie.
Uczestnikami są Koordynator, Użytkownik, Zadanie oraz Klient. Klient zleca Koordynatorowi do wykonania Zadanie, które jest podzielone na kilka części, jedna część Zadania może być wykonana tylko przez jednego Użytkownika. Rola Koordynatora polega na zapewnieniu spójności systemu, czyli podzielne Zadanie musi się wykonać w całości lub żadna z części tego Zadania przydzielona do jednego Użytkownika nie może się w ogóle wykonać.
Przedstawienie problemu
W naszym przykładzie Zadaniem będzie zaktualizowanie jakiegoś rekordu w kilku zdalnych i niezależnych usługach sieciowych będących na różnych serwerach, komunikacja odbywać się może przykładowo przez SOAP. Dla nas ważne jest, aby w każdej usłudze sieciowej był rekord o tym samym stanie, nie możemy dopuścić do sytuacji w której rekord w jednej z usług ma inny stan niż ten rekord w pozostałych usługach.Rozwiązanie nie stosując Koordynatora
[PHP]
//tablica obiektów reprezentujących różne usługi sieciowe //dla ułatwienia przyjmuję, że mają taki sam interfejs - jeśli //tak nie jest, można zastosować wzorzec Adapter opisywany w pierwszym //artykule z cyklu $services = ...; //aktualny rekord, który ma być zapisany w zdalnych usługach $record = ...; foreach($services as $service) { //zapisanie rekordu w jednej ze zdalnych usług $service->save($record); }
Kod ten będzie działał, ale jeśli pewnego razu wystąpi problem z połączeniem z jedną ze zdalnych usług lub z innych nieistotnych powodów rekord nie zostanie zapisany, to stracimy spójność - ten sam rekord będzie miał inne wartości w różnych usługach.
Rozwiązanie problemu
Wydzielamy dwie klasy: Koordynatora (Coordinator) oraz Użytkownika (Coordination).Najpierw zajmiemy się szkieletem klasy Użytkownika (Coordination). Obiekt, który może być koordynowany powinien umieć określić (nie wykonując swojej części zadania) czy wykonanie zadania się powiedzie (metoda prepare()), anulować wykonywanie zadania jeśli metoda prepare() zwróciła false - obiekt ten nie jest gotowy na jego wykonanie (metoda abort()). Powinien również umieć zatwierdzić (wykonać) zadanie (metoda commit()) oraz opcjonalnie wycofać zmiany, które nastąpiły po pomyślnym zatwierdzeniu (metoda rollback()).
[PHP]
class Coordination { //obiekt jednej usługi zdalnej private $service = null; private $state = null; private $isAborted = false; public function __construct($service) { $this->service = $service; } /** * @return bool Czy zadanie ma szansę się powieść? */ public function prepare() { try { //wykonanie jakiegoś testowego żądania, można np. pobrać w tym //żądaniu obecny obiekt rekordu który ma być zaktualizowany, //aby przy ewentualnym wywołaniu metody rollback() obiekt ten dysponował //stabilnym (przed wykonaniem zadania) stanem rekordu. $this->state = $this->service->test(); } catch(Exception $e) { //załóżmy, że jeśli zostanie wyrzucony wyjątek, to żądanie testowe nie powiodło się //poprawność testu można stwierdzić również np. po wartości zwracanej return false; } return true; } /** * Anulowanie wykonania zadania */ public function abort() { $this->isAborted = true; $this->state = null; } /** * Zatwierdzenie zadania * @throws Exception Zadanie nie powiodło się */ public function commit() { if($this->isAborted) { throw new LogicException('Zadanie zostało anulowane'); } $this->service->doTask(...); } /** * Przywrócenie starego stabilnego stanu */ public function rollback() { $this->service->doTask($this->state); } }
W zamyśle metoda abort() wykona się jeśli metoda prepare() zwróci false, jeśli jednak zwróci true to powinna zostać wykonana metoda commit(). Jeśli metoda commit() wyrzuci jakiś wyjątek (zadanie nie powiodło się), to powinna zostać wykonana metoda rollback(). Pamiętajmy jednak, że jeśli metoda prepare() i-tego obiektu Użytkownika zwróci false, to mają zostać wywołane metody abort() obiektów od 0 do i-1, gdyż dany obiekt który uważa że nie jest w stanie wykonać zadania, nie musi anulować swojej gotowości do jego wykonania, gdyż sam powiedział że się nie przygotował ;) To samo tyczy się metody commit(), jeśli i-ty obiekt wyrzuci w niej wyjątek, to powinna zostać wywołana metoda rollback() obiektów od 0 do i-1, gdyż i-ty obiekt tego zadania nie wykonał.
Klasa Koordynatora powinna umieć zarejestrować Użytkowników (metoda register()) oraz w skoordynowany sposób wykonać zadanie (metoda commit()).
[PHP]
class Coordinator { private $index; public function register(Coordination $coordination) { $this->cordinations[] = $coordination; } public function commit() { $abort = false; //sprawdzenie, czy wszyscy uczestnicy są gotowi na wykonanie swojej części zadania for($this->index=0; $this->index < $count; $this->index++) { if(!$this->coordinations[$this->index]->prepare()) { $abort = true; break; } } //jeden z Użytkowników nie jest gotowy na wykonanie zadania, anuluj zadanie if($abort) { for($i=($this->index-1); $i >= 0; $i--) { $this->coordinations[$i]->abort(); } return false; } try { //zatwierdź zadanie for($this->index=0; $this->index < $count; $this->index++) { $this->coordinations[$this->index]->commit(); } } catch(Exception $e) { //jeden z Użytkowników nie zdołał zatwierdzić zadania, wycofaj zmiany for($i=($this->index-1); $i >= 0; $i--) { $this->coordinations[$i]->rollback(); } return false; } return true; } }
Autor wpisu: batman, dodany: 29.01.2010 10:42, tagi: zend_framework
Autor wpisu: Kamil Adryjanek, dodany: 28.01.2010 19:39, tagi: javascript
Lastely, building quite big Symfony and Ext.js application i was looking for simple way to make my application internationalized. I haven’t found any good solutions that would suits my needs. I found this on extjs forum: ‘The simplest way, perhaps, is to extract hardcoded strings to locale files. Then your framework just have to load the selected language file together with YUI-ext.” I didn’t like that idea, i think it would be good for building some custom components but not for internationalize the whole application. I needed something quicker and less complicated.
Than, i thought that i can write my own simple i18n mechanism - maybe there is better way (files can be generated on the server side), but this work for sure.
I simple wrote custom JavaScript function __(string) and put it into one of my .js files:
function __(string) {
if (typeof(i18n)!='undefined' &amp;amp;amp;&amp;amp;amp; i18n[string]) {
return i18n[string];
}
return string;
}
Then i attached another JavaScript file with translations object (before including __() function), for example we can call this file “pl_PL.js” and put there code:
var i18n = {
'Country.' : 'Kraj',
'Description' : 'Opis',
'Population' : 'Populacja',
'Created at' : 'Data utworzenia',
'Symfony 1.2 and Ext.js example' : 'Przykład wykorzystania Symfony 1.2 i Ext.js '
}
In our ExtJS components (or any other JavaScript code) we just simple call (example code from latest entry):
//columns structure
var columns = [
{id:'name',header: __('Country'), width: 160, sortable: true, dataIndex: 'name'},
{header: __('Description'), width: 160, sortable: false, dataIndex: 'description'},
{header: __('Population'), width: 160, sortable: true, dataIndex: 'population'},
{header: __('Created at'), width: 100, sortable: true, dataIndex: 'created_at'}
];
// gridPanel object
var grid = new Ext.grid.GridPanel({
title: __('Symfony 1.2 and Ext.js example'),
loadMask: true, // mask panel when data is loading
store: store,
columns: columns,
autoExpandColumn: 'name',
height: 400,
width: 600,
renderTo: 'grid-example' // DOM element
});
});
For me it’s simple, powerfull and really usefull.
Kanał ATOM
