Tytu
á oryginaáu: CakePHP 1.3 Application Development Cookbook
T
áumaczenie: Przemysáaw Pietraszek (rozdz.1),
Krzysztof Rychlicki-Kicior (wst
Ċp, rozdz. 2 – 11)
ISBN: 978-83-246-3542-9
Copyright © Packt Publishing 2011. First published in the English language under the title
„CakePHP 1.3 Application Development Cookbook”
© Helion 2012
All rights reserved
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording or by any information storage retrieval system,
without permission from the Publisher.
Wszelkie prawa zastrze
Īone. Nieautoryzowane rozpowszechnianie caáoĞci lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metod
ą kserograficzną,
fotograficzn
ą, a takĪe kopiowanie ksiąĪki na noĞniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.
Wszystkie znaki wyst
Ċpujące w tekĞcie są zastrzeĪonymi znakami firmowymi bądĨ towarowymi ich
w
áaĞcicieli.
Autor oraz Wydawnictwo HELION do
áoĪyli wszelkich staraĔ, by zawarte w tej ksiąĪce informacje byáy
kompletne i rzetelne. Nie bior
ą jednak Īadnej odpowiedzialnoĞci ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie
ponosz
ą równieĪ Īadnej odpowiedzialnoĞci za ewentualne szkody wynikáe z wykorzystania informacji
zawartych w ksi
ąĪce.
Pliki z przyk
áadami omawianymi w ksiąĪce moĪna znaleĨü pod adresem:
ftp://ftp.helion.pl/przyklady/ caph3r.zip
Wydawnictwo HELION
ul. Ko
Ğciuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (ksi
Ċgarnia internetowa, katalog ksiąĪek)
Drogi Czytelniku!
Je
Īeli chcesz oceniü tĊ ksiąĪkĊ, zajrzyj pod adres
http://helion.pl/user/opinie/caph3r
Mo
Īesz tam wpisaü swoje uwagi, spostrzeĪenia, recenzjĊ.
Printed in Poland.
•
Kup książkę
•
Poleć książkę
•
Oceń książkę
•
Księgarnia internetowa
•
Lubię to! » Nasza społeczność
Spis tre!ci
O autorze
9
O recenzentach
11
Przedmowa
13
O czym jest ta ksi"#ka?
13
Oprogramowanie wykorzystywane w ksi"#ce
16
Dla kogo jest ta ksi"#ka?
17
Konwencje typograficzne
17
Materia$y dodatkowe i pomoc
17
Rozdzia$ 1. Uwierzytelnianie
19
Wprowadzenie
19
Konfiguracja prostego systemu uwierzytelniania
20
U#ywanie i konfiguracja komponentu Auth
24
Logowanie za pomoc" nazwy u#ytkownika lub adresu e-mail
28
Zapisywanie informacji o u#ytkowniku po zalogowaniu
31
Pobieranie informacji o zalogowanym u#ytkowniku
33
U#ywanie prefiksów do kontroli dost%pu bazuj"cej na rolach
36
Autoryzacja wykorzystuj"ca warstw% kontroli dost%pu (ACL)
38
Integracja z OpenID
45
Rozdzia$ 2. Wi"zania modeli
49
Wprowadzenie
49
Dodanie zachowania Containable do wszystkich modeli
50
Ograniczanie wi"za& zwracanych przez wyszukiwania
51
Modyfikowanie parametrów wi"za& dla wyszukiwa&
59
Modyfikowanie warunków wi"za& dla wyszukiwa&
63
Zmiana typu z$"czenia dla powi"za& jeden-do-jednego
65
Tworzenie wielu powi"za& z tym samym modelem
66
Dodawanie wi"za& w locie
69
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Spis tre!ci
6
Rozdzia$ 3. Wszystko o pobieraniu danych
73
Wprowadzenie
73
Wykonywanie zapyta& GROUP i COUNT
74
Wykorzystywanie pól wirtualnych
80
Tworzenie zapyta& z wykorzystaniem z$"cze& dora'nych
84
Wyszukiwanie elementów spe$niaj"cych okre!lone kryteria
87
Implementacja w$asnego typu wyszukiwania
89
Stronicowanie wyszukiwa& w$asnych typów
93
Implementacja stronicowania na bazie technologii AJAX
96
Rozdzia$ 4. Walidacja i zachowania
99
Wprowadzenie
99
Dodawanie wielu regu$ walidacji
100
Tworzenie w$asnych regu$ walidacji
104
Wykorzystywanie wywo$a& zwrotnych w zachowaniach
109
Wykorzystywanie zachowa& do dodawania nowych pól
116
Wykorzystywanie zachowania Sluggable
118
Geokodowanie adresów przy u#yciu zachowania Geocodable
122
Rozdzia$ 5. *ród$a danych
127
Wprowadzenie
127
Udoskonalanie dziennika zapyta& 'ród$a danych SQL
127
Parsowanie plików CSV za pomoc" 'róde$ danych
134
Konsumowanie kana$ów RSS za pomoc" 'róde$ danych
138
Tworzenie 'ród$a danych przy u#yciu serwisu Twitter
142
Dodawanie obs$ugi transakcji i blokad w 'ródle danych MySQL
152
Rozdzia$ 6. Magia trasowania
161
Wprowadzenie
161
Wykorzystywanie parametrów named i GET
162
Wykorzystywanie tras z prefiksami
168
Praca z elementami tras
172
Dodawanie tras typu catch-all dla stron profilowych
175
Dodawanie walidacji dla klas typu catch-all
179
Tworzenie w$asnych klas trasowania
182
Rozdzia$ 7. Tworzenie i wykorzystywanie us$ug sieciowych
187
Wprowadzenie
187
Tworzenie kana$u RSS
188
Konsumowanie us$ugi JSON
194
Tworzenie us$ug REST przy u#yciu formatu JSON
199
Dodawanie uwierzytelniania do us$ug REST
208
Implementacja autoryzacji dost%pu do API przy u#yciu tokenu
213
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Spis tre!ci
7
Rozdzia$ 8. Praca z pow$okami
219
Wprowadzenie
219
Tworzenie i uruchamianie pow$oki
220
Parsowanie parametrów wiersza polece&
224
Tworzenie zada& pow$oki wielokrotnego u#ytku
229
Wysy$anie wiadomo!ci e-mail z poziomu pow$oki
239
Tworzenie automatycznych zada& za pomoc" wtyczki Robot
243
Rozdzia$ 9. Internacjonalizacja aplikacji
249
Wprowadzenie
249
Internacjonalizacja tekstów w kontrolerach i widokach
250
Internacjonalizacja komunikatów walidacji w modelach
256
T$umaczenie tekstów zawieraj"cych dynamicznie generowan" tre!+
259
Ekstrakcja i t$umaczenie tekstów
262
T$umaczenie rekordów baz danych za pomoc" zachowania Translate
266
Ustawianie i zapami%tywanie j%zyka
270
Rozdzia$ 10. Testowanie
273
Wprowadzenie
273
Konfiguracja frameworka do testów
274
Tworzenie testowych danych i metod modeli
278
Testowanie akcji kontrolera i ich widoków
286
Wykorzystywanie za!lepek do testowania kontrolerów
290
Uruchamianie testów w konsoli
294
Rozdzia$ 11. Narz%dzia i klasy pomocnicze
297
Wprowadzenie
297
Wykorzystywanie klasy Set
298
Operacje na tek!cie przy u#yciu klasy String
305
Wysy$anie wiadomo!ci e-mail
308
Wykrywanie typów plików za pomoc" MagicDb
314
Rzucanie i obs$uga wyj"tków
319
Skorowidz
325
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
9
Internacjonalizacja
aplikacji
W tym rozdziale omówimy nast%puj!ce zagadnienia:
internacjonalizacj% tekstów w kontrolerach i widokach;
internacjonalizacj% komunikatów walidacji w modelach;
t#umaczenie tekstów zawieraj!cych dynamicznie generowan! tre&';
ekstrakcj% i t#umaczenie tekstów;
t#umaczenie rekordów baz danych za pomoc! zachowania
Translate
;
ustawianie i zapami%tywanie j%zyka.
Wprowadzenie
W tym rozdziale zajmiemy si% przyk#adami, które pozwol! na internacjonalizacj% — umi%-
dzynarodowienie — wszystkich elementów aplikacji CakePHP, zarówno statycznych (zawartych
np. w widokach), jak i dynamicznych (np. rekordy baz danych).
W pierwszych dwóch przyk#adach poka$emy, jak udost%pni' elementy widoków, a tak$e ko-
munikaty walidacji modelu do t#umacze". W trzecim przyk#adzie b%dziemy t#umaczy' bardziej
z#o$one wyra$enia. Czwarty przyk#ad to pokaz mo$liwo&ci wbudowanych narz%dzi CakePHP,
które potrafi! wy#uska' statyczn! tre&' aplikacji wymagaj!c! t#umaczenia. Pi!ty przyk#ad
przedstawia mechanizm t#umaczenia rekordów baz danych. Na zako"czenie dowiesz si%, jak
umo$liwi' u$ytkownikowi zmian% aktywnego j%zyka aplikacji.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
250
Internacjonalizacja tekstów
w kontrolerach i widokach
W tym przyk#adzie dowiesz si%, jak zinternacjonalizowa' tekst, który znajduje si% w widokach
naszej aplikacji, a tak$e jak przygotowa' takie teksty do t#umaczenia.
Zanim zaczniesz
Aby wykona' poni$szy przyk#ad, musisz skorzysta' z przyk#adowych danych. Utwórz tabel%
articles
, korzystaj!c z poni$szego zapytania SQL:
CREATE TABLE 'articles'(
'id' INT UNSIGNED AUTO_INCREMENT NOT NULL,
'title' VARCHAR(255) NOT NULL,
'body' TEXT NOT NULL,
'created' DATETIME NOT NULL,
'modified' DATETIME NOT NULL,
PRIMARY KEY('id')
);
Teraz dodaj kilka rekordów, korzystaj!c z poni$szych zapyta" SQL:
INSERT INTO 'articles'('title', 'body', 'created', 'modified') VALUES
('First article', 'The body of the first article.', NOW(), NOW()),
('Second article', 'The body of the second article.', NOW(), NOW()),
('Third article', 'The body of the third article.', NOW(), NOW());
Utwórz plik app/controllers/articles_controller.php i umie&' w nim kontroler o nast%puj!cej
tre&ci:
<?php
class ArticlesController extends AppController {
public function index() {
$this->paginate['limit'] = 2;
$articles = $this->paginate();
$this->set(compact('articles'));
}
public function add() {
if (!empty($this->data)) {
$this->Article->create();
if ($this->Article->save($this->data)) {
$this->Session->setFlash('Article saved!');
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash('Please correct errors!');
}
}
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
251
}
public function view($id) {
$article = $this->Article->find('first', array(
'conditions' => array('Article.id' => $id)
));
if (empty($article)) {
$this->cakeError('error404');
}
$this->set(compact('article'));
}
}
?>
W dalszej kolejno&ci utwórz plik app/models/article.php o nast%puj!cej tre&ci:
<?php
class Article extends AppModel {
public $validate = array(
'title' => 'notEmpty',
'body' => 'notEmpty'
);
}
?>
Utwórz podkatalog app/views/articles, po czym dodaj do niego plik index.ctp o nast%puj!cej
tre&ci:
<h1>Articles</h1>
<p>
<?php echo $this->Paginator->counter(); ?>
-
<?php echo $this->Paginator->prev(); ?>
<?php echo $this->Paginator->numbers(); ?>
<?php echo $this->Paginator->next(); ?>
</p>
<p>
<?php echo count($articles) . ' articles: '; ?>
</p>
<ul>
<?php foreach($articles as $article) { ?>
<li><?php echo $this->Html->link(
$article['Article']['title'],
array('action'=>'view', $article['Article']['id'])
); ?></li>
<?php } ?>
</ul>
<p><?php echo $this->Html->link('Create article', array('action'=>'add'));
?></p>
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
252
Do katalogu app/views/articles dodaj plik add.ctp o nast%puj!cej tre&ci:
<?php
echo $this->Form->create();
echo $this->Form->inputs(array(
'legend' => 'Create article',
'title' => array('label' => 'Title'),
'body' => array('label' => 'Body')
));
echo $this->Form->end('Save');
?>
Na zako"czenie dodaj plik app/views/articles/view.ctp o nast%puj!cej tre&ci:
<h1><?php echo $article['Article']['title']; ?></h1>
<?php echo $article['Article']['body']; ?>
Jak to zrobi(
1. Zmodyfikuj tre&' pliku app/controllers/articles_controller.php, uwzgl%dniaj!c
zmiany zaznaczone pogrubion! czcionk! w metodzie
add()
:
public function add() {
if (!empty($this->data)) {
$this->Article->create();
if ($this->Article->save($this->data)) {
$this->Session->setFlash(__('Article saved', true));
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash(__('Please correct the errors', true));
}
}
}
2. Otwórz plik app/views/articles/add.ctp i wprowad= zaznaczone pogrubion!
czcionk! zmiany:
<?php
echo $this->Form->create();
echo $this->Form->inputs(array(
'legend' => __('New Article', true),
'title' => array('label' => __('Title:', true)),
'body' => array('label' => __('Body:', true))
));
echo $this->Form->end(__('Save', true));
?>
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
253
3. Otwórz plik app/views/articles/index.ctp i wprowad= nast%puj!ce zmiany:
<h1><?php __('Articles'); ?></h1>
<p>
<?php echo $this->Paginator->counter(__('Showing records %start%-%end% in
page %page% out of %pages%', true)); ?>
-
<?php echo $this->Paginator->prev(__('<< Previous', true)); ?>
<?php echo $this->Paginator->numbers(); ?>
<?php echo $this->Paginator->next(__('Next >>', true)); ?>
</p>
<p>
<?php
$count = count($articles);
echo $count . ' ' . __n('article', 'articles', $count, true) . ': ';
?>
</p>
<ul>
<?php foreach($articles as $article) { ?>
<li><?php echo $this->Html->link(
$article['Article']['title'],
array('action'=>'view', $article['Article']['id'])
); ?></li>
<?php } ?>
</ul>
<p><?php echo $this->Html->link(__('Create article', true),
array('action'=>'add')); ?></p>
Przejd= na stron% http://localhost/articles. Powiniene& otrzyma' stronicowan! list% artyku#ów
podobn! do przedstawionej na poni$szym rysunku.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
254
Jak to dzia a
Dwie najwa$niejsze metody udost%pniane przez CakePHP do t#umaczenia to
__()
i
__n()
.
Nazwy metod mog! wyda' si% nieco dziwne; ich pochodzenie wywodzi si% od implementacji
narz%dzia gettext w j%zyku Perl — narz%dzie to stanowi element Projektu T#umaczenia GNU
(GNU Translation Project).
Metoda
__()
jest u$ywana do t#umaczenia tekstów statycznych i przyjmuje dwa parametry
opisane w poni$szej tabeli.
Parametr
Opis
singular
Tekst, który ma by' przet$umaczony.
return
Je%li ten parametr ma warto%'
true
, przet$umaczony tekst zostanie zwrócony, a nie
przes$any do klienta. Domy%lnie
false
.
Metoda
__n()
równie$ pozwala na t#umaczenie tekstów statycznych, jednak uwzgl%dnia ona
tak$e sytuacje, w których konkretna warto&' mo$e zale$e' od liczby — pojedynczej lub mno-
giej. W zwi!zku z tym przyjmuje ona cztery parametry wymienione w poni$szej tabeli.
Parametr
Opis
singular
Tekst, który zostanie przet$umaczony na aktywny j!zyk, je%li parametr
count otrzyma
warto%'
singular
.
plural
Tekst, który zostanie przet$umaczony na aktywny j!zyk, je%li parametr
count otrzyma
warto%'
plural
.
count
Zmienna lub warto%' liczbowa, która zostanie wykorzystana do okre%lenia liczby dla danego
tekstu (
singular
lub
plural
).
return
Je%li ten parametr ma warto%'
true, przet$umaczony tekst zostanie zwrócony, a nie przes$any
do klienta. Domy%lnie
false
.
Rozpoczynamy od zmiany komunikatów generowanych przez klas%
ArticlesController
, wy-
korzystuj!c funkcj%
__()
. Zastrzegamy, $e teksty maj! by' zwracane, a nie przesy#ane do klienta.
Nast%pnie modyfikujemy plik add.ctp, dzi%ki czemu wszystkie etykiety formularza (wraz z jego
legend!) zostan! przet#umaczone.
W podobny sposób opakowujemy tytu# w widoku index.ctp za pomoc! funkcji t#umaczenia.
Nast%pnie korzystamy z pierwszego parametru metod
counter()
,
next()
i
prev()
(stanowi!-
cych sk#adowe klasy
PaginatorHelper
), aby przekaza' przet#umaczone wersje wszystkich tek-
stowych elementów mechanizmu stronicowania. Na zako"czenie korzystamy z funkcji
__n()
,
by wybra' odpowiedni przet#umaczony tekst przy u$yciu warto&ci zmiennej
count
.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
255
Je%li korzystasz z funkcji
__n(), musisz pami!ta', "e trzecim argumentem w wywo$aniach tej funkcji
powinna by' zawsze zmienna, a nie wyra"enie (np. zawieraj#ce indeksy tablic). Wyra"enia mog# do-
prowadzi' do zwrócenia nieoczekiwanych wyników podczas wywo$ywania pow$oki ekstraktora (por.
przyk$ad „Ekstrakcja i t$umaczenie tekstów”).
Domeny i kategorie
Funkcje t#umacze" wykorzystywane w tym przyk#adzie opakowuj! funkcj%
translate()
nale-
$!c! do klasy
I18n
frameworka CakePHP. Metoda ta pozwala nie tylko na przeprowadzanie
prostych t#umacze"; dzi%ki niej programista mo$e okre&li' domen%, z której s! pozyskiwane
t#umaczone teksty, a tak$e kategori%, do której nale$y tekst do t#umaczenia.
Domeny pozwalaj! na wydzielanie grup t#umaczonych tekstów do osobnych plików. Domy&l-
nie, gdy domena nie jest okre&lona jawnie, CakePHP korzysta z domeny
default
(domy&lnej).
Je&li chcesz okre&li' domen%, w której CakePHP powinien szuka' t#umaczonego tekstu, sko-
rzystaj z funkcji
__d()
lub
__dn()
. Wyszukanie t#umaczonego tekstu w domenie
moja_wtyczka
wygl!da#oby nast%puj!co:
$translated = __d('moja_wtyczka', 'Hello World', true);
Kategorie umo$liwiaj! jeszcze wi%ksz! kontrol% nad zarz!dzaniem t#umaczonymi tekstami.
Pozwalaj! one na grupowanie plików t#umacze" w odr%bnych katalogach; mo$na tak$e po-
wi!za' t#umaczony tekst z dodatkowymi metadanymi. Domy&lnie CakePHP zak#ada, $e t#u-
maczone teksty nale$! do kategorii
LC_MESSAGES
. Je&li chcesz zmieni' kategori%, skorzystaj z funk-
cji t#umaczenia
__dc()
i
__dcn()
, ustawiaj!c przedostatni argument —
return
— na wybran!
kategori%. Mo$e ona przyjmowa' jedn! z okre&lonych poni$ej warto&ci:
LC_ALL
:
0
;
LC_COLLATE
:
1
;
LC_CTYPE
:
2
;
LC_MONETARY
:
3
;
LC_NUMERIC
:
4
;
LC_TIME
:
5
;
LC_MESSAGES
:
6
.
Próba znalezienia nale$!cego do kategorii
LC_MESSAGES
tekstu Hello World w domenie
default
wygl!da nast%puj!co:
$translated = __dc('default', 'Hello World', 6, true);
Korzystaj#c z kategorii, zawsze podawaj warto%ci liczbowe, a nie nazwy sta$ych. Te ostatnie s# bowiem
zale"ne od wykorzystywanej platformy.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
256
Zobacz równie/
„Internacjonalizacja komunikatów walidacji w modelach”;
„Ekstrakcja i t#umaczenie tekstów”.
Internacjonalizacja komunikatów
walidacji w modelach
W tym przyk#adzie wykorzystamy ró$ne sposoby na zrealizowanie tego samego zadania: t#u-
maczenia komunikatów walidacji w modelach.
Zanim zaczniesz
Aby wykona' ten przyk#ad, musimy skorzysta' z podstawowego szkieletu aplikacji. W tym
celu musisz wykona' poprzedni przyk#ad.
Jak to zrobi(
Zmie" tre&' pliku app/models/article.php, wprowadzaj!c zmiany zaznaczone pogrubion!
czcionk! we w#a&ciwo&ci
validate
:
public $validate = array(
'title' => array(
'required' => 'notEmpty'
),
'body' => array(
'required' => 'notEmpty'
)
);
Istniej! dwa sposoby t#umaczenia komunikatów walidacji. Pierwszy z nich wymaga przes#o-
ni%cia konstruktora modelu. Wystarczy doda' jego poni$sz! implementacj% do klasy
Article
w pliku app/models/article.php:
public function __construct($id = false, $table = null, $ds = null) {
foreach($this->validate as $field => $rules) {
if (!is_array($rules)) {
$rules = (array) $rules;
}
foreach($rules as $key => $rule) {
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
257
if (!is_array($rule)) {
$rules[$key] = compact('rule');
}
}
$this->validate[$field] = $rules;
}
$this->validate = Set::merge($this->validate, array(
'title' => array(
'required' => array('message'
=> __('A title must be specified', true))
),
'body' => array(
'required' => array('message'
=> __('You must define the body', true))
)
));
parent::__construct($id, $table, $ds);
}
Inn! metod! t#umaczenia komunikatów walidacji jest przeniesienie komunikatów do widoku.
W ten sposób zamiast przes#ania' konstruktor i deklarowa' w nim komunikaty, wystarczy
wprowadzi' zmiany w pliku app/views/articles/add.ctp:
<?php
echo $this->Form->create();
echo $this->Form->inputs(array(
'title' => array(
'label' => __('Title:', true),
'error' => array(
'required' => __('A title must be specified', true)
)
),
'body' => array(
'label' => __('Body:', true),
'error' => array(
'required' => __('You must define the body', true)
)
)
));
echo $this->Form->end(__('Save', true));
?>
Obie metody doprowadz! do uzyskania takiego samego efektu. Przejd= na stron% http://
localhost/articles/add i wy&lij formularz bez wprowadzania jakichkolwiek warto&ci. Powiniene&
uzyska' efekt jak na poni$szym rysunku.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
258
Jak to dzia a
Przed utworzeniem komunikatów o b#%dach dla ka$dej z regu# walidacji musimy owe regu#y
nazwa'. W tym celu modyfikujemy model
Article
, dzi%ki czemu ka$da z regu# jest indekso-
wana przy u$yciu nazwy. W naszym przypadku wybieramy nazw%
required
dla regu#y Cake-
PHP o nazwie
notEmpty
.
Pierwsze rozwi!zanie problemu t#umaczenia komunikatów walidacji mo$na okre&li' mianem
scentralizowanego — wszystkie komunikaty s! umieszczone w tym samym miejscu, w kon-
struktorze modelu. Przes#aniamy konstruktor, dzi%ki czemu mo$emy w jego wn%trzu zadekla-
rowa' komunikaty o b#%dach, które powinny by' przet#umaczone. Musieli&my skorzysta'
z konstruktora, poniewa$ w#a&ciwo&ci klas mog! zawiera' tylko statyczne przypisania. Poni$szy
blok kodu spowoduje b#!d sk#adni PHP:
public $validate = array(
'title' => array(
'required' => array(
'rule' => 'notEmpty',
'message' => __('Nothing defined!', true) // B "D SK ADNI
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
259
)
)
);
W implementacji konstruktora rozpoczynamy od sprawdzenia, czy w#a&ciwo&'
validate
sta-
nowi tablic% regu# (indeksowanych przy u$yciu nazw pól). Musimy tak$e sprawdzi', czy ka$-
da regu#a sama w sobie równie$ stanowi tablic% (indeksowan! za pomoc! nazw), której warto-
&ciami s! ponownie tablice, zawieraj!ce przynajmniej ustawienie
rule
.
Po zweryfikowaniu formatu w#a&ciwo&ci
validate
mo$emy po#!czy' komunikaty walidacji dla
ka$dej z regu#, korzystaj!c z funkcji
__()
w celu przet#umaczenia komunikatów. Na zako"-
czenie wywo#ujemy konstruktor klasy bazowej, aby poprawnie zako"czy' proces tworzenia
ca#ego modelu.
Drugie podej&cie do problemu t#umaczenia przedstawione w tym przyk#adzie przesuwa odpo-
wiedzialno&' za obs#ug% t#umacze" na widok, korzystaj!c z ustawienia
error
dost%pnego w meto-
dzie
input()
klasy
FormHelper
. To ustawienie otrzymuje tablic% indeksowan! za pomoc! nazw
regu# walidacji, której warto&ciami s! komunikaty o b#%dach wykorzystywane w razie niespe#-
nienia poszczególnych regu# walidacji.
Zobacz równie/
„Ekstrakcja i t#umaczenie tekstów”.
T$umaczenie tekstów zawieraj"cych
dynamicznie generowan" tre!+
W tym przyk#adzie dowiesz si%, jak t#umaczy' teksty, które zawieraj! elementy dynamiczne
— np. warto&ci zmiennych.
Zanim zaczniesz
Aby wykona' ten przyk#ad, musimy skorzysta' z podstawowego szkieletu aplikacji. W tym
celu musisz wykona' przyk#ad „Internacjonalizacja tekstów w kontrolerach i widokach”.
Jak to zrobi(
1. Otwórz plik app/controllers/articles_controller.php i wprowad= zaznaczone
pogrubion! czcionk! zmiany w metodzie
add()
:
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
260
public function add() {
if (!empty($this->data)) {
$this->Article->create();
if ($this->Article->save($this->data)) {
$this->Session->setFlash(
sprintf(__('Article "%s" saved', true), $this->Article->
field('title'))
);
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash('Prosz7 poprawiy b:7dy!');
}
}
}
2. Otwórz plik app/views/articles/index.ctp i wprowad= w nim zaznaczone pogrubion!
czcionk! zmiany:
<h1><?php __('Articles'); ?></h1>
<p>
<?php echo $this->Paginator->counter(__('Showing records %start%-
%end% in page %page% out of %pages%', true)); ?>
-
<?php echo $this->Paginator->prev(__('<< Previous', true)); ?>
<?php echo $this->Paginator->numbers(); ?>
<?php echo $this->Paginator->next(__('Next >>', true)); ?>
</p>
<p>
<?php
$count = count($articles);
printf(__n('%d article', '%d articles', $count, true), $count);
?>
</p>
<ul>
<?php foreach($articles as $article) { ?>
<li><?php echo $this->Html->link(
$article['Article']['title'],
array('action'=>'view', $article['Article']['id'])
); ?></li>
<?php } ?>
</ul>
<p><?php echo $this->Html->link(__('Create article', true),
array('action'=>'add')); ?></p>
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
261
Jak to zrobi(
Gdy podczas tworzenia aplikacji napotyka si% problem t#umaczenia tre&ci zawieraj!cych ele-
menty dynamiczne — np. warto&' zmiennej lub warto&' pola z bazy danych — mo$e si% po-
jawi' pokusa do#!czenia zmiennej do #a"cucha statycznego, a nast%pnie przekazania takiego
wyra$enia do funkcji t#umaczenia:
$translated = __('Hello ' . $name, true); // $LE
To wyra$enie nie jest poprawne, poniewa$ ekstraktor CakePHP (omówiony w przyk#adzie
„Ekstrakcja i t#umaczenie tekstów”) dzia#a poprawnie tylko dla tekstów statycznych. W innych j%-
zykach mo$e na przyk#ad wyst!pi' konieczno&' zmiany kolejno&ci s#ów w zdaniu. W zwi!zku
z tym musimy skorzysta' z innej techniki przetwarzania #a"cuchów. Rozwi!zanie jest proste
i stosunkowo popularne — funkcje PHP
printf()
i
sprintf()
.
Obie funkcje przyjmuj! te same argumenty. Pierwszy z nich jest obowi!zkowy i okre&la #a"-
cuch znaków do sformatowania. Wszystkie kolejne argumenty przekazane do funkcji zostan!
wykorzystane do wygenerowania wynikowego #a"cucha znaków. Jedyna ró$nica pomi%dzy
funkcjami
printf()
a
sprintf()
polega na tym, $e pierwsza z nich wy&wietli efekt swojej pra-
cy, druga za& — zwróci go.
Przejd=my teraz do kodu naszej aplikacji. Rozpoczynamy od zmiany komunikatu zwracanego
przez klas%
ArticlesController
po utworzeniu artyku#u. Korzystamy z funkcji
sprintf()
, ponie-
wa$ efekt jej dzia#ania chcemy przekaza' do metody
setFlash()
komponentu
Session
. W na-
szej sytuacji wyra$enie
%s
pozwala na wstawienie do #a"cucha znaków tytu#u nowo utworzo-
nego artyku#u.
W podobny sposób podstawiamy warto&' zmiennej
count
pod ci!g
%d
. Tym razem korzystamy
z funkcji
printf()
, aby wy&wietli' od razu efekt dzia#ania funkcji.
Zmiana kolejno0ci argumentów
Gdy korzystamy z wyra$e"
%s
lub
%d
w funkcjach
printf()
i
sprintf()
, nie mamy kontroli nad
sposobem pozycjonowania warto&ci; nie mo$emy te$ u$y' dwa razy jednej warto&ci, poniewa$
ka$de z wyra$e" jest dopasowywane do konkretnego, pojedynczego argumentu. Za#ó$my, $e
dysponujemy nast%puj!cym wyra$eniem:
printf('Your name is %s and your country is %s', $name, $country);
Pierwsze wyra$enie
%s
zostanie zast!pione warto&ci! zmiennej
name
, drugie — warto&ci!
zmiennej
country
. Problem pojawi#by si% w sytuacji, w której chcieliby&my zmieni' kolejno&'
argumentów w #a"cuchu znaków, zachowuj!c jednocze&nie kolejno&' argumentów w obr%bie
wywo#ania funkcji
printf()
.
Na szcz%&cie do argumentów funkcji
printf()
mo$emy si% odwo#ywa', korzystaj!c z ich nu-
merów porz!dkowych (okre&laj!cych ich pozycj% w&ród wszystkich argumentów przekaza-
nych w danym wywo#aniu). W poni$szym przyk#adzie
name
jest argumentem
1
, a
country
—
argumentem
2
:
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
262
printf('You are from %2$s and your name is %1$s', $name, $country);
Takie podej&cie pozwala na ponowne u$ycie argumentu bez konieczno&ci podawania go wie-
lokrotnie w wywo#aniu funkcji
printf()
:
printf('You are from %2$s and your name is %1$s . Welcome %1$s!', $name,
$country);
Zobacz równie/
„Ekstrakcja i t#umaczenie tekstów”.
Ekstrakcja i t$umaczenie tekstów
W tym przyk#adzie nauczymy si% pozyskiwa' wszystkie #a"cuchy znaków, które podlegaj!
t#umaczeniu w naszych aplikacjach CakePHP, a nast%pnie przeprowadzimy proces t#umacze-
nia, korzystaj!c z darmowego oprogramowania.
Zanim zaczniesz
Aby wykona' ten przyk#ad, musimy skorzysta' z podstawowego szkieletu aplikacji. W tym
celu musisz wykona' przyk#ad „Internacjonalizacja tekstów w kontrolerach i widokach”.
Musisz tak$e zainstalowa' aplikacj% Poedit w swoim systemie. Przejd= na stron% http://www.
poedit.net/download.php, a nast%pnie pobierz plik dla swojego systemu operacyjnego.
Jak to zrobi(
Przejd= do podkatalogu app/ Twojej aplikacji w wierszu polece", a nast%pnie wykonaj poni$sze
polecenie:
je&li pracujesz w systemach GNU Linux/Mac/Unix:
../cake/console/cake i18n extract
je&li jeste& u$ytkownikiem systemu Microsoft Windows:
..\cake\console\cake.bat i18n extract
Powiniene& skorzysta' z ustawie" domy&lnych, jak przedstawiono na kolejnym rysunku.
Po udzieleniu odpowiedzi na ostatnie pytanie pow#oka przeszuka wszystkie pliki Twojej apli-
kacji i na ich podstawie wygeneruje szablon t#umaczenia. Zostanie on umieszczony w pliku
app/locale/default.pot.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
263
Otwórz plik Poedit, a nast%pnie wybierz opcj% Nowy katalog z pliku POT z menu Plik. Pro-
gram wy&wietli okno wyboru pliku. Przejd= do podkatalogu app/locale Twojej aplikacji, za-
znacz plik default.pot i kliknij przycisk Otwórz. Zostanie wy&wietlone okno ustawie" przed-
stawione na poni$szym rysunku.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
264
W oknie Ustawienia wprowad= nazw% projektu i informacje z nim zwi!zane. W polu Formy
liczby mnogiej powiniene& wprowadzi' wyra$enie, przy u$yciu którego Poedit b%dzie w stanie
rozpozna' t#umaczenia dla liczb mnogich. W wielu j%zykach (np. angielskim, hiszpa"skim,
niemieckim i portugalskim) wystarczy wprowadzi' poni$sze wyra$enie:
nplurals=2; plural=(n != 1);
Wi!cej informacji na temat liczb mnogich i warto%ci, które powinno si! wobec nich stosowa' (w zale"-
no%ci od t$umaczonego j!zyka) znajdziesz na stronie
http://drupal.org/node/17564
.
Po wprowadzeniu wszystkich istotnych informacji kliknij przycisk OK. W tym momencie pro-
gram zapyta, gdzie chcesz zapisa' przet#umaczony plik. Utwórz katalog o nazwie pol i umie&' go
w katalogu app/locale/. Wewn!trz katalogu pol utwórz podkatalog LC_MESSAGES. Nast%pnie
przy u$yciu okna dialogowego programu Poedit wybierz folder app/locale/pol/LC_MESSAGES,
po czym kliknij przycisk Zapisz, nie zmieniaj!c przy tym nazwy pliku — powinna mie' war-
to&' default.po.
Program Poedit zaprezentuje wszystkie oryginalne #a"cuchy znaków. Do ka$dego z nich
mo$na doda' t#umaczenie. Wystarczy wybra' jeden z oryginalnych tekstów, a nast%pnie
wprowadzi' tre&' t#umaczenia w polu umieszczonym w dolnej cz%&ci okna. Po wprowadzeniu
t#umacze" okno programu Poedit powinno wygl!da' tak, jak na kolejnej rysunku.
Wybierz opcj% Zapisz z menu Plik, aby zapisa' przet#umaczony plik. W katalogu app/locale/
pol/LC_MESSAGES powinny by' dost%pne dwa pliki: default.po i default.mo.
Jak to dzia a
Na samym pocz!tku ekstraktor CakePHP musi otrzyma' &cie$ki do katalogów, które maj! by'
przez niego przetworzone. Nast%pnie mechanizm rekursywnie przegl!da wszystkie katalogi,
próbuj!c znale=' wszystkie wywo#ania funkcji t#umaczenia (
__()
,
__n()
,
__d()
,
__dn()
,
__dcn()
i
__c()
) w plikach PHP i plikach widoków. Dla ka$dego znalezionego wywo#ania ekstraktor
wy#uska z niego tekst do przet#umaczenia (pierwszy argument wywo#a" funkcji
__n()
i
__c()
;
drugi argument wywo#a" funkcji
__d()
i
__dc()
; pierwszy i drugi argument wywo#a" funkcji
__n()
; drugi i trzeci argument wywo#a" funkcji
__dn()
i
__dcn()
).
W argumentach, które s# wy$uskiwane przez ekstraktor, nale"y stosowa' jedynie statyczne $a+cuchy
znaków PHP. Nie mo"na w "adnym przypadku u"ywa' wyra"e+ PHP. Je%li w t$umaczonych tekstach
chcesz stosowa' zmienne lub inne wyra"enia dynamiczne, zapoznaj si! z przyk$adem „T$umaczenie tek-
stów zawieraj#cych dynamicznie generowan# tre%'”.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
265
Po pobraniu wszystkich tekstów do t#umaczenia przez ekstraktor zostan! utworzone odpo-
wiednie szablony plików t#umacze". Je&li w swojej aplikacji korzystasz z funkcji t#umacze",
które maj! zwi!zek z domenami (
__d()
,
__dn()
,
__dc()
i
__dcn()
), mo$esz uwzgl%dni' wszystkie
#a"cuchy w jednym pliku szablonu albo umie&ci' ka$d! domen% w odr%bnym pliku szablonu.
Pliki szablonów maj! rozszerzenie pot; ich nazwy s! za& nazwami domen (np. default.pot jest
domy&lnym (default) plikiem szablonu).
Je&li otworzysz plik default.pot w zwyk#ym edytorze tekstowym, zauwa$ysz, $e rozpoczyna si%
on od nag#ówka zawieraj!cego wiele ró$nych ustawie", po którym nast%puje w#a&ciwa cz%&'
t#umaczenia. Ka$dy #a"cuch do t#umaczenia jest reprezentowany za pomoc! dwóch linijek —
pierwsza z nich zawiera element oznaczony ci!giem
msgid
(#a"cuch do t#umaczenia), w drugiej
za& znajduje si% ci!g
msgstr
— to w#a&nie tam nale$y wstawia' przet#umaczone #a"cuchy znaków.
Program Poedit umo$liwia wygodn! edycj% plików w formacie pot, a tak$e zapisuje pliki do
odpowiedniego katalogu (app/locale/pol/LC_MESSAGES). W katalogu tym znajduj! si% dwa
pliki — default.po i default.pot. Je&li otworzysz plik .po za pomoc! zwyk#ego edytora, powiniene&
natychmiast dostrzec podobie"stwo tego pliku do pliku szablonu. Wyj!tek stanowi! ustawie-
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
266
nia nag#ówka — zawieraj! one warto&ci okre&lone przez nas w programie Poedit — i przede
wszystkim tre&' t#umacze" wprowadzonych przez nas w aplikacji Poedit. Plik default.mo stanowi
binarn! wersj% pliku default.po, tak$e wygenerowanego przez Poedit. Plik default.mo jest
wykorzystywany przez CakePHP w celu szybszego przetwarzania pliku t#umacze".
T$umaczenie rekordów baz danych
za pomoc" zachowania Translate
W tym przyk#adzie nauczymy si% t#umaczy' rekordy z bazy danych za pomoc! zachowania
Translate
.
Zanim zaczniesz
Aby wykona' ten przyk#ad, musimy skorzysta' z podstawowego szkieletu aplikacji. W tym
celu musisz wykona' przyk#ad „Internacjonalizacja tekstów w kontrolerach i widokach”.
Jak to zrobi(
Uruchom wiersz polece", a nast%pnie przejd= do podkatalogu app/ aplikacji i wykonaj poni$sze
polecenie.
je&li pracujesz w systemach GNU Linux/Mac/Unix:
../cake/console/cake i18n initdb
je&li jeste& u$ytkownikiem systemu Microsoft Windows:
..\cake\console\cake.bat i18n initdb
Zaakceptuj domy&lne opcje. Po wybraniu wszystkich opcji pow#oka utworzy tabel%
i18n
, co
przedstawiono na kolejnym rysunku.
Zmodyfikuj tre&' pliku app/models/article.php zgodnie z poni$szym listingiem:
<?php
class Article extends AppModel {
public $validate = array(
'title' => 'notEmpty',
'body' => 'notEmpty'
);
public $actsAs = array(
'Translate' => array('title', 'body')
);
}
?>
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
267
Teraz musimy przenie&' warto&ci pól
title
i
body
z tabeli
articles
do tabeli
i18n
, a nast%pnie
— usun!' oryginalne kolumny z tabeli
articles
. Wykonaj poni$sze zapytania SQL:
INSERT INTO 'i18n'('locale', 'model', 'foreign_key', 'field', 'content')
SELECT 'eng', 'Article', 'articles'.'id', 'title', 'articles'.'title'
FROM 'articles';
INSERT INTO 'i18n'('locale', 'model', 'foreign_key', 'field', 'content')
SELECT 'eng', 'Article', 'articles'.'id', 'body', 'articles'.'body'
FROM 'articles';
ALTER TABLE 'articles'
DROP COLUMN 'title',
DROP COLUMN 'body';
Dodaj polskie t#umaczenia naszych artyku#ów, wykonuj!c poni$sze zapytania SQL:
INSERT INTO 'i18n'('locale', 'model', 'foreign_key', 'field', 'content') VALUES
('pol', 'Article', 1, 'title', 'Pierwszy artyku:'),
('pol', 'Article', 1, 'body', 'Tre]y pierwszego artyku:u'),
('pol', 'Article', 2, 'title', 'Drugi artyku:'),
('pol', 'Article', 2, 'body', 'Tre]y drugiego artyku:u'),
('pol', 'Article', 3, 'title', 'Trzeci artyku:'),
('pol', 'Article', 3, 'body', 'Tre]y trzeciego artyku:u');
Na zako"czenie wstaw poni$sz! instrukcj% na ko"cu pliku app/config/bootstrap.php, tu$ przed
znacznikiem zamykaj!cym PHP:
Configure::write('Config.language', 'eng');
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
268
Przejd= na stron% http://localhost/articles. Powiniene& zobaczy' ten sam wykaz artyku#ów, który
przedstawiono w pierwszym przyk#adzie w tym rozdziale.
Jak to dzia a
Rozpoczynamy od utworzenia tabeli wymaganej przez zachowanie
Translate
, korzystaj!c
z pow#oki
i18n
. Nosi ona tak! sam! nazw% i zawiera (poza kluczem g#ównym) pola opisane
w poni$szej tabeli.
Pole
Opis
locale
J!zyk t$umaczenia danego rekordu.
model
Model, do którego nale"y t$umaczony rekord.
foreign_key
ID (klucz g$ówny) w modelu, który identyfikuje t$umaczony rekord.
field
Nazwa pola, które podlega t$umaczeniu.
content
Przet$umaczona warto%' dla danego pola.
Nast%pnie mo$emy doda' zachowanie
Translate
do modelu
Article
i ustawi' je tak, aby by#y
t#umaczone pola
title
i
body
. Taki zapis sprawi, $e pola te nie b%d! wchodzi#y w sk#ad tabeli
articles
— od tego momentu b%d! one przechowywane w tabeli
i18n
. Korzystaj!c z warto&ci
model
i
foreign_key
w tabeli
i18n
, zachowanie
Translate
pobierze odpowiednie warto&ci dla
danych pól przy ka$dym pobraniu rekordu modelu
Article
(dla aktywnego j%zyka).
Kopiujemy warto&ci pól
title
i
body
do tabeli
i18n
, dzi%ki czemu mo$emy usun!' pola z ta-
beli
articles
. Wywo#anie funkcji
call()
wykorzystywane w klasie
ArticlesController
nie
wymaga $adnych zmian. Co wi%cej, proces tworzenia artyku#ów b%dzie przebiega# bez $ad-
nych modyfikacji, poniewa$ zachowanie
Translate
skorzysta z aktywnego j%zyka podczas za-
pisywania rekordów modelu
Article
.
Na zako"czenie musimy wybra' j%zyk domy&lny. W tym celu korzystamy z ustawienia
Config.language
. Pomini%cie tego kroku spowoduje ustawienie j%zyka na podstawie warto&ci
nag#ówka
HTTP_ACCEPT_LANGUAGE
wysy#anego przez przegl!darki.
Wykorzystywanie odr*bnych tabel t umaczenia
Wszystkie modele, które korzystaj! z zachowania
Translate
, b%d! zapisywa' t#umaczenia
swoich pól domy&lnie do tabeli
i18n
. Takie zachowanie mo$e by' nieco problematyczne, je&li
dysponujemy du$! liczb! rekordów lub du$! liczb! t#umaczonych modeli. Na szcz%&cie za-
chowanie
Translate
pozwala na skonfigurowanie innego modelu t#umaczenia.
W ramach przyk#adu zapiszemy wszystkie t#umaczenia zwi!zane z artyku#ami do tabeli
article_translations
. Utwórz tabel%, a nast%pnie skopiuj rekordy z tabeli
i18n
, korzystaj!c
z poni$szych zapyta" SQL:
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
269
CREATE TABLE 'article_translations'(
'id' INT UNSIGNED AUTO_INCREMENT NOT NULL,
'model' VARCHAR(255) NOT NULL,
'foreign_key' INT UNSIGNED NOT NULL,
'locale' VARCHAR(6) NOT NULL,
'field' VARCHAR(255) NOT NULL,
'content' TEXT default NULL,
KEY 'model__foreign_key'('model', 'foreign_key'),
KEY 'model__foreign_key__locale'('model', 'foreign_key', 'locale'),
PRIMARY KEY('id')
);
INSERT INTO 'article_translations'
SELECT 'id', 'model', 'foreign_key', 'locale', 'field', 'content'
FROM 'i18n';
Utwórz plik app/models/article_translation.php i wstaw do niego poni$szy kod:
<?php
class ArticleTranslation extends AppModel {
public $displayField = 'field';
}
?>
W#a&ciwo&'
displayField
w modelu t#umacze" poinformuje zachowanie
Translate
o tym,
które pole tabeli przechowuje nazw% t#umaczonego pola.
Na zako"czenie wprowad= zaznaczone pogrubion! czcionk! zmiany w pliku app/models/
article.php:
<?php
class Article extends AppModel {
public $validate = array(
'title' => 'notEmpty',
'body' => 'notEmpty'
);
public $actsAs = array(
'Translate' => array('title', 'body')
);
public $translateModel = 'ArticleTranslation';
}
?>
Zobacz równie/
„Ustawianie i zapami%tywanie j%zyka”.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
270
Ustawianie i zapami%tywanie j%zyka
W tym przyk#adzie dowiesz si%, jak udost%pni' u$ytkownikom mo$liwo&' zmiany j%zyka apli-
kacji, a tak$e jak zapami%ta' ich wybór za pomoc! ciasteczek (ang. cookies).
Zanim zaczniesz
Aby wykona' ten przyk#ad, niezb%dna b%dzie w pe#ni umi%dzynarodowiona aplikacja. W tym celu
musisz wykona' ca#y przyk#ad „T#umaczenie rekordów baz danych za pomoc! zachowania
Translate”.
Musimy tak$e skorzysta' z uk#adu aplikacji, który mo$emy modyfikowa'. Skopiuj plik default.ctp
z katalogu cake/libs/view/layouts do katalogu app/views/layouts.
Jak to zrobi(
1. Dodaj poni$sze instrukcje na ko"cu pliku app/config/bootstrap.php, tu$ przed
znacznikiem zamykaj!cym PHP:
Configure::write('Config.languages', array(
'eng' => __('English', true),
'pol' => __('Polski', true)
));
2. Zmie" uk#ad pliku app/views/layouts/default.ctp, dodaj!c w wybranym przez siebie
miejscu list% j%zyków (np. przed wywo#aniem metody
flash()
w komponencie
Session
):
<div style="float: right">
<?php
$links = array();
$currentLanguage = Configure::read('Config.language');
foreach(Configure::read('Config.languages') as $code => $language)
{
if ($code == $currentLanguage) {
$links[] = $language;
} else {
$links[] = $this->Html->link($language, array('lang' => $code));
}
}
echo implode(' - ', $links);
?>
</div>
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Rozdzia= 9. • Internacjonalizacja aplikacji
271
Wykorzystywane w kodzie ustawienie
Config.language zosta$o ustawione w pliku
app/config/
bootstrap.php
w ramach przyk$adu „T$umaczenie rekordów baz danych za pomoc# zachowania Translate”.
3. Utwórz plik app/app_controller.php o nast%puj!cej tre&ci:
<?php
class AppController extends Controller {
public $components = array('Language', 'Session');
}
?>
4. Utwórz plik app/controller/components/language.php o nast%puj!cej tre&ci:
<?php
class LanguageComponent extends Object {
public $controller = null;
public $components = array('Cookie');
public $languages = array();
public function initialize($controller) {
$this->controller = $controller;
if (empty($languages)) {
$this->languages = Configure::read('Config.languages');
}
$this->set();
}
public function set($language = null) {
$saveCookie = false;
if (empty($language) && isset($this->controller)) {
if (!empty($this->controller->params['named']['lang'])) {
$language = $this->controller->params['named']['lang'];
} elseif (!empty($this->controller->params['url']['lang'])) {
$language = $this->controller->params['url']['lang'];
}
if (!empty($language)) {
$saveCookie = true;
}
}
if (empty($language)) {
$language = $this->Cookie->read('language');
if (empty($language)) {
$saveCookie = true;
}
}
if (empty($language) && !array_key_exists($language, $this->
languages)) {
$language = Configure::read('Config.language');
}
Configure::write('Config.language', $language);
if ($saveCookie) {
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
CakePHP 1.3. Programowanie aplikacji. Receptury
272
$this->Cookie->write('language', $language, false, '1 year');
}
}
}
?>
Przejd= na stron% http://localhost/articles. Powiniene& zobaczy' list% artyku#ów, a w prawym
górnym rogu powinno si% pojawi' #!cze, które pozwoli na zmian% aktywnego j%zyka na polski.
Klikni%cie #!cza spowoduje wy&wietlenie polskich wersji artyku#ów. Efekt zosta# przedsta-
wiony na poni$szym rysunku.
Jak to dzia a
Zaczynamy od zadeklarowania listy wszystkich dost%pnych j%zyków, dzi%ki czemu mo$emy
bez problemu umie&ci' #!cze do zmiany aktywnego j%zyka. Lista s#u$y nam do stworzenia li-
sty hiper#!czy, która nast%pnie jest umieszczana w pliku uk#adu default.ctp. Jednocze&nie
faktyczne #!cza (a nie teksty) s! generowane dla wszystkich j%zyków poza aktywnym.
Aktywny j%zyk ustawiamy w zmiennej konfiguracyjnej CakePHP o nazwie
Config.language
.
Otrzymuje ona pewn! warto&' — w naszym przypadku eng — w pliku konfiguracyjnym
bootstrap.php. Je&li konieczna jest zmiana j%zyka, warto&' tego ustawienia powinna ulec
zmianie przed pierwszym u$yciem funkcji t#umaczenia.
Aby zachowa' porz!dek w kontrolerze, postanowili&my utworzy' komponent
Language
, który
obs#uguje zmian% j%zyka. Komponent ten przeszuka parametry nazwane, a tak$e te przekaza-
ne w adresie URL, pod k!tem parametru
lang
. Je&li j%zyk nie zosta# okre&lony w ten sposób,
komponent spróbuje znale=' j%zyk, korzystaj!c z ciasteczka.
Je&li nie zosta#o ustawione $adne ciasteczko, a tak$e w sytuacji, gdy nast!pi#o $!danie zmiany
j%zyka, komponent zapisze aktywny j%zyk w ciasteczku
language
, które b%dzie przechowywa-
ne przez okres jednego roku.
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Skorowidz
__(), 254
__n(), 254, 255
_authorize(), 151
_checkArgs(), 228
_findMethods, w#a&ciwo&', 92
_help(), 238
_helpCommand(), 238
_importCSV(), 229
_isJSON(), 207
_parseCSV(), 228
_randomPassword(), 223
_restLogin(), 212
_stop(), 223, 318
_usageCommand(), 238
A
Access Control Layer, Patrz
warstwa kontroli dost%pu
ACL, Patrz warstwa kontroli
dost%pu
acl_extras, plugin, 45
add(), 228
afterFind(), 77, 83, 114
AJAX, 96, 98
allow(), 23
allowedActions, parametr, 23
analyze(), 318
AppController, klasa, 24, 26
AppException, klasa, 322, 323
ArticlesController, klasa, 196,
254, 261
ArticlesController::index(), 191
assertEqual(), 283
assertFalse(), 283
assertIsA(), 283
assertNull(), 283
assertPattern(), 283
assertTags(), 284, 289
assertTrue(), 283
Auth, komponent, 23, 24, 26,
30, 33, 212
haszowanie hase#, 224
schemat autoryzacji, 27
u$ywanie i konfiguracja, 24
authorize, parametr, 23
automatyzacja zada", 243
autoryzacja
przy u$yciu tokenu, 213
schemat, 27
wykorzystanie warstwy
kontroli dost%pu, 38
B
backAutoCommit, w#a&ciwo&',
159
beforeFilter(), 23, 26
beforeFind(), 77, 83, 114
beforeRender, wywo#anie
zwrotne, 133
beforeSave, wywo#anie zwrotne,
117, 122
beforeValidate(), 318
bindModel(), 50, 63, 65, 71
blackHole(), 213, 217
C
Cache, klasa, 114
CakePHP, 13, 14, 15, 16
ekstraktor, 261
konwencja nazewnicza, 68
pow#oki, 219
regu#y walidacji, 104
t#umaczenia, 254
trasy domy&lne, 162
wyj!tki, 319
wyszukiwanie, 73, 74
CakeTestCase, klasa, 282, 283
CakeTestFixture, klasa, 281
catch-all, trasy, 175
dodawanie walidacji, 179
ciasteczka, 270
ClassRegistry::init(), 282
cleanInsert(), 306
commands, w#a&ciwo&', 237, 238
comma-separated values, Patrz
CSV
Config.language, 271, 272
Configure, klasa, 292
Configure::listObjects(), 182
ConnectionManager::getDataSo
urce(), 133
contain(), 58
contain, parametr, 58
format, 57
Containable, zachowanie, 50,
51, 53, 55, 56, 57, 59, 63, 65
cookies, Patrz ciasteczka
COUNT, 74, 83
Crookes, Neil, 142
CSV, 136
dynamiczne #adowanie
plików, 137
parsowanie plików, 134
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Skorowidz
326
D
dane testowe, 273, 278, 281
datasources, wtyczka, 134,
136, 139
diff(), 305
Dispatcher, klasa, 294
download(), 317, 318
E
wysy#anie, 308
wysy#anie z poziomu
pow#oki, 239
Email, komponent, 239, 308,
311, 312, 313
w#a&ciwo&ci, 242
endCase(), 283
endTest(), 283
error(), 223
execute(), 236, 238
expectAtLeastOnce(), 293
expectException(), 283
expectNever(), 293
expectOnce(), 293
EXPLAIN, 133
extract(), 237, 301
F
fclose(), 228
fgetcsv(), 228
fiksturki, 278, 281, 283, 284
filter(), 305
fixture, zadanie, 284
fixtures, Patrz dane testowe
fopen(), 228
foreignKey, ustawienie, 68
G
Geocodable, 15, 100, 122
Geocode, wtyczka, 122, 124, 126
geokodowanie adresów, 122
GET, parametr, 162, 166
get_class_vars(), 238
getInfo(), 323
getLog(), 134
getMagicDb(), 318
Google Maps, 122, 124, 126
GROUP, 74
H
has#a
hasz, 20
haszowanie, 223, 224
szyfrowanie, 24
help(), 228, 236
HelpTask, klasa, 236, 238
HttpSocket, klasa, 107, 194
HttpSocket::get(), 197
HttpSocketOauth, klasa, 142
I
I18n, klasa, 255
import(), 228, 237
in(), 222, 223
index(), 311
initialize(), 238
INNER JOIN, 66
InnoDb, 152
inputs, metoda, 23
insert(), 306, 307
internacjonalizacja, 249
komunikatów walidacji
w modelach, 256
tekstów w kontrolerach
i widokach, 250
isAuthorized, metoda, 23
isInterfaceSupported(), 133
J
JavaScript Object Notation,
Patrz JSON
j%zyk
ustawianie, 270
zapami%tywanie, 270
JOIN, z#!czenia, 66
jQuery, 96
JSON, 194
konsumowanie us#ugi, 194
json_decode(), 151, 198
json_encode(), 207
JSON-C, 194
K
Kairys, Donatas, 139
konwencja nazewnicza, 68
L
Language, komponent, 272
layout, w#a&ciwo&', 312
LEFT JOIN, 66
LIKE, 87, 88
listSources(), 138
lock(), 159
lockTimeoutErrorCode,
w#a&ciwo&', 159
logException(), 323
login(), 22, 23, 30
logout(), 22, 23
logowanie, 28
pobieranie informacji
o zalogowanym
u$ytkowniku, 33
zapisanie informacji
o u$ytkowniku, 31
<
#a"cuchy znaków, 305
#!cza trwa#e, 118
M
MagicDb, 314, 317, 318
main(), 222, 236
mapowanie obiektowo-
relacyjne, Patrz ORM
merge(), 305
mocks, Patrz za&lepki
Model, klasa, 50
Model-View-Controller,
Patrz MVC
model-widok-kontroler,
Patrz MVC
MVC, 196
MyISAM, 152
MySQL
blokady w =ródle danych, 152
transakcje, 152
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Skorowidz
327
N
named parameters, Patrz
parametry nazwane
named, parametr, 162, 166
numeric(), 305
O
OAuth, 142, 149, 151
OpenAuth, komponent, 46, 47
OpenID, 45
biblioteka, 45
integracja, 45, 46
plugin, 46, 47
options(), 98
optymalizacja dla wyszukiwarek
internetowych, 161
ORM, 298
P
paginate(), 95
Paginator, 98
pami%' podr%czna, 192
parametry nazwane, 164, 166
parsowanie
parametrów wiersza
polece", 224
plików CSV, 134
partial mocks, Patrz za&lepki
cz%&ciowe
permanent links,
Patrz #!cza trwa#e
PHP OAuth, biblioteka, 142
Poedit, 262, 263, 264, 265, 266
pola wirtualne, 80, 82, 83
PostsController, klasa, 193
powi!zania, 49
anulowanie zmian, 58
pow#oki, 219
tworzenie, 220
tworzenie zada"
wielokrotnego u$ytku, 229
uruchamianie, 220, 221, 222
wysy#anie wiadomo&ci
e-mail, 239
prefiksy, 36, 168, 172
printf(), 261, 262
ProfileRoute, klasa, 184
ProfilesController, klasa, 172, 217
przypadki testowe, 273, 282
pushDiff(), 305
Q
QueryLog, komponent, 133
R
read(), 151
readfile(), 318
redirect(), 291, 292
renderException(), 323
Representational State Transfer,
Patrz REST
RequestHandler, komponent,
191, 207
requireLogin(), 212
reset(), 242
resetBindings(), 58
REST, 199, 207
dodawanie uwierzytelniania,
208
reverse(), 305
RIGHT JOIN, 66
Robot, wtyczka, 243, 246
RobotTask, model, 246
Router::connect(), 174
Router::connectNamed(), 167
Router::parseExtensions(), 190
Routing.prefixes, 172
RSS, 138, 188
a pami%' podr%czna, 192
tworzenie kana#u, 188
RssHelper, klasa, 192
RssHelper::item(), 192
S
schedule(), 246
schema(), 82
Search Engine Optimization,
Patrz SEO
Security, komponent, 212,
213, 217
Security.salt, parametr, 24
Security::hash(), 223, 224
send(), 242, 312, 313
SEO, 118, 161
Session, komponent, 261
Set, klasa, 298, 301, 305
Set::combine(), 303
Set::extract(), 301, 302, 303
Set::format(), 304
Set::map(), 304
Set::matches(), 137
setAutoCommit(), 160
setConfig(), 138, 141, 151
setFlash(), 261
setup(), 113
Shell, klasa, 222
SimpleTest, biblioteka, 276, 278
slug, 118, 122
Sluggable, 15, 100, 118, 122
sort(), 305
sprintf(), 261, 304
startCase(), 283
startTest(), 283
startup(), 236
Story, Mark, 45, 294
String, klasa, 305, 306
stronicowanie, 93, 95
AJAX, 96
Syrup, wtyczka, 120
szperacze sieciowe, 192
T
TagsController::view(), 162
tekst, manipulacja, 305
template, w#a&ciwo&', 313
test cases, Patrz przypadki
testowe
test jednostkowy, 282
test_suite, 282
testAction(), 288, 289
testowanie, 273
akcji kontrolera i ich
widoków, 286
konfiguracja frameworka, 274
tworzenie danych testowych,
278
za&lepki, 290
testRedirect, w#a&ciwo&', 292
testsuite, pow#oka, 294
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ
Skorowidz
328
testy
jednostkowe, 273
uruchamianie w konsoli, 294
timeline(), 113, 114
t#umaczenie, 254, 262
rekordów baz danych, 266
tekstów zawieraj!cych
dynamicznie generowan!
tre&', 259
token(), 151
tokenize(), 306, 307
tokeny, 213, 217
transakcje, 152
translate(), 255
Translate, zachowanie, 266, 268
trasowanie, 161, 168, 172
odwrotne, 175
tworzenie w#asnych klas, 182
Twitter, rejestracja aplikacji,
143, 144
TwitterAccountBehavior,
klasa, 113
U
unbindModel(), 50, 56, 57
uniwersalnie unikalny
identyfikator, Patrz UUID
unlock(), 159, 160
UploadsController, klasa, 322
User::useToken(), 218
UsersController, klasa, 217
UserShell, klasa, 222, 237, 238
useToken(), 217
us#ugi sieciowe, 187
UUID, 217, 307
uuid(), 307
uwierzytelnianie, 19
konfiguracja prostego
systemu, 20
u$ytkownik
pobieranie informacji, 33
zapisanie informacji po
zalogowaniu, 31
zmiana domy&lnego
modelu, 27
V
validate, w#a&ciwo&', 102, 259
validateTwitter(), 106, 107
Validation, klasa, 104
Video::search(), 198
W
walidacja, 99
dodawanie wielu regu#, 100,
102
tworzenie w#asnych regu#,
104, 107
warstwa kontroli dost%pu, 38
web crawlers, Patrz szperacze
sieciowe
web services, Patrz us#ugi
sieciowe
welcome(), 246
wi!zania modeli, 49
modyfikowanie
parametrów, 59
modyfikowanie warunków, 63
tworzenie wielu powi!za", 66
wiersz polece", parsowanie
parametrów, 224
wirtualne pola, 80, 82, 83
write(), 151
wyj!tki, 319
wyszukiwanie, 73, 74, 87
stronicowanie, 93, 95
w#asne typy, 89, 92, 93
wywo#ania zwrotne, 109
X
Xdebug, 285
XmlHelper, klasa, 192
X-Path 2.0, 301, 302
Z
zachowania, 99
dodawanie nowych pól, 116
zadania, automatyzacja, 243
za&lepki, 290, 292, 293
cz%&ciowe, 293
z#!czenia, 65, 66
z#!czenia dora=ne, 84
*
=ród#a danych, 127
konsumowanie kana#ów RSS,
138
parsowanie plików CSV, 134
tworzenie, 142
Kup ksi
ąĪkĊ
Pole
ü ksiąĪkĊ