background image
background image

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ść

background image

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Ċ

background image

 

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Ċ

background image

 

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Ċ

background image

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Ċ

background image

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Ċ

background image

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(); ?>
&nbsp;-&nbsp;
<?php echo $this->Paginator->prev(); ?>
&nbsp;
<?php echo $this->Paginator->numbers(); ?>
&nbsp;
<?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Ċ

background image

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Ċ

background image

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)); ?>
&nbsp;-&nbsp;
<?php echo $this->Paginator->prev(__('<< Previous', true)); ?>
&nbsp;
<?php echo $this->Paginator->numbers(); ?>
&nbsp;
<?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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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)); ?>
&nbsp;-&nbsp;
<?php echo $this->Paginator->prev(__('<< Previous', true)); ?>
&nbsp;
<?php echo $this->Paginator->numbers(); ?>
&nbsp;
<?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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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()

__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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

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Ċ

background image

 

Skorowidz

 

326

D

dane testowe, 273, 278, 281
datasources, wtyczka, 134,

136, 139

diff(), 305
Dispatcher, klasa, 294
download(), 317, 318

E

e-mail

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Ċ

background image

 

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Ċ

background image

Czytaj dalej...

 

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Ċ