diff --git a/de/The-Manual.rst b/de/The-Manual.rst new file mode 100644 index 0000000000000000000000000000000000000000..f8f8ec632becbaa6fdb24f7cf7acfd1beec48633 --- /dev/null +++ b/de/The-Manual.rst @@ -0,0 +1,83 @@ +Das Handbuch +############ + +**`Hier klicken um zur CakePHP 1.1.x Version des Handbuchs zu +gelangen `_** + +Willkommen zum "Cookbook", der neuen wiki-ähnlichen CakePHP +Dokumentations Anwendung. Wir hoffen, daß diese neue Anwendung +Dokumentations Beiträge leichter machen wird, während gleichzeitig ein +hoher Qualitätsstandard für Veröffentlichung eingehalten wird. + +Ein ***großer*** Dank geht an `AD7six `_, den +Vorkämpfer der "Kochbuch" Idee. Er hat endlose Stunden mit der +Entwicklung, dem Testen und Verbessern dieser Anwendung zugebracht. + +Wie es funktioniert: +==================== + +#. Ihr geht auf eine bestimmte Seite und entdeckt einen Fehler, bemerkt, + daß etwas unvollständig ist, daß etwas noch überhaupt nicht erklärt + wird oder es einfach nicht so vorliegt, wie ihr es gerne hättet. +#. Logt euch in das "Kochbuch" mit Hilfe eures + `"Bakery" `_ Zuganges ein. +#. Editiert Abschnitte mit Hilfe einfachen HTMLs +#. Schaut in den nächsten Tagen vorbei um eure Änderungen zu sehen + +Diese Anwendung is absolut neu, deshalb habt bitte ein wenig Geduld weil +wir gerade ein paar letzte Dinge in Vorbereitung auf das CakePHP 1.2 +Release ausarbeiten + +Übersetzungen +============= + +Übersetzungen + +Wenn ihr mit Übersetzungen aushelfen wollt, schickt bitte eine Email an +John David Anderson (docs at cakephp punkt org) oder kontaktiert ihn via +IRC (#cakephp bei freenode, unter dem Namen \_psychic\_). + +Hinweise für Übersetzer: + +- Bitte keine `HTML + Zeichenreferenzen `_ + für Umlaute u.ä. verwenden. Im *Cookbook* ist alles UTF-8 kodiert, + sodass dies Dinge nur unnötig verkomplizieren würde. +- Haltet den Schreibstil locker, nicht übermäßig formell. +- Übersetzt bitte die Überschrift und den Inhalt. +- Ändert Links so ab, dass sie auf die noch zu + übersetzenden/übersetzten Seiten verlinken. +- Bitte setzt die Sprache in eurem Browser auf die Sprache in die ihr + übersetzt, sonst werden die Änderungen als englische Änderung + gespeichert und der Korrekturleser weiß ggf. nicht, um welche Sprache + es sich handeln soll. +- Bitte ändert das *Markup* nicht zu doll oder stellt übermäßig viele + neue Inhalte ein. Wenn es etwas zu ergänzen gibt, tut dies bitte + zuerst in der Originalfassung. +- Wenn Ihr einen englischen Begriff verwenden müßt, klammert ihn bitte + in ```` Tags. Z.B. "im *Controller* werden..." oder "in der + Kontrollklasse (*Controller*) werden...". +- Sende keine lückenhaften Übersetzungen ein. +- Bearbeite keinen Abschnitt, bei dem noch nicht freigegebene + Änderungen vorliegen. + +Wir arbeiten ständig an der Verbesserung der CakePHP Dokumentation und +hoffen, daß ihr uns bei unseren Bemühungen mit Hilfe des *Cookbooks* +unterstützen werdet. Jeder kann etwas zum Projekt beisteuern und wir +wären froh, wenn ihr etwas an die *Community* zurückgeben würdet. + + +.. toctree:: + :maxdepth: 1 + + The-Manual/Beginning-With-CakePHP + The-Manual/Basic-Principles-of-CakePHP + The-Manual/Developing-with-CakePHP + The-Manual/Common-Tasks-With-CakePHP + The-Manual/Core-Components + The-Manual/Core-Behaviors + The-Manual/Core-Helpers + The-Manual/Core-Utility-Libraries + The-Manual/Core-Console-Applications + The-Manual/Tutorials-Examples + The-Manual/Appendices \ No newline at end of file diff --git a/de/The-Manual/Appendices.rst b/de/The-Manual/Appendices.rst new file mode 100644 index 0000000000000000000000000000000000000000..10cc856a5d98251fe20bcf70a72c701b17ba3850 --- /dev/null +++ b/de/The-Manual/Appendices.rst @@ -0,0 +1,4 @@ +Anhänge +####### + +Standardinhalt. diff --git a/de/The-Manual/Basic-Principles-of-CakePHP.rst b/de/The-Manual/Basic-Principles-of-CakePHP.rst new file mode 100644 index 0000000000000000000000000000000000000000..333c9a09c29b14f8b3db0a22852518bcc673ab62 --- /dev/null +++ b/de/The-Manual/Basic-Principles-of-CakePHP.rst @@ -0,0 +1,23 @@ +Grundlagen von CakePHP +###################### + +Das CakePHP-Framework ist eine zuverlässige Grundlage für Deine +Anwendungen. Es kann jeden Bereich bewältigen, angefangen bei der ersten +Anfrage bis zur endgültigen Aufbereitung einer Webseite. Und da das +Framework den MVC-Prinzipien folgt, ermöglicht es Dir es einfach +anzupassen und die meisten Bereiche Deiner Anwendung zu erweitern. + +Das Framework bietet zudem eine grundsätzliche Organisationstruktur, von +Datei- und Tabellennamen, die Deine gesamte Anwendung einheitlich und +logisch erscheinen lässt. Das Prinzip ist einfach und zugleich mächtig. +Folge den Vorgaben und Du weisst immer genau wo welche Dinge zu finden +und wie sie organisiert sind. + + +.. toctree:: + :maxdepth: 1 + + Basic-Principles-of-CakePHP/CakePHP-Structure + Basic-Principles-of-CakePHP/A-Typical-CakePHP-Request + Basic-Principles-of-CakePHP/CakePHP-Folder-Structure + Basic-Principles-of-CakePHP/CakePHP-Conventions \ No newline at end of file diff --git a/de/The-Manual/Basic-Principles-of-CakePHP/A-Typical-CakePHP-Request.rst b/de/The-Manual/Basic-Principles-of-CakePHP/A-Typical-CakePHP-Request.rst new file mode 100644 index 0000000000000000000000000000000000000000..dcf8e2ddc2e8366b1ab45fd84da934c848ddd0e0 --- /dev/null +++ b/de/The-Manual/Basic-Principles-of-CakePHP/A-Typical-CakePHP-Request.rst @@ -0,0 +1,51 @@ +Eine normale CakePHP Anfrage +############################ + +Wir haben uns die grundlegenden Bestandteile von CakePHP angesehen. Nun +laßt uns gemeinsam anschauen, wie die Objekte bei einer normalen Anfrage +zusammenarbeiten. Nehmen wir unser ursprüngliches Beispiel wieder auf +und erinnern uns, dass unser Freund Ricardo gerade auf den “Buy A Custom +Cake Now!” Link der Seite einer CakePHP Anwendung geklickt hat. + +.. figure:: http://tempdocs.cakephp.org/index_files/droppedImage_1.png + :align: center + :alt: + +.. figure:: /img/typical-cake-request.gif + :align: center + :alt: Flow diagram showing a typical CakePHP request + + Flow diagram showing a typical CakePHP request +Figure: 2. Standardmässige Cake Anfrage. + +Schwarz = benötiges Element, Grau = optionales Element, Blau = callback + +#. Ricardo klickt auf den Link der nach http://www.example.com/cakes/buy + verweist und sein Browser startet eine Anfrage zu deinem Webserver. +#. Der Router analysiert die URL um die Parameter der Anfrage zu + bestimmen: Den controller, action, und alle anderen Argumente dieser + Anfrage, die Auswirkung auf die Business-Logik haben. +#. Das Routing verweist die Anfrage an die Controller Action (die + Methode der Controller Klasse). In diesem Falle die buy() Methode des + CakeController. Der Controllers beforeFilter Callback wird + aufgerufen, bevor irgendeine Action Logik ausgeführt wird +#. Der Controller kann das Model nutzen, um auf die Applikationsdaten + zuzugreifen. In unserem Beispiel nutzt der Controller das Modell um + Ricardos letzte Käufe aus der Datenbank zu lesen. Alle Model + Callbacks, Behaviours und Datenquellen können dabei verwendet werden. + Obwohl es nicht notwendig ist, dass eine Action überhaupt auf ein + Model zurückgreift, braucht jede CakePHP Controllerklasse mindestens + ein Model. +#. Nun hat das Modell die Daten an den Controller zurückgeliefert. +#. Der Controller kann Components nutzen, um weitere Aktionen + durchzuführen(Sessionmanipulation, Authentifizierung oder Emails + versenden beispielsweise). +#. Nachdem der Controller mittels Model und Components die Daten + vorbereitet hat, werden diese Daten mittels der set() Methode an den + View weitergeleitet. Controller Callbacks müssen eventuell noch + bearbeitet werden, bevor die Daten verschickt werden. Die View Logik + wird ausgeführt, welche Elements und/oder Helpers nutzen kann. Nach + Voreinstellung wird der View in einem Layout gerendert. +#. Zusätzliche Controller Callbacks (afterFilter() können nun + durchgeführt werden. Die Seite wird zu Ricardo geschickt. + diff --git a/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Conventions.rst b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Conventions.rst new file mode 100644 index 0000000000000000000000000000000000000000..500ae4f0f1734eaac9d823116beb35dc48b75234 --- /dev/null +++ b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Conventions.rst @@ -0,0 +1,202 @@ +CakePHP Konventionen +#################### + +Wir sind große Fans von festen Vereinbarungen über die Konfiguration. +Zwar dauert es ein bisschen mehr Zeit die Konventionen von CakePHP +kennen zu lernen, auf lange Sicht sparst du dir damit aber eine menge +Zeit: Wenn du dich an die Konventionen hältst, kannst du frei +erhältliche Funktionalitäten nutzen, und du befreist dich selbst von der +alptraumhaften Wartung die Übersicht über die Konfigurationsdateien zu +behalten. Konventionen erlauben auch eine sehr einheitliche +System-Entwicklung, die Entwicklern den Einstieg und die Unterstützung +erleichtert. + +Die CakePHP-Konventionen entstanden aus jahrelanger Erfahrung in der +Web-Entwicklung deren bewährten Praktiken. Obwohl wir empfehlen diese +Konventionen bei der Entwicklung mit CakePHP zu verwenden, wollen wir +nicht unerwähnt lassen, dass viele dieser Grundsätze auch einfach außer +Kraft gesetzt werden können. Dies kann insbesondere bei der Arbeit mit +*Legacy*-Systemen nützlich sein. + +Konventionen der Datei- und Klassennamen +======================================== + +Üblicherweise werden in Dateinamen unterstriche verwendet, während +Klassennamen zusammenhängend Gross/Klein geschrieben sind. Die Klasse +KissesAndHugsController kann zum Beispiel in der Datei +kisses\_and\_hugs\_controller.php gefunden werden. + +Der Name der Klasse die sich in einer Datei befindet, muss sich nicht +zwangsweise in dem Dateinamen wiederfinden. Die Klasse EmailComponent +befindet sich in der Datei mit dem Namen email.php, und die Klasse +HtmlHelper befindet sich in der Datei html.php. + +Hier sind einige Beispiele, wie Dateien für die verschiedenen +Klassen-Typen typischerweise in einer CakePHP Applikation benannt +werden: + +- Die Controller-Klasse **KissesAndHugsController** liegt in der Datei + namens **kisses\_and\_hugs\_controller.php** (beachte \_controller im + Dateinamen) +- Die Component-Klasse **MyHandyComponent** liegt in der Datei namens + **my\_handy.php** +- Die Model-Klasse **OptionValue** liegt in der Datei namens + **option\_value.php** +- Die Behavior-Klasse **EspeciallyFunkableBehavior** liegt in der Datei + namens **especially\_funkable.php** +- Die View-Klasse **SuperSimpleView** liegt in der Datei namens + **super\_simple.php** +- Die Helper-Klasse **BestEverHelper** liegt in der Datei namens + **best\_ever.php** + +Jede Datei liegt inner- oder unterhalb (in einem Unterverzeichnis) des +entsprechenden Verzeichnisses im app-Ordner. + +Model and Database Conventions +============================== + +Namen von Model-Klassen sind im Singular und GemischtKleinGross +geschrieben. + +Person, BigPerson, und ReallyBigPerson sind Beispiele für Klassennamen, +die den Konventionen entsprechen. + +Die Namen der Tabellen zu den Model-Klassen sind im Plural, mit +Unterstrichen und klein geschrieben. Die Tabellen zu den oben genannten +Modellen wären also people, big\_people, and really\_big\_people. + +Foreign-keys in hasMany-, belongsTo- oder hasOne-Beziehungen werden per +default erkannt an dem Namen (im Singular) des entsprechenden Modells +gefolgt von \_id. Als Beispiel nehmen wir die Beziehung: baker hasMany +cakes. In der cakes-Tabelle gibt es dann eine foreign-key-Spalte +baker\_id die den baker referenziert. + +Verknüpfungs-Tabellen, wie sie in hasAndBelongsToMany-Beziehungen +(HABTM) benötigt werden, sollten benannt werden, in dem die Namen der +verknüpften Modell-Tabellen in alphabetischer Reihenfolge und durch +Unterstrich verbunden werden (apples\_zebras statt zebras\_apples) + +. + +Alle Tabellen, mit denen CakePHP-Modelle interagieren, müssen eine +Spalte mit einem primary key haben damit jede Zeile der Tabelle +identifiziert werden kann. Solltest Du eine Tabelle benötigen, die keine +solche Spalte hat, wie z.B. die Verknüpfungs-Tabelle posts\_tags, so +wird nach der CakePHP-Konvention ein primary key zu der Tabelle +hinzugefügt. + +CakePHP unterstützt keine composite primary keys. Wenn du die +Join-Tabelle direkt bearbeiten willst, benutze +`query `_ - Aufrufe oder füge einen primary key zu +der Tabelle hinzu und arbeite auf ihr wie mit einem normalen Modell. +Z.B.: + +:: + + CREATE TABLE posts_tags ( + id INT(10) NOT NULL AUTO_INCREMENT, + post_id INT(10) NOT NULL, + tag_id INT(10) NOT NULL, + PRIMARY KEY(id)); + +Controller Conventions +====================== + +Controller Klassennamen sind im Plural, CamelCased (GroßKleinSchreibung) +und enden in ``Controller``. ``PeopleController`` und +``LatestArticlesController`` sind beides Beispiele von konventionellen +Controller-Namen. + +Die erste Methode, die du für einen Controller schreibst wird sicherlich +die ``index()`` Methode sein. Falls eine Anfrage einen Controller aber +keine Action beschreibt, führt CakePHP standardmäßig die ``index()`` +Methode des Controllers aus. Beispielsweise führt die Anfrage der URL +http://www.example.com/apples/ zum Aufruf der ``index()`` Methode des +``ApplesController``, wobei http://www.example.com/apples/view/ die +``view()`` Methode des ``ApplesController`` aufruft. + +Du kannst auch die Sichtbarkeit von Controller Methoden in CakePHP +verändern, indem du die Controller-Methoden um Unterstriche ergänzt, +dann wird die Methode nicht mehr direkt über das Web, aber für interne +Zwecke weiterhin zugänglich. Ein kleines Beispiel: + +:: + + _findNewArticles(); + } + + function _findNewArticles() { + //Logic to find latest news articles + } + } + + ?> + +Während die Seite http://www.example.com/news/lates/ weiterhin für den +User normal erreichbar ist, bekommt jemand, der die Seite +http://www.example.com/news/\_findNewArticles/ einen Fehler erhalten, +weil die Methode mit einem Unterstrich versehen wurde. + +URL Considerations for Controller Names +--------------------------------------- + +Wie du gesehen hast, Ein-Wort-Controller werden zu einem einfachen URL +Pfad weitergeleitet. Zum Beispiel: ``ApplesController`` (der in der +Datei 'apples\_controller.php' definiert ist) ist über +http://example.com/apples zugänglich. + +Mehr-Wort-Controller *können* irgendeine Form habe, die ähnlich dem +Controller Namen ist: + +- /redApples +- /RedApples +- /Red\_apples +- /red\_apples + +Diese werden alle zum index des RedApples Controller aufgelöst. Wie auch +immer, die Konvention lautet, dass deine URLs immer klein geschrieben +und mit Unterstrichen versehen sind, deshalb ist /red\_apples/go\_pick +die korrekte Form um die ``RedApplesController::go_pick`` Action +aufzurufenl. + +Weitere Informationen zu CakePHP URLs und Parameterbehandlung findest du +unter `Routes Configuration `_. + +View Konventionen +================= + +Die Vorlagendateien (template) der Views werden nach den +Controllerfunktionen die sie anzeigen - die Wörter werden durch +Unterstriche getrennt - benannt. Für die Funktion getReady() des +PeopleController würde die Viewvorlage /app/views/people/get\_ready.ctp +erwartet werden. + +Das Muster für die Vorlagendateien ist ganz einfach: +/app/views/controller/funktions\_name\_mit\_unterstrichen.ctp. + +Indem du die Dateien und Klassen deiner Applikation nach den CakePHP +Konventionen benennst, bekommst du schnell und einfach eine +funktionierendes System ohne in Konfigurationen wühlen zu müssen. Hier +noch ein abschließendes Beispiel um die Konventionen zu festigen: + +- Tabelle in der Datenbank: "people" +- Modellklasse: "Person", in der Datei /app/models/person.php +- Kontrollerklasse: "PeopleController", in der Datei + /app/controllers/people\_controller.php +- Viewvorlage in der Datei /app/views/people/index.ctp + +Wenn man diese Konventionen befolgt, dann weiß CakePHP das eine Anfrage +an die Adresse http://example.com/people/ einen Aufruf der Funktion +index() des PeopleController bedeutet. Weiterhin wird das Modell von +Person (welches an die Datenbanktabelle people gebunden ist) automatisch +verfügbar gemacht und das Ergebnis mit der Viewvorlage +/app/views/people/index.ctp ausgegeben. Diese Zusammenhänge wurden +vollautomatisch erstellt und müssen nicht von Hand erzeugt werden. + +Nachdem dir die Grundlagen von CakePHP bekannt sind, kannst du dich am +`CakePHP Blog Tutorial `_ versuchen und sehen wie das +ganze in der Praxis funktioniert. diff --git a/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Folder-Structure.rst b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Folder-Structure.rst new file mode 100644 index 0000000000000000000000000000000000000000..fff47cbb5905cadee9a07c6a2162e4a2e3cd4da3 --- /dev/null +++ b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Folder-Structure.rst @@ -0,0 +1,77 @@ +CakePHP Ordnerstruktur +###################### + +Nachdem du CakePHP heruntergeladen und entpackt hast, solltest du diese +Dateien und Ordner sehen: + +- app +- cake +- vendors +- .htaccess +- index.php +- README + +  + +Du wirst 3 Hauptordner bemerken: + +- Der *app*-Ordner ist der Ordner, in dem du deine Wunder wirkst: Hier + werden deine Anwendungsdateien abgelegt. +- Der *cake*-Ordner ist der Ordner in dem wir unsere Magie wirken + lassen. Merke dir, in diesem Ordner keine Dateien oder Ordner zu + verändern. Wenn du in diesem Kernbereich Veränderungen vornimmst, + können wir dir bei der Lösung von Problemen nicht mehr weiterhelfen. +- Der *vendors*-Ordner schließlich ist der Ort an dem du die + *third-party PHP libraries* (Dritt-Anbieter PHP-Bibliotheken) + ablegst, welche du für dein CakePHP benötigst. + +Der App Ordner +============== + +Der CakePHP’s app Ordner ist der Ort, in dem du den größten Teil deiner +Anwendungsentwicklung vornimmst. Lass uns einen genaueren Blick in den +Ordner werfen. + +config + Enthält die (wenigen) Konfigurationsdateien die CakePHP benötigt. + Details der Datenbankverbindung, bootstrapping, + Hauptkonfigurationsdateien und weiteres mehr sollte hier gespeichert + sein. +controllers + Enthält deine Anwendungscontroller und ihre Komponenten. +locale + Beinhaltet Textdateien für die Internationalisierung (I18N). +models + Enthält deine Anwendungsmodelle, behaviors und Datenquellen. +plugins + Enthält plugin Pakete. +tmp + Dies ist der Ort an dem CakePHP temporäre Daten ablegt. Wo aktuell + die Daten gespeichert werden hängt davon ab, wie du CakePHP + konfiguriert hast, aber dieser Ordner wird normalerweise dazu + verwendet, um Modellbeschreibungen, Logdateien und manchmal auch + Sessioninformationen zu speichern. + + Stelle sicher, dass dieses Verzeichnis existiert und Schreibrechte + besitzt, anderenfalls wird die Ausführbarkeit deiner + App::import('application') ernsthaft angeschlagen. Im *debug mode*, + warnt dich CakePHP, wenn das nicht der Fall ist. + +vendors + Jede third-party Klasse oder Bibliothek kann hier abgelegt werden. + Dies vereinfacht den Zugriff durch die Nutzung der + App::import('vendor', 'name') Funktion. Einigen Beobachtern wird die + als scheinbar überflüssig erscheinen, da es auf der obersten + Verzeichnissebene bereits einen vendors Ordner in der + Verzeichnisstruktur gibt. Wir werden später die Unterschiede dieser + Ordner näher beleuchten, wenn wir über die Verwaltung mehrerer + Anwendungen und komplexeren System-Setups sprechen. +views + Präsentationsdatein sind hier abgelegt: Elemente, error pages, + helpers, layouts und view Dateien. +webroot + In einer Produktionsumgebung wird dieser Ordner für die + Bereitstellung des Stammordners für deine Anwendung dienen. Weitere + darin enthaltene Ordner dienen als Platzhalter für die + Bereitstellung von CSS stylesheets, Bildern und JavaScript Dateien. + diff --git a/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Structure.rst b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Structure.rst new file mode 100644 index 0000000000000000000000000000000000000000..32d9c687c10bb21d0238ce0782fc47b2f23e36d8 --- /dev/null +++ b/de/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Structure.rst @@ -0,0 +1,111 @@ +Die Struktur von CakePHP +######################## + +CakePHP verfügt über Controller-, Model- und View-Klassen, bietet aber +darüber hinaus auch einige zusätzliche Klassen und Objekte, um die +Entwicklung mittels des MVC-Entwurfsmuster zu beschleunigen und zu +erleichtern. Komponenten, Behaviors und Helper sind erweiterbare und +wiederverwendbare Klassen, die es ermöglichen die MVC Basis-Klassen +schnell um eine gewünschte Funktionalität zu erweitern. Details zur +Benutzung dieser Werkzeuge befinden sich in den nachfolgenden Kapiteln. +In diesem wollen wir uns zunächst einen Überblick verschaffen. + +Controller-Erweiterungen +======================== + +Eine Komponente ist eine Klasse, die uns bei der Controller-Logik +unterstützt. Soll eine Programm-Logik von verschiedenen Controllern +(oder Applikationen) gemeinsam benutzt werden, ist eine Komponente in +der Regel die richtige Wahl. Als Beispiel sei die Core-Klasse +EmailComponent erwähnt, mit der das Erstellen und Versenden von E-Mails +ein Kinderspiel ist. Anstatt eine Methode die diese Aufgabe erfüllt in +einem Controller zu implementieren, kann diese Logik gebündelt werden um +sie gemeinsam benutzen zu können. + +Controller verfügen des Weiteren über Callback-Routinen. Diese Callbacks +sind für den Fall gedacht, daß Programmlogik zwischen CakePHP's internen +Transaktionen eingefügt werden soll. Verfügbare Callbacks sind: + +- ``beforeFilter()``, wird vor jeglicher Controller-Aktion ausgeführt +- ``beforeRender()``, wird nach der Controller-Logik, aber vor dem + Rendern des Views ausgeführt +- ``afterFilter()``, wird nach allen Controller-Aktionen einschließlich + dem Rendern des Views ausgeführt. Zwischen afterRender() und + afterFilter() gibt es keinen Unterschied, außer wenn die Funktion + render() manuell in einer Controller-Methode aufgerufen wurde und + anschließend noch Code ausgeführt wird. + +View Extensions +=============== + +Ein *Helper* ist eine Klasse die die Ansichtslogik unterstützt. Ähnlich +wie eine Komponente in einem *Controller* verwendet wird, ermöglichen +Helfer den Zugriff und Aufteilung der Präsentations-Logik zwischen den +*Views*. Eine der Haupthelfer, *AjaxHelper*, macht Ajax requests +innerhalb der *Views* viel einfacher. + +Viele Programme haben Teile von *View* code die wiederholt verwendet +werden. CakePHP erleichtert die Wiederverwendung von *View* Code mit +Layouts und Elementen. Normalerweise wird jeder *View* der von einem +*Controller* erstellt wird in ein Layout eingefügt. Elemente werden +verwendet wenn kleine Teile von Inhalten in mehreren *Views* +wiederverwendet werden sollen. + +Model Extensions +================ + +In ähnlicher weise arbeiten Behaviors als der Weg gemeinsame +Funktionalitäten zwischen den Modellen hinzuzufügen. Wenn du zum +Beispiel Nutzerdaten in einer Baumstruktur speicherst, kannst du das +Verhalten deines Nutzer-Modell wie ein Baum definieren und freie +Funktionalitäten für das Entfernen, Einfügen und Verlagerung der Knoten +in deiner zugrundeliegenden Baumstruktur hinzufügen. + +Modelle werden auch von einer anderen Klasse genannt DataSource +verwendet. DataSources sind Abstraktionen, die es Modellen ermöglichen, +verschiedene Arten von Daten konsistent zu verändern. Während die +Hauptquelle der Daten in einer CakePHP Anwendung oft eine Datenbank ist, +könntest du auch weitere DataSources schreiben, die es deinen Modellen +ermöglichen RSS feeds, CSV files, LDAP entries, oder iCal events +darzustellen. DataSources erlaubt es dir aus verschiedenen Quellen +Datensätze zu assoziieren: anstatt auf SQL-Joins beschränkt zu sein, +ermöglichen es dir die DataSources festzulegen, daß dein LDAP model mit +vielen iCal events verbunden ist. + +Genau wie bei den Kontrollern, sind Modelle mit Call-Back +Funktionalitäten ausgestattet: + +- beforeFind() +- afterFind() +- beforeValidate() +- beforeSave() +- afterSave() +- beforeDelete() +- afterDelete() + +Die Namen dieser Methoden sollte beschreibend genug sein, um zu wissen, +was sie tun. Die weiteren Details werden im Kapitel über die Modelle +behandelt. + +Application Extensions +====================== + +Controller, Helper und Modelle haben alle eine Elternklasse, welche du +nutzen kannst um applikationsweite Veränderungen zu definieren. +AppController (hier: /app/app\_controller.php), AppHelper (hier: +/app/app\_helper.php) und AppModel (hier: /app/app\_model.php) sind gute +Plätze um dort Methoden, die du zwischen den Controllern, Helpers oder +Modellen teilen willst, einzufügen. + +Obwohl sie keine Klassen oder Dateien sind spielen Routen eine Rolle in +den Requests an CakePHP. Eine Route definiert wie CakePHP die URL den +ControllerAktionen zuordnet. Das Standardverhalten für diese URL +“/controller/action/var1/var2” mapt zu Controller +Controller::action($var1, $var2), aber du kannst Routen auch bearbeiten +um die URLs und ihre Verarbeitung anzupassen. + +Manche Funktionen in einer Applikation werden als ein ganzes +zusammengepackt. Ein Plugin ist ein Packet aus Modellen, Controllern und +Views welches einen bestimmten Bereich abdeckt. Ein +Userverwaltungssystem oder ein einfacher Blog passen möglicherweise gut +als CakePHP-Plugins. diff --git a/de/The-Manual/Beginning-With-CakePHP.rst b/de/The-Manual/Beginning-With-CakePHP.rst new file mode 100644 index 0000000000000000000000000000000000000000..f4da3dd530083df220a20cb430a0eb08cda10b61 --- /dev/null +++ b/de/The-Manual/Beginning-With-CakePHP.rst @@ -0,0 +1,22 @@ +Der Anfang mit CakePHP +###################### + +Willkommen im Kochbuch, dem Handbuch für das CakePHP web application +framework, das die Entwicklung in PHP zum Kinderspiel macht! (Im +Englischen werden einfache Aufgaben oft als 'piece of cake' bezeichnet.) + +Dieses Handbuch setzt ein allgemeines Verständnis der Sprache PHP und +Grundlagenwissen im Bereich Objektorientierter Programmierung (OOP) +voraus. Die verschiedenen Funktionalitäten des Frameworks verwenden +unterschiedlichste Technologien — wie z.b. SQL, JavaScript, und XML — +dieses Handbuch geht nicht im Detail auf die Funktionsweisen dieser +Technologien ein, sondern beschreibt nur wie sie im Umfeld des +Frameworks eingesetzt werden. + + +.. toctree:: + :maxdepth: 1 + + Beginning-With-CakePHP/What-is-CakePHP-Why-Use-it + Beginning-With-CakePHP/Where-to-Get-Help + Beginning-With-CakePHP/Understanding-Model-View-Controller \ No newline at end of file diff --git a/de/The-Manual/Beginning-With-CakePHP/Understanding-Model-View-Controller.rst b/de/The-Manual/Beginning-With-CakePHP/Understanding-Model-View-Controller.rst new file mode 100644 index 0000000000000000000000000000000000000000..5831bde45dbdaf0c9217e26601eb1cfddadd7749 --- /dev/null +++ b/de/The-Manual/Beginning-With-CakePHP/Understanding-Model-View-Controller.rst @@ -0,0 +1,65 @@ +Model-View-Controller verstehen +############################### + +Gut geschriebene CakePHP Anwendungen folgen dem MVC +(Model-View-Controller) Software Entwurfsmuster. Programmierungen nach +MVC teilen die Anwendung in drei Haupt Bestandteile. Das Model +präsentiert die Anwendungsdaten, die View generiert eine Präsentation +der Model-Daten, und der Controller behandelt und steuert +Benutzeranfragen. + +.. figure:: /img/basic_mvc.png + :align: center + :alt: Abblidung 1 + + Abblidung 1 +Abbildung 1: Eine elementare MVC Anfrage + +Abbildung 1 zeigt ein Beispiel einer einfachen MVC Anfrage in CakePHP. +Zur Veranschaulichung nehmen wir an, ein Benutzer namens Ricardo hat +gerade auf den "Kaufe einen eigenen Kuchen!"-Link deiner Webanwendung +geklickt. + +#. Ricardo klickt auf den Link der zur Adresse + http://www.example.com/cakes/buy führt, und der Browser sendet eine + Anfrage an Deinen Webserver. +#. Der Dispatcher überprüft die angeforderte URL (/cakes/buy) und leitet + die Anfrage zum zuständigen Controller weiter. +#. Der Controller verarbeitet Anwendungsspezifische Abläufe. Zum + Beispiel überprüft die Anwendnung ob Ricardo eingeloggt ist. +#. Der Controller benutzt die Models um Zugang zu den Anwendungsdaten zu + bekommen. In den meisten Fällen präsentieren Models die verschiedenen + Tabellen einer Datenbank, aber es könnten genau so gut + `LDAP `_ Einträge, + `RSS `_ Feeds oder Dateien sein. In + unserem Beispiel wird das Model benutzt um Ricardo's letzte Einkäufe + aus der Datenbank auszulesen. +#. Sobald der Controller die Daten verarbeitet hat, werden diese an die + View weitergeleitet. Die View formatiert die Daten und bereitet Sie + zur Ausgabe vor. Views in CakePHP sind meistens im HTML Format. Es + könnte aber auch einfach ein PDF, XML Dokument oder ein JSON Objekt, + je nach Anforderung, ausgegeben werden. +#. Sobald die View die Ausgabe vorbereitet hat, wird der Inhalt an + Ricardos Browser ausgegeben. + +Fast jede Anfrage deiner Anwendung folgt diesem Grundmuster. Es kommen +später noch einige Cake-spezifische Details hinzu. + +Warum MVC verwenden? +==================== + +Weil es ein bewährtes und effektives Software Entwurfsmuster ist, das +eine Webanwendung in eine wartbare, modulare und effizient entwickelte +Anwendung verwandelt. Anwendungsaufgaben in Models, Views und +Controllers zu teilen macht Deine Anwendung sehr schlank. Neue Features +sind einfach hinzugefügt, alte Features schnell in einer neuen +Oberfläche verpackt. Die modular und unterteilte Logik erlaubt +Entwicklern und Designern gleichzeitig an der Anwendung zu arbeiten. +Dies beinhaltet ebenso die schnelle Entwicklung eines ersten Prototyps. +Dadurch ist es ebenso möglich einen Teil der Anwendung zu verändern, +ohne einen anderen Teil zu beeinflussen. + +Wenn Du noch nie eine Anwendung mit dieser Methode entwickelt hast, wird +es eine Weile dauern. Aber wir sind sehr zuversichtlich, dass wenn Du +Deine erste CakePHP Anwendung erstellt hast, Du nie wieder anders +Arbeiten möchtest. diff --git a/de/The-Manual/Beginning-With-CakePHP/What-is-CakePHP-Why-Use-it.rst b/de/The-Manual/Beginning-With-CakePHP/What-is-CakePHP-Why-Use-it.rst new file mode 100644 index 0000000000000000000000000000000000000000..913c32fc831f54f052d30354bcd08b6ba5e0832f --- /dev/null +++ b/de/The-Manual/Beginning-With-CakePHP/What-is-CakePHP-Why-Use-it.rst @@ -0,0 +1,44 @@ +Was ist CakePHP? Für was brauche ich das? +######################################### + +CakePHP is ein freies, open-source-basiertes, rapides +Entwicklungsframework für PHP. Es ist eine Grundlage für das +Programmieren von Web-Applikationen. Unser primäres Ziel ist, in einer +strukturierten und rapiden Umgebung zu arbeiten - ohne die Flexiblität +zu verlieren. + +CakePHP lässt das Eintönige der Web-Entwicklung links liegen. Wir +stellen alle Werkzeuge zur Verfügung, die man wirklich braucht, wenn man +zum Programmieren beginnt: Die logische Struktur in Ihrer Applikation. +Anstatt immer wieder das Rad neu zu erfinden, probieren Sie CakePHP und +starten Sie mit den wichtigen Sachen Ihrer Applikation. + +CakePHP hat ein aktives Entwicklungsteam und eine Community, die zum +Erfolg des Projekts beiträgt. Im Gegensatz zu einer 0815-Kopie, finden +Sie mit CakePHP eine Applikation, die ausreichlich getestet wurde und +laufend erweitert wird. + +Hier ist eine Liste von Features, die Sie beim Benutzen von CakePHP +sicherlich genießen werden: + +- Aktive, freundliche Community +- Flexible Lizenz +- Kompatibel mit PHP4 und PHP5 +- Integrierte CRUD für Datenbank-Interkationen +- Scaffolding +- Code-Generierung +- "Model-View-Controller (MVC)"-Architektur +- Automatische Erstellung von "schönen" URLs +- Integrierte Validierung +- Schnelle und flexible Template-Erstellung (PHP-Syntax, mit + "Helferleins") +- "Helferleins" für AJAX, JavaScript, HTML Formulare und mehr +- E-Mail-, Cookie-, Sicherheit-, Sitzung- (Session-) und + Anfrage-Management-Komponenten +- Flexible Benutzerrechteverwaltung +- Automatische Daten-Transformierung +- Flexibles Caching +- Mulitilingual (lokalisiert) +- Funktioniert in jedem Verzeichnis und nur mit wenigen bis gar nicht + notwendige Apache-Konfigurationen + diff --git a/de/The-Manual/Beginning-With-CakePHP/Where-to-Get-Help.rst b/de/The-Manual/Beginning-With-CakePHP/Where-to-Get-Help.rst new file mode 100644 index 0000000000000000000000000000000000000000..5fef1c629507224db2027489215d53d03e479f8e --- /dev/null +++ b/de/The-Manual/Beginning-With-CakePHP/Where-to-Get-Help.rst @@ -0,0 +1,111 @@ +Wo finde ich Hilfe? +################### + +  + +Die offizielle CakePHP Webseite +=============================== + +`http://www.cakephp.org `_ + +Die offizielle CakePHP Webseite ist immer einen Besuch Wert. Sie bietet +Links zu oft genutzten Entwicklerwerkzeugen, *Screencasts*, der +Möglichkeit zu spenden und Downloads. + +Das Cookbook +============ + +`http://book.cakephp.org `_ + +Das Cookbook sollte wahrscheinlich die erste Anlaufstelle sein um +Antworten zu erhalten. Wie bei vielen anderen open source Projekten, +bekommen wir ständig neue Leute. Versuche Dein Bestes Deine Fragen +zuerst selbst zu beantworten. Antworten können sich Zeit lassen, aber +bleiben besser in Erinnerung und außerdem reduzierst Du die Last bei +unserem Support. Sowohl das Handbuch als auch die *API* haben einen +Online-Komponente. + +Die Bakery +========== + +`http://bakery.cakephp.org `_ + +Die CakePHP Bakery ist eine Clearingstelle für Alles was CakePHP +betrifft. Zieh Dir die Tutorials, Fallstudien und den Beispielkode rein. +Nachdem Du CakePHP kennengelernt hast, log Dich ein und teile deine +Erfahrung mit der Gemeinschaft und erlange sofortigen Ruhm und Reichtum. + +Die API +======= + +`http://api.cakephp.org/ `_ + +Genau auf den Punkt und direkt von den Kern-Entwicklern ist die CakePHP +API (Anwendungsprogrammierschnittstelle) die zuverlässigste verfügbare +Dokumentation für all die wesentlichen Details des internen +Zusammenspiels des Frameworks. Es ist eine direkte Kode-Referenz, also +bring Deinen Sturzhelm mit. + +CakeForge +========= + +`http://www.cakeforge.org `_ + +CakeForge ist eine weitere Entwickler-Resource, die Du nutzen kannst um +Deine CakePHP Projekte mit Anderen zu teilen. Wenn Du Deine +Killer-Komponente oder ein lobenswertes Plugin planst oder +veröffentlichen willst, dann wirf einen Blick auf CakeForge an. + +Die Test-Fälle +============== + +`http://api.cakephp.org/tests `_ + +Wenn Du jemals das Gefühl hast, dass die Informationen, die in der API +zur Verfügung gestellt werden, nicht ausreichend sind, nutze den +Quelltext der Testfälle, die CakePHP 1.2 verfügbar sind. Sie können als +praktische Beispiele für Funktionen und die Nutzung von Datenmitgliedern +einer Klasse angesehen werden. In Deiner CakePHP Distribution sind die +Testfälle unter + +:: + + cake/tests/cases + +zu finden. + +Der IRC-Kanal +============= + +`#cakephp @ irc.freenode.net `_ + +Wenn Du ratlos bist, melde dich auf dem CakePHP-IRC-Kanal. Jemand vom +Entwicklerteam ist in der Regel dort, insbesondere während der +Tageslichtstunden von Nord- und Südamerika. Wir freuen uns von Dir zu +hören, egal ob Du Hilfe brauchst, einen Benutzer in Deiner Region suchst +oder Deinen nagelneuen Sportwagen spenden willst. + +Die Google-Group +================ + +`http://groups.google.com/group/cake-php `_ + +CakePHP hat außerdem eine sehr aktive Google-Gruppe (Google Group). Sie +kann eine großartige Quelle für bereits archivierte Antworten sein, +häufig gestellte Fragen und um Antworten auf dringende Probleme zu +erhalten. + +In Deutschland gibt es für deutschsprachige CakePHP-Benutzer eine eigene +Google-Gruppe, die zwar bisher nicht so aktiv ist, wie das amerikanische +Gegenstück, dafür aber wird Deutsch gesprochen. Die deutsche +Google-Gruppe zu CakePHP findest Du hier + +`http://groups.google.com/group/cakephp-de `_ + +Das inoffizielle CakePHP Forum +============================== + +`http://www.cakephpforum.net `_ + +Das inoffizielle CakePHP Forum ist eine alternative Anlaufstelle für +CakePHP Bäcker um Informationen über CakePHP auszutauschen. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP.rst b/de/The-Manual/Common-Tasks-With-CakePHP.rst new file mode 100644 index 0000000000000000000000000000000000000000..c85596af08ac42a65efd3e7e6ad1fe3ec50cbbd6 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP.rst @@ -0,0 +1,19 @@ +Einfache Funktionen mit CakePHP +############################### + +  + + +.. toctree:: + :maxdepth: 1 + + Common-Tasks-With-CakePHP/Data-Validation + Common-Tasks-With-CakePHP/Data-Sanitization + Common-Tasks-With-CakePHP/Error-Handling + Common-Tasks-With-CakePHP/Debugging + Common-Tasks-With-CakePHP/Caching + Common-Tasks-With-CakePHP/Logging + Common-Tasks-With-CakePHP/Testing + Common-Tasks-With-CakePHP/Internationalization-Localization + Common-Tasks-With-CakePHP/Pagination + Common-Tasks-With-CakePHP/REST \ No newline at end of file diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Caching.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Caching.rst new file mode 100644 index 0000000000000000000000000000000000000000..7c7254e6339147b9057c9b4866b5eca225ba8286 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Caching.rst @@ -0,0 +1,9 @@ +Caching +####### + +Caching kann in CakePHP-Anwendungen auf mehreren Ebenen verwendet +werden. Siehe dazu `wie das Browser-Caching unterbunden werden +kann `_, `Ganzseiten- oder +Element-Caching `_, `per-request +Query-Caching `_ oder `die Cache-Klasse - zum +Cachen alles und jedem `_ für weitere Informationen. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Data-Sanitization.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Data-Sanitization.rst new file mode 100644 index 0000000000000000000000000000000000000000..fd343ccd99c6bf45cac39c870e252c7be892fe70 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Data-Sanitization.rst @@ -0,0 +1,112 @@ +Data Sanitization +################# + +The CakePHP Sanitize class can be used to rid user-submitted data of +malicious data and other unwanted information. Sanitize is a core +library, so it can be used anywhere inside of your code, but is probably +best used in controllers or models. + +CakePHP already protects you against SQL Injection **if** you use +CakePHP's ORM methods (such as find() and save()) and proper array +notation (ie. array('field' => $value)) instead of raw SQL. For +sanitization against XSS its generally better to save raw HTML in +database without modification and sanitize at the time of +output/display. + +All you need to do is include the Sanitize core library (e.g. before the +controller class definition): + +:: + + App::import('Sanitize'); + + class MyController extends AppController { + ... + ... + } + +Once you've done that, you can make calls to Sanitize statically. + +paranoid +======== + +paranoid(string $string, array $allowedChars); + +This function strips anything out of the target $string that is not a +plain-jane alphanumeric character. The function will overlook certain +characters by passing them in $allowedChars array. + +:: + + $badString = ";:'; + echo Sanitize::html($badString); + // output: <font size="99" color="#FF0000">HEY</font><script>...</script> + echo Sanitize::html($badString, true); + // output: HEY... + +escape +====== + +escape(string $string, string $connection) + +Used to escape SQL statements by adding slashes, depending on the +system's current magic\_quotes\_gpc setting. $connection is the name of +the database to quote the string for, as named in your +app/config/database.php file. + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +This function is an industrial-strength, multi-purpose cleaner, meant to +be used on entire arrays (like $this->data, for example). The function +takes an array (or string) and returns the clean version. The following +cleaning operations are performed on each element in the array +(recursively): + +- Odd spaces (including 0xCA) are replaced with regular spaces. +- Double-checking special chars and removal of carriage returns for + increased SQL security. +- Adding of slashes for SQL (just calls the sql function outlined + above). +- Swapping of user-inputted backslashes with trusted backslashes. + +The $options argument can either be a string or an array. When a string +is provided it's the database connection name. If an array is provided +it will be merged with the following options: + +- connection +- odd\_spaces +- encode +- dollar +- carriage +- unicode +- escape +- backslash + +Usage of clean() with options looks something like the following: + +:: + + $this->data = Sanitize::clean($this->data, array('encode' => false)); + diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..131b3433c144c39cf52653f638aa4462e8f925d5 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,1040 @@ +Datenvalidierung +################ + +Die Validierung von Daten ist ein enorm wichtiger Teil in jeder +Anwendung. Sie hilft, sicherzustellen, dass die Daten in einem Model mit +den Business Rules der Anwendung übereinstimmen. Zum Beispiel könntest +Du sicher stellen wollen, dass Passwörter mindestens acht Zeichen lang +sind oder dass Benutzernamen einzigartig sind. Validierungsregeln +festzulegen macht die Verarbeitung von Formularverarbeitung wesentlich +einfacher. + +Es gibt viele verschiedene Aspekte im Validierungsprozess. In diesem +Abschnitt möchten wir dies aus Sicht des Models abdecken, im +Wesentlichen was passieren soll, wenn Du die save()-Methode Deines +Models aufrufst. Mehr Informationen zur Darstellung der +Validierungsfehler finden sich im Abschnitt zum FormHelper. + +Der erste Schritt zur Validierung, ist die Festlegung der +Validierungsregeln im Model. Diese Regeln werden im +Model::validate-Array in der Definition Deiner Modelklasse dargestellt, +zum Beispiel: + +:: + + + +Im diesem Beispiel wurde das $validate-Array im User-Model definiert, es +beinhaltet aber keine Regeln. Angenommen, die users-Tabelle besteht aus +den Feldern login, password, email und born, zeigt das folgende Beispiel +einige simple Validierungsregeln, die sich auf diese Felder beziehen: + +:: + + 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + ?> + +Das folgende Beispiel zeigt, wie Validierungsregeln zu den Model-Feldern +hinzugefügt werden können. Das login-Feld soll nur aus Buchstaben und +Zahlen bestehen, die E-Mail-Adresse soll gültig sein und das born-Feld +soll ein gültiges Datum sein. Durch die Definition dieser Regeln kann +CakePHP automagisch Fehlermeldungen in Formularen anzeigen, wenn dieses +nicht valide sind. + +CakePHP besitzt viele eingebaute Validierungsregeln, deren Benutzung +einfacher nicht sein könnte. Einige der eingebauten Regeln erlauben Dir, +die Formatierung von E-Mail-Adressen, URLs und Kreditkartennummern, zur +überprüfen - dazu später mehr. + +Hier ein etwas komplexeres Beispiel, in welchem einige der eingebauten +Regeln verwendet werden: + +:: + + array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Alphabets and numbers only' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Between 5 to 15 characters' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Mimimum 8 characters long' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date', + 'allowEmpty' => true + ) + ); + } + ?> + +Für das Feld login sind zwei Regeln definiert: es darf nur aus +Buchstaben und Zahlen bestehen und zwischen fünf und 15 Zeichen lang +sein. Dass Passwort muss mindestens acht Zeichen lang sein. Außerdem +müssen E-Mail-Adresse Gebursdatum gültig sein. Hier siehst Du auch, wie +man eigene Fehlermeldungen einbauen kann, falls die Validierung +fehlschlägt. + +Wie das Beispiel zeigt, kann ein einzelnes Feld mehrerer +Validierungsregeln besitzen. Falls die eingebauten Regeln nicht +ausreichen, können eigene definiert werden. + +Jetzt, wo Du einen groben Überblick über die Validierung hast, lass uns +lernen, wie Regeln im Model definiert werden. Es gibt hier drei +verschiedene Wege: einfache Arrays, eine Regel pro Feld und mehrere +Regeln pro Feld. + +Einfache Regeln +=============== + +Wie der Name schon sagt, ist dies der einfachste Weg, eine +Validierungsregel zu definieren. Die allgemeine Syntax um auf diese Art +Regeln festzulegen sieht wie folgt aus: + +:: + + var $validate = array('fieldName' => 'ruleName'); + +Wobei 'fieldName' der Name des Feldes ist, auf welches die Regel +angewendet werden soll und 'ruleName' der Name einer vordefinierten +Regel wie 'alphaNumeric', 'email' oder 'isUnique'. + +Um zum Beispiel sicherzustellen, dass ein Nutzer eine formal korrekte +E-Mail-Adresse angibt, kann man diese Regel verwenden: + +:: + + var $validate = array('user_email' => 'email'); + +Eine Regel pro Feld +=================== + +Diese Definitions-Technik schafft eine bessere Kontrolle über die +Funktionsweise der Validierungsregeln: + +:: + + var $validate = array( + 'fieldName1' => array( + 'rule' => 'ruleName', // or: array('ruleName', 'param1', 'param2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // or: 'update' + 'message' => 'Your Error Message' + ) + ); + +Wie man hier sehen kann, ist jedes Feld (nur ein Feld im obigen +Beispiel) mit einem *Array* verknüpft, welches fünf Schlüsselwörter +beinhaltet: ‘\ *rule*\ ’, ‘\ *required*\ ’, ‘\ *allowEmpty*\ ’, +‘\ *on*\ ’ und ‘\ *message*\ ’. Alle Schlüsselwörter, ausser +‘\ *rule*\ ’ sind optional. Laß uns all diese Schlüsselwörter näher +Betrachten. + +rule +---- + +Das ‘\ *rule*\ ’ Schlüsselwort definiert die Validierungsmethode und +erwartet einen einzelnen Wert oder ein *Array*. Der angegebene Wert für +‘\ *rule*\ ’ kann der Name einer Methode des Models sein, eine Methode +der Kern-Klasse *Validation*, oder eine *regular expression*. Eine +komplette Liste aller integrierten Regeln ist im nächsten Kapitel +"Mehrere Regeln pro Feld" dargestellt. + +Falls die Regel keine Parameter benötigt, kann ‘\ *rule*\ ’ auch ein +einzelner Wert sein. z.B.: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +Falls die Regel Parameter erwartet (wie z.B. *max*, *min* oder *range*) +sollte ‘\ *rule*\ ’ als *Array* ausgezeichnet werden: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +Verigss nicht, das Schlüsselwort ‘\ *rule*\ ’ wird für *Array*-basierte +Regeldefinitionen benötigt. + +required +-------- + +Dieser Schlüssel sollte ein boolscher Wert sein. Falls *‘required’* +*TRUE* ist, muss das Feld im Daten-*Array* existieren. Zum Beispiel wenn +die Validierungsregel wie folgt definiert ist: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +Die Daten die zur *save()* Methode des Models gesendet werden, müssen +das Feld *'login'* enthalten. Wenn das nicht der Fall ist, schlägt die +Validierung fehl. Der Standard-Wert dieses Schlüssels ist der boolsche +Wert *FALSE*. + +Ist der *'login'* Schlüssel enthalten, jedoch leer, wird die Validierung +erfolgreich sein. Ist der Schlüssel *‘required’* auf TRUE gesetzt, wird +nur überprüft ob der Feldname existiert. + +allowEmpty +---------- + +Dem Schlüssel ``allowEmpty`` sollte ein boolscher Wert zugewiesen +werden. Falls ``allowEmpty`` "false" ist, müssen jene Daten, die an die +``save()``-Methode des Modells übergeben werden, den Feldnamen und einen +nicht leeren Wert enthalten. Wird "true" gesetzt, bewirkt ein leeres +Feld, dass die Gültigkeitsprüfung ausgelassen wird. + +Der Standardwert von ``allowEmpty`` ist "null". Das bedeutet, dass das +Feld immer auf die Gültigkeitsregeln hin überprüft wird und selbst +erstellte Gültigkeitsregeln beachtet werden. + +on +-- + +Der Schlüssel ‘on’ kann auf einen der folgenden Werte gesetzt werden: +‘update’ oder ‘create’ (ins Deutsche würde man ‘on’ mit dem +umgangssprachlichen ‘beim’ übersetzen). Dies erlaubt Dir zu steuern, +dass eine Regel wahlweise bei der Erstellung oder einem Update eines +Datensatzes angewandt werden soll. + +Wenn eine Regel ‘on’ => ‘create’ beinhaltet, so wird sie nur angewandt, +wenn ein neuer Datensatz erstellt werden soll. Andersherum, wenn die +Regel ‘on’ => ‘update’ enthält, wird sie nur bei einer Änderung eines +Datensatzes beachtet. + +Der Standardwert von ‘on’ ist "null". Wenn ‘on’ "null" ist wird die +entsprechende Regel sowohl bei ‘update’ als auch bei ‘create’ angewandt. + +message +------- + +Der Schlüssel ‘message’ erlaubt es Dir, eine eigene Fehlermeldung bzw. +Verletzungsmeldung für diese Regel zu bestimmen: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'Password must be at least 8 characters long' + ) + ); + +last +---- + +Den ``'last'`` Schlüssel auf ``true`` zu setzen, lässt den Validator im +Fehlerfall stoppen, anstatt mit der nächsten Regel fortzufahren. Dies +ist praktisch, wenn man die Validierung beenden möchte, wenn das Feld +einem `multi-rule Feld `_ +notEmpty ist. + +:: + + var $validate = array( + 'username' => array( + 'usernameRule-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Please enter a username.', + 'last' => true + ), + 'usernameRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters.' + ) + ) + ); + +Der Defaultwert für ``'last'`` ist ``false``. + +Multiple Rules per Field +======================== + +The technique outlined above gives us much more flexibility than simple +rules assignment, but there’s an extra step we can take in order to gain +more fine-grained control of data validation. The next technique we’ll +outline allows us to assign multiple validation rules per model field. + +If you would like to assign multiple validation rules to a single field, +this is basically how it should look: + +:: + + + var $validate = array( + 'fieldName' => array( + 'ruleName' => array( + 'rule' => 'ruleName', + // extra keys like on, required, etc. go here... + ), + 'ruleName2' => array( + 'rule' => 'ruleName2', + // extra keys like on, required, etc. go here... + ) + ) + ); + +As you can see, this is quite similar to what we did in the previous +section. There, for each field we had only one array of validation +parameters. In this case, each ‘fieldName’ consists of an array of rule +indices. Each ‘ruleName’ contains a separate array of validation +parameters. + +This is better explained with a practical example: + +:: + + var $validate = array( + 'login' => array( + 'loginRule-1' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Only alphabets and numbers allowed', + 'last' => true + ), + 'loginRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters' + ) + ) + ); + +The above example defines two rules for the login field: loginRule-1 and +loginRule-2. As you can see, each rule is identified with an arbitrary +name. + +By default CakePHP tries to validate a field using all the validation +rules declared for it and returns the error message for the last failing +rule. But if the key ``last`` is set to ``true`` for a rule and it +fails, then the error message for that rule is returned and further +rules are not validated. So if you prefer to show the error message for +the first failing rule then set ``'last' => true`` for each rule. + +If you plan on using internationalized error messages, you may want to +specify error messages in your view instead: + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'loginRule-1' => __('Only alphabets and numbers allowed', true), + 'loginRule-2' => __('Minimum length of 8 characters', true) + ) + ) + ); + +The field is now fully internationalized, and you are able to remove the +messages from the model. For more information on the \_\_() function, +see `Localization & +Internationalization `_ + +Core Validation Rules +===================== + +Die Validierungsklasse in CakePHP enthält viele Validierungsregeln, die +die Validierung von Model-Daten sehr vereinfachen. Es sind viele oft +benutzte Validierungstechniken enthalten, so dass es nicht nötig ist, +eigene Regeln zu schreiben. In diesem Abschnitt findet sich eine +komplette Liste aller Regeln mit einem Verwendungsbeispiel. + +alphaNumeric +------------ + +Der Feldwert darf nur Buchstaben und Zahlen enthalten. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Benutzernamen dürfen nur Buchstaben und Zahlen enthalten.' + ) + ); + +between +------- + +Die Länge eines Datenfeldes muss innerhalb der angegebenen Zahlenwerte +liegen. Sowohl Minimum als auch Maximum müssen angegeben werden. Es gilt +dabei <= nicht < . + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Passwörter müssen zwischen 5 und 15 Zeichen lang sein.' + ) + ); + +Die Länge der Daten ist "die Anzahl von Bytes in einer +String-Repräsentation der Daten". Beim Arbeiten mit nicht-ASCII-Zeichen +könnte der String also größer sein als die Anzahl der Zeichen. + +blank +----- + +Diese Regel wird verwendet um sicherzustellen, dass ein Feld leer ist +oder nur White Spaces enthalten sind. White Spaces beinhalten +characters, inklusive Space, Tab, Carriage Return und Newline. + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +Für das Feld ist nur ein boolescher Wert erlaubt. Gültige Werte sind +true oder false, Zahlen 0 oder 1 oder die Zeichen '0' oder '1'. + +:: + + var $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'Incorrect value for myCheckbox' + ) + ); + +cc +-- + +This rule is used to check whether the data is a valid credit card +number. It takes three parameters: ‘type’, ‘deep’ and ‘regex’. + +The ‘type’ key can be assigned to the values of ‘fast’, ‘all’ or any of +the following: + +- amex +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +If ‘type’ is set to ‘fast’, it validates the data against the major +credit cards’ numbering formats. Setting ‘type’ to ‘all’ will check with +all the credit card types. You can also set ‘type’ to an array of the +types you wish to match. + +The ‘deep’ key should be set to a boolean value. If it is set to true, +the validation will check the Luhn algorithm of the credit card +(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_). +It defaults to false. + +The ‘regex’ key allows you to supply your own regular expression that +will be used to validate the credit card number. + +:: + + var $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'The credit card number you supplied was invalid.' + ) + ); + +Vergleiche +---------- + +Vergleiche werden benutzt um numerische Werte miteinander zu +vergleichen. Unterstützt werden "is greater" (größer als), "is less" +(kleiner als), "greater or equal" (größer/gleich), "less or equal" +(kleiner/gleich), "equal to" (gleich) und "not equal" (ungleich). Es +folgen einige Beispiel: + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Man muss mindestens 18 Jahre alt sein, um sich zu qualifizieren.' + ) + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Man muss mindestens 18 Jahre alt sein, um sich zu qualifizieren.' + ) + ); + +date +---- + +This rule ensures that data is submitted in valid date formats. A single +parameter (which can be an array) can be passed that will be used to +check the format of the supplied date. The value of the parameter can be +one of the following: + +- ‘dmy’ e.g. 27-12-2006 or 27-12-06 (separators can be a space, period, + dash, forward slash) +- ‘mdy’ e.g. 12-27-2006 or 12-27-06 (separators can be a space, period, + dash, forward slash) +- ‘ymd’ e.g. 2006-12-27 or 06-12-27 (separators can be a space, period, + dash, forward slash) +- ‘dMy’ e.g. 27 December 2006 or 27 Dec 2006 +- ‘Mdy’ e.g. December 27, 2006 or Dec 27, 2006 (comma is optional) +- ‘My’ e.g. (December 2006 or Dec 2006) +- ‘my’ e.g. 12/2006 or 12/06 (separators can be a space, period, dash, + forward slash) + +If no keys are supplied, the default key that will be used is ‘ymd’. + +:: + + var $validate = array( + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date in YY-MM-DD format.', + 'allowEmpty' => true + ) + ); + +While many data stores require a certain date format, you might consider +doing the heavy lifting by accepting a wide-array of date formats and +trying to convert them, rather than forcing users to supply a given +format. The more work you can do for your users, the better. + +decimal +------- + +Diese Regel stellt sicher, dass die Daten eine gültige Dezimalzahl +darstellen. Ein angegebener Parameter legt die Anzahl der +Nachkommastellen fest. Wenn kein Parameter angegeben ist, werden die +Daten als wissenschaftliche Float-Zahl validiert. Dies führt dazu, dass +die Validierung fehlschlägt, wenn keine Nachkommstellen angegeben +werden. + +:: + + var $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +Diese Regel überprüft, ob die Daten eine gültige E-Mail-Adresse +darstellen. Wird ein zweiter Parameter vom Typ Boolean als true +übergeben, versucht diese Regel außerdem, den Host der E-Mail-Adresse zu +verifizieren. + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Bitte geben Sie eine gültige E-Mail-Adresse an.' + ) + ); + +equalTo +------- + +Diese Regel stellt sicher, dass Wert und Typ einem gegeben Wert +entsprechen. + +:: + + var $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'Dieser Wert muss der String cake sein.' + ) + ); + +extension +--------- + +Mit dieser Regel kann die Dateiendung (z.B. .jpg oder .png) überprüft +werden. Es ist dabei möglich, mehrere valide Dateiendungen in einem +Array zu definieren. + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), + 'message' => 'Please supply a valid image.' + ) + ); + +file +---- + +Mit dieser Regel wird sichergestellt, dass der Wert ein korrekter +Dateiname ist. Diese Regel ist momentan noch nicht in gebrauch. + +ip +-- + +Diese Regel stellt sicher, dass der Wert eine korrekte IPv4 Adresse ist. + +:: + + var $validate = array( + 'clientip' => array( + 'rule' => 'ip', + 'message' => 'Please supply a valid IP address.' + ) + ); + +isUnique +-------- + +Der Wert dieses Felder muss einzigartig sein und kann nicht in einer +anderen Zeile verwendet werden. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'This username has already been taken.' + ) + ); + +minLength +--------- + +Der Wert muss eine bestimmte Anzahl Zeichen haben. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', 8), + 'message' => 'Usernames must be at least 8 characters long.' + ) + ); + +Die Länge hier ist die Anzahl Bytes in dem String. Beachte bitte, dass +die Länge daher grösser sein kann als die Anzahl Zeichen, beim Gebrauch +von nicht-ASCII Zeichen. + +maxLength +--------- + +This rule ensures that the data stays within a maximum length +requirement. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', 15), + 'message' => 'Usernames must be no larger than 15 characters long.' + ) + ); + +The length here is "the number of bytes in the string representation of +the data". Be careful that it may be larger than the number of +characters when handling non-ASCII characters. + +money +----- + +Diese Regel stellt sicher, dass der Wert eine korrekte Währungszahl ist. + +Der zweite Parameter definiert, wo sich das Währungszeichen befindet +(links/rechts). + +:: + + var $validate = array( + 'salary' => array( + 'rule' => array('money', 'left'), + 'message' => 'Please supply a valid monetary amount.' + ) + ); + +multiple +-------- + +Use this for validating a multiple select input. It supports parameters +"in", "max" and "min". + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('do', 'ray', 'me', 'fa', 'so', 'la', 'ti'), 'min' => 1, 'max' => 3)), + 'message' => 'Please select one, two or three options' + ) + ); + +inList +------ + +This rule will ensure that the value is in a given set. It needs an +array of values. The field is valid if the field's value matches one of +the values in the given array. + +Example: + +:: + + var $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Enter either Foo or Bar.' + ) + ) + ); + +numeric +------- + +Checks if the data passed is a valid number. + +:: + + var $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => 'Please supply the number of cars.' + ) + ); + +notEmpty +-------- + +The basic rule to ensure that a field is not empty. + +:: + + var $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'This field cannot be left blank' + ) + ); + +Do not use this for a multiple select input as it will cause an error. +Instead, use "multiple". + +phone +----- + +Phone validates US phone numbers. If you want to validate non-US phone +numbers, you can provide a regular expression as the second parameter to +cover additional number formats. + +:: + + var $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +Postal is used to validate ZIP codes from the U.S. (us), Canada (ca), +U.K (uk), Italy (it), Germany (de) and Belgium (be). For other ZIP code +formats, you may provide a regular expression as the second parameter. + +:: + + var $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +This rule ensures that the value is in a given range. If no range is +supplied, the rule will check to ensure the value is a legal finite on +the current platform. + +:: + + var $validate = array( + 'number' => array( + 'rule' => array('range', -1, 11), + 'message' => 'Please enter a number between 0 and 10' + ) + ); + +The above example will accept any value which is larger than 0 (e.g., +0.01) and less than 10 (e.g., 9.99). Note: The range lower/upper are not +inclusive!!! + +ssn +--- + +Ssn validates social security numbers from the U.S. (us), Denmark (dk), +and the Netherlands (nl). For other social security number formats, you +may provide a regular expression. + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +This rule checks for valid URL formats. Supports http(s), ftp(s), file, +news, and gopher protocols. + +:: + + var $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + +To ensure that a protocol is in the url, strict mode can be enabled like +so. + +:: + + var $validate = array( + 'website' => array( + 'rule' => array('url', true) + ) + ); + +Benutzer Validierung Regeln +=========================== + +If you haven’t found what you need thus far, you can always create your +own validation rules. There are two ways you can do this: by defining +custom regular expressions, or by creating custom validation methods. + +Benutzerdefinierte Validierung mit Regulären Ausdrücken +------------------------------------------------------- + +Falls Sie eine Validierungsmethode benötigen, die Sie mit Regulären +Ausdrücken erreichen können/wollen, können Sie auch eigene +benutzerdefinierte Reguläre Ausdrücke als Validierungsregel für die +Daten des zu validierenden Feldes definieren. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('custom', '/^[a-z0-9]{3,}$/i'), + 'message' => 'Nur Buchstaben und Zahlen, mindestens 3 Zeichen' + ) + ); + +Das Beispiel hier überprüft, ob die Daten des login Feldes nur +Buchstaben und Zahlen mit einer Mindestlänge von 3 Zeichen enthalten. + +Adding your own Validation Methods +---------------------------------- + +Sometimes checking data with regular expression patterns is not enough. +For example, if you want to ensure that a promotional code can only be +used 25 times, you need to add your own validation function, as shown +below: + +:: + + array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'This code has been used too many times.' + ) + ); + + function limitDuplicates($check, $limit){ + //$check will have value: array('promomotion_code' => 'some-value') + //$limit will have value: 25 + $existing_promo_count = $this->find( 'count', array('conditions' => $check, 'recursive' => -1) ); + return $existing_promo_count < $limit; + } + } + ?> + +The current field to be validated is passed into the function as first +parameter as an associated array with field name as key and posted data +as value. + +If you want to pass extra parameters to your validation function, add +elements onto the ‘rule’ array, and handle them as extra params (after +the main ``$check`` param) in your function. + +Your validation function can be in the model (as in the example above), +or in a behavior that the model implements. This includes mapped +methods. + +Model/behavior methods are checked first, before looking for a method on +the ``Validation`` class. This means that you can override existing +validation methods (such as ``alphaNumeric()``) at an application level +(by adding the method to ``AppModel``), or at model level. + +When writing a validation rule which can be used by multiple fields, +take care to extract the field value from the $check array. The $check +array is passed with the form field name as its key and the field value +as its value. The full record being validated is stored in $this->data +member variable. + +:: + + array( + 'rule' => 'alphaNumericDashUnderscore', + 'message' => 'Slug can only be letters, numbers, dash and underscore' + ) + ); + + function alphaNumericDashUnderscore($check) { + // $data array is passed using the form field name as the key + // have to extract the value to make the function generic + $value = array_values($check); + $value = $value[0]; + + return preg_match('|^[0-9a-zA-Z_-]*$|', $value); + } + } + ?> + +Daten im Controller validieren +============================== + +Normalerweise werden Daten beim Aufruf der ``save``-Methode des Models +validiert. In einigen Fällen jedoch möchte man Daten validieren, ohne +sie gleichzeitig zu speichern. Zum Beispiel wenn dem Benutzer noch +zusätzliche Informationen anzeigt werden sollen, bevor die Daten in die +Datenbank geschrieben werden. Die Validierung erfordert ein etwas +anderes Vorgehen als beim Speichern: + +Zuerst werden die Daten an das Model übergeben: + +:: + + $this->ModelName->set( $this->data ); + +Anschließend wird zur Validierung der Daten die ``validates``-Methode +des Models aufgerufen. Die Methode gibt ``true`` zurück, wenn die +Validierung erfolgreich ist oder ``false``, wenn sie fehlschlägt. + +:: + + if ($this->ModelName->validates()) { + // die Daten sind valide + } else { + // die Daten sind nicht valide + } + +Es könnte sein, dass du nur eine Teilmenge der Validierungsregeln in +deinem Model zur Validierung einsetzen möchtest. Angenommen, du hast ein +User-Model mit Feldern "first\_name", "last\_name", "email" und +"password". Wird ein User hinzugefügt oder bearbeitet, möchtest du alle +vier entsprechenden Regeln validieren. Loggt sich ein User jedoch nur +ein, sollen nur die "email" und "password" Regeln geprüft werden. In dem +Fall kannst du ein Array, welches die zu validierenden Felder angibt, +mit übergeben, für unser Beispiel also: + +:: + + if ($this->User->validates(array('fieldList' => array('email', 'password')))) { + // Gültig + } else { + // Ungültig + } + +Die ``validates``-Methode ruft die ``invalidFields``-Methode auf, welche +die Eigenschaft ``validationErrors`` des Models befüllt. Die +``invalidFields``-Methode gibt die Daten zudem als Rückgabewert aus. + +:: + + $errors = $this->ModelName->invalidFields(); // enthält das validationErrors-Array + +Denke daran, dass Du die Daten über die ``set``-Methode an das Model +übergeben musst, bevor sie validiert werden können. Dies ist ein +Unterschied zum Speichern über die ``save``-Methode, bei der Du die +Daten als Parameter übergeben kannst. Es ist jedoch nicht notwendig, die +``validates``-Methode vor jedem ``save`` aufzurufen, da die Daten beim +Speichern automatisch validiert werden. + +Um mehrere Models in einem Schritt zu validieren, solltest Du +folgendermaßen vorgehen: + +:: + + if ($this->ModelName->saveAll($this->data, array('validate' => 'only'))) { + // die Daten sind valide + } else { + // die Daten sind nicht valide + } + diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..b1d7827a825c734515e6be0f4435a82decaa3bc8 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,146 @@ +Debugging +######### + +Debugging ist ein unersetzlicher und wichtiger Bestandteil im Zyklus der +Software-Entwicklung. Während CakePHP keine Funktionen die direkt mit +einer IDE oder einem Editor verknüpft sind, so bietet CakePHP dennoch +einige Tools um eine Hilfestellung beim Debuggen und Entwickeln von +Anwendungen zu geben. + +Einfaches Debugging +=================== + +debug($var, $showHTML = false, $showFrom = true) + +Die debug() Methode ist eine global verfügbare Methode die analog zum +PHP internen print\_r() funktioniert. Die debug() Methode erlaubt es, +die Inhalte von Objekten und Variablen auf unterschiedliche Weise +anzuzeigen. Zunächst erlaubt es die Anzeige der Informationen in einer +für HTML optimierten Version. Die Methode zeigt ausserdem auch an, in +welcher Datei und welcher Zeile die Methode ausgeführt wurde. Sie wird +nur ausgeführt, wenn die Variable ``debug`` in der core.php einen Wert +größer 0 hat. + +Using the Debugger Class +======================== + +To use the debugger, first ensure that Configure::read('debug') is set +to a value greater than 0. + +dump($var) + +Dump prints out the contents of a variable. It will print out all +properties and methods (if any) of the supplied variable. + +:: + + $foo = array(1,2,3); + + Debugger::dump($foo); + + //outputs + array( + 1, + 2, + 3 + ) + + //simple object + $car = new Car(); + + Debugger::dump($car); + + //outputs + Car:: + Car::colour = 'red' + Car::make = 'Toyota' + Car::model = 'Camry' + Car::mileage = '15000' + Car::acclerate() + Car::decelerate() + Car::stop() + +log($var, $level = 7) + +Creates a detailed stack trace log at the time of invocation. The log() +method prints out data similar to that done by Debugger::dump(), but to +the debug.log instead of the output buffer. Note your app/tmp directory +(and its contents) must be writable by the web server for log() to work +correctly. + +trace($options) + +Returns the current stack trace. Each line of the trace includes the +calling method, including which file and line the call originated from. + +:: + + //In PostsController::index() + pr( Debugger::trace() ); + + //outputs + PostsController::index() - APP/controllers/downloads_controller.php, line 48 + Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 265 + Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 + [main] - APP/webroot/index.php, line 84 + +Above is the stack trace generated by calling Debugger::trace() in a +controller action. Reading the stack trace bottom to top shows the order +of currently running functions (stack frames). In the above example, +index.php called Dispatcher::dispatch(), which in-turn called +Dispatcher::\_invoke(). The \_invoke() method then called +PostsController::index(). This information is useful when working with +recursive operations or deep stacks, as it identifies which functions +are currently running at the time of the trace(). + +excerpt($file, $line, $context) + +Grab an excerpt from the file at $path (which is an absolute filepath), +highlights line number $line with $context number of lines around it. + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + //will output the following. + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +Although this method is used internally, it can be handy if you're +creating your own error messages or log entries for custom situations. + +exportVar($var, $recursion = 0) + +Converts a variable of any type to a string for use in debug output. +This method is also used by most of Debugger for internal variable +conversions, and can be used in your own Debuggers as well. + +invoke($debugger) + +Replace the CakePHP Debugger with a new Error Handler. + +Debugger Class +============== + +The debugger class is new in CakePHP 1.2 and offers even more options +for obtaining debugging information. It has several functions which are +invoked statically, and provide dumping, logging, and error handling +functions. + +The Debugger Class overrides PHP's default error handling, replacing it +with far more useful error reports. The Debugger's error handling is +used by default in CakePHP. As with all debugging functions, +Configure::debug must be set to a value higher than 0. + +When an error is raised, Debugger both outputs information to the page +and makes an entry in the error.log file. The error report that is +generated has both a stack trace and a code excerpt from where the error +was raised. Click on the "Error" link type to reveal the stack trace, +and on the "Code" link to reveal the error-causing lines. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..926346717c2cacef699992f06b625fde88557961 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,83 @@ +Error Handling +############## + +In the event of an unrecoverable error in your application, it is common +to stop processing and show an error page to the user. To save you from +having to code error handling for this in each of your controllers and +components, you can use the provided method: + +``$this->cakeError(string $errorType [, array $parameters]);`` + +Calling this method will show an error page to the user and halt any +further processing in your application. + +``parameters`` must be an array of strings. If the array contains +objects (including Exception objects), they will be cast into strings. + +CakePHP pre-defines a set of error-types, but at the time of writing, +most are only really useful by the framework itself. One that is more +useful to the application developer is the good old 404 error. This can +be called with no parameters as follows: + +:: + + $this->cakeError('error404'); + +Or alternatively, you can cause the page to report the error was at a +specific URL by passing the ``url`` parameter: + +:: + + $this->cakeError('error404', array('url' => 'some/other.url')); + +This all starts being a lot more useful by extending the error handler +to use your own error-types. Application error handlers are largely like +controller actions; You typically will set() any passed parameters to be +available to the view and then render a view file from your +``app/views/errors`` directory. + +Create a file ``app/app_error.php`` with the following definition. + +:: + + + +Handlers for new error-types can be implemented by adding methods to +this class. Simply create a new method with the name you want to use as +your error-type. + +Let's say we have an application that writes a number of files to disk +and that it is appropriate to report write errors to the user. We don't +want to add code for this all over the different parts of our +application, so this is a great case for using a new error type. + +Add a new method to your ``AppError`` class. We'll take one parameter +called ``file`` that will be the path to the file we failed to write. + +:: + + function cannotWriteFile($params) { + $this->controller->set('file', $params['file']); + $this->_outputMessage('cannot_write_file'); + } + +Create the view in ``app/views/errors/cannot_write_file.ctp`` + +:: + +

Unable to write file

+

Could not write file to the disk.

+ +and throw the error in your controller/component + +:: + + $this->cakeError('cannotWriteFile', array('file'=>'somefilename')); + +The default implementation of ``$this->_outputMessage()`` +will just display the view in ``views/errors/.ctp``. If +you wish to override this behaviour, you can redefine +``_outputMessage($template)`` in your AppError class. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..56369255cefe3773840cc4b954d6be9c63a494bd --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,201 @@ +Internationalisierung & Lokalisierung +##################################### + +Eine der besten Möglichkeiten eine größere Menge an Besuchern für eine +Anwendung zu gewinnen ist es, sie in mehreren Sprachen anzubieten. +Obwohl es sich oftmals als gewaltige Aufgabe herausstellt, bietet +CakePHP Internationalisierungs- und Lokalisierungsunterstützung die es +um einiges einfacher machen. + +Am Anfang ist es jedoch wichtig, einige Grundbegriffe zu verstehen. +Unter *Internationalisierung* versteht man die Fähigkeit einer Anwendung +(sprachlich) lokalisiert zu sein. Der Begriff *Lokalisierung* +allerdings, steht für die Anpassung einer Anwendung an spezielle Sprach- +oder Kultureigenschaften (z.B. Währungssymbole, ...). Die Begriffe +Internationalisierung und Lokalisierung werden oft als i18n bzw l10n +abgekürzt. Die Zahlen 18 bzw. 10 bezeichnen die Anzahl der Buchstaben +zwischen dem ersten und letzten Zeichen des englischen Wortes +(Internationalization wird zu i18n und localization zu l10n). + +Internationalizing Your Application +=================================== + +There are only a few steps to go from a single-language application to a +multi-lingual application, the first of which is to make use of the +```__()`` `_ +function in your code. Below is an example of some code for a +single-language application: + +:: + +

Posts

+ +To internationalize your code, all you need to do is to wrap strings in +`the translate +function `_ +like so: + +:: + +

+ +If you do nothing further, these two code examples are functionally +identical - they will both send the same content to the browser. The +```__()`` +function `_ +will translate the passed string if a translation is available, or +return it unmodified. It works similar to other +`Gettext `_ implementations (as do +the other translate functions, such as +```__d()`` `_, +```__n()`` `_ +etc) + +With your code ready to be multilingual, the next step is to create your +`pot file `_, which is the +template for all translatable strings in your application. To generate +your pot file(s), all you need to do is run the `i18n console +task `_, +which will look for where you've used a translate function in your code +and generate your pot file(s) for you. You can and should re-run this +console task any time you change the translations in your code. + +The pot file(s) themselves are not used by CakePHP, they are the +templates used to create or update your `po +files `_, which contain the +translations. Cake will look for your po files in the following +location: + +:: + + /app/locale//LC_MESSAGES/.po + +The default domain is 'default', therefore your locale folder would look +something like this: + +:: + + /app/locale/eng/LC_MESSAGES/default.po (English) + /app/locale/fre/LC_MESSAGES/default.po (French) + /app/locale/por/LC_MESSAGES/default.po (Portuguese) + +To create or edit your po files it's recommended that you do *not* use +your favorite editor. To create a po file for the first time it is +possible to copy the pot file to the correct location and change the +extension *however* unless you're familiar with their format, it's quite +easy to create an invalid po file or to save it as the wrong charset (if +you're editing manually, use UTF-8 to avoid problems). There are free +tools such as `PoEdit `_ which make editing and +updating your po files an easy task; especially for updating an existing +po file with a newly updated pot file. + +The three-character locale codes conform to the `ISO +639-2 `_ +standard, although if you create regional locales (en\_US, en\_GB, etc.) +cake will use them if appropriate. + +there is a 1014-character limit for each msgstr value (source needed). + +Remember that po files are useful for short messages, if you find you +want to translate long paragraphs, or even whole pages - you should +consider implementing a different solution. e.g.: + +:: + + // App Controller Code. + function beforeFilter() { + $locale = Configure::read('Config.language'); + if ($locale && file_exists(VIEWS . $locale . DS . $this->viewPath)) { + // e.g. use /app/views/fre/pages/tos.ctp instead of /app/views/pages/tos.ctp + $this->viewPath = $locale . DS . $this->viewPath; + } + } + +or + +:: + + // View code + echo $this->element(Configure::read('Config.language') . '/tos') + +Localization in CakePHP +======================= + +To change or set the language for your application, all you need to do +is the following: + +:: + + Configure::write('Config.language', 'fre'); + +This tells Cake which locale to use (if you use a regional locale, such +as fr\_FR, it will use the `ISO +639-2 `_ locale +as a fallback if it doesn't exist), you can change the language at any +time, e.g. in your bootstrap if you're setting the application default +language, in your (app) controller beforeFilter if it's specific to the +request or user, or in fact anytime at all before you want a message in +a different language. + +It's a good idea to serve up public content available in multiple +languages from a unique url - this makes it easy for users (and search +engines) to find what they're looking for in the language they are +expecting. There are several ways to do this, it can be by using +language specific subdomains (en.example.com, fra.example.com, etc.), or +using a prefix to the url such as is done with this application. You may +also wish to glean the information from the browser’s user-agent, among +other things. + +As mentioned in the previous section, displaying localized content is +done using the \_\_() convenience function, or one of the other +translation functions all of which are globally available, but probably +be best utilized in your views. The first parameter of the function is +used as the msgid defined in the .po files. + +Remember to use the return parameter for the various ``__*`` methods if +you don't want the string echo'ed directly. For example: + +:: + + error( + 'Card.cardNumber', + __("errorCardNumber", true), + array('escape' => false) + ); + ?> + +If you would like to have all of your validation error messages +translated by default, a simple solution would be to add the following +code in you app\_model.php: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + +The i18n console task will not be able to determine the message id from +the above example, which means you'll need to add the entries to your +pot file manually (or via your own script). To prevent the need to edit +your default.po(t) file every time you run the i18n console task, you +can use a different domain such as: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __d('validation_errors', $value, true)); + } + +This will look for ``$value`` in the validation\_errors.po file. + +There's one other aspect of localizing your application which is not +covered by the use of the translate functions, and that is date/money +formats. Don't forget that CakePHP is PHP :), therefore to set the +formats for these things you need to use +```setlocale`` `_. + +If you pass a locale that doesn't exist on your computer to +```setlocale`` `_ it will have no effect. +You can find the list of available locales by running the command +$locale -a in a terminal. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..e1c3d77db0e1a39f1434121f5d09b26f46ff8c39 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,67 @@ +Logging +####### + +While CakePHP core Configure Class settings can really help you see +what's happening under the hood, there are certain times that you'll +need to log data to the disk in order to find out what's going on. In a +world that is becoming more dependent on technologies like SOAP and +AJAX, debugging can be rather difficult. + +Logging can also be a way to find out what's been going on in your +application over time. What search terms are being used? What sorts of +errors are my users being shown? How often is a particular query being +executed? + +Logging data in CakePHP is easy - the log() function is a part of the +Object class, which is the common ancestor for almost all CakePHP +classes. If the context is a CakePHP class (Model, Controller, +Component... almost anything), you can log your data. + +Using the log function +====================== + +The log() function takes two parameters. The first is the message you'd +like written to the log file. By default, this error message is written +to the error log found in app/tmp/logs/error.log. + +:: + + //Executing this inside a CakePHP class: + + $this->log("Something didn't work!"); + + //Results in this being appended to app/tmp/logs/error.log + + 2007-11-02 10:22:02 Error: Something didn't work! + +The second parameter is used to define the log type you wish to write +the message to. If not supplied, it defaults to LOG\_ERROR, which writes +to the error log previously mentioned. You can set this second parameter +to LOG\_DEBUG to write your messages to an alternate debug log found at +app/tmp/logs/debug.log: + +:: + + //Executing this inside a CakePHP class: + + $this->log('A debugging message.', LOG_DEBUG); + + //Results in this being appended to app/tmp/logs/debug.log (rather than error.log) + + 2007-11-02 10:22:02 Error: A debugging message. + +You can also specify a different name for the log file, by setting the +second parameter to the name of the file. + +:: + + //Executing this inside a CakePHP class: + + $this->log('A special message for activity logging', 'activity'); + + //Results in this being appended to app/tmp/logs/activity.log (rather than error.log) + + 2007-11-02 10:22:02 Activity: A special message for activity logging + +Your app/tmp directory must be writable by the web server user in order +for logging to work correctly. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..e8ed5e8febea5eae850469bad7ef4a929c3d767b --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,378 @@ +Pagination +########## + +One of the main obstacles of creating flexible and user-friendly web +applications is designing an intuitive UI. Many applications tend to +grow in size and complexity quickly, and designers and programmers alike +find they are unable to cope with displaying hundreds or thousands of +records. Refactoring takes time, and performance and user satisfaction +can suffer. + +Displaying a reasonable number of records per page has always been a +critical part of every application and used to cause many headaches for +developers. CakePHP eases the burden on the developer by providing a +quick, easy way to paginate data. + +The PaginatorHelper offers a great solution because it's so easy to use. +Apart from pagination, it bundles some very easy-to-use sorting +features. Last but not least, Ajax sorting and pagination are supported +as well. + +Controller Setup +================ + +In the controller, we start by defining the pagination defaults in the +*$paginate* controller variable. It is important to note here that the +order key must be defined in the array structure given. + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +You can also include other find() options, such as *fields*: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +Other keys that can be included in the *$paginate* array are similar to +the parameters of the *Model->find('all')* method, that is: +*conditions*, *fields*, *order*, *limit*, *page*, *contain*, *joins*, +and *recursive*. In fact, you can define more than one set of pagination +defaults in the controller, you just name the pieces of the array after +the model you wish to configure: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'Recipe' => array (...), + 'Author' => array (...) + ); + } + +Example of syntax using Containable Behavior: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +Once the *$paginate* variable has been defined, we can call the +*paginate()* method in controller actions. This method returns paged +*find()* results from the model, and grabs some additional paging +statistics, which are passed to the View behind the scenes. This method +also adds PaginatorHelper to the list of helpers in your controller, if +it has not been added already. + +:: + + function list_recipes() { + // similar to findAll(), but fetches paged results + $data = $this->paginate('Recipe'); + $this->set('data', $data); + } + +You can filter the records by passing conditions as second parameter to +the ``paginate()`` function. + +:: + + $data = $this->paginate('Recipe', array('Recipe.title LIKE' => 'a%')); + +Or you can also set *conditions* and other keys in the ``$paginate`` +array inside your action. + +:: + + function list_recipes() { + $this->paginate = array( + 'conditions' => array('Recipe.title LIKE' => 'a%'), + 'limit' => 10 + ); + $data = $this->paginate('Recipe'); + $this->set(compact('data')); + ); + +Pagination in Views +=================== + +It's up to you to decide how to show records to the user, but most often +this will be done inside HTML tables. The examples below assume a +tabular layout, but the PaginatorHelper available in views doesn't +always need to be restricted as such. + +See the details on +`PaginatorHelper `_ in +the API. + +As mentioned, the PaginatorHelper also offers sorting features which can +be easily integrated into your table column headers: + +:: + + // app/views/recipes/list_recipes.ctp + + + + + + + + + + + +
sort('ID', 'id'); ?>sort('Title', 'title'); ?>
+ +The links output from the sort() method of the PaginatorHelper allow +users to click on table headers to toggle the sorting of the data by a +given field. + +It is also possible to sort a column based on associations: + +:: + + + + + + + + + + + + +
sort('Title', 'title'); ?>sort('Author', 'Author.name'); ?>
+ +The final ingredient to pagination display in views is the addition of +page navigation, also supplied by the PaginationHelper. + +:: + + + numbers(); ?> + + prev('« Previous ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Next »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +The wording output by the counter() method can also be customized using +special markers: + +:: + + counter(array( + 'format' => 'Page %page% of %pages%, showing %current% records out of + %count% total, starting on record %start%, ending on %end%' + )); + ?> + +To pass all URL arguments to paginator functions, add the following to +your view: + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +Route elements that are not named arguments should manually be merged +with ``$this->passedArgs``: + +:: + + //for urls like http://www.example.com/en/controller/action + //that are routed as Router::connect('/:lang/:controller/:action/*', array(),array('lang'=>'ta|en')); + $paginator->options(array('url'=>array_merge(array('lang'=>$lang),$this->passedArgs))); + +Or you can specify which params to pass manually: + +:: + + $paginator->options(array('url' => array("0", "1"))); + +AJAX Pagination +=============== + +It's very easy to incorporate Ajax functionality into pagination. The +only extra coding required is the inclusion of the the Prototype +JavaScript library, setting the indicator (loading icon inside of DIV) +and the specifying of a DIV to be updated (instead of reloading the +page). + +Do not forget to add the RequestHandler component to use Ajax calls to +your controller: + +:: + + var $components = array('RequestHandler'); + +If you include PaginatorHelper in your $helpers array, and want to use a +specific JsHelper adapter, be sure to put Paginator after JsHelper. +Failing to do so will cause JsHelper to use the default adapter which is +jQuery. + +Layout Changes +-------------- + +First, we'll include the Prototype library in the header, set up our +status indicator image (spinner.gif), and set up our main content +wrapper DIV, "content". + +Here’s what a layout including those elements might look like +(partially): + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +View Changes +------------ + +The only extra configuration for Ajax pagination is done using the +options() method of the PaginationHelper, which specifies required Ajax +parameters. In this case, we're specifying that all pagination links +should update the element with the ID 'content' with the resulting data, +and we want to show 'spinner' as the loading indicator. + +If the ‘update’ key is not specifed, the PaginationHelper will output +non-Ajax pagination sorting and paging links. + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Previous', null, null, array('class' => 'disabled')); + + echo $paginator->next('Next >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +Custom Query Pagination +======================= + +Fix me: Please add an example where overriding paginate is justified + +A good example of when you would need this is if the underlying DB does +not support the SQL LIMIT syntax. This is true of IBM's DB2. You can +still use the CakePHP pagination by adding the custom query to the +model. + +Should you need to create custom queries to generate the data you want +to paginate, you can override the ``paginate()`` and ``paginateCount()`` +model methods used by the pagination controller logic. + +Before continuing check you can't achieve your goal with the core model +methods. + +The ``paginate()`` method uses the same parameters as ``Model::find()``. +To use your own method/logic override it in the model you wish to get +the data from. + +:: + + /** + * Overridden paginate method - group by week, away_team_id and home_team_id + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $recursive = -1; + $group = $fields = array('week', 'away_team_id', 'home_team_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')); + } + +You also need to override the core ``paginateCount()``, this method +expects the same arguments as ``Model::find('count')``. The example +below uses some Postgres-specifc features, so please adjust accordingly +depending on what database you are using. + +:: + + /** + * Overridden paginateCount method + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +The observant reader will have noticed that the paginate method we've +defined wasn't actually necessary - All you have to do is add the +keyword in controller's ``$paginate`` class variable. + +:: + + /** + * Add GROUP BY clause + */ + var $paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + /** + * Or on-the-fly from within the action + */ + function index() { + $this->paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + +However, it will still be necessary to override the ``paginateCount()`` +method to get an accurate value. diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/de/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..34f8cab0d7a64cbcf225d43ef604d8120d7e1fa0 --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,185 @@ +REST +#### + +Viele Programmierer erkennen heute den Nutzen, die Kernfunktionalitäten +ihrer Applikation einer breiteren Zielgruppe zu öffen. Ein einfacher, +uneingeschränkter Zugang zu deiner API trägt zur Akzeptanz deiner +Platform bei, ermöglicht Mashups und eine einfache Integration in andere +System. + +Obwohl es auch andere Lösungen gibt, ist REST ein sehr gute Methode, den +Zugriff auf die Programmlogik deiner Applikation zu ermöglichen. Es ist +einfach, basiert überlicherweise auf XML (wir reden hier von schlichtem +XML, nicht von einem SOAP envelope) und wird durch die HTTP headers +gesteuert. Eine API mit REST in CakePHP nach außen zu öffnen, ist +einfach. + +Einfaches Setup +=============== + +Der schnellste Weg REST einzusetzen sind ein paar Zeilen in deiner +routes.php Datei in app/config. Das Router Objekt hat eine Methode +namens mapResources(), mit der einige Standard-Routen für den +REST-Zugriff auf den Controller konfiguriert werden können. Wenn wir +etwa den Zugriff per REST auf eine Rezept-Datenbank erlauben wollen, +machen wir das etwa wie folgt: + +:: + + //In app/config/routes.php... + + Router::mapResources('recipes'); + Router::parseExtensions(); + +Die erste Zeile konfiguriert einige Standard-Routen für einfachen +REST-Zugriff. Diese Routen hängen von der HTTP-Request-Methode ab. + ++----------------+----------------+----------------------------------+ +| HTTP Methode | URL | Controller action | ++================+================+==================================+ +| GET | /recipes | RecipesController::index() | ++----------------+----------------+----------------------------------+ +| GET | /recipes/123 | RecipesController::view(123) | ++----------------+----------------+----------------------------------+ +| POST | /recipes | RecipesController::add() | ++----------------+----------------+----------------------------------+ +| POST | /recipes/123 | RecipesController::edit(123) | ++----------------+----------------+----------------------------------+ +| PUT | /recipes/123 | RecipesController::edit(123) | ++----------------+----------------+----------------------------------+ +| DELETE | /recipes/123 | RecipesController::delete(123) | ++----------------+----------------+----------------------------------+ + +Die CakePHP Router Klasse benutzt mehrere verschiedenen Indizien, um die +HTTP-Methode zu erkennen. Hier folgen sie in der eingesetzten +Reihenfolge: + +#. Die *\_method* POST-Variable +#. Die X\_HTTP\_METHOD\_OVERRIDE +#. Der REQUEST\_METHOD Header + +Die *\_method* POST-Variable ist hilfreich, um einen Browser als +REST-Client zu benutzen (oder irgendeine andere Software, mit der man +einfach POST-Anfragen durchführen kann). Setze dafür einfach den Wert +von \_method mit dem Namen der HTTP-Request-Methode, die du emulieren +willst. + +Wurde der Router einmal so konfiguriert, dass er REST-Anfragen +bestimmten Controller-Actions zuordnet, können wir zur Logik in unserem +Controller übergehen. Ein Basis-Controller könnte etwa so aussehen: + +:: + + // controllers/recipes_controller.php + + class RecipesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recipes = $this->Recipe->find('all'); + $this->set(compact('recipes')); + } + + function view($id) { + $recipe = $this->Recipe->findById($id); + $this->set(compact('recipe')); + } + + function edit($id) { + $this->Recipe->id = $id; + if ($this->Recipe->save($this->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recipe->delete($id)) { + $message = 'Deleted'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + } + +Indem wir den Aufruf Router::parseExtensions() hinzugefügt haben, ist +der CakePHP-Router fertig gerüstet, um verschiedene Views abhängig von +der Art der Anfrage auszuliefern. Wenn wir mit REST-Anfragen arbeiten, +ist der View type XML. Die REST-Views für unseren RecipesController +legen wir in app/views/xml an. Wir können außerdem den XmlHelper für +einfache und schnelle XML-Ausgaben in diesen Views benutzen. So könnte +unser index view aussehen: + +:: + + // app/views/recipes/xml/index.ctp + + + serialize($recipes); ?> + + +Erfahrene Benutzer von CakePHP werden bemerkt haben, dass wir den +XmlHelper nicht in unserem $helpers Array im RecipesController eingefügt +haben. Das ist so beabsichtigt - wenn über parseExtensions() bestimmte +Inhalte angefragt werden, sucht CakePHP automatisch einen passenden +Helper aus. Weil wir XML-Content benutzen, wird der XmlHelper +automatisch für diese Views geladen. + +Das fertige XML würde dann ungefähr so aussehen: + +:: + + + + + + + + + + + + +Die Logik für die edit-Action zu erstellen ist schon etwas kniffliger, +aber nicht sehr viel. Weil wir eine API benutzen, die XML ausgibt, +müssen wir logischerweise auch XML für die Eingaben benutzen. Aber kein +Grund zur Sorge: Der RequestHandler und die Router-Klasse machen das +ganz einfach. Wenn eine POST oder PUT-Anfrage bei einem XML-Typ erkannt +wird, werden die Eingabewerte automatisch an ein Cake-XML-Objekt +übergeben, das über das $data-Attribut des Controllers erreichbar ist. +Dadurch gehen XML- und POST-Anfragen nahtlos ineinander über: Weder am +Controller, noch am Model müssen irgendwelche Änderungen vorgenommen +werden. Alles, was man braucht, findet man in $this->data. + +Custom REST Routing +=================== + +If the default routes created by mapResources() don't work for you, use +the Router::connect() method to define a custom set of REST routes. The +connect() method allows you to define a number of different options for +a given URL. The first parameter is the URL itself, and the second +parameter allows you to supply those options. The third parameter allows +you to specify regex patterns to help CakePHP identify certain markers +in the specified URL. + +We'll provide a simple example here, and allow you to tailor this route +for your other RESTful purposes. Here's what our edit REST route would +look like, without using mapResources(): + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9]+") + ) + +Advanced routing techniques are covered elsewhere, so we'll focus on the +most important point for our purposes here: the [method] key of the +options array in the second parameter. Once that key has been set, the +specified route works only for that HTTP request method (which could +also be GET, DELETE, etc.) diff --git a/de/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/de/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..cfaf97d0df1aa157d0ee89e6265624544e9be68c --- /dev/null +++ b/de/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,1081 @@ +Testen +###### + +Mit CakePHP 1.2\_RC2 wurde eine umfangreiche Test-Umgebung integriert. +Diese Test-Umgebung ist eine Ergänzung bzw. Erweiterung zur bisherigen +SimpleTest-Umgebung für PHP. Dieser Abschnitt befasst sich damit, wie +man Tests vorbereitet, aufbaut und ausführt. + +Vorbereitung zum Test-Marathon +============================== + +Fertig zum Beginn der Tests? Optimal! Dann lass uns endlich anfangen! +:-) + +Installation von SimpleTest +--------------------------- + +Das in CakePHP 1.2 enthaltene Test-Framework ist aufgebaut auf dem +SimpleTest-Test-Framework. SimpleTest ist in der +Standard-CakePHP-Installation nicht enthalten, somit müssen wir das nun +runterladen. + Hier findest du SimpleTest: +`http://simpletest.sourceforge.net/ `_. + Besorg dir die letzte Version und entpack die Dateien entweder in +deinen /cake/vendors/ oder in den /app/vendors/ Ordner, je nach euren +Anforderungen. Du solltest also jetzt einen ../vendors/simpletest/ +Ordner samt der dazugehörigen SimpleTest-Dateien haben!? + Vergiss nicht, dass du in deiner app/config/core.php das "DEBUG-level" +mindestens auf 1 setzt, bevor du mit irgendwelchen Test\`s anfängst! + +Starten der integrierten Test-Funktionen +---------------------------------------- + +CakePHP 1.2 kommt mit einem Bündel an Test-Fallbeispielen, involviert in +die Core-CakePHP-Funktionen, daher. Auf diese Test\`s kann zugegriffen +werden indem man mit dem Browser +http://deine.cake.adresse/dein\_cake\_ordner/test.php (je nachdem wie +euer spezifisches Setup aussieht..), ansteuert. + Probier eine von den Core-Test-Gruppen aus (indem du die Links +anklickst... ;-D). + Das Ausführen der Test-Gruppen könnte möglicherweise ein Weilchen +dauern, aber schlussendlich sollte etwas wie: "2/2 test casese complete: +49 passes, 0 fails and 0 exceptions." angezeigt werden. + Gratuliere, du bist jetzt bereit um Schreib-Test\`s zu machen! + +Test Überblick - Einzel-Test vs. Web-Test +========================================= + +Das CakePHP-Test-Framework unterstützt 2 Arten von Tests. Die eine ist +der Unit-Test bzw. Einzel-Test, mit welchem du kleine Teile deines Codes +testen kannst, wie eine "Methode in einer Aktion" oder eine "Aktion in +einem Controller". Die andere unterstützte Test-Art ist der Web-Test, +mit dem du die Arbeit der Tests, durch Seiten-Navigation, Formulare +ausfüllen, Links anklicken u.s.w, automatisieren kannst. + +Vorbereiten der Test Daten +========================== + +  + +Über "fixtures" +--------------- + +Wenn man Code testet der auf models und Daten basiert, dann kann man für +eines davon ***fixtures*** nutzen, als einen Weg um temporäre Datensätze +mit Beispieldaten zu laden und diese dann zum testen zu verwenden. + +Der Vorteil beim verwenden von *fixtures* liegt darin, dass dein Test +keine Chance hat geladene Anwendungsdaten zu zerstören. Zusätzlich +kannst du damit Anfangen deine Code-Priorität, mit aktuell gefertigten +Live-Inhalten für eine Anwendung, zu testen. + +CakePHP versucht die Verbindung namens ``$test`` in deiner +/app/config/database.php Einstellungs-Datei zu benutzen. Wenn diese +Verbindung nicht brauchbar ist, dann wird die ``$default`` +Datenbank-Verbindung genutzt und die Test-Tabellen werden in der dort +definierten Datenbank erstellt. + +In einem anderen Fall, wird "test\_suite" zu deinem eigenen +Tabellen-Zusatz(falls vorhanden) hinzu gefügt um einer Kollision, mit +einer vorhandenen Tabelle, vorzubeugen. + +CakePHP bietet folgende Möglichkeiten wärend der Tour durch +*fixtures*-basierte Test Fälle: + +#. Erstellt Tabellen für alles was von den *fixtures* gebraucht wird +#. Füllt Tabellen mit Daten, wenn Daten in *fixture* angefordert werden +#. Startet Test Methoden +#. Entleert *fixture*-Tabellen +#. Löscht *fixture*-Tabellen aus der Datenbank + +Erstellen von Vorrichtungen (fixtures) +-------------------------------------- + +Beim Erstellen von **Vorrichtungen** solltest du hauptsächlich 2 Dinge +definieren: +Wie wurde die Tabelle erstellt (welche Felder sind ein Teil der Tabelle) +und welche Datensätze werden zu Beginn die Test-Tabelle belegen. + Dann lass uns mal die erste Vorrichtung (fixture) erstellen, womit wir +dann unsere Modell-Artikel testen. + Erstelle eine Datei mit dem Namen **article\_test\_fixture.php** in +deinem **../app/tests/fixtures/** Ordner, mit folgendem Inhalt: + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'title' => array('type' => 'string', 'length' => 255, 'null' => false), + 'body' => 'text', + 'published' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Wir brauchen **$fields** um zu spezifizieren welche Felder Bestandteil +der Tabelle sind und wie diese Felder definiert sind. + Das Format das gebraucht wird, um diese Felder zu definieren, ist dass +gleiche das in der Funktion **generateColumnSchema()**, aus der +Cake-Datenbank-Engineklasse, definiert wird (zum Beispiel die Datei +dbo\_mysql.php). + Lass uns mal schauen welche Attribute ein Feld haben kann und was diese +aussagen: + +type + CakePHP\`s interner Daten-Typ. Momentan unterstützt: string + (Speicherort für diverse Zeichenketten/-folgen), text (Speicherort + für Texte), integer (Speicherort für Ganzzahlen), float (Speicherort + für Fliesskommazahlen), datetime (Speicherort für Datum+Zeit), + timestamp (Speicherort für Zeitmarken), time (Speicherort für + Zeiten), date (Speicherort für Datumsangaben), und binary + (Speicherort für Binärzahlen/zeichen) +key + auf primary setzen damit das Feld AUTO-ANWACHSEN lassen zu können + und um einen Hauptschlüssel für die Tabelle zu erstellen. +length + setzen um dem Feld die spezielle länge zu geben die es haben sollte. +null + Setze den Wert auf "true" ("wahr" => Zum Erlauben von NULLen) oder + "false" ("unwahr" => um NULLen zu verbieten) +default + Standart-Einheit des Feldes.. + +Zum Schluss könnten wir eine Reihe von Datensätzen setzen, welche +publiziert werden nach dem die Test-Tabelle erstellt wurde. Das Format +ist ziemlich ordentlich und braucht wenig weitere Erklärung. Versuch +daran zu denken, dass jeder Datensatz im $records Ordner einen Schlüssel +haben muss für **jedes** Feld das im $fields Ordner angegeben ist. Falls +ein Feld für für einen speziellen Datensatz einen NULL-Wert braucht, +dann deklariere den Wert des Schlüssel\`s als NULL. + +Importieren von Datensätzen und Tabellen-Daten +---------------------------------------------- + +Deine Anwendung mag möglicherweise funktionierende Entwürfe beinhalten, +mit echten + untereinander verknüpften Daten, und du könntest dich dazu entscheiden +deine Entwürfe + mit diesen Daten zu testen... Das wäre wohl dann eine +Doppelanstrengung, wenn man die + Tabellendefinition festlegen und/oder die Datensätze mitsamt deinen +Inhalten definieren sollte. + Glücklicherweise, gibt es da\`die Möglichkeit zum festlegen der +Tabellendefinition + und/oder den Datensätzen für bestimmte Inhalte, die kommen von fertigen +Modellen oder + einer bereits vorhandenen Tabelle. + Dann lass uns mal eine Musteraufgabe anfangen. + Mal angenommen du hast ein Model mit dem Namen **Article** in deiner +Anwendung + verfügbar (das weist auf die Tabelle **articles** hin!), ändere die +Muster-Inhalte + aus der vorherigen Sektion +(**app/tests/fixtures/article\_test\_fixture.php**) +wie folgt: + +:: + + + + + Die og Anweisungen weisen die Testfolge an, deine Tabellendefinition +aus der Tabelle, + welche im Model **Article** verlinkt ist, zu importieren. + Du kannst dazu jegliches Muster verwenden, dass du in deiner Anwendung +verfügbar ist. + Oben die Anweisung importiert keinerlei Datensätze, dies kannst du dann +tun wenn + du die Anweisung umänderst zu: + +:: + + 'Article', 'records' => true); + } + ?> + + Wenn du andererseits eine Tabelle erstellt hast, aber keine +Muster-Inhalte dazu + vorhanden sind, dann kannst du anweisen dass ein **import** veranlasst +wird, indem + anstatt der Model-Daten dessen Tabellen-Informationen ausgelesen +werden. + Zum Bsp: + +:: + + 'articles'); + } + ?> + + Der obere Code-Fetzen wird einen import der Tabellendefinition von der +Tabelle + **articles** veranlassen, indem die CakePHP-Datenbankverbindung +**'default'** + genutzt wird. + Wenn du die aktuell verwendete Datenbank-Verbindung ändern möchtest, +musst du + folgende Zeilen ändern: + +:: + + 'articles', 'connection' => 'other'); + } + ?> + + Seit es deine CakePHP Datenbankverbindung verwendet hat wird es, wenn +da irgendwelche + Tabellen-Vorzeichen erkennbar sind, automatisch gebraucht, da +attraktive Tabellen- + Informationen automatisch verwendet werden. + Die zwei Code-Schnipsel da oben importieren keine Datensätze aus der +Tabelle. Um also + die Inhalte zum import der Datensätze zu zwingen, ädere folgende Dinge: + +:: + + 'articles', 'records' => true); + } + ?> + + Du kannst natürlich auch deine Tabellendefinition aus einem +exitierenden Muster oder + einer Tabelle importieren, aber halte deine Datensätze direkt +definiert, genau nach den +Vorgaben aus der vorhergehenden Sektion. zum Beispiel: + +:: + + 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Tests erstellen +=============== + +Zunächst erstmal eine Latte von Regeln oder Richtlinien bezüglich der +Test\`s: + +#. PHP Dateien die Test\`s beinhalten, sollten im + **app/tests/cases/[some\_folder]** Ordner sein. +#. Die Dateinamen dieser Dateien sollten am Ende etwa so + aussehen:\ **.test.php** anstatt sowas: .php. +#. Die Klassen die die Test enthalten, müssen **CakeTestCase** oder + **CakeWebTestCase** ausführen (extend). +#. Der Name von einigen Methoden die ebenfalls Tests enthalten können + (d.h. enthält eine Erklärung) sollten dann mit **test** beginnen, wie + zum Beispiel: **testPublished()**. + +Wenn du einen Test-Fall erstellt hast, dann kannst du diesen starten, +indem du mit deinem Browser folgende Adresse ansteuerst: +**http://deine.cake.domain/cake\_ordner/test.php** (abhängig von deinem +persönlichen Setup für CakePHP!). Im Anschluss an\`s durchklicken der +Programm-Test-Möglichkeiten, bitte den Link zu deiner persönlichen Datei +anklicken. + +CakeTestCase Callback Methods +----------------------------- + +If you want to sneak in some logic just before or after an individual +CakeTestCase method, and/or before or after your entire CakeTestCase, +the following callbacks are available: + +**start()** + First method called in a *test case*. + +**end()** + Last method called in a *test case*. + +**startCase()** + called before a *test case* is started. + +**endCase()** + called after a *test case* has run. + +**before($method)** + Announces the start of a *test method*. + +**after($method)** + Announces the end of a *test method*. + +**startTest($method)** + Called just before a *test method* is executed. + +**endTest($method)** + Called just after a *test method* has completed. + +Testing models +============== + +Erstellen einer Versuchs-Anwendung (test case) +---------------------------------------------- + +Lass uns darauf einigen dass wir unseren Muster-Artikel (article model) +bereits unter **../app/models/article.php** erstellt haben und dieser +sollte in etwa wie folgt aussehen: + +:: + + name . '.published' => 1 + ); + + return $this->findAll($conditions, $fields); + } + + } + ?> + + Wir möchten jetzt einen Versuch aufbauen welcher Beispielmodule +verwenden wird, allerdings durch Vorrichtungen für Versuche, lässt sich +einiges an Funktionalität im Versuch testen. + Die CakePHP Versuchs-Umgebung läd nur einen sehr kleinen Teil der +Anwendungen (um Versuche isoliert zu lassen), somit müssen wir zum +starten das vorhergehende Module verwenden (in diesem Fall ist das +Anwendungsmodul ja schon fertig definiert), dann informiere die +Versuchs-Umgebung darüber, dass wir das Modul testen wollen, indem wir +herausfinden welche Datenbank-Konfiguration benutzt werden sollte!. Die +CakePHP Test-Umgebung ermöglicht eine Datenbank-Konfiguration namen\`s +**test\_suite**, diese wird gebraucht für alle Module, die auf +Vorrichtungen angewiesen sind. Der Datensatz $useDbConfig zu dieser +Konfigurationsdatei lässt CakePHP wissen das dieses Modul die +**test\_suite** DB-Verbindung benutzt. + Seit wir darüber nachdenken all unsere Anwendungen nochmal +wiederzuverwerten umd das ganze dazu benutzen das wir alle want to reuse +all our existing modules we will create a test model that will extend +from Article, set $useDbConfig and $name appropiately. Let's now create +a file named **article.test.php** in your **app/tests/cases/models** +directory, with the following contents: + +:: + + + +Wie du erkennen solltest we're not really adding any test methods yet, +we have just defined our ArticleTest model (that inherits from Article), +and created the ArticleTestCase. In variable **$fixtures** we define the +set of fixtures that we'll use. + +Creating a test method +---------------------- + +Let's now add a method to test the function published() in the Article +model. Edit the file **app/tests/cases/models/article.test.php** so it +now looks like this: + +:: + + Article =& ClassRegistry::init('Article'); + + $result = $this->Article->published(array('id', 'title')); + $expected = array( + array('Article' => array( 'id' => 1, 'title' => 'First Article' )), + array('Article' => array( 'id' => 2, 'title' => 'Second Article' )), + array('Article' => array( 'id' => 3, 'title' => 'Third Article' )) + ); + + $this->assertEqual($result, $expected); + } + } + ?> + + You can see we have added a method called **testPublished()**. We start +by creating an instance of our fixture based **Article** model, and then +run our **published()** method. In **$expected** we set what we expect +should be the proper result (that we know since we have defined which +records are initally populated to the article table.) We test that the +result equals our expectation by using the **assertEqual** method. See +the section Creating Tests for information on how to run the test. + +Testing controllers +=================== + +Creating a test case +-------------------- + +Say you have a typical articles controller, with its corresponding +model, and it looks like this: + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $result = $this->Article->findAll(null, array('id', + 'title')); + } else { + $result = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $result; + } + + $this->set('title', 'Articles'); + $this->set('articles', $result); + } + } + ?> + +Create a file named articles\_controller.test.php in your +app/tests/cases/controllers directory and put the following inside: + +:: + + Starting Test Case'; + } + function endCase() { + echo '

Ending Test Case

'; + } + function startTest($method) { + echo '

Starting method ' . $method . '

'; + } + function endTest($method) { + echo '
'; + } + function testIndex() { + $result = $this->testAction('/articles/index'); + debug($result); + } + function testIndexShort() { + $result = $this->testAction('/articles/index/short'); + debug($result); + } + function testIndexShortGetRenderedHtml() { + $result = $this->testAction('/articles/index/short', + array('return' => 'render')); + debug(htmlentities($result)); + } + function testIndexShortGetViewVars() { + $result = $this->testAction('/articles/index/short', + array('return' => 'vars')); + debug($result); + } + function testIndexFixturized() { + $result = $this->testAction('/articles/index/short', + array('fixturize' => true)); + debug($result); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'published' + => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body')); + $result = $this->testAction('/articles/index', + array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($result); + } + } + ?> + +The testAction method +--------------------- + +The new thing here is the **testAction** method. The first argument of +that method is the Cake url of the controller action to be tested, as in +'/articles/index/short'. + +The second argument is an array of parameters, consisting of: + +return + Set to what you want returned. + Valid values are: + + - 'vars' - You get the view vars available after executing action + - 'view' - You get The rendered view, without the layout + - 'contents' - You get the rendered view's complete html, including + the layout + - 'result' - You get the returned value when action uses + $this->params['requested']. + + The default is 'result'. +fixturize + Set to true if you want your models auto-fixturized (so your + application tables get copied, along with their records, to test + tables so if you change data it does not affect your real + application.) If you set 'fixturize' to an array of models, then + only those models will be auto-fixturized while the other will + remain with live tables. If you wish to use your fixture files with + testAction() do not use fixturize, and instead just use fixtures as + you normally would. +method + set to 'post' or 'get' if you want to pass data to the controller +data + the data to be passed. Set it to be an associative array consisting + of fields => value. Take a look at + ``function testIndexPostFixturized()`` in above test case to see how + we emulate posting form data for a new article submission. + +Pitfalls +-------- + +If you use testAction to test a method in a controller that does a +redirect, your test will terminate immediately, not yielding any +results. + See +`https://trac.cakephp.org/ticket/4154 `_ +for a possible fix. + +For an in-depth explanation of controller testing please see this blog +post by Mark Story `Testing CakePHP Controllers the hard +way `_. + +Testing Helpers +=============== + +Since a decent amount of logic resides in Helper classes, it's important +to make sure those classes are covered by test cases. + +Helper testing is a bit similar to the same approach for Components. +Suppose we have a helper called CurrencyRendererHelper located in +``app/views/helpers/currency_renderer.php`` with its accompanying test +case file located in +``app/tests/cases/helpers/currency_renderer.test.php`` + +Creating Helper test, part I +---------------------------- + +First of all we will define the responsibilities of our +CurrencyRendererHelper. Basically, it will have two methods just for +demonstration purpose: + +function usd($amount) + +This function will receive the amount to render. It will take 2 decimal +digits filling empty space with zeros and prefix 'USD'. + +function euro($amount) + +This function will do the same as usd() but prefix the output with +'EUR'. Just to make it a bit more complex, we will also wrap the result +in span tags: + +:: + + + +Let's create the tests first: + +:: + + currencyRenderer = new CurrencyRendererHelper(); + } + + //testing usd() function. + public function testUsd() { + $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); + //We should always have 2 decimal digits. + $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); + $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); + //Testing the thousands separator + $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70)); + } + } + +Here, we call ``usd()`` with different parameters and tell the test +suite to check if the returned values are equal to what is expected. + +Executing the test now will result in errors (because +currencyRendererHelper doesn't even exist yet) showing that we have 3 +fails. + +Once we know what our method should do, we can write the method itself: + +:: + + `_ we need a controller to access the +data in the model. + +If the startup() function of the component looks like this: + +:: + + public function startup(&$controller){ + $this->Transporter = $controller->Transporter; + } + +then we can just design a really simple fake class: + +:: + + class FakeTransporterController {} + +and assign values into it like this: + +:: + + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + +Creating a test method +---------------------- + +Just create a class that extends CakeTestCase and start writing tests! + +:: + + class TransporterTestCase extends CakeTestCase { + var $fixtures = array('transporter'); + function testGetTransporter() { + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); + $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); + + $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); + $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); + + $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); + $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); + $this->assertEqual($result, 0, "Noone can service Norway"); + } + } + + +Web testing - Testing views +=========================== + +Most, if not all, CakePHP projects result in a web application. While +unit tests are an excellent way to test small parts of functionality, +you might also want to test the functionality on a large scale. The +**CakeWebTestCase** class provides a good way of doing this testing from +a user point-of-view. + +About CakeWebTestCase +--------------------- + +**CakeWebTestCase** is a direct extension of the SimpleTest WebTestCase, +without any extra functionality. All the functionality found in the +`SimpleTest documentation for Web +testing `_ +is also available here. This also means that no functionality other than +that of SimpleTest is available. This means that you cannot use +fixtures, and **all web test cases involving updating/saving to the +database will permanently change your database values**. Test results +are often based on what values the database holds, so making sure the +database contains the values you expect is part of the testing +procedure. + +Creating a test +--------------- + +In keeping with the other testing conventions, you should create your +view tests in tests/cases/views. You can, of course, put those tests +anywhere but following the conventions whenever possible is always a +good idea. So let's create the file +tests/cases/views/complete\_web.test.php + +First, when you want to write web tests, you must remember to extend +**CakeWebTestCase** instead of CakeTestCase: + +:: + + class CompleteWebTestCase extends CakeWebTestCase + +If you need to do some preparation before you start the test, create a +constructor: + +:: + + function CompleteWebTestCase(){ + //Do stuff here + } + +When writing the actual test cases, the first thing you need to do is +get some output to look at. This can be done by doing a **get** or +**post** request, using **get()**\ or **post()** respectively. Both +these methods take a full url as the first parameter. This can be +dynamically fetched if we assume that the test script is located under +http://your.domain/cake/folder/webroot/test.php by typing: + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +You can then do gets and posts using Cake urls, like this: + +:: + + $this->get($this->baseurl."/products/index/"); + $this->post($this->baseurl."/customers/login", $data); + +The second parameter to the post method, **$data**, is an associative +array containing the post data in Cake format: + +:: + + $data = array( + "data[Customer][mail]" => "user@user.com", + "data[Customer][password]" => "userpass"); + +When you have requested the page you can do all sorts of asserts on it, +using standard SimpleTest web test methods. + +Walking through a page +---------------------- + +CakeWebTest also gives you an option to navigate through your page by +clicking links or images, filling forms and clicking buttons. Please +refer to the SimpleTest documentation for more information on that. + +Testing plugins +=============== + +Tests for plugins are created in their own directory inside the plugins +folder. + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +They work just like normal tests but you have to remember to use the +naming conventions for plugins when importing classes. This is an +example of a testcase for the PizzaOrder model from the plugins chapter +of this manual. A difference from other tests is in the first line where +'Pizza.PizzaOrder' is imported. You also need to prefix your plugin +fixtures with '``plugin.plugin_name.``\ '. + +:: + + PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); + + // do some useful test here + $this->assertTrue(is_object($this->PizzaOrderTest)); + } + } + ?> + +If you want to use plugin fixtures in the app tests you can reference +them using 'plugin.pluginName.fixtureName' syntax in the $fixtures +array. + +That is all there is to it. + +Miscellaneous +============= + +Customizing the test reporter +----------------------------- + +The standard test reporter is **very** minimalistic. If you want more +shiny output to impress someone, fear not, it is actually very easy to +extend. + The only danger is that you have to fiddle with core Cake code, +specifically **/cake/tests/libs/cake\_reporter.php**. + +To change the test output you can override the following methods: + +paintHeader() + Prints before the test is started. +paintPass() + Prints everytime a test case has passed. Use $this->getTestList() to + get an array of information pertaining to the test, and $message to + get the test result. Remember to call parent::paintPass($message). +paintFail() + Prints everytime a test case has failed. Remember to call + parent::paintFail($message). +paintFooter() + Prints when the test is over, i.e. when all test cases has been + executed. + +If, when running paintPass and paintFail, you want to hide the parent +output, enclose the call in html comment tags, as in: + +:: + + echo "\n\n"; + +A sample **cake\_reporter.php**\ setup that creates a table to hold the +test results follows: + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
Res.Test caseMessage
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tPass: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\tFail: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +Grouping tests +-------------- + +If you want several of your test to run at the same time, you can try +creating a test group. Create a file in **/app/tests/groups/** and name +it something like **your\_test\_group\_name.group.php**. In this file, +extend **GroupTest** and import test as follows: + +:: + + + +The code above will group all test cases found in the +**/app/tests/cases/models/** folder. To add an individual file, use +**TestManager::addTestFile**\ ($this, filename). + +Running tests in the Command Line +================================= + +If you have simpletest installed you can run your tests from the command +line of your application. + +from **app/** + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +As the help menu suggests, you'll be able to run all, part, or just a +single test case from your app, plugin, or core, right from the command +line. + +If you have a model test of **test/models/my\_model.test.php** you'd run +just that test case by running: + +:: + + cake testsuite app case models/my_model + diff --git a/de/The-Manual/Core-Behaviors.rst b/de/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..e76d435114c91ee7c90e9b7dc174c7d1b948c6cf --- /dev/null +++ b/de/The-Manual/Core-Behaviors.rst @@ -0,0 +1,15 @@ +Kern Verhaltensweisen +##################### + +Verhaltensweisen erweitern die Funktionalität von *Models*. CakePHP +kommt direkt mit einer Reihe von eingebauten Verhaltensweisen wie zum +Beispiel *Tree* (Baumstruktur) oder *Containable* (Beinhaltet). + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/de/The-Manual/Core-Behaviors/ACL.rst b/de/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..9aedee59be4f8db69b62657c6831dcc82113be3f --- /dev/null +++ b/de/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,106 @@ +ACL +### + +Das ACL Verhalten bietet die Möglichkeit bequem und einfach alle +*Models* mit dem ACL System zu verbinden. Dabei können sowohl *AROs* als +auch *ACOs* definiert werden. + +Um diese Verhaltensweise nutzen zu können, muss man es zu dem *$actsAs* +Array im Model hinzufügen. Dabei gibt man direkt an, ob es sich hierbei +um ein *ARO* oder ein *ACO* handelt. Standardmäßig erstellt man *AROs*. + +Folgender Code würde das ACL-Verhalten im *ARO* Modus aktivieren. + +:: + + class User extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +Folgender Code würde das ACL-Verhalten im *ACO* Modus aktivieren. + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +So kann man das ACL-Verhalten direkt zur Laufzeit aktivieren: + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +Benutzung von AclBehavior +========================= + +Das meiste von AclBehavior arbeitet transparent nachdem die Model +Funktion afterSave() aufgerufen wurde. Wird AclBehaviour verwendet, muss +im Model die Funktion parentNode() definiert sein. Diese Funktion wird +von AclBehavior genutzt um Eltern->Kind Beziehungen festzulegen. Die +parentNode() Methode muss 'null' oder eine Referenz eines Eltern Models +zurückliefern. + +:: + + function parentNode() { + return null; + } + +If you want to set an ACO or ARO node as the parent for your Model, +parentNode() must return the alias of the ACO or ARO node. + +:: + + function parentNode() { + return 'root_node'; + } + +A more complete example. Using an example User Model, where User +belongsTo Group. + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['User']['group_id']) { + return null; + } else { + $this->Group->id = $data['User']['group_id']; + $groupNode = $this->Group->node(); + return array('Group' => array('id' => $groupNode[0]['Aro']['foreign_key'])); + } + } + +In the above example the return is an array that looks similar to the +results of a model find. It is important to have the id value set or the +parentNode relation will fail. The AclBehavior uses this data to +construct its tree structure. + +node() +====== + +Das ACL-Verhalten erlaubt es zu einem *Model-Record* die passende +*ACL-Node* zu finden. Wenn *$model->id* gesetzt ist, kann man einfach +*$model->node()* benutzen, um die dazu passende ACL-Node zu erhalten. + +Man kann auch die *ACL-Node* für jede Zeile finden, indem man ein +data-array übergibt. + +:: + + $this->User->id = 1; + $node = $this->User->node(); + + $user = array('User' => array( + 'id' => 1 + )); + $node = $this->User->node($user); + +Beides würde die gleiche, dazugehörige *ACL-Node* zurückliefern. diff --git a/de/The-Manual/Core-Behaviors/Containable.rst b/de/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..ce562e39b1e269e0606aeb0c900117deb1801f3c --- /dev/null +++ b/de/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,674 @@ +Containable +########### + +Eine Neuerung im CakePHP Kern ist das ContainableBehavior. "To contain" +bedeutet "enthalten". Dieses Modell Verhalten erlaubt es Dir, Filter und +Beschränkungen für find() Operationen auf dem Modell zu einzusetzen. Der +Gebrauch von Containable wir Dir dabei helfen, unnötige +Datenbankabfragen zu verhindern, was im Ergebnis die Geschwindigkeit +Deiner Cake Anwendung erhöht. Diese Klasse wird Dir auch helfen, Deine +Daten sauber und konsistent zu suchen und zu filtern. + +Um dieses neue Verhalten zu benutzen kannst Du $actsAs (Deutsch: "agiert +als") als Eigenschaft Deines Modells setzen: + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +Du kannst das Verhalten auch "on the fly" im Controller hervorrufen: + +:: + + $this->Post->Behaviors->attach('Containable'); + +Um zu sehen, wie Containable arbeitet, seheh wir uns einige Bespiele an. +Als erstesbeginnen wir mit einem find() Aufruf auf das Modell mit dem +Namen Post. Sagen wir, dass Post hasMany Comment, und Post +hasAndBelongsToMany Tag. Die gesammelten Daten aus einem normalen find() +Aufruf sind eher viele: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => A + ) + [1] => Array + ( + [id] => 2 + [name] => B + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +Für einige Ansichten Deiner Anwendung brauchst Du wahrscheinlich nicht +all diese Daten aus dem Post model. ContainableBehavior kann Dir dabei +helfen, die Datenmenge zu reduzieren, die find() zurückliefert. + +Um nur die post-relevanten Informationen anzufordern kannst Du folgendes +schreiben: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +Du kannst Containable auch innerhalb des find() Aufrufes einsetzen: + +:: + + $this->Post->find('all', array('contain' => false)); + +Dies hat zum Ergebnis die folgende Rückgabe: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +Diese Art von Hilfe ist aber nicht neu: genau genommen konntest Du das +bisher auch ohne das ContainableBehavior tun. Etwa so: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable zeigt seinen wahren Glanz, wenn es um komplexere Beziehungen +geht und Du die Daten herausholen willst, die auf der selben Ebene +sitzen. Die $recursive Eigenschaft des Modells ist hilfreich, wenn Du +einen kompletten Zweig von Daten abhacken willst, jedoch nicht, wenn Du +auswählen willst, was Du an Daten behalten willst. Lasst uns mal sehen, +wie das mit der contain() Methode funktioniert. Das erste Argument der +contain Methode erwartet den Modell Namen bzw. ein Array, das die Namen +der zu liefernden Modelle enthält. Würden wir alle "Posts" und die damit +verknüpften "Tags" abfragen wollen (ohne die dazugehörigen Kommentare), +würden wir so etwas versuchen: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Zu Erinnerung, wir können den contain Schlüssel auch in einem find() +Aufruf einsetzen: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Ohne Containable würdest Du die unbindModel() Methode des Modells +einsetzen. Und zwar so oft, wie Du mit Deinem Modell verknüpfte Modelle +hast. Containable bietet einen saubereren Weg, dies zu erreichen. + +Containable geht sogar einen Schritt weiter: Du kannst die abgefragten +Daten der *verknüpften* Modelle filtern. Wenn Du die Ergebnisse vom +ursprünglichen find() Aufruf ansiehst, sieh Dir das "Author" Feld mal +an. Wenn Dich in den Posts aber der Name des Kommentar Autors +interessiert —und nichts anderes— könntest Du folgendes schreiben: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //oder.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Hier haben wir Containable angewiesen, uns die Post Information zu +übergeben, sowie ausschließlich das "author" Feld den verknüpften +Modells "Comment". Die Ausgabe des find Aufrufs können etwa so aussehen: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +Wie Du sehen kannst enthält das Comments Array lediglich das Feld +"author" (und die post\_id, die CakePHP benötigt, um die +Abfrage-Ergebnisse zu zuzuordnen). + +In dem Du eine Bedingung stellst, kannst Du die verknüpften Kommentare +sogar filtern : + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +Im Ergebnis erhalten wir alle Posts zurückgeliefert, die einen Kommentar +von Daniel enthalten: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Zusätzliche Filter können mit den gewohnten\ ``Model->find()`` Optionen +gesetzt werden: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Hier ist ein Beispiel zum Containble Verhalten, wenn Du tiefe, komplexe +Modell-Assoziationen hast. + +Lass uns mal die folgende Modell-Assoziationen annehmen: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +So erhalten wir die oben genannten Assoziationen mit Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Merke Dir, das der 'contain' Schlüssel nur ein mal für das Haupt Modell +gesetzt wird, 'contain' wird danach nicht mehr für assoziierte Modelle +gesetzt. + +Sei vorsichtig, wenn Du 'fields' zusammen mit 'contain' Optionen benutzt +- erwähne alle Fremdschlüssel, die Deine Abfrage direkt oder indirekt +benötigt. Nimm auch zur Kenntnis, dass Du das Containable Verhalten +möglicherweise zum AppModel hinzufügst, da es eh allen Modellen +zugeordnet werden muss, die es benutzen sollen. + +Hier ist ein Beispiel für contain Assoziationen unter Benutzung von +paginating: + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +Using Containable +================= + +To see how Containable works, let's look at a few examples. First, we'll +start off with a find() call on a model named Post. Let's say that Post +hasMany Comment, and Post hasAndBelongsToMany Tag. The amount of data +fetched in a normal find() call is rather extensive: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => Awesome + ) + [1] => Array + ( + [id] => 2 + [name] => Baking + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +For some interfaces in your application, you may not need that much +information from the Post model. One thing the ``ContainableBehavior`` +does is help you cut down on what find() returns. + +For example, to get only the post-related information, you can do the +following: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +You can also invoke Containable's magic from inside the find() call: + +:: + + $this->Post->find('all', array('contain' => false)); + +Having done that, you end up with something a lot more concise: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +This sort of help isn't new: in fact, you can do that without the +``ContainableBehavior`` doing something like this: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable really shines when you have complex associations, and you +want to pare down things that sit at the same level. The model's +``$recursive`` property is helpful if you want to hack off an entire +level of recursion, but not when you want to pick and choose what to +keep at each level. Let's see how it works by using the ``contain()`` +method. + +The contain method's first argument accepts the name, or an array of +names, of the models to keep in the find operation. If we wanted to +fetch all posts and their related tags (without any comment +information), we'd try something like this: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Again, we can use the contain key inside a find() call: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Without Containable, you'd end up needing to use the ``unbindModel()`` +method of the model, multiple times if you're paring off multiple +models. Containable creates a cleaner way to accomplish this same task. + +Containing deeper associations +============================== + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors — and +nothing else — you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the ``ContainableBehavior`` when you've got +deep and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that ``contain`` key is only used once in the main model, +you don't need to use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must be attached to all models used +in containment, you may consider attaching it to your AppModel. + +Using Containable with pagination +================================= + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +By including the 'contain' parameter in the ``$paginate`` property it +will apply to both the find('count') and the find('all') done on the +model + +ContainableBehavior options +=========================== + +The ``ContainableBehavior`` has a number of options that can be set when +the Behavior is attached to a model. The settings allow you to fine tune +the behavior of Containable and work with other behaviors more easily. + +- **recursive** (boolean, optional) set to true to allow containable to + automatically determine the recursiveness level needed to fetch + specified models, and set the model recursiveness to this level. + setting it to false disables this feature. The default value is + ``true``. +- **notices** (boolean, optional) issues E\_NOTICES for bindings + referenced in a containable call that are not valid. The default + value is ``true``. +- **autoFields**: (boolean, optional) auto-add needed fields to fetch + requested bindings. The default value is ``true``. + +You can change ContainableBehavior settings at run time by reattaching +the behavior as seen in `Using behaviors `_ + +ContainableBehavior can sometimes cause issues with other behaviors or +queries that use aggregate functions and/or GROUP BY statements. If you +get invalid SQL errors due to mixing of aggregate and non-aggregate +fields, try disabling the ``autoFields`` setting. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/de/The-Manual/Core-Behaviors/Translate.rst b/de/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..cdc5df5981430ec51994519a67bf76fd01a80934 --- /dev/null +++ b/de/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,377 @@ +Translate +######### + +Das TranslateBehavior ist einfach aufzusetzen und kann direkt mit nur +geringer Konfigurationsarbeit eingesetzt werden. In diesem Abschnitt +wird gezeigt, wie man dieses Behavior zu einem beliebigen Model +hinzufügen und es konfigurieren kann. + +Wenn man TranslateBehavior neben containable verwenden möchte, müssen +die 'fields'-Keys für die Queries gesetzt werden. Ansonsten könnte +invalider SQL-Code generiert werden. + +Initialisieren der i18n Datenbank-Tabellen +========================================== + +Man kann entweder die CakePHP-Konsole dazu verwenden oder die Tabellen +manuell erzeugen. Es wird aber empfohlen, die Konsole zu verwenden, da +sich das Layout in künftigen Versionen von CakePHP ändern könnte. Die +Arbeit mit der Konsole stellt sicher, dass das korrekte Layout benutzt +wird. + +:: + + ./cake i18n + +Wenn man ``[I]`` eingibt, wir das Initialisierungs-Skript für die +i18n-Datenbank aufgerufen. Danach wird gefragt, ob man eine existierende +Datenbank löschen möchte und man eine Datenbank erzeugen möchte. Wenn +man sich sicher ist, dass man noch keine i18n-Tabelle hat, antwortet man +mit yes. Das zweite yes erzeugt dann die Tabelle. + +Das Translate-Behavior an eigene Models binden +============================================== + +Mit der ``$actsAs``-Eigenschaft fügt man es wie im folgenden Beispiel zu +seinem Model hinzu. + +:: + + + +Dies führt so noch zu keinen Ergebnis, da einige Optionen erwartet +werden. Man muss definieren, welche Felder des aktuellen Models in der +Übersetzungs-Tabelle gesucht werden sollen, die in einem ersten Schritt +erzeugt wurde. + +Defining the Fields +=================== + +You can set the fields by simply extending the ``'Translate'`` value +with another array, like so: + +:: + + array( + 'fieldOne', 'fieldTwo', 'and_so_on' + ) + ); + } + ?> + +After you have done that (for example putting "name" as one of the +fields) you already finished the basic setup. Great! According to our +current example the model should now look something like this: + +:: + + array( + 'name' + ) + ); + } + ?> + +When defining fields for TranslateBehavior to translate, be sure to omit +those fields from the translated model's schema. If you leave the fields +in, there can be issues when retrieving data with fallback locales. + +Conclusion +========== + +From now on each record update/creation will cause TranslateBehavior to +copy the value of "name" to the translation table (default: i18n) along +with the current locale. A locale is the identifier of the language, so +to speak. + +The *current locale* is the current value of +``Configure::read('Config.language')``. The value of *Config.language* +is assigned in the L10n Class - unless it is already set. However, the +TranlateBehavior allows you to override this on-the-fly, which allows +the user of your page to create multiple versions without the need to +change his preferences. More about this in the next section. + +Retrieve all translation records for a field +============================================ + +If you want to have all translation records attached to the current +model record you simply extend the *field array* in your behavior setup +as shown below. The naming is completely up to you. + +:: + + array( + 'name' => 'nameTranslation' + ) + ); + } + ?> + +With this setup the result of $this->Post->find() should look something +like this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +**Note**: The model record contains a *virtual* field called "locale". +It indicates which locale is used in this result. + +Note that only fields of the model you are directly doing \`find\` on +will be translated. Models attached via associations won't be translated +because triggering callbacks on associated models is currently not +supported. + +Using the bindTranslation method +-------------------------------- + +You can also retrieve all translations, only when you need them, using +the bindTranslation method + +``bindTranslation($fields, $reset)`` + +``$fields`` is a named-key array of field and association name, where +the key is the translatable field and the value is the fake association +name. + +:: + + $this->Post->bindTranslation(array ('name' => 'nameTranslation')); + $this->Post->find('all', array ('recursive'=>1)); // need at least recursive 1 for this to work. + +With this setup the result of your find() should look something like +this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +Saving in another language +========================== + +You can force the model which is using the TranslateBehavior to save in +a language other than the one detected. + +To tell a model in what language the content is going to be you simply +change the value of the ``$locale`` property on the model before you +save the data to the database. You can do that either in your controller +or you can define it directly in the model. + +**Example A:** In your controller + +:: + + data) { + $this->Post->locale = 'de_de'; // we are going to save the german version + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**Example B:** In your model + +:: + + array( + 'name' + ) + ); + + // Option 1) just define the property directly + var $locale = 'en_us'; + + // Option 2) create a simple method + function setLanguage($locale) { + $this->locale = $locale; + } + } + ?> + +Multiple Translation Tables +=========================== + +If you expect a lot of entries you probably wonder how to deal with a +rapidly growing database table. There are two properties introduced by +TranslateBehavior that allow you to specify which "Model" to bind as the +model containing the translations. + +These are **$translateModel** and **$translateTable**. + +Lets say we want to save our translations for all posts in the table +"post\_i18ns" instead of the default "i18n" table. To do so you need to +setup your model like this: + +:: + + array( + 'name' + ) + ); + + // Use a different model (and table) + var $translateModel = 'PostI18n'; + } + ?> + +**Important** to note is that you have to pluralize the table. It is now +a usual model and can be treated as such and thus comes with the +conventions involved. The table schema itself must be identical with the +one generated by the CakePHP console script. To make sure it fits one +could just initialize an empty i18n table using the console and rename +the table afterwards. + +Create the TranslateModel +------------------------- + +For this to work you need to create the actual model file in your models +folder. The reason is that there is no property to set the displayField +directly in the model using this behavior yet. + +Make sure that you change the ``$displayField`` to ``'field'``. + +:: + + + +That's all it takes. You can also add all other model stuff here like +$useTable. But for better consistency we could do that in the model +which actually uses this translation model. This is where the optional +``$translateTable`` comes into play. + +Changing the Table +------------------ + +If you want to change the name of the table you simply define +$translateTable in your model, like so: + +:: + + array( + 'name' + ) + ); + + // Use a different model + var $translateModel = 'PostI18n'; + + // Use a different table for translateModel + var $translateTable = 'post_translations'; + } + ?> + +Please note that **you can't use $translateTable alone**. If you don't +intend to use a custom ``$translateModel`` then leave this property +untouched. Reason is that it would break your setup and show you a +"Missing Table" message for the default I18n model which is created in +runtime. diff --git a/de/The-Manual/Core-Behaviors/Tree.rst b/de/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..e9247f8133ba1ed48664afedfdb8193f3187f2c8 --- /dev/null +++ b/de/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,628 @@ +Tree (Baumstruktur) +################### + +Es ist ganz normal auch hierarchische Daten in einer Datenbank zu +speichern. Zum Beispiel möchte man Kategorien mit unbegrenzten +Unterkategorien oder Daten in einem unbegrenzten Multilevel Menüsystem +speichern. Auch die ACL Logik basiert auf einer Baumstruktur. + +Für kleine Bäume - oder wo nur wenige Level tief Daten verschachtelt +werden - reicht es aus, eine ``parent_id`` zu haben. Anhand dieser lässt +sich dann leicht die Hierarchische Struktur ablesen. Zusammen mit +cakePHP kommt ein mächtiges Verhalten (``Behavior``), das sich ``Tree`` +nennt. Mit diesem ist es möglich, sogenannte `MPTT +Bäume `_ +aufzubauen und zu bedienen. Und das alles, ohne sich näher mit der +Technik auseinandersetzen zu müssen, solange man das nicht möchte ;). + +Anforderungen +============= + +Um das Baumverhalten zu benutzen, muss Deine Tabelle folgende 3 Felder +haben. Alle Felder sind als ``int``-Wert angegeben. + +- parent - Standard ist ``parent_id``, speichert die Beziehung zum + übergeordnetem Element +- Links - Standard ist ``lft``, speichert die Beziehung nach links der + aktuellen Zeile. +- Rechts - Standard ist ``rght``, speichert die Beziehung nach rechts + der aktuellen Zeile. + +Wenn Du Dich mit der MPTT Logik auskennst, wunderst Du Dich vielleicht +warum man noch die ``parent_id`` benötigt. Das ist ganz einfach: Manche +Dinge lassen sich eben noch einfacher und schneller lösen, wenn man die +``parent_id`` noch hat - zum Beispiel um alle Kinder-Elemente zu finden. + +Grundsätzliche Benutzung +======================== + +Im tree-Behavior sind viele Komponenten enthalten, aber an dieser Stelle +wird zunächst mit einem einfachen Beispiel begonnen, in dem die folgende +Datenbank mit einigen Daten erzeugt wird: + +:: + + CREATE TABLE categories ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + name VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(1, 'My Categories', NULL, 1, 30); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(2, 'Fun', 1, 2, 15); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(3, 'Sport', 2, 3, 8); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(4, 'Surfing', 3, 4, 5); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(5, 'Extreme knitting', 3, 6, 7); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(6, 'Friends', 2, 9, 14); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(7, 'Gerald', 6, 10, 11); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(8, 'Gwendolyn', 6, 12, 13); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(9, 'Work', 1, 16, 29); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(10, 'Reports', 9, 17, 22); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(11, 'Annual', 10, 18, 19); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(12, 'Status', 10, 20, 21); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(13, 'Trips', 9, 23, 28); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(14, 'National', 13, 24, 25); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(15, 'International', 13, 26, 27); + +Um zu testen, ob alles korrekt installiert wurde, kann man eine +Test-Methode erzeugen und den Inhalt des Kategorie-Trees ausgeben +lassen. Dies geht mit folgendem, einfachen Controller: + +:: + + data = $this->Category->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +und dieser noch einfacheren Model-Definition: + +:: + + + +Durch Aufrufen von /categories kann getestet werden, wie der Tree +aussieht. Der Tree sollte in etwa so aussehen: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +Daten hinzufügen +---------------- + +Im vorherigen Abschnitt wurden vorhandene Daten genutzt und +sichergestellt, dass diese mit der Methode ``generatetreelist`` +hierarchisch dargestellt wurden. Das Hinzufügen von Daten funktioniert +normalerweise so wie bei jedem anderen Model auch. Siehe dazu folgendes +Beispiel: + +:: + + // pseudo controller code + $data['Category']['parent_id'] = 3; + $data['Category']['name'] = 'Skating'; + $this->Category->save($data); + +Bei Verwendung des Tree-Behaviors ist es nicht notwendig, mehr als +parent\_id zu setzen, da das Tree-Behavior den Rest übernimmt. Wird +parent\_id nicht gesetzt, wird das neue Element als neues Top-Element +eingefügt: + +:: + + // pseudo controller code + $data = array(); + $data['Category']['name'] = 'Other People\'s Categories'; + $this->Category->save($data); + +Das Ausführen dieser beiden Code-Schnipsel führt folgende Veränderungen +am Baum durch: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating **New** + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories **New** + +Modifying data +-------------- + +Modifying data is as transparent as adding new data. If you modify +something, but do not change the parent\_id field - the structure of +your data will remain unchanged. For example: + +:: + + // pseudo controller code + $this->Category->id = 5; // id of Extreme knitting + $this->Category->save(array('name' =>'Extreme fishing')); + +The above code did not affect the parent\_id field - even if the +parent\_id is included in the data that is passed to save if the value +doesn't change, neither does the data structure. Therefore the tree of +data would now look like: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme fishing **Updated** + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + +Moving data around in your tree is also a simple affair. Let's say that +Extreme fishing does not belong under Sport, but instead should be +located under Other People's Categories. With the following code: + +:: + + // pseudo controller code + $this->Category->id = 5; // id of Extreme fishing + $newParentId = $this->Category->field('id', array('name' => 'Other People\'s Categories')); + $this->Category->save(array('parent_id' => $newParentId)); + +As would be expected the structure would be modified to: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + + - Extreme fishing **Moved** + +Daten löschen +------------- + +Das tree-Behavior stellt mehrere Möglichkeiten zum Löschen von Daten zur +Verfügung. Um mit dem einfachsten Beispiel zu beginnen sagen wir, dass +die Kategorie reports nicht mehr benötigt wird. Um diese Kategorie *und +eventuell vorhandene Kinder* zu löschen, ruft man einfach delete so auf, +wie man das bei jedem beliebigen Model auch tun würde, z.B. mit dem +folgenden Code: + +:: + + // pseudo controller code + $this->Category->id = 10; + $this->Category->delete(); + +The category tree would be modified as follows: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Trips + + - National + - International + +- Other People's Categories + + - Extreme fishing + +Querying and using your data +---------------------------- + +Using and manipulating hierarchical data can be a tricky business. In +addition to the core find methods, with the tree behavior there are a +few more tree-orientated permutations at your disposal. + +Most tree behavior methods return and rely on data being sorted by the +``lft`` field. If you call ``find()`` and do not order by ``lft``, or +call a tree behavior method and pass a sort order, you may get +undesirable results. + +Children +~~~~~~~~ + +The ``children`` method takes the primary key value (the id) of a row +and returns the children, by default in the order they appear in the +tree. The second optional parameter defines whether or not only direct +children should be returned. Using the example data from the previous +section: + +:: + + $allChildren = $this->Category->children(1); // a flat array with 11 items + // -- or -- + $this->Category->id = 1; + $allChildren = $this->Category->children(); // a flat array with 11 items + + // Only return direct children + $directChildren = $this->Category->children(1, true); // a flat array with 2 items + +If you want a recursive array use ``find('threaded')`` + +Kindknoten zählen +~~~~~~~~~~~~~~~~~ + +Genauso woe die Methode ``children``, erwartet auch ``childCount`` den +primary key value (das id-Feld) eines Datensatzes und liefert uns die +Anzahl der Kindknoten zurück. Der zweite optionale Parameter gibt an, ob +nur direkte Kindknoten der nachfolgenden Generation gezählt werden +sollen. Wir benutzen einfach das Beispiel aus der vorherigen Sektion: + +:: + + $totalChildren = $this->Category->childCount(1); // gibt 11 zurück + // -- or -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // gibt 11 zurück + + // Nur Kindknoten der direkt nachfolgenden Generation jeder Kategorie werden gezählt + $numChildren = $this->Category->childCount(1, true); // gibt 2 zurück + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist ($conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)`` + +This method will return data similar to find('list'), with an indented +prefix to show the structure of your data. Below is an example of what +you can expect this method to return see the api for the other find-like +parameters. + +:: + + array( + [1] => "My Categories", + [2] => "_Fun", + [3] => "__Sport", + [4] => "___Surfing", + [16] => "___Skating", + [6] => "__Friends", + [7] => "___Gerald", + [8] => "___Gwendolyn", + [9] => "_Work", + [13] => "__Trips", + [14] => "___National", + [15] => "___International", + [17] => "Other People's Categories", + [5] => "_Extreme fishing" + ) + +getparentnode +~~~~~~~~~~~~~ + +This convenience function will, as the name suggests, return the parent +node for any node, or *false* if the node has no parent (its the root +node). For example: + +:: + + $parent = $this->Category->getparentnode(2); //<- id for fun + // $parent contains All categories + +getpath +~~~~~~~ + +The 'path' when refering to hierachial data is how you get from where +you are to the top. So for example the path from the category +"International" is: + +- My Categories + + - ... + - Work + + - Trips + + - ... + - International + +Using the id of "International" getpath will return each of the parents +in turn (starting from the top). + +:: + + $parents = $this->Category->getpath(15); + +:: + + // contents of $parents + array( + [0] => array('Category' => array('id' => 1, 'name' => 'My Categories', ..)), + [1] => array('Category' => array('id' => 9, 'name' => 'Work', ..)), + [2] => array('Category' => array('id' => 13, 'name' => 'Trips', ..)), + [3] => array('Category' => array('id' => 15, 'name' => 'International', ..)), + ) + +Advanced Usage +============== + +The tree behavior doesn't only work in the background, there are a +number of specific methods defined in the behavior to cater for all your +hierarchical data needs, and any unexpected problems that might arise in +the process. + +moveDown +-------- + +Used to move a single node down the tree. You need to provide the ID of +the element to be moved and a positive number of how many positions the +node should be moved down. All child nodes for the specified node will +also be moved. + +Here is an example of a controller action (in a controller named +Categories) that moves a specified node down the tree: + +:: + + function movedown($name = null, $delta = null) { + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('There is no category named ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Please provide the number of positions the field should be moved down.'); + } + + $this->redirect(array('action' => 'index'), null, true); + } + +For example, if you'd like to move the "Sport" category one position +down, you would request: /categories/movedown/Sport/1. + +moveUp +------ + +Used to move a single node up the tree. You need to provide the ID of +the element to be moved and a positive number of how many positions the +node should be moved up. All child nodes will also be moved. + +Here's an example of a controller action (in a controller named +Categories) that moves a node up the tree: + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('There is no category named ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Please provide a number of positions the category should be moved up.'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +For example, if you would like to move the category "Gwendolyn" up one +position you would request /categories/moveup/Gwendolyn/1. Now the order +of Friends will be Gwendolyn, Gerald. + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Using this method wil either delete or move a node but retain its +sub-tree, which will be reparented one level higher. It offers more +control than ```delete()`` `_, which for a model +using the tree behavior will remove the specified node and all of its +children. + +Taking the following tree as a starting point: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating + +Running the following code with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id); + +The Sport node will be become a top level node: + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +- Sport **Moved** + +This demonstrates the default behavior of ``removeFromTree`` of moving +the node to have no parent, and re-parenting all children. + +If however the following code snippet was used with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id,true); + +The tree would become + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +This demonstrates the alternate use for ``removeFromTree``, the children +have been reparented and 'Sport' has been deleted. + +reorder +------- + +This method can be used to sort hierarchical data. + +Data Integrity +============== + +Due to the nature of complex self referential data structures such as +trees and linked lists, they can occasionally become broken by a +careless call. Take heart, for all is not lost! The Tree Behavior +contains several previously undocumented features designed to recover +from such situations. + +These functions that may save you some time are: + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +The mode parameter is used to specify the source of info that is +valid/correct. The opposite source of data will be populated based upon +that source of info. E.g. if the MPTT fields are corrupt or empty, with +the $mode 'parent' the values of the parent\_id field will be used to +populate the left and right fields. The missingParentAction parameter +only applies to "parent" mode and determines what to do if the parent +field contains an id that is not present. + +reorder(&$model, $options = array()) + +Reorders the nodes (and child nodes) of the tree according to the field +and direction specified in the parameters. This method does not change +the parent of any node. + +The options array contains the values 'id' => null, 'field' => +$model->displayField, 'order' => 'ASC', and 'verify' => true, by +default. + +verify(&$model) + +Returns true if the tree is valid otherwise an array of (type, incorrect +left/right index, message). diff --git a/de/The-Manual/Core-Components.rst b/de/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..4ef5a18b271ed4266f9e1b313ef302a11416f9ef --- /dev/null +++ b/de/The-Manual/Core-Components.rst @@ -0,0 +1,61 @@ +Kern Komponenten +################ + +CakePHP hat eine Vielzahl von eingebauten Komponenten. Sie stellen eine +Kiste von Funktionen zur verfügung, für allgemein nutzbare Aufgaben. + +Acl + +Die Acl-Komponente stellt ein einfach zu nutzendes Interface für +Datenbanken und ini-basierte Acces-Control-List\`s +(Zugangs-Kontroll-Liste). + +Auth + +Die auth-Komponente sorgt für ein einfach nutzbares +Authentifikations-System welches eine ganze Palette von +Authentifikationsprozessen nutzt, wie *controller callbacks*, ACL oder +*Objekt Callbacks*. + +Session + +Die session-Komponente stellt einen Ordnerunabhängigen Schutzumschlag +für die PHP-Sitzungen zur verfügung. + +RequestHandler + +Der request handler erlaubt erlaubt ihnen weiterhin zu prüfen zwischen +den Besucher-Anforderungen und dem informieren deiner Anwendungen über +den Typ des Inhalts und der angeforderten Information. + +Security + +Die security-Komponente erlaubt es dir für eine dichtere Sicherheit zu +sorgen und eine HTTP Authentifizierung zu benutzen und zu managen. + +Email + +Ein Inferface um mit mehreren enthaltener Mail-Transfer-Agenten Emails +zu versenden, inclusive php\`s mail() und smtp. + +Cookie + +Die cookie-Komponente verhält sich ähnlich wie die *session-Komponente* +und stellt eine Umgebung für den heimischen Cookie-Support von PHP zur +Verfügung. + +Um mehr über jede Komponente zu erfahren, sehen in dem Menü links nach, +oder erfahren mehr über: `Eigene Komponenten +Erstellen `_. + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/de/The-Manual/Core-Components/Access-Control-Lists.rst b/de/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..505c5c088a9230d5a823a6ca99f588c6d705bc59 --- /dev/null +++ b/de/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,883 @@ +Access Control Lists (Zugangskontrolldokumente) +############################################### + +CakePHPs Access(Zugriffs-/Zugangs-) control(-Kontroll-) +list(-Listen-)-Funktion ist wohl nicht nur eine der am meisten +diskutierten Funktionen von CakePHP, sondern auch die am häufigsten +gesuchte und wohl auch die verwirrendste Anwendung. Falls Du jetzt grade +nach einem guten Weg suchst um CakePHP-ACL\`s generell zum Laufen zu +bringen, dann lies auf jeden Fall weiter! + +Bleib' tapfer und halte durch, auch wenn's sicherlich noch hart für Dich +wird. Sobald Du es einmal verstanden hast, sind ACL's extrem hilfreiche +Werkzeuge um die Kontrolle über Deine Anwendungen und deren Entwicklung +zu behalten. + +Die Funktionsweise von ACL +========================== + +Mächtige Werkzeuge benötigen eine Zugangkontrolle. Zugangskontrolllisten +(Access Control Lists / ACL) stellen eine Möglichkeit dar, +Berechtigungen für Applikationen sehr detailliert zu verwalten und +gleichzeitig leicht wartbar zu halten. + +Zugangskontrolllisten, oder ACL, kontrollieren zwei wesentliche Dinge: +Objekte, die etwas anfragen und Objekte, die angefragt werden. Im +ACL-Jargon werden Objekte, die etwas anfragen (zumeist Benutzer), Access +Request Objects, kurz AROs, genannt. Objekte im System, die angefragt +werden (zumeist Actions oder Daten), werden Access Control Objects, kurz +ACOs, genannt. Die Entitäten werden "Objekte" genannt, da die +anfragenden Objekte nicht immer Personen sind - in einigen Fällen +möchtest Du vielleicht den Zugriff gewisser Cake Controller beschränken, +um das Initiieren der Programmlogik in anderen Applikationsteilen zu +unterbinden. ACOs kann alles sein, was Du kontrollieren möchtest. Von +einer Controller Action über einen Webservice bis hin zu einer Zeile aus +dem Onlinetagebuch Deiner Großmutter. + +Zur Wiederholung: + +- ACO - Access Control Object - Etwas, das angefragt wird +- ARO - Access Request Object - Etwas das etwas anderes anfragt + +Im Wesentlichen werden ACLs genutzt, um zu entscheiden, wann ein ARO +Zugriff auf ein ACO haben darf. + +Um Dir dabei zu helfen zu verstehen, wie alles zusammenarbeitet, lass +uns ein halb-praktisches Beispiel verwenden. Stell Dir für einen Moment +vor, dass ein Computersytsem von einer bekannten Gruppe von +Fantasiegestalten aus *Herr der Ringe* genutzt wird. Der Anführer der +Gruppe, Gandalf, möchte den Besitz der Gemeinschaft verwalten, dabei +aber einen gesunden Grad an Privatsphäre und Sicherheit für die anderen +Gemeinschaftsmitglieder erhalten. Als erstes muss er nun eine Liste der +beteiligten AROs erstellen: + +- Gandalf +- Aragorn +- Bilbo +- Frodo +- Gollum +- Legolas +- Gimli +- Pippin +- Merry + +Beachte, dass ACL *nicht* das gleiche ist wie eine Authorisierung. Die +Nutzung der ACL passiert, *nachdem* sich ein Benutzer authorisiert hat. +Zwar werden diese beiden normalerweise gemeinsam genutzt, jedoch ist es +wichtig den Unterschied zwischen dem zu erkennen, wer jemand ist +(Authorisierung) und dem, was jemand darf (ACL). + +Als nächstes braucht Gandalf eine Liste von Dingen, bzw. ACOs, die das +System verwalten soll. Diese Liste könnte so aussehen: + +- Waffen +- Der eine Ring +- Gesalzenes Schweinefleisch +- Diplomatie +- Bier + +Üblicherweise werden Systeme unter Verwendung einer Art Matrix +verwaltet, dessen Grundgerüst aus Benutzern und berechtigungsrelevanten +Objekten besteht. Wenn diese Daten in einer Tabelle abgelegt würden, +sähe diese Tabelle etwa wie folgt aus: + +Waffen + +Der eine Ring + +Gesalzenes Schweinefleisch + +Diplomatie + +Bier + +Gandalf + +erlaubt + +erlaubt + +erlaubt + +Aragorn + +erlaubt + +erlaubt + +erlaubt + +erlaubt + +Bilbo + +erlaubt + +Frodo + +erlaubt + +erlaubt + +Gollum + +erlaubt + +Legolas + +erlaubt + +erlaubt + +erlaubt + +erlaubt + +Gimli + +erlaubt + +erlaubt + +Pippin + +erlaubt + +erlaubt + +Merry + +erlaubt + +Auf den ersten Blick sieht dieses System so aus, als könnte es ziemlich +gut funktionieren. Es können Zuordnungen gemacht werden, um die +Sicherheit zu gewährleisten (nur Frodo hat Zugriff auf den Ring) und +"Unfälle" zu vermeiden (die Hobbits sollten von gesalzenem +Schweinefleisch und Waffen fern gehalten werden). Das scheint nun +feinkörnig genug und leicht zu lesen zu sein, oder? + +Für kleine Systeme wie dieses kann ein Matrixaufbau durchaus +funktionieren. Jedoch kann bei wachsenden Systemen oder bei Systemen mit +einer großen Anzahl an Ressourcen (ACOs) und Benutzern (AROs) eine +solche Tabelle schnell unhandlich werden. Stelle Dir eine +Zugriffskontrolle für hunderte von Kriegsfeldlagern vor und versuche, +jede einzelne Einheit zu verwalten. Ein weiterer Nachteil von Matizen +ist, dass es nicht wirklich möglich ist, Benutzer in logische Bereiche +zu gruppieren oder Änderungen an kaskadierenden Berechtigungen, die auf +diesen Bereichen basieren, durchzuführen. Zum Beispiel wäre es schön, +den Hobbits automatisch den Zugriff auf das Bier und das Schweinefleisch +zu gewähren, sobald eine Schlacht geschlagen ist: Das für jeden +einzelnen Benutzer zu tun, wäre lästig und fehleranfällig. Jedoch eine +kaskadierende Berechtigung für alle Hobbits zu erstellen, wäre einfach. + +ACL wird normalerweise über eine Baumstruktur implementiert. In der +Regel existiert dann ein Baum aus AROs und ein Baum aus ACOs. Durch die +Organisation der Objekte in Bäumen, können Berechtigungen noch immer +granular gehandhabt werden, während man das "große Ganze" noch gut im +Blick behält. Als weiser Anführer, der Gandalf ist, wählt er die Nutzung +von ACL in diesem neuen System und organisiert seine Objekte anhand +folgender Liste: + +- Die Gemeinschaft des Rings™ + + - Krieger + + - Aragorn + - Legolas + - Gimli + + - Zauberer + + - Gandalf + + - Hobbits + + - Frodo + - Bilbo + - Merry + - Pippin + + - Besucher + + - Gollum + +Die Nutzung der ARO-Baumstruktur erlaubt es Gandalf, Berechtigungen für +ganze Gruppen von Benutzern auf einmal zu vergeben. Mit dem ARO-Baum +kann Gandalf nun einige gruppenbasierten Berechtigungen erstellen: + +- Gemeinschaft des Rings + (**verboten**: alle) + + - Krieger + (**erlaubt**: Waffen, Bier, Lembasbrot, gesalzenes + Schweinefleisch) + + - Aragorn + - Legolas + - Gimli + + - Zauberer + (**erlaubt**: gesalzenes Schweinefleisch, Diplomatie, Bier) + + - Gandalf + + - Hobbits + (**erlaubt**: Bier) + + - Frodo + - Bilbo + - Merry + - Pippin + + - Besucher + (**erlaubt**: gesalzenes Schweinefleisch) + + - Gollum + +Wenn wir ACL nun nutzen würden, um zu sehen, ob Pippin berechtigt war, +das Bier zu erhalten, würden wir zunächst seinen Pfad im Baum suchen, +der im Beispiel wie folgt aussieht: Gemeinschaft->Hobbits->Pippin Dann +können wir nun die verschiedenen Berechtigungen erkennen, die bei jedem +dieser Punkte vergeben worden sind und können die spezifischen +Berechtigungen benutzen, die sich auf Pippin und das Bier beziehen. + ++--------------------------+----------------------------+----------------------------------------------------+ +| ARO Knoten | Berechtigungsinformation | Ergebnis | ++==========================+============================+====================================================+ +| Gemeinschaft des Rings | alle verboten | Verbietet den Zugriff auf das Bier | ++--------------------------+----------------------------+----------------------------------------------------+ +| Hobbits | erlaube 'Bier' | Erlaubt den Zugriff auf das Bier! | ++--------------------------+----------------------------+----------------------------------------------------+ +| Pippin | -- | Noch immer ist der Zugriff auf das Bier erlaubt! | ++--------------------------+----------------------------+----------------------------------------------------+ + +Da der Knoten 'Pippin' im ACL-Baum nicht explizit den Zugriff auf das +Bier-ACO verweigert, bleibt im Endeffekt der Zugriff auf dieses ACO +erlaubt. + +Der Baum bietet uns ebenfalls die Möglichkeit, weitere Anpassungen für +eine feinere Zugriffkontrolle zu definieren - wobei gleichzeitig die +Möglichkeit erhalten bleibt, pauschal Änderungen an ARO-Gruppen +durchzuführen:: + +- Gemeinschaft des Rings + (**verboten**: alle) + + - Krieger + (**erlaubt**: Waffen, Bier, Lambasbrot, gesalzenes + Schweinefleisch) + + - Aragorn + (erlaubt: Diplomacy) + - Legolas + - Gimli + + - Zauberer + (**erlaubt**: gesalzenes Schweinefleisch, Diplomatie, Bier) + + - Gandalf + + - Hobbits + (**erlaubt**: Bier) + + - Frodo + (erlaubt: Ring) + - Bilbo + - Merry + (verboten: Bier) + - Pippin + (erlaubt: Diplomatie) + + - Besucher + (**erlaubt**: gesalzenes Schweinefleisch) + + - Gollum + +Dieser Ansatz erlaubt uns sowohl weitreichende Berechtigungsänderungen, +als auch granulare Anpassungen. Er ermöglicht uns festzulegen, dass alle +Hobbits Zugriff auf das Bier haben, mit einer Ausnahme—Merry. Um +herauszufinden, ob Merry auf das Bier zugreifen darf, suchen wir seinen +Pfad innerhalb des Baums: Gemeinschaft->Hobbits->Merry und arbeiten uns +anhand der "Bierberechtigung" weiter vor: + ++--------------------------+----------------------------+---------------------------------------+ +| ARO Knoten | Berechtigungsinformation | Ergebnis | ++==========================+============================+=======================================+ +| Gemeinschaft des Rings | alle verboten | Verbietet den Zugriff auf das Bier. | ++--------------------------+----------------------------+---------------------------------------+ +| Hobbits | erlaubt 'Bier' | Erlaubt den Zugriff auf das Bier! | ++--------------------------+----------------------------+---------------------------------------+ +| Merry | verbiete 'Bier' | Verbietet das Bier | ++--------------------------+----------------------------+---------------------------------------+ + +Zugriffsberechtigungen festlegen: Cakes INI-basierte ACL +======================================================== + +Die erste von Cake implementierte ACL basiert auf INI-Dateien, welche im +Cake-Pfad gespeichert werden. Obwohl es einfach ist und gut +funktioniert, empfehlen wir, die datenbankbasierte ACL zu benutzen weil +es möglich ist, neue ACOs und AROs aus der Anwendung heraus zu erzeugen. +Die INI-basierte Variante war für einfache Anwendungen gedacht - und +speziell für die Leute, die aus irgendwelchen Gründen keine Datenbank +benutzen möchten. + +In der Voreinstellung benutzt CakePHP eine datenbankbasierte ACL. Um die +INI-basierte ACL zu aktivieren, muss man CakePHP beibringen, welches +System es benutzen soll. Dies kann man durch ändern folgender Zeilen in +app/config/core.php tun + +:: + + //Diese Zeilen ändern: + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + //in diese: + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +ARO/ACO Berechtigungen werden in **/app/config/acl.ini.php** definiert. +Der Grundgedanke ist, dass die AROs in INI Abschnitten mit drei +Eigenschaften gespeichert werden: groups, allow, und deny. + +- groups: Namen der ARO Gruppen, in dem dieses ARO Mitglied ist. +- allow: Namen der ACOs auf die diese Gruppe Zugriff haben soll +- deny: Namen der ACOs auf die diese Gruppe keinen Zugriff haben soll. + +ACOs werden in Abschnitten der INI-Datei definiert, welche nur die +Eigenschaften allow und deny haben. + +Um ein Beispiel zu geben, schauen wir uns an, wie die AROs, welche für +Die Gemeinschaft des Rings™ erzeugt wurden, in INI-Syntax aussehen +würden: + +:: + + ;------------------------------------- + ; AROs + ;------------------------------------- + [aragorn] + groups = Krieger + allow = Diplomatie + + [legolas] + groups = Krieger + + [gimli] + groups = Krieger + + [gandalf] + groups = Zauberer + + [frodo] + groups = Hobbits + allow = Ring + + [bilbo] + groups = Hobbits + + [merry] + groups = Hobbits + deny = Bier + + [pippin] + groups = Hobbits + + [gollum] + groups = Besucher + + ;------------------------------------- + ; ARO Groups + ;------------------------------------- + [Krieger] + allow = Waffen, Bier, Gesalzenes_Schweinefleisch + + [Zauberer] + allow = Gesalzenes_Schweinefleisch, Diplomatie, Bier + + [Hobbits] + allow = Bier + + [Besucher] + allow = Gesalzenes_Schweinefleisch + +Nachdem die Zugriffsberechtigungen gesetzt sind, kannst Du mit dem +`Abschnitt zur +Zugriffsberechtigungsprüfung `_ +mithilfe der ACL Komponente weitermachen. + +Zugriffsberechtigungen festlegen: Cakes Datenbank-basierte ACL +============================================================== + +Nachdem die INI-basierten ACL-Berechtigungen abgehandelt sind, wenden +wir uns (den weiter verbreiteten) Datenbank-basierten ACL zu. + +Getting Started +--------------- + +The default ACL permissions implementation is database powered. Cake's +database ACL consists of a set of core models, and a console application +that comes with your Cake installation. The models are used by Cake to +interact with your database in order to store and retrieve nodes in tree +format. The console application is used to initialize your database and +interact with your ACO and ARO trees. + +To get started, first you'll need to make sure your +``/app/config/database.php`` is present and correctly configured. See +section 4.1 for more information on database configuration. + +Once you've done that, use the CakePHP console to create your ACL +database tables: + +:: + + $ cake schema run create DbAcl + +Running this command will drop and re-create the tables necessary to +store ACO and ARO information in tree format. The output of the console +application should look something like the following: + +:: + + --------------------------------------------------------------- + Cake Schema Shell + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +This replaces an older deprecated command, "initdb". + +You can also use the SQL file found in ``app/config/sql/db_acl.sql``, +but that's nowhere near as fun. + +When finished, you should have three new database tables in your system: +acos, aros, and aros\_acos (the join table to create permissions +information between the two trees). + +If you're curious about how Cake stores tree information in these +tables, read up on modified database tree traversal. The ACL component +uses CakePHP's `Tree Behavior `_ to manage +the trees' inheritances. The model class files for ACL are all compiled +in a single file +`db\_acl.php `_. + +Now that we're all set up, let's work on creating some ARO and ACO +trees. + +Creating Access Request Objects (AROs) and Access Control Objects (ACOs) +------------------------------------------------------------------------ + +In creating new ACL objects (ACOs and AROs), realize that there are two +main ways to name and access nodes. The *first* method is to link an ACL +object directly to a record in your database by specifying a model name +and foreign key value. The *second* method can be used when an object +has no direct relation to a record in your database - you can provide a +textual alias for the object. + +In general, when you're creating a group or higher level object, use an +alias. If you're managing access to a specific item or record in the +database, use the model/foreign key method. + +You create new ACL objects using the core CakePHP ACL models. In doing +so, there are a number of fields you'll want to use when saving data: +``model``, ``foreign_key``, ``alias``, and ``parent_id``. + +The ``model`` and ``foreign_key`` fields for an ACL object allows you to +link up the object to its corresponding model record (if there is one). +For example, many AROs will have corresponding User records in the +database. Setting an ARO's ``foreign_key`` to the User's ID will allow +you to link up ARO and User information with a single User model find() +call if you've set up the correct model associations. Conversely, if you +want to manage edit operation on a specific blog post or recipe listing, +you may choose to link an ACO to that specific model record. + +The ``alias`` for an ACL object is just a human-readable label you can +use to identify an ACL object that has no direct model record +correlation. Aliases are usually useful in naming user groups or ACO +collections. + +The ``parent_id`` for an ACL object allows you to fill out the tree +structure. Supply the ID of the parent node in the tree to create a new +child. + +Before we can create new ACL objects, we'll need to load up their +respective classes. The easiest way to do this is to include Cake's ACL +Component in your controller's $components array: + +:: + + var $components = array('Acl'); + +Once we've got that done, let's see what some examples of creating these +objects might look like. The following code could be placed in a +controller action somewhere: + +While the examples here focus on ARO creation, the same techniques can +be used to create an ACO tree. + +Keeping with our Fellowship setup, let's first create our ARO groups. +Because our groups won't really have specific records tied to them, +we'll use aliases to create these ACL objects. What we're doing here is +from the perspective of a controller action, but could be done +elsewhere. What we'll cover here is a bit of an artificial approach, but +you should feel comfortable using these techniques to build AROs and +ACOs on the fly. + +This shouldn't be anything drastically new - we're just using models to +save data like we always do: + +:: + + function anyAction() + { + $aro =& $this->Acl->Aro; + + //Here's all of our group info in an array we can iterate through + $groups = array( + 0 => array( + 'alias' => 'warriors' + ), + 1 => array( + 'alias' => 'wizards' + ), + 2 => array( + 'alias' => 'hobbits' + ), + 3 => array( + 'alias' => 'visitors' + ), + ); + + //Iterate and create ARO groups + foreach($groups as $data) + { + //Remember to call create() when saving in loops... + $aro->create(); + + //Save data + $aro->save($data); + } + + //Other action logic goes here... + } + +Once we've got them in there, we can use the ACL console application to +verify the tree structure. + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [2]wizards + + [3]hobbits + + [4]visitors + + --------------------------------------------------------------- + +I suppose it's not much of a tree at this point, but at least we've got +some verification that we've got four top-level nodes. Let's add some +children to those ARO nodes by adding our specific user AROs under these +groups. Every good citizen of Middle Earth has an account in our new +system, so we'll tie these ARO records to specific model records in our +database. + +When adding child nodes to a tree, make sure to use the ACL node ID, +rather than a foreign\_key value. + +:: + + function anyAction() + { + $aro = new Aro(); + + //Here are our user records, ready to be linked up to new ARO records + //This data could come from a model and modified, but we're using static + //arrays here for demonstration purposes. + + $users = array( + 0 => array( + 'alias' => 'Aragorn', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'Legolas', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'Gimli', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'Gandalf', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'Frodo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'Bilbo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'Merry', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'Pippin', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'Gollum', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + //Iterate and create AROs (as children) + foreach($users as $data) + { + //Remember to call create() when saving in loops... + $aro->create(); + + //Save data + $aro->save($data); + } + + //Other action logic goes here... + } + +Typically you won't supply both an alias and a model/foreign\_key, but +we're using both here to make the structure of the tree easier to read +for demonstration purposes. + +The output of that console application command should now be a little +more interesting. Let's give it a try: + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [5]Aragorn + + [6]Legolas + + [7]Gimli + + [2]wizards + + [8]Gandalf + + [3]hobbits + + [9]Frodo + + [10]Bilbo + + [11]Merry + + [12]Pippin + + [4]visitors + + [13]Gollum + + --------------------------------------------------------------- + +Now that we've got our ARO tree setup properly, let's discuss a possible +approach for structuring an ACO tree. While we can structure more of an +abstract representation of our ACO's, it's often more practical to model +an ACO tree after Cake's Controller/Action setup. We've got five main +objects we're handling in this Fellowship scenario, and the natural +setup for that in a Cake application is a group of models, and +ultimately the controllers that manipulate them. Past the controllers +themselves, we'll want to control access to specific actions in those +controllers. + +Based on that idea, let's set up an ACO tree that will mimic a Cake app +setup. Since we have five ACOs, we'll create an ACO tree that should end +up looking something like the following: + +- Weapons +- Rings +- PorkChops +- DiplomaticEfforts +- Ales + +One nice thing about a Cake ACL setup is that each ACO automatically +contains four properties related to CRUD (create, read, update, and +delete) actions. You can create children nodes under each of these five +main ACOs, but using Cake's built in action management covers basic CRUD +operations on a given object. Keeping this in mind will make your ACO +trees smaller and easier to maintain. We'll see how these are used later +on when we discuss how to assign permissions. + +Since you're now a pro at adding AROs, use those same techniques to +create this ACO tree. Create these upper level groups using the core Aco +model. + +Assigning Permissions +--------------------- + +After creating our ACOs and AROs, we can finally assign permissions +between the two groups. This is done using Cake's core Acl component. +Let's continue on with our example. + +Here we'll work in the context of a controller action. We do that +because permissions are managed by the Acl Component. + +:: + + class SomethingsController extends AppController + { + // You might want to place this in the AppController + // instead, but here works great too. + + var $components = array('Acl'); + + } + +Let's set up some basic permissions using the AclComponent in an action +inside this controller. + +:: + + function index() + { + //Allow warriors complete access to weapons + //Both these examples use the alias syntax + $this->Acl->allow('warriors', 'Weapons'); + + //Though the King may not want to let everyone + //have unfettered access + $this->Acl->deny('warriors/Legolas', 'Weapons', 'delete'); + $this->Acl->deny('warriors/Gimli', 'Weapons', 'delete'); + + die(print_r('done', 1)); + } + +The first call we make to the AclComponent allows any user under the +'warriors' ARO group full access to anything under the 'Weapons' ACO +group. Here we're just addressing ACOs and AROs by their aliases. + +Notice the usage of the third parameter? That's where we use those handy +actions that are in-built for all Cake ACOs. The default options for +that parameter are ``create``, ``read``, ``update``, and ``delete`` but +you can add a column in the ``aros_acos`` database table (prefixed with +\_ - for example ``_admin``) and use it alongside the defaults. + +The second set of calls is an attempt to make a more fine-grained +permission decision. We want Aragorn to keep his full-access privileges, +but deny other warriors in the group the ability to delete Weapons +records. We're using the alias syntax to address the AROs above, but you +might want to use the model/foriegn key syntax yourself. What we have +above is equivalent to this: + +:: + + // 6342 = Legolas + // 1564 = Gimli + + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342), 'Weapons', 'delete'); + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564), 'Weapons', 'delete'); + +Addressing a node using the alias syntax uses a slash-delimited string +('/users/employees/developers'). Addressing a node using model/foreign +key syntax uses an array with two parameters: +``array('model' => 'User', 'foreign_key' => 8282)``. + +The next section will help us validate our setup by using the +AclComponent to check the permissions we've just set up. + +Checking Permissions: The ACL Component +--------------------------------------- + +Let's use the AclComponent to make sure dwarves and elves can't remove +things from the armory. At this point, we should be able to use the +AclComponent to make a check between the ACOs and AROs we've created. +The basic syntax for making a permissions check is: + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +Let's give it a try inside a controller action: + +:: + + function index() + { + //These all return true: + $this->Acl->check('warriors/Aragorn', 'Weapons'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'create'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'read'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'update'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'delete'); + + //Remember, we can use the model/foreign key syntax + //for our user AROs + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), 'Weapons'); + + //These also return true: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'create'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'read'); + + //But these return false: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'delete'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'delete'); + } + +The usage here is demonstrational, but hopefully you can see how +checking like this can be used to decide whether or not to allow +something to happen, show an error message, or redirect the user to a +login. diff --git a/de/The-Manual/Core-Components/Authentication.rst b/de/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..561611d4bcb399b754c69cbf62a0833045b7d1f6 --- /dev/null +++ b/de/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,832 @@ +Authentifizierung +################# + +Systeme zur Authentifikation für Benutzer sind ein üblicher Bestandteil +vieler Web Applikationen. In CakePHP gibt es verschiedene Systeme, um +Benutzer zu authentifizieren. Dabei bietet jedes unterschiedliche +Optionen an. Im Kern prüft die Authentifikations-Komponente, ob ein +Benutzer ein Benutzerkonto für diese Seite hat. Wenn ja, bekommt dieser +Nutzer uneingeschränkten Zugang zur kompletten Seite. + +Diese Komponente kann mit der ACL Komponente (Zugangskontrolllisten) +kombiniert werden, um komplexere Zugangskontrollen innerhalb der Seite +zu schaffen. Die ACL Komponente kann beispielsweise einem Nutzer nur +Zugang zu öffentlichen Bereichen einer Seite gewähren, während ein +andere Nutzer auf geschützte Administrationsbereiche dieser Seite +Zugriff erlangt. + +CakePHPs Auth Komponente kann dazu benutzt werden ein solches System +schnell und einfach erstellen. Werfen wir doch mal einen Blick darauf, +wie ein sehr einfaches System zur Authentifizierung gebaut werden kann: + +Wie alle anderen Komponenten benutzt man es, indem man 'Auth' zur Liste +der benutzten Komponenten im Controller hinzufügt.: + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +Oder, man fügt es zum AppController hinzu, damit alle Controller sie +benutzen: + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +So weit so gut. Es gibt ein paar Konventionen, die man berücksichtigen +sollte, wenn man die Auth Komponente benutzt. Standardmäßig erwartet die +Auth Komponente die Verwendung einer Datenbanktabelle namens 'users' mit +den Feldern 'username' und 'password'. *In einigen Fällen läßt die +Datenbank die Benennung 'password' als Spaltenname nicht zu. Weiter +unten zeigen wir dir, wie du die standardmäßig verwendeten Feldernamen +änderst, damit es trotzdem auf deinem System funktioniert.* + +Erstellen wir zunächst unser Benutzertablle mit folgender +SQL-Anweisung:: + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(40), + PRIMARY KEY (id) + ); + +Was man bedenken sollte, wenn man die Datenbanktabelle erstellt, um die +Benutzerdaten für die Authentifizierung zu speichern: Die Auth +Komponente erwartet, dass das Passwort gehashed in der Datenbank +gespeichert wird, anstatt als Klartext hinterlegt zu werden. Stelle +sicher, dass das Feld, indem du das Benutzerpasswort speichern willst, +groß genug für den Hash ist (z.B. 40 Stellen für SHA1). + +Willst Du einen Benutzer manuell anlegen, ist der einfachste Weg, die +richtig gehashten Daten zu bekommen, zu versuchen, sich mit diesen +einzuloggen und dann den SQL Log auszuwerten. + +Für den einfachsten Aufbau musst du nur zwei Aktionen in deinem +Controller erstellen: + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); // Nicht notwendig, wenn bereits in deinem app controller deklariert + + /** + * Die Auth Komponente bietet die benötigte Funktionalität + * für Login, du kannst die Funktion also leer lassen. + */ + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +Während du die login() Funktion leer lassen kannst, musst aber das +Template des login View anlegen (unter app/views/users/login.ctp). Dies +ist das einzige View Template des UsersController, das du erstellen +musst. Das nachfolgende Beispiel geht davon aus, dass du bereits den +Form Helper benutzt: + +:: + + flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +Dieser View erstellt ein einfaches Login-Formular, in das Benutzername +und Passwort eingegeben werden können. Nach dem Abschicken des Formulars +übernimmt die Auth Komponente den Rest. Die Session Flash Mitteilung +zeigt jeden Hinweis an, der von der Auth Komponente generiert wird. + +Unglaublich aber wahr: Wir sind fertig! So wird unglaublich einfach ein +datenbankgestütztes Authentifikationssystem mit Hilfe der Auth +Komponente implementiert. Wie auch immer, wir können natürlich noch viel +mehr machen. Sehen wir uns mal eine erweiterte Nutzung der Komponte an. + +Auth Component Variablen setzen +=============================== + +Wann immer du die Standardoptionen der AuthComponent ändern möchtest, +kannst du das über die Methode beforeFilter() in deinem Controller +machen. Dort können dann die zahlreichen eingebauten Methoden aufgerufen +oder die Variablen der Komponente direkt gesetzt werden. + +Ein Beispiel: um den Namen des Feldes, das benutzt wird, um das Passwort +zu speichern, von 'password' zu 'secretword' zu ändern, muss du das +Folgende tun: + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'secretword' + ); + } + } + +In diesem speziellen Fall musst du ebenfalls die Feldnamen im View +Template ändern! + +Ein anderer üblicher Gebrauch der Variablen der Auth Komponente ist es, +einem Benutzer Zugriff zu bestimmten Methoden zu erlauben, ohne das +dieser angemeldet sein muss (standardmäßig beschränkt Auth den Zugriff +zu jeder Aktion außer Login und Logout Methoden). + +Wenn wir beispielsweise allen Nutzern Zugang zu den index und view +Methoden gewähren wollen (aber keiner anderen), würden wir das Folgende +machen: + +:: + + function beforeFilter() { + $this->Auth->allow('index','view'); + } + +Anzeigen der Fehlermeldungen von Auth +===================================== + +Um Fehlermeldungen anzuzeigen, die Auth ausgespuckt hat, muss folgender +Code zu deinem View hinzugefügt werden. In diesem Fall wird die +Nachricht unter den regulären Flash Nachrichten angezeigt: + +Um alle regulären Flash-Mitteilungen und auch die Benachrichtigungen von +Auth in allen Views anzuzeigen, füge die folgenden zwei Zeilen zur Datei +views/layouts/default.ctp file in die Sektion body hinzu - am besten vor +der Zeile mit "content\_for\_layout line". + +:: + + flash(); + $session->flash('auth'); + ?> + +Fehlersuche bei Auth Problemen +============================== + +Manchmal kann es schwierig sein, Probleme zu diagnostizieren, wenn das +Verhalten nicht dem erwarteten Verhalten entspricht. Im folgenden finden +sich daher einige wichtige Punkte, die es zu beachten gilt. + +*Passwort-Hashing* + +Wenn Informationen über ein Formular an eine Action gepostet werden, +hasht die Auth-Komponente automatisch den Inhalt des Passwort-Feldes, +wenn Daten im Username-Feld eingetragen sind. Wenn man also eine +Registrierungsseite erzeugt, sollte man sicherstellen, dass der Nutzer +auch ein "Passwort bestätigen"-Feld ausfüllt, so dass man den Wert +dieser beiden Fehler vergleichen kann. Hier ist ein Beispiel: + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +Ändern der Hash-Funktion +======================== + +Die Auth-Komponenten verwendet die Sicherheitsklasse um ein Passwort zu +hashen. Standardmäßig verwendet die Sicherheitsklasse das SHA1-Schema. +Um die Hash-Funktion zu wechseln, die von der Auth-Komponente verwendet +wird, wird die ``setHash`` -Methode verwendet, der ``md5``, ``sha1`` or +``sha256`` als erster und einziger Parameter übergeben wird. + +:: + + Security::setHash('md5'); // oder sha1 oder sha256. + +Die Sicherheitsklasse verwendet einen Salt-Wert (in /app/config/core.php +gesetzt), um das Passwort zu hashen. + +Wenn ein anderer Algorithmus zum Passwort-Hashen der Salt-Anwendung +verwendet werden soll als md5/sha1, muss der Standard-Mechanismus +hashPassword überschrieben werden. Dies ist zum Beispiel dann notwendig, +wenn bereits eine Datenbank existiert, die bisher ein Hashing-Schema +ohne Salt verwendet hat. Zu diesem Zweck erzeugt man die Methode +``hashPasswords`` in der Klasse, die für das Hashen der Passwörter +zuständig sein soll (gewöhnlicherweise das User-Model) und setzt +``authenticate`` auf das Objekt, gegen das authentifiert wird +(normalerweise ist dies User), wie hier gezeigt: + +:: + + function beforeFilter() { + $this->Auth->authenticate = ClassRegistry::init('User'); + ... + parent::beforeFilter(); + } + +Mit dem oben gezeigten Code, wird die Methode hashPasswords() des +User-Model jedesmal dann aufgerufen, wenn Cake die +AuthComponent::hashPasswords() aufruft. Hier ist ein Beispiel für eine +hashPassword-Funktion, wenn man bereits eine user-Tabelle mit Plaintext +md5-hash-Passwörtern hat: + +:: + + class User extends AppModel { + function hashPasswords($data) { + if (isset($data['User']['password'])) { + $data['User']['password'] = md5($data['User']['password']); + return $data; + } + return $data; + } + } + +AuthComponent-Methoden +====================== + +action +------ + +``action (string $action = ':controller/:action')`` + +Wenn man als Teil seiner ACL-Struktur ACOs verwendet, kann man +folgendermaßen den Pfad zum ACO-Knoten erhalten, der an ein bestimmtes +Controller/Action-Paar geknüpft ist: + +:: + + $acoNode = $this->Auth->action('users/delete'); + +Wenn keine Werte übergeben werden, wird das aktuelle +Controller/Action-Paar verwendet. + +allow +----- + +If you have some actions in your controller that you don't have to +authenticate against (such as a user registration action), you can add +methods that the AuthComponent should ignore. The following example +shows how to allow an action named 'register'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('register'); + } + +If you wish to allow multiple actions to skip authentication, you supply +them as parameters to the allow() method: + +:: + + function beforeFilter() { + ... + $this->Auth->allow('foo', 'bar', 'baz'); + } + +Shortcut: you may also allow all the actions in a controller by using +'\*'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('*'); + } + +If you are using requestAction in your layout or elements you should +allow those actions in order to be able to open login page properly. + +The auth component assumes that your actions names `follow +conventions `_ and +are underscored. + +deny +---- + +Es kann sein, dass du Actions aus der Liste der erlaubten Actions +(festgelegt mittels $this->Auth->allow()) entfernen möchtest. Hier ist +ein Beispiel: + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +This method checks if the ``$data`` contains the username and password +fields as specified by the variable ``$fields`` indexed by the model +name as specified by ``$userModel``. If the ``$data`` array contains +both the username and password, it hashes the password field in the +array and returns the ``data`` array in the same format. This function +should be used prior to insert or update calls of the user when the +password field is affected. + +:: + + $data['User']['username'] = 'me@me.com'; + $data['User']['password'] = 'changeme'; + $hashedPasswords = $this->Auth->hashPasswords($data); + pr($hashedPasswords); + /* returns: + Array + ( + [User] => Array + ( + [username] => me@me.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +The *$hashedPasswords['User']['password']* field would now be hashed +using the ``password`` function of the component. + +If your controller uses the Auth component and posted data contains the +fields as explained above, it will automatically hash the password field +using this function. + +mapActions +---------- + +If you are using Acl in CRUD mode, you may want to assign certain +non-default actions to each part of CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('someAction'), + 'read' => array('someAction', 'someAction2'), + 'update' => array('someAction'), + 'delete' => array('someAction') + ) + ); + +login +----- + +``login($data = null)`` + +If you are doing some sort of Ajax-based login, you can use this method +to manually log someone into the system. If you don't pass any value for +``$data``, it will automatically use POST data passed into the +controller. + +for example, in an application you may wish to assign a user a password +and auto log them in after registration. In an over simplified example: + +View: + +:: + + echo $form->create('User',array('action'=>'register')); + echo $form->input('username'); + echo $form->end('Register'); + +Controller: + +:: + + function register() { + if(!empty($this->data)) { + $this->User->create(); + $assigned_password = "password"; + $this->data['User']['password'] = $this->Auth->password($assigned_password); + if($this->User->save($this->data)) { + // send signup email containing password to the user + $this->Auth->login($this->data); + $this->redirect("home"); + } + } + +One thing to note is that you must manually redirect the user after +login as loginRedirect is not called. + +``$this->Auth->login($data)`` returns 1 on successful login, 0 on a +failure + +logout +------ + +Mittels dieser Methode kann ein Nutzer schnell de-authentisiert und auf +eine beliebige Seite weitergeleitet werden. Die Methode ist ferner +nützlich, falls man einen "Ausloggen"-Link innerhalb eines geschützten +Bereichs einer Anwendung bereitstellen möchte. + +Beispiel: + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +Pass in a string, and you can get what the hashed password would look +like. This is an essential functionality if you are creating a user +registration screen where you have users enter their password a second +time to confirm it. + +:: + + if ($this->data['User']['password'] == + $this->Auth->password($this->data['User']['password2'])) { + + // Passwords match, continue processing + ... + } else { + $this->flash('Typed passwords did not match', 'users/register'); + } + +The auth component will automatically hash the password field if the +username field is also present in the submitted data + +Cake appends your password string to a salt value and then hashes it. +The hashing function used depends on the one set by the core utility +class ``Security`` (sha1 by default). You can use the +``Security::setHash`` function to change the hashing method. The salt +value is used from your application's configuration defined in your +``core.php`` + +user +---- + +``user(string $key = null)`` + +This method provides information about the currently authenticated user. +The information is taken from the session. For example: + +:: + + if ($this->Auth->user('role') == 'admin') { + $this->flash('You have admin access'); + } + +It can also be used to return the whole user session data like so: + +:: + + $data['User'] = $this->Auth->user(); + +If this method returns null, the user is not logged in. + +In the view you can use the Session helper to retrieve the currently +authenticated user's information: + +:: + + $session->read('Auth.User'); // returns complete user record + $session->read('Auth.User.first_name') //returns particular field value + +The session key can be different depending on which model Auth is +configured to use. Eg. If you use model ``Account`` instead of ``User``, +then the session key would be ``Auth.Account`` + +AuthComponent-Variablen +======================= + +Es sind zahlreiche Variablen im Zusammenhang mit Auth vorhanden, die +verwendet werden können. Normalerweise fügt man diese Einstellungen in +die beforeFilter()-Methode des Controllers ein. Man kann diese +Einstellungen aber auch zur beforeFilter()-Methode des App-Controllers +hinzufügen, wenn die Einstellungen für die gesamten Seiten gelten +sollen. + +userModel +--------- + +Es ist auch kein Problem, wenn nicht gegen ein user-Model +authentifiziert werden soll. Mann kann dies einfach dadurch ändern, +indem man diesen Wert auf den Namen des Models setzt, das verwendet +werden soll. + +:: + + Auth->userModel = 'Member'; + ?> + +fields +------ + +Überschreibt die Standard-Felder für Username und Passwort, die für die +Authentifizierung verwendet werden. + +:: + + Auth->fields = array('username' => 'email', 'password' => 'passwd'); + ?> + +userScope +--------- + +Wird benutzt, um zusätzliche Bedingungen für eine erfolgreiche +Authentifizierung zu erstellen. + +:: + + Auth->userScope = array('User.active' => true); + ?> + +loginAction +----------- + +Man kann den Standard-Login von */users/login* zu einer Action seiner +Wahl ändern. + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'members', 'action' => 'login'); + ?> + +loginRedirect +------------- + +The AuthComponent remembers what controller/action pair you were trying +to get to before you were asked to authenticate yourself by storing this +value in the Session, under the ``Auth.redirect`` key. However, if this +session value is not set (if you're coming to the login page from an +external link, for example), then the user will be redirected to the URL +specified in loginRedirect. + +Example: + +:: + + Auth->loginRedirect = array('controller' => 'members', 'action' => 'home'); + ?> + +logoutRedirect +-------------- + +You can also specify where you want the user to go after they are logged +out, with the default being the login action. + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'members', 'action' => 'logout'); + ?> + +loginError +---------- + +Change the default error message displayed when someone does not +successfully log in. + +:: + + Auth->loginError = "No, you fool! That's not the right password!"; + ?> + +authError +--------- + +Change the default error message displayed when someone attempts to +access an object or action to which they do not have access. + +:: + + Auth->authError = "Sorry, you are lacking access."; + ?> + +autoRedirect +------------ + +Normally, the AuthComponent will automatically redirect you as soon as +it authenticates. Sometimes you want to do some more checking before you +redirect users: + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- code inside this function will execute only when autoRedirect was set to false (i.e. in a beforeFilter). + if ($this->Auth->user()) { + if (!empty($this->data['User']['remember_me'])) { + $cookie = array(); + $cookie['username'] = $this->data['User']['username']; + $cookie['password'] = $this->data['User']['password']; + $this->Cookie->write('Auth.User', $cookie, true, '+2 weeks'); + unset($this->data['User']['remember_me']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.User'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // Clear auth message, just in case we use it. + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +The code in the login function will not execute *unless* you set +$autoRedirect to false in a beforeFilter. The code present in the login +function will only execute *after* authentication was attempted. This is +the best place to determine whether or not a successful login occurred +by the AuthComponent (should you desire to log the last successful login +timestamp, etc.). + +With autoRedirect set to false, you can also inject additional code such +as keeping track of the last successful login timestamp + +:: + + data)) && $this->Auth->user() ){ + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('last_login', date('Y-m-d H:i:s') ); + $this->redirect($this->Auth->redirect()); + } + } + ?> + +authorize +--------- + +Normally, the AuthComponent will attempt to verify that the login +credentials you've entered are accurate by comparing them to what's been +stored in your user model. However, there are times where you might want +to do some additional work in determining proper credentials. By setting +this variable to one of several different values, you can do different +things. Here are some of the more common ones you might want to use. + +:: + + Auth->authorize = 'controller'; + ?> + +When authorize is set to 'controller', you'll need to add a method +called isAuthorized() to your controller. This method allows you to do +some more authentication checks and then return either true or false. + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } + } + if ($this->action == 'view') { + return true; + } + ... + return false; + } + ?> + +Remember that this method will be checked after you have already passed +the basic authentication check against the user model. + +:: + + Auth->authorize = 'model'; + ?> + +Don't want to add anything to your controller and might be using ACO's? +You can get the AuthComponent to call a method in your user model called +isAuthorized() to do the same sort of thing: + +:: + + + +Lastly, you can use authorize with actions such as below + +:: + + Auth->authorize = 'actions'; + ?> + +By using actions, Auth will make use of ACL and check with +AclComponent::check(). An isAuthorized function is not needed. + +:: + + Auth->authorize = 'crud'; + ?> + +By using crud, Auth will make use of ACL and check with +AclComponent::check(). Actions should be mapped to CRUD (see +`mapActions `_). + +sessionKey +---------- + +Name of the session array key where the record of the current authed +user is stored. + +Defaults to "Auth", so if unspecified, the record is stored in +"Auth.{$userModel name}". + +:: + + Auth->sessionKey = 'Authorized'; + ?> + +ajaxLogin +--------- + +If you are doing Ajax or Javascript based requests that require +authenticated sessions, set this variable to the name of a view element +you would like to be rendered and returned when you have an invalid or +expired session. + +As with any part of CakePHP, be sure to take a look at `AuthComponent +class `_ for a more +in-depth look at the AuthComponent. + +authenticate +------------ + +This variable holds a reference to the object responsible for hashing +passwords if it is necessary to change/override the default password +hashing mechanism. See `Changing the Encryption +Type `_ for more info. + +actionPath +---------- + +If using action-based access control, this defines how the paths to +action ACO nodes is computed. If, for example, all controller nodes are +nested under an ACO node named 'Controllers', $actionPath should be set +to 'Controllers/'. diff --git a/de/The-Manual/Core-Components/Cookies.rst b/de/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..841d0d6d2ffd6513c0aad3321b330dfc95d14282 --- /dev/null +++ b/de/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,187 @@ +Cookies +####### + +The CookieComponent is a wrapper around the native PHP setcookie method. +It also includes a host of delicious icing to make coding cookies in +your controllers very convenient. Before attempting to use the +CookieComponent, you must make sure that 'Cookie' is listed in your +controllers' $components array. + +Controller-Setup +================ + +Es existieren zahlreiche Controller-Variablen, mit denen die Erzeugung +und die Verwaltung von Cookies konfiguriert werden kann. Die Definition +dieser speziellen Variablen in der beforeFilter()-Methode des +Controllers ermöglicht die Konfiguration des Verhaltens der +CookieComponent. + +Cookie-Variable + +Standardwert + +Beschreibung + +String $name + +'CakeCookie' + +Der Name des Cookies. + +String $key + +null + +Dieser String wird benutzt um den Wert zu verschlüsseln, der in das +Cookie geschrieben wird. Dieser String sollte zufällig erzeugt und +schwierig zu erraten sein. + +String $domain + +'' + +Der Domainname, dem der Zugriff auf das Cookie erlaubt ist. Man schreibt +'.yourdomain.com' um von allen Subdomains dieser Domain zugreifen zu +können. + +Int or String $time + +'5 Days' + +Der Zeitpunkt, wenn das Cookie abläuft. Integer-Werte werden als +Sekunden interpretiert und ein Wert von 0 bezeichnet ein 'session +cookie', d.h. das Cookie wird ungültig, wenn der Browser geschlossen +wird. Wenn ein String gesetzt ist, wird dieser als PHP-Funktion +strtotime() interpretiert. Dieser kann direkt innerhalb der +write()-Methode gesetzt werden. + +String $path + +'/' + +Der Server-Pfad, auf den das Cookie angewendet wird. Wenn $cookiePath +auf '/foo/' gesetzt ist, ist das Cookie nur innerhalb des +/foo/-Verzeichnisses der Domain und seiner Unterverzeichnisse verfügbar. +Der Standardwert ist die gesamte Domain. Auch dieser Wert kann direkt in +der write()-Methode gesetzt werden. + +Boolean $secure + +false + +Gibt an, dass das Cookie nur über eine sichere HTTPS-Verbindung +übertragen werden soll. Wenn dieser Wert auf true gesetzt ist, wird das +Cookie nur gesetzt, wenn eine sichere Verbindung existiert. Auch dieser +Wert kann direkt in der write()-Methode gesetzt werden. + +Der folgende Code-Ausschnitt zeigt, wie CookieComponent eingefügt werden +kann und wie die Controller-Variablen gesetzt werden können, um ein +Cookie namens 'baker\_id' für die Domain 'example.com', das eine sichere +Verbindung benötigt gesetzt werden kann. Das Cookie ist im Pfad +'/bakers/preferences/' verfügbar und verliert in einer Stunde seine +Gültigkeit. + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'baker_id'; + $this->Cookie->time = 3600; // or '1 hour' + $this->Cookie->path = '/bakers/preferences/'; + $this->Cookie->domain = 'example.com'; + $this->Cookie->secure = true; //i.e. only sent if using secure HTTPS + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +Als nächstes schauen wir uns an, wie die verschiedenen Methoden von +CookieComponent bentutzt werden können. + +Using the Component +=================== + +The CookieComponent offers a number of methods for working with Cookies. + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +The write() method is the heart of cookie component, $key is the cookie +variable name you want, and the $value is the information to be stored. + +:: + + $this->Cookie->write('name','Larry'); + +You can also group your variables by supplying dot notation in the key +parameter. + +:: + + $this->Cookie->write('User.name', 'Larry'); + $this->Cookie->write('User.role','Lead'); + +If you want to write more than one value to the cookie at a time, you +can pass an array: + +:: + + $this->Cookie->write( + array('name'=>'Larry','role'=>'Lead') + ); + +All values in the cookie are encrypted by default. If you want to store +the values as plain-text, set the third parameter of the write() method +to false. The encryption performed on cookie values is fairly +uncomplicated encryption system. It uses Security.salt and a predefined +``CIPHER_SEED`` constant to encrypt values. To make your cookies more +secure you should define ``CIPHER_SEED`` in your bootstrap to ensure a +better encryption. The default value of ``CIPHER_SEED`` is +``76859309657453542496749683645`` + +:: + + $this->Cookie->write('name','Larry',false); + +The last parameter to write is $expires – the number of seconds before +your cookie will expire. For convenience, this parameter can also be +passed as a string that the php strtotime() function understands: + +:: + + //Both cookies expire in one hour. + $this->Cookie->write('first_name','Larry',false, 3600); + $this->Cookie->write('last_name','Masters',false, '1 hour'); + +**read(mixed $key)** + +This method is used to read the value of a cookie variable with the name +specified by $key. + +:: + + // Outputs “Larry” + echo $this->Cookie->read('name'); + + //You can also use the dot notation for read + echo $this->Cookie->read('User.name'); + + //To get the variables which you had grouped + //using the dot notation as an array use something like + $this->Cookie->read('User'); + + // this outputs something like array('name' => 'Larry', 'role'=>'Lead') + +**del(mixed $key)** + +Deletes a cookie variable of the name in $key. Works with dot notation. + +:: + + //Delete a variable + $this->Cookie->del('bar') + + //Delete the cookie variable bar, but not all under foo + $this->Cookie->del('foo.bar') + + +**destroy()** + +Destroys the current cookie. diff --git a/de/The-Manual/Core-Components/Email.rst b/de/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..703b40721d0b2dfd60463f4d78088b56ee0003d4 --- /dev/null +++ b/de/The-Manual/Core-Components/Email.rst @@ -0,0 +1,258 @@ +Email +##### + +Die *emailComponent* ist eine Möglichkeit um einfaches E-Mail-versenden +in einer CakePHP-Applikation zu realisieren. Dabei wird auf das selbe +Konzept wie bei den Layout- und View-ctp-Dateien zurückgegriffen um +formatierte E-Mails als HTML, Text oder beides zu versenden. Die +Komponente unterstützt das Versenden mit den PHP-eigenen +Mail-Funktionen, einem SMTP-Server oder einem Debug-Modus, indem sie die +Mails in eine Session-Flash-Nachricht schreibt. Dateianhänge werden +dabei genauso unterstützt wie die Komponente auch ein paar +grundsätzliche Header-Injection Überprüfungen durchführt. Es gibt noch +eine Menge Dinge, die die Komponente nicht übernimmt, aber für den +Anfang reicht sie sicherlich. + +Klassenattribute und Variablen +============================== + +Diese Werte können gesetzt werden, bevor ``EmailComponent::send()`` +aufgerufen wird: + +to + +Adresse an die die Nachricht gesendet wird. (string) + +cc + +Array von Adressen die Kopien erhalten sollen. + +bcc + +Array von Adressen die Blindkopien erhalten sollen. + +replyTo + +Antwortadresse (string) + +from + +Absenderadresse (string) + +subject + +Betreff der Nachricht (string) + +template + +Das E-Mail-Template welches für die Nachricht verwendet werden soll (zu +finden in ``app/views/elements/email/html/`` und +``app/views/elements/email/text/``). + +layout + +Das Layout welches für die Nachricht verwendet werden soll (zu finden in +``app/views/layouts/email/html/`` und +``app/views/layouts/email/text/``). + +lineLength + +Anzahl Zeichen, nach der ein automatischer Zeilenumbruch erfolgen soll. +Standardwert ist 70 Zeichen. (integer) + +sendAs + +Gibt an ob die Nachricht im Textformat (``text``), als HTML-Nachricht +(``html``) oder in beiden Formaten (``both``) gesendet werden soll. + +attachments + +Array von Dateien zum anhängen. Absolute und relative Pfade sind +möglich. + +delivery + +Wie soll die Nachricht versendet werden. (``mail``, ``smtp`` [erfordert +smtpOptions s.u.] und ``debug``) + +smtpOptions + +Assoziatives Array mit Optionen welcher Smtp-Mailer benutzt werden soll +(``port``, ``host``, ``timeout``, ``username``, ``password``, +``client``). + +Es gibt noch ein paar weitere Optionen die genutzt werden können. In der +Dokumentation zur API finden sich weitere Informationen. + +Mehrfache E-Mails in einer Schleife versenden +--------------------------------------------- + +Wenn du mehrere E-Mails in einer Schleife versenden möchtest, ist es +wichtig die E-Mail-Felder vor dem erneuten setzen der Eigenschaften +wieder mit der reset() Methode der E-Mail Komponente rückzusetzen. + +:: + + $this->Email->reset() + +Einfaches E-Mails versenden +=========================== + +Um eine Nachricht ohne Vorlage (template) zu versenden reicht es, +einfach den Nachrichtentext als Zeichenkette (string) oder Zeilenarray +der send() Methode zu übergeben: + +:: + + $this->Email->from = 'Irgendjemand '; + $this->Email->to = 'Irgendjemand Anderes '; + $this->Email->subject = 'Test'; + $this->Email->send('Dies ist der Nachrichtenrumpf!'); + +Setting up the Layouts +---------------------- + +To use both text and html mailing message you need to create layout +files for them, just like in setting up your default layouts for the +display of your views in a browser, you need to set up default layouts +for your email messages. In the ``app/views/layouts/`` directory you +need to set up (at a minimum) the following structure + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +These are the files that hold the layout templates for your default +messages. Some example content is below + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +Setup an email element for the message body +------------------------------------------- + +In the ``app/views/elements/email/`` directory you need to set up +folders for ``text`` and ``html`` unless you plan to just send one or +the other. In each of these folders you need to create templates for +both types of messages referring to the content that you send to the +view either by using $this->set() or using the $contents parameter of +the send() method. Some simple examples are shown below. It is +worthwhile to note that $this->set() should be done before invoking +Email's send(), a little break in mindset of the usual CakePHP view +conventions. For this example we will call the templates +simple\_message.ctp + +``text`` + +:: + + Dear , + Thank you for your interest. + +``html`` + +:: + +

Dear ,
+    Thank you for your interest.

+ +Controller +---------- + +In your controller you need to add the component to your ``$components`` +array or add a $components array to your controller like: + +:: + + + +In this example we will set up a private method to handle sending the +email messages to a user identified by an $id. In our controller (let's +use the User controller in this example) + +:: + + + User->read(null,$id); + $this->Email->to = $User['User']['email']; + $this->Email->bcc = array('secret@example.com'); + $this->Email->subject = 'Welcome to our really cool thing'; + $this->Email->replyTo = 'support@example.com'; + $this->Email->from = 'Cool Web App '; + $this->Email->template = 'simple_message'; // note no '.ctp' + //Send as 'html', 'text' or 'both' (default is 'text') + $this->Email->sendAs = 'both'; // because we like to send pretty mail + //Set view variables as normal + $this->set('User', $User); + //Do not pass any args to send() + $this->Email->send(); + } + ?> + +You have sent a message, you could call this from another method like + +:: + + + $this->_sendNewUserMail( $this->User->id ); + +Sending A Message Using SMTP +============================ + +To send an email using an SMTP server, the steps are similar to sending +a basic message. Set the delivery method to ``smtp`` and assign any +options to the Email object's ``smtpOptions`` property. You may also +retrieve SMTP errors generated during the session by reading the +``smtpError`` property of the component. + +:: + + /* SMTP Options */ + $this->Email->smtpOptions = array( + 'port'=>'25', + 'timeout'=>'30', + 'host' => 'your.smtp.server', + 'username'=>'your_smtp_username', + 'password'=>'your_smtp_password', + 'client' => 'smtp_helo_hostname' + ); + + /* Set delivery method */ + $this->Email->delivery = 'smtp'; + + /* Do not pass any args to send() */ + $this->Email->send(); + + /* Check for SMTP errors. */ + $this->set('smtp-errors', $this->Email->smtpError); + +If your SMTP server requires authentication, be sure to specify the +username and password parameters for ``smtpOptions`` as shown in the +example. + +If you don't know what an SMTP HELO is, then you most likely will not +need to set the ``client`` parameter for the ``smtpOptions``. This is +only needed for compatibility with SMTP servers which do not fully +respect RFC 821 (SMTP HELO). diff --git a/de/The-Manual/Core-Components/Request-Handling.rst b/de/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..4a0692477db525c9c5c271a79ef235d7e1ac1298 --- /dev/null +++ b/de/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,272 @@ +Request Handling +################ + +The Request Handler component is used in CakePHP to obtain additional +information about the HTTP requests that are made to your applications. +You can use it to inform your controllers about Ajax as well as gain +additional insight into content types that the client accepts and +automatically changes to the appropriate layout when file extensions are +enabled. + +By default RequestHandler will automatically detect Ajax requests based +on the HTTP-X-Requested-With header that many javascript libraries use. +When used in conjunction with Router::parseExtensions() RequestHandler +will automatically switch the layout and view files to those that match +the requested type. Furthermore, if a helper with the same name as the +requested extension exists, it will be added to the Controllers Helper +array. Lastly, if XML data is POST'ed to your Controllers, it will be +parsed into an XML object which is assigned to Controller::data, and can +then be saved as model data. In order to make use of Request Handler it +must be included in your $components array. + +:: + + + +Obtaining Request Information +============================= + +Request Handler has several methods that provide information about the +client and its request. + +accepts ( $type = null) + +$type can be a string, or an array, or null. If a string, accepts will +return true if the client accepts the content type. If an array is +specified, accepts return true if any one of the content types is +accepted by the client. If null returns an array of the content-types +that the client accepts. For example: + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // Execute code only if client accepts an HTML (text/html) response + } elseif ($this->RequestHandler->accepts('xml')) { + // Execute XML-only code + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // Executes if the client accepts any of the above: XML, RSS or Atom + } + } + } + +Other request 'type' detection methods include: + +isAjax() + +Returns true if the request contains the X-Requested-Header equal to +XMLHttpRequest. + +isSSL() + +Returns true if the current request was made over an SSL connection. + +isXml() + +Returns true if the current request accepts XML as a response. + +isRss() + +Returns true if the current request accepts RSS as a response. + +isAtom() + +Returns true if the current call accepts an Atom response, false +otherwise. + +isMobile() + +Returns true if user agent string matches a mobile web browser, or if +the client accepts WAP content. The supported Mobile User Agent strings +are: + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +Returns true if the client accepts WAP content. + +All of the above request detection methods can be used in a similar +fashion to filter functionality intended for specific content types. For +example when responding to Ajax requests, you often will want to disable +browser caching, and change the debug level. However, you want to allow +caching for non-ajax requests. The following would accomplish that: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + //Continue Controller action + +You could also disable caching with the functionally analogous +``Controller::disableCache`` + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + //Continue Controller action + +Request Type-Erkennung +====================== + +Der RequestHandler liefert ebenfalls Informationen darüber, welcher Typ +von HTTP-Request gemacht wurde und erlaubt so, auf jeden Request Type zu +antworten. + +isPost() + +Gibt true zurück, wenn der Request ein POST-Request ist. + +isPut() + +Gibt true zurück, wenn der Request ein PUT-Request ist. + +isGet() + +Gibt true zurück, wenn der Request ein GET-Request ist. + +isDelete() + +Gibt true zurück, wenn der Request ein DELETE-Request ist. + +Zusätzliche Informationen über den Client erhalten +================================================== + +getClientIP() + +Gibt die IP-Adresse des remote-Clients zurück. + +getReferrer() + +Gibt den Domainnamen zurück, von dem der Request ursprünglich ausgelöst +wurde. + +getAjaxVersion() + +Gibt die Prototype-Version zurück, wenn der Aufruf von Ajax kommt und +ansonsten einen leeren String. Die Prototype-Bibliothek setzez einen +speziellen "Prototype version" HTTP header. + +Auf Requests antworten +====================== + +Zusätzlich zur Request-Erkennung ermöglicht der RequestHandler einen +einfachen Zugang zum Ändern des Mappings für die Output- und +Content-Types der Anwendung. + +setContent($name, $type = null) + +- $name string - Der Name des Content-Types, d.h. html, css, json, xml. +- $type mixed - Die mime-type(s), zu denen der Content-type gehört. + +setContent setzt bzw. fügt den Content-Type für einen gegebenen Namen +hinzu und erlaubt das Mappen der Content-Types zu benutzerfreundlicheren +Abkürzungen und Aliassen. Dies erlaubt dem RequestHandler automatisch +auf jeden Requesttyp in seiner startup-Methode zu antworten. Weiterhin +werden diese Content-Types von prefers() und accepts() verwendet. + +setContent wird am besten in der beforeFilter()-Methode der Controller +benutzt, da dies am ehesten der Automatisierung der Content-Type-Aliasse +dient. + +Das Standard-Mapping sieht folgendermaßen aus: + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +Gibt an, welchen Content-Type der Client bevorzugt. Wenn kein Parameter +angegeben ist, wird der wahrscheinlichste Wert zurückgegeben. Wenn $type +ein Array ist, wird der erste Typ zurückgegeben, den der Client +aktzeptiert. Das bevorzugte Betriebssystem wird hauptsächlich durch die +Dateiendung ermittelt, falls diese vom Router geparst und übermittelt +wird. Als Alternative wird die Liste von Content-Types in HTTP\_ACCEPT +genutzt. + +renderAs($controller, $type) + +- $controller - Controller-Referenz +- $type - Benutzerfreundlicher Name für den Content-Type, um den + Content z.B. auf xml oder rss zu rendern. + +Ändert den Render-Modus des Controllers auf den angegebenen Typ. Hängt, +falls verfügbar und noch nicht im Array vorhanden, ebenfalls den +passenden Helper zum Helper-Array des Controllers hinzu. + +respondAs($type, $options) + +- $type - Benutzerfreundlicher Name für den Content-Type wie xml, rss + oder ein kompletter Content-Type wie application/x-shockwave. +- $options - Wenn $type ein benutzerfreundlicher Name ist, der mehr als + eine Content-Assoziation hat, wird $index verwendet um einen + Content-Type auszuwählen. + +Setzt den Response-Header basierend auf dem Namen der Content-Type-Map. +Falls DEBUG größer als 2 ist, wird dieser Header nicht gesetzt. + +responseType() + +Gibt den aktuellen Response-Type des Content-Type zurück oder null, wenn +dieser noch gesetzt werden muss. + +mapType($ctype) + +Mapt einen Content-Type auf ein Alias zurück. diff --git a/de/The-Manual/Core-Components/Security-Component.rst b/de/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..dd648a140a7f0047e8507bdd7fc435c9c7cf853e --- /dev/null +++ b/de/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,249 @@ +Sicherheits Komponente +###################### + +Die Sicherheits Komponente erlaubt den einfachen Einsatz von erhöhter +Sicherheit in der erstellten Anwendung. Es ist unter anderem ein +Interface um HTTP-Authentifizierte Anfragen an den Webserver zu stellen. +Es hat eine Reihe von konfigurierbaren Parametern. Jede dieser Parameter +können entweder direkt oder mithilfe der dazu passenden Setter-Methoden +gesetzt werden. + +Configuration +============= + +$blackHoleCallback + A Controller callback that will handle any requests that are + blackholed. +$requirePost + A List of controller actions that require a POST request to occur. + An array of controller actions or '\*' to force all actions to + require a POST. +$requireSecure + List of actions that require an SSL connection to occur. An array of + controller actions or '\*' to force all actions to require a SSL + connection. +$requireAuth + List of actions that requires a valid authentication key. This + validation key is set by Security Component. +$requireLogin + List of actions that require HTTP-Authenticated logins (basic or + digest). Also accepts '\*' indicating that all actions of this + controller require HTTP-authentication. +$loginOptions + Options for HTTP-Authenticate login requests. Allows you to set the + type of authentication and the controller callback for the + authentication process. +$loginUsers + An associative array of usernames => passwords that are used for + HTTP-authenticated logins. If you are using digest authentication, + your passwords should be MD5-hashed. +$allowedControllers + A List of Controller from which the actions of the current + controller are allowed to receive requests from. This can be used to + control cross controller requests. +$allowedActions + Actions from which actions of the current controller are allowed to + receive requests. This can be used to control cross controller + requests. +$disabledFields + List of form fields that shall be ignored when validating POST - The + value, presence or absence of these form fields will not be taken + into account when evaluating whether a form submission is valid. + Specify fields as you do for the Form Helper (``Model.fieldname``). +$validatePost + Set to ``false`` to completely skip the validation of POST requests, + essentially turning CSRF protection off. + +Methods +======= + +requirePost() +------------- + +Sets the actions that require a POST request. Takes any number of +arguments. Can be called with no arguments to force all actions to +require a POST. + +requireSecure() +--------------- + +Sets the actions that require a SSL-secured request. Takes any number of +arguments. Can be called with no arguments to force all actions to +require a SSL-secured. + +requireAuth() +------------- + +Sets the actions that require a valid Security Component generated +token. Takes any number of arguments. Can be called with no arguments to +force all actions to require a valid authentication. + +requireLogin() +-------------- + +Sets the actions that require a valid HTTP-Authenticated request. Takes +any number of arguments. Can be called with no arguments to force all +actions to require valid HTTP-authentication. + +loginCredentials(string $type) +------------------------------ + +Attempt to validate login credentials for a HTTP-authenticated request. +$type is the type of HTTP-Authentication you want to check. Either +'basic', or 'digest'. If left null/empty both will be tried. Returns an +array with login name and password if successful. + +loginRequest(array $options) +---------------------------- + +Generates the text for an HTTP-Authenticate request header from an array +of $options. + +$options generally contains a 'type', 'realm' . Type indicate which +HTTP-Authenticate method to use. Realm defaults to the current HTTP +server environment. + +parseDigestAuthData(string $digest) +----------------------------------- + +Parse an HTTP digest authentication request. Returns and array of digest +data as an associative array if succesful, and null on failure. + +generateDigestResponseHash(array $data) +--------------------------------------- + +Creates a hash that to be compared with an HTTP digest-authenticated +response. $data should be an array created by +SecurityComponent::parseDigestAuthData(). + +blackHole(object $controller, string $error) +-------------------------------------------- + +Black-hole an invalid request with a 404 error or a custom callback. +With no callback, the request will be exited. If a controller callback +is set to SecurityComponent::blackHoleCallback, it will be called and +passed any error information. + +Usage +===== + +Using the security component is generally done in the controller +beforeFilter(). You would specify the security restrictions you want and +the Security Component will enforce them on its startup. + +:: + + Security->requirePost('delete'); + } + } + ?> + +In this example the delete action can only be successfully triggered if +it receives a POST request. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +This example would force all actions that had admin routing to require +secure SSL requests. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->blackHoleCallback = 'forceSSL'; + $this->Security->requireSecure(); + } + } + + function forceSSL() { + $this->redirect('https://' . env('SERVER_NAME') . $this->here); + } + } + ?> + +This example would force all actions that had admin routing to require +secure SSL requests. When the request is black holed, it will call the +nominated forceSSL() callback which will redirect non-secure requests to +secure requests automatically. + +Basic HTTP Authentication +========================= + +The SecurityComponent has some very powerful authentication features. +Sometimes you may need to protect some functionality inside your +application using `HTTP Basic +Authentication `_. +One common usage for HTTP Auth is protecting a REST or SOAP API. + +This type of authentication is called basic for a reason. Unless you're +transferring information over SSL, credentials will be transferred in +plain text. + +Using the SecurityComponent for HTTP authentication is easy. The code +example below includes the SecurityComponent and adds a few lines of +code inside the controller's beforeFilter method. + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MyRealm' + ); + $this->Security->loginUsers = array( + 'john'=>'johnspassword', + 'jane'=>'janespassword' + ); + $this->Security->requireLogin(); + } + + function index() { + //protected application logic goes here... + } + } + +The loginOptions property of the SecurityComponent is an associative +array specifying how logins should be handled. You only need to specify +the **type** as **basic** to get going. Specify the **realm** if you +want display a nice message to anyone trying to login or if you have +several authenticated sections (= realms) of your application you want +to keep separate. + +The loginUsers property of the SecurityComponent is an associative array +containing users and passwords that should have access to this realm. +The examples here use hard-coded user information, but you'll probably +want to use a model to make your authentication credentials more +manageable. + +Finally, requireLogin() tells SecurityComponent that this Controller +requires login. As with requirePost(), above, providing method names +will protect those methods while keeping others open. diff --git a/de/The-Manual/Core-Components/Sessions.rst b/de/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..a1dd82ed44a6e737dbc377c739c0d1bdb39e539a --- /dev/null +++ b/de/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,198 @@ +Sessions +######## + +Die CakePHP Session-Komponente ermöglicht es Benutzerdaten zwischen +verschiedenen Seitenanbesuchen zwischen zu speichern. Sie handelt als +Wrapper für die $\_SESSION von PHP und bietet verschiedene Methoden für +Funktionen in Bezug auf $\_SESSION. + +Sitzungen können auf einigen Wegen dauerhaft gespeichert werden. Der +Standard ist es, dass die Einstellungen von PHP verwendet werden; wie +auch immer, es gibt auch andere Möglichkeiten. + +cake + Speichert die Sitzungsdateien im Ordner app/tmp/sessions. +cake + Die Session-Dateien werden im Ordner app/tmp/sessions gespeichert. +database + Die Sitzungen werden in CakePHP's Datenbank gespeichert. +cache + Nutzt die mit Cache::config() konfigurierte Caching-Engine. Sehr + hilfreich im Zusammenhang mit Memcache (bei Setups mit mehreren + Applikations-Servern), um sowohl gecachte, als auch Sitzungsdaten zu + speichern. +php + Die Standardeinstellung. Speichert Sitzungsdaten, wie in der php.ini + angegeben. + +Um die Standardeinstellung zu ändern, muss die +Session.save-Konfiguration in die gewünschte Option geändert werden. +Wenn Sie 'database' wählen, dann sollten Sie ebenfalls die +Session.database-Einstellung auskommentieren und die +SQL-Session-Datenbank-Datei, welche sich im Ordner app/config befindet +in Ihrer Datenbank ausführen. + +Um eine selbst definierte Konfiguration zu verwenden, können Sie die +Session.save-Einstellung auf einen Dateinamen setzen. CakePHP wird dann +diese Datei aus Ihrem CONFIGS-Verzeichnis für diese Einstellung +benutzen. + +:: + + // app/config/core.php + Configure::write('Session.save','my_session'); + +Dies erlaubt Ihnen das Session-Handling zu verändern. + +:: + + // app/config/my_session.php + // + // Revert value and get rid of the referrer check even when, + // Security.level is medium + ini_restore('session.referer_check'); + + ini_set('session.use_trans_sid', 0); + ini_set('session.name', Configure::read('Session.cookie')); + + // Cookie is now destroyed when browser is closed, doesn't + // persist for days as it does by default for security + // low and medium + ini_set('session.cookie_lifetime', 0); + + // Cookie path is now '/' even if you app is within a sub + // directory on the domain + $this->path = '/'; + ini_set('session.cookie_path', $this->path); + + // Session cookie now persists across all subdomains + ini_set('session.cookie_domain', env('HTTP_BASE')); + +Methoden +======== + +Die Session Komponente dient dem Zugriff auf die Session Daten. Sie +beinhaltet sowohl Standardfunktionen zum Schreiben, Lesen, Aktualisieren +und Löschen der Session, also auch die Möglichkeit Nachrichten für die +User zu erstellen + +Es gilt zu beachten, dass Arrays innerhalb der Session über die +Punktnotation zu erstellen und abzurufen sind. User.username +referenziert dabei auf folgende Array Struktur: + +:: + + array('User' => + array('username' => 'clarkKent@dailyplanet.com') + ); + +Dies Punktnotation wird bei allen Methoden der Session Komponente +verwendet, bei der $name verwendet wird. + +write +----- + +``write($name, $value)`` + +Schreibt in die Session den Wert $value in $name. $name kann dabei in +der Punktnotation geschrieben werden, um ein assoziatives Array zu +verwenden. Zum Beispiel: + +:: + + $this->Session->write('Person.eyeColor', 'Green'); + +Dies schreibt den Wert 'Green' in die Session unter Person => eyeColor. + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +Setzt eine Sessionvariable, die für die Ausgabe im View verwendet werden +kann. $layout definiert das zu verwendende Layout (zu finden in +``/app/views/layouts``) für das Rendern der Message. Wenn Sie für +``$layout`` den Wert 'default' gesetzt lassen, wird die Message im +folgenden Code eingebettet: + +:: + +
[message]
+ +$params ermöglicht es Ihnen, zusätzliche Viewvariablen an das gerenderte +Layout zu übergeben. $key setzt den $messages Index im Message Array. +Standardmäßig ist 'flash' gesetzt. + +Es können Parameter gesetzt werden, die das Verhalten des gerenderten +div beeinflussen, zum Beispiel durch das Hinzufügen von "class" im +$params Array wird eine Klasse zu dem ``div`` hinzugefügt, welches durch +``$session->flash()`` in Ihrem Layout oder View ausgegeben wird. + +:: + + $this->Session->setFlash('Example message text', 'default', array('class' => 'example_class')) + +Die Ausgabe von ``$session->flash()`` des Beispiels von oben wäre: + +:: + +
Example message text
+ +read +---- + +``read($name)`` + +Returns the value at $name in the Session. If $name is null the entire +session will be returned. E.g. + +:: + + $green = $this->Session->read('Person.eyeColor'); + +Retrieve the value Green from the session. + +check +----- + +``check($name)`` + +Used to check if a Session variable has been set. Returns true on +existence and false on non-existence. + +delete +------ + +``delete($name)del($name) `` + +Clear the session data at $name. del($name) is deprecated from 1.3. + +:: + + $this->Session->delete('Person.eyeColor'); + +Our session data no longer has the value 'Green', or the index eyeColor +set. However, Person is still in the Session. To delete the entire +Person information from the session use. + +:: + + $this->Session->delete('Person'); + +destroy +------- + +The ``destroy`` method will delete the session cookie and all session +data stored in the temporary file system. It will then destroy the PHP +session and then create a fresh session. + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +Used to determine the last error in a session. diff --git a/de/The-Manual/Core-Console-Applications.rst b/de/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..9ba9817ce7479f3d1ff7b6099b36a5b01fa5076a --- /dev/null +++ b/de/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,23 @@ +Kern Konsole Anwendungen +######################## + +CakePHP liefert eine ganze Reihe von Konsolenanwendungen direkt mit. +Einige dieser Anwendungen funktionieren vor allem in Verbindung mit +anderen CakePHP Funktionalitäten wie ACL oder i18n. Andere sind nur +dafür da, um bestimmte Aufgaben schneller zu erledigen. + +Dieses Kapitel erklärt, wie man die Kern Konsolen Anwendungen benutzt, +die CakePHP direkt mitliefert. + +Bevor Du dieses Kapitel liest, solltest Du das `CakePHP Konsole +Kapitel `_ gelesen und verstanden +haben. Außerdem wird dort ebenfalls erklärt, wie man die Konsole korrekt +konfiguriert. + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/de/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/de/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..38fd3a24d01b6b837cb6592db4917501919ffc65 --- /dev/null +++ b/de/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,58 @@ +Code Generation with Bake +######################### + +You’ve already learned about scaffolding in CakePHP: a simple way to get +up and running with only a database and some bare classes. CakePHP’s +Bake console is another effort to get you up and running in CakePHP – +fast. The Bake console can create any of CakePHP’s basic ingredients: +models, views and controllers. And we aren’t just talking skeleton +classes: Bake can create a fully functional application in just a few +minutes. In fact, Bake is a natural step to take once an application has +been scaffolded. + +Those new to Bake (especially Windows users) may find the `Bake +screencast `_ helpful in setting +things up before continuing. + +Depending on the configuration of your setup, you may have to set +execute rights on the cake bash script or call it using ./cake bake. The +cake console is run using the PHP CLI (command line interface). If you +have problems running the script, ensure that you have the PHP CLI +installed and that it has the proper modules enabled (eg: MySQL). + +When running Bake for the first time, you'll be prompted to create a +Database Configuration file, if you haven't created one already. + +After you've created a Database Configuration file, running Bake will +present you with the following options: + +:: + + --------------------------------------------------------------- + App : app + Path: /path-to/project/app + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake? (D/M/V/C/P/Q) + > + +Alternatively, you can run any of these commands directly from the +command line: + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake all + $ cake bake project + $ cake bake test + diff --git a/de/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/de/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..bc473c4a60caedb4195074baaf023677feacefcc --- /dev/null +++ b/de/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,38 @@ +Modify default HTML produced by "baked" templates +################################################# + +If you wish to modify the default HTML output produced by the "bake" +command, follow these simple steps: + +**For baking custom views:** + +#. Go into: cake/console/libs/templates/views +#. Notice the 4 files there +#. Copy them to your: app/vendors/shells/templates/views. +#. Make changes to the HTML output to control the way "bake" builds your + views + +**For baking custom projects:** + +#. Go into: cake/console/libs/templates/skel +#. Notice the base application files there +#. Copy them to your: app/vendors/shells/templates/skel +#. Make changes to the HTML output to control the way "bake" builds your + views +#. Pass the skeleton path parameter to the project task + + :: + + cake bake project -skel vendors/shells/templates/skel + +Notes + +- You must run the specific project task ``cake bake project`` so that + the path parameter can be passed. +- The template path is relative to the current path of the Command Line + Interface. +- Since the full path to the skeleton needs to be manually entered, you + can specify any directory holding your template build you want, + including using multiple templates. (Unless Cake starts supporting + overriding the skel folder like it does for views) + diff --git a/de/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/de/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..d38e4d766e1dae8bf8f497b01410a24245ab84fe --- /dev/null +++ b/de/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,87 @@ +Schema management and migrations +################################ + +The SchemaShell provides a functionality to create schema objects, +schema sql dumps as well as create snapshots and restore database +snapshots. + +Generating and using Schema files +================================= + +A generated schema file allows you to easily transport a database +agnostic schema. You can generate a schema file of your database using: + +:: + + $ cake schema generate + +This will generate a schema.php file in you ``app/config/sql`` +directory. + +The schema shell will only process tables for which there are models +defined. To force the schema shell to process all the tables, you must +add the ``-f`` option in the command line. + +To later rebuild the database schema from your previously made +schema.php file run: + +:: + + $ cake schema run create + +This will drop and create the tables based on the contents of the +schema.php. + +Schema files can also be used to generate sql dump files. To generate a +sql file containing the ``CREATE TABLE`` statements, run: + +:: + + $ cake schema dump filename.sql + +Where filename.sql is the desired filename for the sql dump. If you omit +filename.sql the sql dump will be output to the console but not written +to a file. + +Migrations with CakePHP schema shell +==================================== + +Migrations allow for versioning of your database schema, so that as you +develop features you have an easy and database agnostic way to +distribute database changes. Migrations are achieved through either SCM +controlled schema files or schema snapshots. Versioning a schema file +with the schema shell is quite easy. If you already have a schema file +created running + +:: + + $ cake schema generate + +Will bring up the following choices: + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +Choosing [s] (snapshot) will create an incremented schema.php. So if you +have schema.php, it will create schema\_2.php and so on. You can then +restore to any of these schema files at any time by running: + +:: + + $ cake schema run update -s 2 + +Where 2 is the snapshot number you wish to run. The schema shell will +prompt you to confirm you wish to perform the ``ALTER`` statements that +represent the difference between the existing database the currently +executing schema file. + +You can perform a dry run by adding a ``-dry`` to your command. + +A schema file generated using the -f option should be updated using the +-f option. diff --git a/de/The-Manual/Core-Helpers.rst b/de/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..b277b702af06fd9c9bee5e6daf434634e37fd355 --- /dev/null +++ b/de/The-Manual/Core-Helpers.rst @@ -0,0 +1,29 @@ +Kern-Helfer +########### + +Helfer (``Helpers``) bei Views entsprechen den Komponenten bei +Controllern. Sie kapseln Code der von vielen Views, Elementen oder +Layouts benutzt wird. + +Dieser Abschnitt beschreibt die mit CakePHP ausgelieferten Helper, wie +den Form-, Html-, JavaScript- und RSS-Helper + +Für mehr Informationen zu helfern siehe auch Siehe auch +`Helpers `_. + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/de/The-Manual/Core-Helpers/AJAX.rst b/de/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..f8a45b34c67541ea7b13b48b70bf527dae14746f --- /dev/null +++ b/de/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,776 @@ +AJAX +#### + +Der AjaxHelper benutzt die populären Bibliotheken Prototype und +script.aculo.us für Ajax und clientseitige Effekte. Um diesen Helfer +benutzen zu können musst eine aktuelle Version von +`www.prototypejs.org `_ und +`http://script.aculo.us `_ in das +/app/webroot/js/-Verzeichnis packen. Außerdem müssen die Bibliotheken in +allen Layouts oder Views die AjaxHelper-Funktionalität benötigen, +eingebunden werden. + +Zuerst müssen die Ajax- und JavaScriptHelper im Controller eingebunden +werden: + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +Ist das geschehen, kann die ``link()``-Methode von JavaScriptHelper +verwendet werden um die Bibliotheken einzubinden: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +Jetzt kann der AjaxHelper verwendet werden: + +:: + + $ajax->whatever(); + +Wenn der Controller die `RequestHandler +Komponente `_ eingebunden hat, benutzt +CakePHP automatisch das Ajax-Layout wenn eine Action per Ajax angefragt +wird. + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +AjaxHelper Options +================== + +Most of the methods of the AjaxHelper allow you to supply an $options +array. You can use this array to configure how the AjaxHelper behaves. +Before we cover the specific methods in the helper, let’s look at the +different options available through this special array. You’ll want to +refer to this section as you start using the methods in the AjaxHelper +later on. + +General Options +--------------- + +``$option`` keys + +Description + +``$options['evalScripts']`` + +Determines if script tags in the returned content are evaluated. Set to +*true* by default. + +``$options['frequency']`` + +The number of seconds between interval based checks. + +``$options['indicator']`` + +The DOM id of an element to show while a request is loading and to hide +when a request is completed. + +``$options['position']`` + +To insert rather than replace, use this option to specify an insertion +position of *top*, *bottom*, *after*, or *before*. + +``$options['update']`` + +The id of the DOM element to be updated with returned content. + +``$options['url']`` + +The url of the controller/action that you want to call. + +``$options['type']`` + +Indicate whether the request should be 'synchronous' or 'asynchronous' +(default). + +``$options['with']`` + +A URL-encoded string which will be added to the URL for get methods or +in to the post body for any other method. Example: ``x=1&foo=bar&y=2``. +The parameters will be available in ``$this->params['form']`` or +available in ``$this->data`` depending on formatting. For more +information see the `Prototype +Serialize `_ method. + +Callback Options +---------------- + +Callback options allow you to call JavaScript functions at specific +points in the request process. If you’re looking for a way to inject a +bit of logic before, after, or during your AjaxHelper operations, use +these callbacks to set things up. + +$options keys + +Description + +$options['condition'] + +JavaScript code snippet that needs to evaluate to *true* before request +is initiated. + +$options['before'] + +Executed before request is made. A common use for this callback is to +enable the visibility of a progress indicator. + +$options['confirm'] + +Text to display in a JavaScript confirmation alert before proceeding. + +$options['loading'] + +Callback code to be executed while data is being fetched from server. + +$options['after'] + +JavaScript called immediately after request has run; fires before the +$options['loading'] callback runs. + +$options['loaded'] + +Callback code to be executed when the remote document has been received +by client. + +$options['interactive'] + +Called when the user can interact with the remote document, even though +it has not finished loading. + +$options['complete'] + +JavaScript callback to be run when XMLHttpRequest is complete. + +Methods +======= + +link +---- + +``link(string $title, mixed $href, array $options, string $confirm, boolean $escapeTitle)`` + +Returns a link to a remote action defined by ``$options['url']`` or +``$href`` that's called in the background using XMLHttpRequest when the +link is clicked. The result of that request can then be inserted into a +DOM object whose id can be specified with ``$options['update']``. + +If ``$options['url']`` is blank the href is used instead + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +By default, these remote requests are processed asynchronously during +which various callbacks can be triggered + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Hello World" )' ) + ); + ?> + +To use synchronous processing specify +``$options['type'] = 'synchronous'``. + +To automatically set the ajax layout include the *RequestHandler* +component in your controller + +By default the contents of the target element are replaced. To change +this behaviour set the ``$options['position']`` + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +``$confirm`` can be used to call up a JavaScript confirm() message +before the request is run. Allowing the user to prevent execution. + +Example: + +:: + +
+
+ link( + 'Delete Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Do you want to delete this post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +This function creates the JavaScript needed to make a remote call. It is +primarily used as a helper for link(). This is not used very often +unless you need to generate some custom scripting. + +The ``$options`` for this function are the same as for the ``link`` +method + +Example: + +:: + +
+
+ + +It can also be assigned to HTML Event Attributes: + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Mouse Over This +
+ +If ``$options['update']`` is not passed, the browser will ignore the +server response. + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +Periodically calls the action at ``$options['url']``, every +``$options['frequency']`` seconds. Usually used to update a specific div +(specified by ``$options['update']``) with the result of the remote +call. Callbacks can be used. + +``remoteTimer`` is the same as the ``remoteFunction`` except for the +extra ``$options['frequency']`` + +Example: + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post', 'complete' => 'alert( "request completed" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +The default ``$options['frequency']`` is 10 seconds + +form +---- + +``form(string $action, string $type, array $options)`` + +Returns a form tag that submits to $action using XMLHttpRequest instead +of a normal HTTP request via $type ('post' or 'get'). Otherwise, form +submission will behave exactly like normal: data submitted is available +at $this->data inside your controllers. If $options['update'] is +specified, it will be updated with the resulting document. Callbacks can +be used. + +The options array should include the model name e.g. + +:: + + $ajax->form('edit','post',array('model'=>'User','update'=>'UserInfoDiv')); + +Alternatively, if you need to cross post to another controller from your +form: + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'User', + 'update'=>'UserInfoDiv', + 'url' => array( + 'controller' => 'comments', + 'action' => 'edit' + ) + ) + )); + +You should not use the ``$ajax->form()`` and ``$ajax->submit()`` in the +same form. If you want the form validation to work properly use the +``$ajax->submit()`` method as shown below. + +submit +------ + +``submit(string $title, array $options)`` + +Returns a submit button that submits the form to ``$options['url']`` and +updates the div specified in ``$options['update']`` + +:: + +
+ create('User'); + echo $form->input('email'); + echo $form->input('name'); + echo $ajax->submit('Submit', array('url'=> array('controller'=>'users', 'action'=>'add'), 'update' => 'testdiv')); + echo $form->end(); + ?> +
+ +Use the ``$ajax->submit()`` method if you want form validation to work +properly. i.e. You want the messages you specify in your validation +rules to show up correctly. + +observeField +------------ + +``observeField(string $field, array $options)`` + +Observes the field with the DOM id specified by $field (every +$options['frequency'] seconds) and makes an XMLHttpRequest when its +contents have changed. + +When no frequency or a small frequency interval (between 0 and 1) is +specified, a prototype ``Form.Element.EventObserver`` will be used +instead of a ``Form.Element.Observer``. The +``Form.Element.EventObserver`` is not timed and will execute at the same +time the value of the element has changed. + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'title', array( 'options' => $titles ) ) ?> + + + observeField( 'PostTitle', + array( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +``observeField`` uses the same options as ``link`` + +The field to send up can be set using ``$options['with']``. This +defaults to ``Form.Element.serialize('$field')``. Data submitted is +available at ``$this->data`` inside your controllers. Callbacks can be +used with this function. + +To send up the entire form when the field changes use +``$options['with'] = Form.serialize( $('Form ID') )`` + +observeForm +----------- + +``observeForm(string $form, array $options)`` + +Similar to observeField(), but operates on an entire form identified by +the DOM id $form. The supplied $options are the same as observeField(), +except the default value of the $options['with'] option evaluates to the +serialized (request string) value of the form. + +autoComplete +------------ + +``autoComplete(string $field, string $url, array $options)`` + +Renders a text field with $field with autocomplete. The remote action at +$url should return a suitable list of autocomplete terms. Often an +unordered list is used for this. First, you need to set up a controller +action that fetches and organizes the data you'll need for your list, +based on user input: + +:: + + function autoComplete() { + //Partial strings will come from the autocomplete field as + //$this->data['Post']['subject'] + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.subject LIKE' => $this->data['Post']['subject'].'%' + ), + 'fields' => array('subject') + ))); + $this->layout = 'ajax'; + } + +Next, create ``app/views/posts/auto_complete.ctp`` that uses that data +and creates an unordered list in (X)HTML: + +:: + +
    + +
  • + +
+ +Finally, utilize autoComplete() in a view to create your auto-completing +form field: + +:: + + create('User', array('url' => '/users/index')); ?> + autoComplete('Post.subject', '/posts/autoComplete')?> + end('View Post')?> + +Once you've got the autoComplete() call working correctly, use CSS to +style the auto-complete suggestion box. You might end up using something +similar to the following: + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +If you want the user to enter a minimum number of characters before the +autocomplete starts, you can use the minChars-Option as follows: + +:: + + $ajax->autoComplete('Post.subject', '/posts/autoComplete',array('minChars' => 3)); + +isAjax +------ + +``isAjax()`` + +Allows you to check if the current request is a Prototype Ajax request +inside a view. Returns a boolean. Can be used for presentational logic +to show/hide blocks of content. + +drag & drop +----------- + +``drag(string $id, array $options)`` + +Makes a Draggable element out of the DOM element specified by $id. For +more information on the parameters accepted in $options see +`http://github.com/madrobby/scriptaculous/wikis/draggable `_. + +Common options might include: + ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++==========================+=======================================================================================================================================================================================================================================================================================================+ +| $options['handle'] | Sets whether the element should only be draggable by an embedded handle. The value must be an element reference or element id or a string referencing a CSS class value. The first child/grandchild/etc. element found within the element that has this CSS class value will be used as the handle. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | If set to true, the element returns to its original position when the drags ends. Revert can also be an arbitrary function reference, called when the drag ends. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | Constrains the drag to either 'horizontal' or 'vertical', leave blank for no constraints. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +Makes the DOM element specified by $id able to accept dropped elements. +Additional parameters can be specified with $options. For more +information see +`http://github.com/madrobby/scriptaculous/wikis/droppables `_. + +Common options might include: + ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++===========================+==========================================================================================================================================================================================+ +| $options['accept'] | Set to a string or javascript array of strings describing CSS classes that the droppable element will accept. The drop element will only accept elements of the specified CSS classes. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | The droppable element will only accept the dragged element if it is contained in the given elements (element ids). Can be a string or a javascript array of id references. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | If set to 'horizontal' or 'vertical', the droppable element will only react to a draggable element if it is overlapping the droparea by more than 50% in the given axis. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | A javascript call back that is called when the dragged element is dropped on the droppable element. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +Makes a drop target that creates an XMLHttpRequest when a draggable +element is dropped on it. The $options array for this function are the +same as those specified for drop() and link(). + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +Creates a directional slider control. For more information see +`http://wiki.github.com/madrobby/scriptaculous/slider `_. + +Common options might include: + +$options keys + +Description + +$options['axis'] + +Sets the direction the slider will move in. 'horizontal' or 'vertical'. +Defaults to horizontal + +$options['handleImage'] + +The id of the image that represents the handle. This is used to swap out +the image src with disabled image src when the slider is enabled. Used +in conjunction with handleDisabled. + +$options['increment'] + +Sets the relationship of pixels to values. Setting to 1 will make each +pixel adjust the slider value by one. + +$options['handleDisabled'] + +The id of the image that represents the disabled handle. This is used to +change the image src when the slider is disabled. Used in conjunction +handleImage. + +$options['change'] + $options['onChange'] + +JavaScript callback fired when the slider has finished moving, or has +its value changed. The callback function receives the slider's current +value as a parameter. + +$options['slide'] + $options['onSlide'] + +JavaScript callback that is called whenever the slider is moved by +dragging. It receives the slider's current value as a parameter. + +editor +------ + +``editor(string $id, string $url, array $options)`` + +Creates an in-place editor at DOM id. The supplied ``$url`` should be an +action that is responsible for saving element data. For more information +and demos see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_. + +Common options might include: + +$options keys + +Description + +``$options['collection']`` + +Activate the 'collection' mode of in-place editing. +$options['collection'] takes an array which is turned into options for +the select. To learn more about collection see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_. + +``$options['callback']`` + +A function to execute before the request is sent to the server. This can +be used to format the information sent to the server. The signature is +``function(form, value)`` + +``$options['okText']`` + +Text of the submit button in edit mode + +``$options['cancelText']`` + +The text of the link that cancels editing + +``$options['savingText']`` + +The text shown while the text is sent to the server + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +The row height of the input field + +``$options['cols']`` + +The number of columns the text area should span + +``$options['size']`` + +Synonym for ‘cols’ when using single-line + +``$options['highlightcolor']`` + +The highlight color + +``$options['highlightendcolor']`` + +The color which the highlight fades to + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +Example + +:: + +
Text To Edit
+ editor( + "in_place_editor_id", + array( + 'controller' => 'Posts', + 'action' => 'update_title', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +Makes a list or group of floated objects contained by $id sortable. The +options array supports a number of parameters. To find out more about +sortable see +`http://wiki.github.com/madrobby/scriptaculous/sortable `_. + +:: + +
+
+ Element 1 +
+
+ Element 2 +
+
+ Element 3 +
+
+ + sortable('sortableContainer',array('tag'=>'div','only'=>'sortableItem','onUpdate'=>'writeupdate')); + ?> + +Make sure that you do not include the parenthesis on the onUpdate +callback, or it will not execute. + +Common options might include: + +$options keys + +Description + +$options['tag'] + +Indicates what kind of child elements of the container will be made +sortable. Defaults to 'li'. + +$options['only'] + +Allows for further filtering of child elements. Accepts a CSS class. + +$options['overlap'] + +Either 'vertical' or 'horizontal'. Defaults to vertical. + +$options['constraint'] + +Restrict the movement of the draggable elements. accepts 'horizontal' or +'vertical'. Defaults to vertical. + +$options['handle'] + +Makes the created Draggables use handles, see the handle option on +Draggables. + +$options['onUpdate'] + +Called when the drag ends and the Sortable's order is changed in any +way. When dragging from one Sortable to another, the callback is called +once on each Sortable. + +$options['hoverclass'] + +Give the created droppable a hoverclass. + +$options['ghosting'] + +If set to true, dragged elements of the sortable will be cloned and +appear as a ghost, instead of directly manipulating the original +element. diff --git a/de/The-Manual/Core-Helpers/Cache.rst b/de/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..24f0e3a64c0191b02eb70d1518f48fae7627e186 --- /dev/null +++ b/de/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,189 @@ +Cache +##### + +Der Cache-Helper ist hilfreich um ganze Layouts und Views zu cachen und +damit häufig abgefragte Daten schneller an den Benutzer zu senden. View +Caching in Cake legt geparste Layouts und Views temporär in der Cache +Engine der Wahl ab. Dabei ist zu beachten, dass der Cache-Helper ganz +anders arbeitet als andere Helper. Er hat keine Methoden, die direkt +aufgerufen werden können. Dafür wird ein View mit sogenannten Cache-Tags +markiert um anzugeben, welcher Teil des Inhalts gecached werden soll. + +Wenn eine URL angefragt wird, überprüft Cake, ob dieser Request-String +bereits gecached worden ist. Wenn ja, wird der Rest des URL-Parsings +übersprungen. Alle nicht gecachten Blocks werden normal verarbeitet und +der View wird präsentiert. Damit wird eine große Einsparnis an +Verarbeitungszeit erreicht, da nur minimaler Code ausgeführt wird. Falls +der View nicht gecached ist, oder die Cachedauer abgelaufen ist, wird +die Anfrage normale bearbeitet. + +Allgemeines über Caching +======================== + +Caching ist als Methode zur temporären Speicherung gedacht, um die +Serverlast zu reduzieren. Zum Beispiel können die Ergebnisse eine +zeitintensiven Datenbankabfrage gespeichert werden, damit die Abfrage +nicht bei jedem Aufruf der Seite ausgeführt werden muss. + +Wenn man dies im Hinterkopf hat wird klar, dass Caching kein permanenter +Speicher ist und nicht dazu benutzt wird, um irgendetwas permanent zu +speichern. Sondern nur zum Cachen von Daten dient, die wieder abgerufen +werden können, wenn sie benötigt werden. + +Cache Engines in Cake +===================== + +New in 1.2 are several cache engines or cache backends. These interface +transparently with the cache helper, allowing you to store view caches +in a multitude of media without worrying about the specifics of that +media. The choice of cache engine is controlled through the +app/config/core.php config file. Most options for each caching engine +are listed in the core.php config file and more detailed information on +each caching engine can be found in the Caching Section. + +File + +The File Engine is the default caching engine used by cake. It writes +flat files to the filesystem and it has several optional parameters but +works well with the defaults. + +APC + +The APC engine implements the `Alternative PHP +Cache `_ opcode Cacher. Like XCache, this engine +caches the compiled PHP opcode. + +XCache + +The XCache caching engine is functionally similar to APC other than it +implements the `XCache `_ opcode caching +engine. It requires the entry of a user and password to work properly. + +Memcache + +The Memcache engine works with a memcaching server allowing you to +create a cache object in system memory. More information on memcaching +can be found on `php.net `_ and +`memcached `_ + +Cache Helper Configuration +========================== + +View Caching and the Cache Helper have several important configuration +elements. They are detailed below. + +To use the cache helper in any view or controller, you must first +uncomment and set Configure::Cache.check to true in ``core.php`` of your +app/config folder. If this is not set to true, then the cache will not +be checked or created. + +:: + + Configure::write('Cache.check', true); + +Caching in the Controller +========================= + +Any controllers that utilize caching functionality need to include the +CacheHelper in their $helpers array. + +:: + + var $helpers = array('Cache'); + +You also need to indicate which actions need caching, and how long each +action will be cached. This is done through the $cacheAction variable in +your controllers. $cacheAction should be set to an array which contains +the actions you want cached, and the duration in seconds you want those +views cached. The time value can be expressed in a strtotime() format. +(ie. "1 hour", or "3 minutes"). + +Using the example of an ArticlesController, that receives a lot of +traffic that needs to be cached. + +Cache frequently visited Articles for varying lengths of time + +:: + + var $cacheAction = array( + 'view/23' => 21600, + 'view/48' => 36000, + 'view/52' => 48000 + ); + +Remember to use your routes in the $cacheAction if you have any. + +Cache an entire action in this case a large listing of articles + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +Cache every action in the controller using a strtotime() friendly time +to indicate Controller wide caching time. + +:: + + var $cacheAction = "1 hour"; + +You can also enable controller/component callbacks for cached views +created with ``CacheHelper``. To do so you must use the array format for +``$cacheAction`` and create an array like the following: + +:: + + var $cacheAction = array( + 'view' => array('callbacks' => true, 'duration' => 21600), + 'add' => array('callbacks' => true, 'duration' => 36000), + 'index' => array('callbacks' => true, 'duration' => 48000) + ); + +By setting ``callbacks => true`` you tell CacheHelper that you want the +generated files to create the components and models for the controller. +As well as, fire the component initialize, controller beforeFilter, and +component startup callbacks. + +callbacks => true partly defeats the purpose of caching. This is also +the reason it is disabled by default. + +Marking Non-Cached Content in Views +=================================== + +There will be times when you don't want an *entire* view cached. For +example, certain parts of the page may look different whether a user is +currently logged in or browsing your site as a guest. + +To indicate blocks of content that are *not* to be cached, wrap them in +`` `` like so: + +:: + + + check('User.name')) : ?> + Welcome, read('User.name')?>. + + link('Login', 'users/login')?> + + + +It should be noted that once an action is cached, the controller method +for the action will not be called - otherwise what would be the point of +caching the page. Therefore, it is not possible to wrap +`` `` around variables which are set from +the controller as they will be *null*. + +Clearing the Cache +================== + +It is important to remember that the Cake will clear a cached view if a +model used in the cached view is modified. For example, if a cached view +uses data from the Post model, and there has been an INSERT, UPDATE, or +DELETE query made to a Post, the cache for that view is cleared, and new +content is generated on the next request. + +If you need to manually clear the cache, you can do so by calling +Cache::clear(). This will clear **all** cached data, excluding cached +view files. If you need to clear the cached view files, use +``clearCache()``. diff --git a/de/The-Manual/Core-Helpers/Form.rst b/de/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..d256e46bf0e73830757c7e3beafb57dc438ea5f5 --- /dev/null +++ b/de/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,1327 @@ +Forms +##### + +Der FormHelper ist eine neue Erweiterung von CakePHP. Die meiste Arbeit +bei der Formularerstellung wird nun von dieser neuen Klasse übernommen. +Durch den neuen FormHelper sind die Methoden zur Formularerstellung im +HtmlHelper veraltet geworden. Der FormHelper konzentriert sich auf das +schnelle Erstellen von Formularen, hinsichtlich Validierung, +Wiederbefüllung und Layout. Der FormHelper ist auch sehr flexibel - er +kann alles automatisch erledigen, oder du kannst spezifische Methoden +verwenden, damit du nur das bekommst was du brauchst. + +Formulare erstellen +=================== + +Die erste Methode die du brauchen wirst ist create(). Diese Methode gibt +einen öffnenden Form Tag aus. + +:: + + create(string $model = null, array $options = array()); + ?> + +Alle Parameter sind optional. Wenn create() ohne Parameter aufgerufen +wird, wird angenommen, dass du ein Formular erstellst, das an die add() +Action des momentanen Controllers mithilfe von Post gesendet werden +soll. Das Form Element wird auch als DOM id, benannt nach der Controller +Action in CamelCase, zurückgegeben. Wenn ich create() innerhalb eines +Views des UsersController aufrufe, dann würde die Ausgabe etwa so +aussehen: + +:: + +
+ +Die create() Methode erlaubt noch viel mehr Anpassung über ihre +Parameter. Zuerst kannst du den Namen des Models angeben. Dieses Model +wird dann für alle Felder des Formulars angenommen und die DOM id des +Form Tags wird geändert. + +:: + + create('Recipe'); ?> + + //Ausgabe: + + +Im $options Feld findet das Meiste der Konfiguration statt. Dieses +spezielle Feld kann eine Anzahl von verschiedenen Schlüssel-Wert Paaren +(key-value pairs) enthalten, die die Erstellung des Formulars +beeinflussen. + +$options[‘type’] +---------------- + +Dieser Schlüssel wird benutzt um die Art des Formulars zu bestimmen, das +erstellt werden soll. Gültige Werte sind 'post', 'get', 'file', 'put' +und 'delete'. + +Das Übergeben von 'post' oder 'get' verändert die Methode zur +Übertragung der Formulardaten entsprechend. + +:: + + create('User', array('type' => 'get')); ?> + + //Ausgabe: + + +Durch den Wert 'file' wird die Formularübertragung auf 'post' geändert +und eine Eigenschaft "enctype" mit Wert "multipart/form-data angehängt. +Dieses wird benutzt wenn irgendwelche file Elemente im Formular sind. +Ohne dem "enctype" Attribut wird der Datei Upload nicht funktionieren. + +:: + + create('User', array('type' => 'file')); ?> + + //Ausgabe: + + +Wenn 'put' oder 'delete' verwendet wird, ist das Formular gleich wie ein +normales 'post' Formular, mit dem Unterschied, dass wenn es abgeschickt +wird, die HTTP Request Methode mit 'PUT' oder 'DELETE' überschrieben +wird. Das erlaubt CakePHP eine einwandfreie REST Unterstützung für +Web-Browser zu emulieren. + +$options[‘action’] +------------------ + +Der Schlüssel action erlaubt dir eine spezielle action in deinem +aktuellen Controller anzugeben. Wenn du z.B. das Formular für die action +login() des aktuellen Controllers erstellen möchtest, so würde der +form-Aufruf wie folgt aussehen: + +:: + + create('User', array('action' => 'login')); ?> + + //Output: + +
+ +$options[‘url’] +--------------- + +Wenn die gewünschte Zieladresse (action) nicht der aktuelle Controller +ist, kann ein Ziel-Url über den URL Schlüssel des $option Array +angegeben werden. Hier ist ein relativer Verweis innerhalb der CakePhp +Installation, oder eine externe URL möglich. + +:: + + create(null, array('url' => '/recipes/add')); ?> + // or + create(null, array('url' => array('controller' => 'recipes', 'action' => 'add'))); ?> + + + //Output: +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + //Output: + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +$options[‘default’] +------------------- + +Wenn ‘default’ auf den Boolean false gestzt wurde, wird die +Submit-Action des Formulars geändert, so dass Klicken auf den +Submit-Button das Formular nicht abschickt. Wenn das Formular via AJAX +übermittelt werden soll, unterdrückt die false-Einstellung von ‘default’ +das Standard-Verhalten des Formulars, so dass die Daten via AJAX +abgegriffen und übermittelt werden können. + +Formular schließen +================== + +Der FormHelper beinhaltet ebenso eine end() Methode, die das Formular +schließt. Oftmals gibt end() nur einen schließenden Formular-Tag aus, +jedoch erlaubt end() dem FormHelper benötigte, versteckte +Formularelemente einzusetzen die andere Methoden benötigen könnten. + +:: + + create(); ?> + + + + end(); ?> + +Wird ein String der Methode end() als erstem Parameter mitgegeben, gibt +der FormHelper einen Submit Button aus, der entsprechend diesem +Parameter beschriftet ist. + +:: + + end('Schluss jetzt'); ?> + + Ausgabe: + +
+ +
+ + +Automagic Form Elements +======================= + +Schauen wir zuerst auf einige der automatischen +Formularerstellungsmethoden des FormHelpers. Besonderes Augenmerk +schenken wir der ``input()`` Methode. Diese Methode inspiziert +automatisch das passende Model-Feld, um ein entsprechendes +Formulareingabefeld zu erstellen. + +input(string $fieldName, array $options = array()) + ++------------------------------------------------+-------------------------------------------------------------+ +| Spaltentyp | Resultierendes Formularfeld | ++================================================+=============================================================+ +| string (char, varchar, etc.) | text | ++------------------------------------------------+-------------------------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++------------------------------------------------+-------------------------------------------------------------+ +| text | textarea | ++------------------------------------------------+-------------------------------------------------------------+ +| text, mit name password, passwd, oder psword | passwort | ++------------------------------------------------+-------------------------------------------------------------+ +| date | tag, monat, und Jahresauswahl | ++------------------------------------------------+-------------------------------------------------------------+ +| datetime, timestamp | Tag, Monat, Jahr, Stunden, Minuten, und meridian- Auswahl | ++------------------------------------------------+-------------------------------------------------------------+ +| time | Stunden, Minuten, und meridian-Auswahl | ++------------------------------------------------+-------------------------------------------------------------+ + +Angenommen, mein User model beinhaltet Felder für einen usernaen +(varchar), password (varchar), approved (datetime) und quote (text). Die +``input()`` Methode des FormHelpers kann nun genutzt werden, um die +passenden Eingabefelder fpr alle diese Felder zu erzeugen. + +:: + + create(); ?> + + input('username'); //text + echo $form->input('password'); //passwort + echo $form->input('approved'); //tag, monat, jahr, stunden, minuten, meridian + echo $form->input('quote'); //textfeld + ?> + + end('Hinzufügen'); ?> + +Ein umfangreicheres Beispiel, welches Optionen für ein date Feld +anschaulich darstellt: + +:: + + echo $form->input('birth_dt', array( 'label' => 'Date of birth' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +Besides the specific input options found below you can specify any html +attribute (for instance onfocus). For more information on $options and +$htmlAttributes see `HTML Helper `_. + +And to round off, here's an example for creating a hasAndBelongsToMany +select. Assume that User hasAndBelongsToMany Group. In your controller, +set a camelCase plural variable (group -> groups in this case, or +ExtraFunkyModel -> extraFunkyModels) with the select options. In the +controller action you would put the following: + +:: + + $this->set('groups', $this->User->Group->find('list')); + +And in the view a multiple select can be expected with this simple code: + +:: + + echo $form->input('Group'); + +If you want to create a select field while using a belongsTo- or +hasOne-Relation, you can add the following to your Users-controller +(assuming your User belongsTo Group): + +:: + + $this->set('groups', $this->User->Group->find('list')); + +Afterwards, add the following to your form-view: + +:: + + echo $form->input('group_id'); + +If your model name consists of two or more words, e.g., "UserGroup", +when passing the data using set() you should name your data in a +pluralised and camelCased format as follows: + +:: + + $this->set('userGroups', $this->UserGroup->find('list')); + // or + $this->set('reallyInappropriateModelNames', $this->ReallyInappropriateModelName->find('list')); + +Konventionen für Feldnamen +-------------------------- + +Der FormHelper denkt mit. Wenn ein Feldname mit den FormHelper Methoden +erstellt wird, nutzt dieser automatisch den aktuellen Modelnamen, um ein +Eingabefeld in der folgenden Form zu erstellen: + +:: + + + +Man kann manuell den Modelnamen spezifizieren, wenn man diesen in der +Form Modelname.fieldname als ersten Parameter übergibt. + +:: + + echo $form->input('Modelname.fieldname'); + +Falls mehrere Felder, die den gleichen Feldamen nutzen, spezifiziert +werden sollen, und somit ein Array erzeugt werden kann, welches durch +saveAll() in einem Zug gespeichert werden kann, muss die folgende +Konventio genutzt werden: + +:: + + input('Modelname.0.fieldname'); + echo $form->input('Modelname.1.fieldname'); + ?> + + + + +$options[‘type’] +---------------- + +Den Typ eines Eingabefeldes kann man mittels der Option type erzwingen +(und damit auch die Model Selbstprüfung überschreiben). Zusätzlich zu +den Feldtypen, die in der `Tabelle weiter +oben `_ aufgeführt sind, können +auch ‘file’ und ‘password’ Eingabefelder erzeugt werden. + +:: + + input('field', array('type' => 'file')); ?> + + Ergebnis: + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +Use these keys if you need to inject some markup inside the output of +the input() method. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + ));?> + + Output: + +
+ --before-- + + --between--- + + --after-- +
+ +For radio type input the 'separator' attribute can be used to inject +markup to separate each input/label pair. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ +For ``date`` and ``datetime`` type elements the 'separator' attribute +can be used to change the string between select elements. Defaults to +'-'. + +$options[‘options’] +------------------- + +This key allows you to manually specify options for a select input, or +for a radio group. Unless the ‘type’ is specified as ‘radio’, the +FormHelper will assume that the target output is a select input. + +:: + + input('field', array('options' => array(1,2,3,4,5))); ?> + +Output: + +:: + +
+ + +
+ +Options can also be supplied as key-value pairs. + +:: + + input('field', array('options' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2', + 'Value 3'=>'Label 3' + ))); ?> + +Output: + +:: + +
+ + +
+ +If you would like to generate a select with optgroups, just pass data in +hierarchical format. Works on multiple checkboxes and radio buttons too, +but instead of optgroups wraps elements in fieldsets. + +:: + + input('field', array('options' => array( + 'Label1' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2' + ), + 'Label2' => array( + 'Value 3'=>'Label 3' + ) + ))); ?> + +Output: + +:: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +Wenn bei einem Eingabefeld, welches eine Select-Feld erzeugt, ‘multiple’ +auf true gesetzt wird, dann erlaubt das Select-Feld eine +Mehrfachauswahl. Alternativ kann ‘multiple’ auch auf ‘checkbox’ gesetzt +werden, um eine Liste von zugehörigen Checkboxen zu erhalten. + +:: + + $form->input('Model.field', array( 'type' => 'select', 'multiple' => true )); + $form->input('Model.field', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +Definiert die maximale Anzahl an Buchstaben für ein Texteingabefeld. + +$options[‘div’] +--------------- + +Nutze diese Option, um die Attribute für das umgebene div-Element des +Eingabefeldes zu setzen. Ein String wird als CSS Klasenname +interpretiert. Ein Array setzt die entsprechenden Schlüssel/Wert Paare +als div-Attribute. Es ist auch möglich, den Wert false zu übergeben, um +die Ausgabe eines umgebenenen div-Elements zu verhindern. + +Klassenname setzen + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +Ergebnis: + +:: + +
+ + +
+ +Mehrere Attribute + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +Ergebnis: + +:: + +
+ + +
+ +Kein umgebenes div-Element + +:: + + input('User.name', array('div' => false));?> + +Ergebnis: + +:: + + + + +$options[‘label’] +----------------- + +Gib bei dieser Option den String ein, der innerhalb des Label-Elements +angezeigt werden soll. Das Label-Element begleitet normalerweise eine +Eingabefeld. + +:: + + input( 'User.name', array( 'label' => 'The User Alias' ) );?> + +Ergebnis: + +:: + +
+ + +
+ +Wird der Wert der Label-Option auf false gesetzt, so wird kein +Label-Element ausgegeben. + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Ergebnis: + +:: + +
+ +
+ +Wird ein Array als Wert der Label-Option ünergeben, so können mehrere +Attribute für das Label-Element übergeben werden. So kann über den +Schlüssel ``text`` innerhalb des Arrays der Label-Text angepasst werden. + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'The User Alias') ) ); ?> + +Ergebnis: + +:: + +
+ + +
+ +$options['legend'] +------------------ + +Some inputs like radio buttons will be automatically wrapped in a +fieldset with a legend title derived from the fields name. The title can +be overridden with this option. Setting this option to false will +completely eliminate the fieldset. + +$options[‘id’] +-------------- + +Set this key to force the value of the DOM id for the input. + +$options['error'] +----------------- + +Using this key allows you to override the default model error messages +and can be used, for example, to set i18n messages. It has a number of +suboptions which control the wrapping element, wrapping element class +name, and whether HTML in the error message will be escaped. + +To disable error message output set the error key to false. + +:: + + $form->input('Model.field', array('error' => false)); + +To modify the wrapping element type and its class, use the following +format: + +:: + + $form->input('Model.field', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +To prevent HTML being automatically escaped in the error message output, +set the escape suboption to false: + +:: + + $form->input('Model.field', array('error' => array('escape' => false))); + +To override the model error messages use an associate array with the +keyname of the validation rule: + +:: + + $form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough', true) ))); + +As seen above you can set the error message for each validation rule you +have in your models. In addition you can provide i18n messages for your +forms. + +$options['default'] +------------------- + +Wird genutzt, um einen Standardwert für das Eingabefeld zu setzen. Der +Wert wird genau dann genutzt, wenn das Eingabefeld beim Absenden nicht +befüllt wurde (oder wenn gar keine Daten übermittelt wurden). + +Beispiel: + +:: + + input('zutat', array('default'=>'')); + ?> + +Beispiel mit ausgewählten Feld(Größe "Mittel" soll der Standardwert +sein): + +:: + + 'klein', 'm'=>'mittel', 'l'=>'gross'); + echo $form->input('groesse', array('options'=>$groesse, 'default'=>'m')); + ?> + +Man kann ``default`` nicht nutzen, um eine Checkbox zu aktivieren. +Stattdessen kann man den Wert im ``$this->data`` Array im Controller, +``$form->data`` Array im View setzen, oder die Option ``checked`` der +Input Methode auf 'true' setzen. + +Die Standardwerte für date und datetime Felder können mit Hilfe des +'selected' Schlüssels gesetzt werden. + +$options[‘selected’] +-------------------- + +Used in combination with a select-type input (i.e. For types select, +date, time, datetime). Set ‘selected’ to the value of the item you wish +to be selected by default when the input is rendered. + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +The selected key for date and datetime inputs may also be a UNIX +timestamp. + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +These two keys specify the number of rows and columns in a textarea +input. + +:: + + echo $form->input('textarea', array('rows' => '5', 'cols' => '5')); + +Output: + +:: + +
+ + +
+ +$options[‘empty’] +----------------- + +If set to true, forces the input to remain empty. + +When passed to a select list, this creates a blank option with an empty +value in your drop down list. If you want to have a empty value with +text displayed instead of just a blank option, pass in a string to +empty. + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(choose one)')); ?> + +Output: + +:: + +
+ + +
+ +If you need to set the default value in a password field to blank, use +'value' => '' instead. + +Options can also supplied as key-value pairs. + +$options[‘timeFormat’] +---------------------- + +Used to specify the format of the select inputs for a time-related set +of inputs. Valid values include ‘12’, ‘24’, and ‘none’. + +$options[‘dateFormat’] +---------------------- + +Used to specify the format of the select inputs for a date-related set +of inputs. Valid values include ‘DMY’, ‘MDY’, ‘YMD’, and ‘NONE’. + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +Used in combination with a date/datetime input. Defines the lower and/or +upper end of values shown in the years select field. + +$options['interval'] +-------------------- + +This option specifies the number of minutes between each option in the +minutes select box. + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +Would create 4 options in the minute select. One for each 15 minutes. + +$options['class'] +----------------- + +You can set the classname for an input field using ``$options['class']`` + +:: + + echo $form->input('title', array('class' => 'custom-class')); + +File Fields +=========== + +To add a file upload field to a form, you must first make sure that the +form enctype is set to "multipart/form-data", so start off with a create +function such as the following. + +:: + + echo $form->create('Document', array('enctype' => 'multipart/form-data') ); + // OR + echo $form->create('Document', array('type' => 'file')); + +Next add either of the two lines to your form view file. + +:: + + echo $form->input('Document.submittedfile', array('between'=>'
','type'=>'file')); + + // or + + echo $form->file('Document.submittedfile'); + +Due to the limitations of HTML itself, it is not possible to put default +values into input fields of type 'file'. Each time the form is +displayed, the value inside will be empty. + +Upon submission, file fields provide an expanded data array to the +script receiving the form data. + +For the example above, the values in the submitted data array would be +organized as follows, if the CakePHP was installed on a Windows server. +'tmp\_name' will have a different path in a Unix environment. + +:: + + + $this->data['Document']['submittedfile'] = array( + 'name' => conference_schedule.pdf + 'type' => application/pdf + 'tmp_name' => C:/WINDOWS/TEMP/php1EE.tmp + 'error' => 0 + 'size' => 41737 + ); + +This array is generated by PHP itself, so for more detail on the way PHP +handles data passed via file fields `read the PHP manual section on file +uploads `_. + +Validating Uploads +------------------ + +Below is an example validation method you could define in your model to +validate whether a file has been successfully uploaded. + +:: + + // Based on comment 8 from: http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty( $val['tmp_name']) && $val['tmp_name'] != 'none')) { + return is_uploaded_file($val['tmp_name']); + } + return false; + } + +Form Element-Specific Methods +============================= + +The rest of the methods available in the FormHelper are for creating +specific form elements. Many of these methods also make use of a special +$options parameter. In this case, however, $options is used primarily to +specify HTML tag attributes (such as the value or DOM id of an element +in the form). + +:: + + text('username', array('class' => 'users')); ?> + +Will output: + +:: + + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +Creates a checkbox form element. This method also generates an +associated hidden form input to force the submission of data for the +specified field. + +:: + + checkbox('done'); ?> + +Will output: + +:: + + + + +button +------ + +``button(string $title, array $options = array())`` + +Erstellt eine Schaltfläche mit Titel und einem Standardtyp. Mit der +Option ``$options['type']`` können drei verschiedene Typen für die +Schaltfläche angegeben werden. + +#. button: Es wird eine Standardschaltfläche erstellt. +#. reset: Es wird eine Reset Schaltfläche erstellt. +#. submit: Es wird eine Schaltfläche, für welche auch der Code + ``$form->submit`` angewendet werden kann, erstellt. + +:: + + button('Schaltfläche'); + echo $form->button('andere Schaltfläche', array('type'=>'button')); + echo $form->button('Formular resetten', array('type'=>'reset')); + echo $form->button('Formular absenden', array('type'=>'submit')); + ?> + +Entspricht der HTML-Ausgabe: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, mixed $showEmpty)`` + +Creates a select element populated with the years from ``$minYear`` to +``$maxYear``, with the ``$selected`` year selected by default. +``$selected`` can either be a four-digit year (e.g. 2004) or string +``'now'``. HTML attributes may be supplied in ``$attributes``. + +:: + + year('purchased', 2005, 2009); + ?> + +Will output: + +:: + + + +If ``$showEmpty`` is false, the select will not include an empty option. +If ``$showEmpty`` is a string, it will be used as empty option's name. + +:: + + year('returned', 2008, 2010, null, null, 'Select a year'); + ?> + +Will output: + +:: + + + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with month names. + +:: + + month('mob'); + ?> + +Will output: + +:: + + + +You can pass in your own array of months to be used by setting the +'monthNames' attribute (CakePHP 1.3 only), or have months displayed as +numbers by passing false. (Note: the default months are +internationalized and can be translated using localization.) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +Erstellt einen Satz Dropdowns für Datum und Zeit. Gültige Werte für +$dateformat sind ‘DMY’, ‘MDY’, ‘YMD’ und ‘NONE’. Gültige Werte für +$timeFormat sind ‘12’, ‘24’, und ‘NONE’. + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the (numerical) days of the +month. + +To create an empty option with prompt text of your choosing (e.g. the +first option is 'Day'), you can supply the text as the final parameter +as follows: + +:: + + day('created'); + ?> + +Will output: + +:: + + + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the hours of the day. + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the minutes of the hour. + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with ‘am’ and ‘pm’. + +error +----- + +``error(string $fieldName, string $text, array $options)`` + +Shows a validation error message, specified by $text, for the given +field, in the event that a validation error has occurred. + +Options: + +- 'escape' bool Whether or not to html escape the contents of the + error. +- 'wrap' mixed Whether or not the error message should be wrapped in a + div. If a string, will be used as the HTML tag to use. +- 'class' string The classname for the error message + +file +---- + +``file(string $fieldName, array $options)`` + +Creates a file input. + +:: + + create('User',array('type'=>'file')); + echo $form->file('avatar'); + ?> + +Will output: + +:: + +
+ + +When using ``$form->file()``, remember to set the form encoding-type, by +setting the type option to 'file' in ``$form->create()`` + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +Creates a hidden form input. Example: + +:: + + hidden('id'); + ?> + +Will output: + +:: + + + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +Returns true if the supplied $fieldName has an active validation error. + +:: + + isFieldError('gender')){ + echo $form->error('gender'); + } + ?> + +When using ``$form->input()``, errors are rendered by default. + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +Creates a label tag, populated with $text. + +:: + + label('status'); + ?> + +Will output: + +:: + + + +password +-------- + +``password(string $fieldName, array $options)`` + +Creates a password field. + +:: + + password('password'); + ?> + +Will output: + +:: + + + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +Creates a radio button input. Use ``$attributes['value']`` to set which +value should be selected default. + +Use ``$attributes['separator']`` to specify HTML in between radio +buttons (e.g.
). + +Radio elements are wrapped with a label and fieldset by default. Set +``$attributes['legend']`` to false to remove them. + +:: + + 'Male','F'=>'Female'); + $attributes=array('legend'=>false); + echo $form->radio('gender',$options,$attributes); + ?> + +Will output: + +:: + + + + + + + +If for some reason you don't want the hidden input, setting +``$attributes['value']`` to a selected value or boolean false will do +just that. + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element, populated with the items in ``$options``, with +the option specified by ``$selected`` shown as selected by default. Set +``$showEmpty`` to false if you do not want an empty select option to be +displayed. + +:: + + 'Male','F'=>'Female'); + echo $form->select('gender',$options) + ?> + +Will output: + +:: + + + +submit +------ + +``submit(string $caption, array $options)`` + +Creates a submit button with caption ``$caption``. If the supplied +``$caption`` is a URL to an image (it contains a ‘.’ character), the +submit button will be rendered as an image. + +It is enclosed between ``div`` tags by default; you can avoid this by +declaring ``$options['div'] = false``. + +:: + + submit(); + ?> + +Will output: + +:: + +
+ +You can also pass a relative or absolute url to an image for the caption +parameter instead of caption text. + +:: + + submit('ok.png'); + ?> + +Will output: + +:: + +
+ +text +---- + +``text(string $fieldName, array $options)`` + +Creates a text input field. + +:: + + text('first_name'); + ?> + +Will output: + +:: + + + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +Creates a textarea input field. + +:: + + textarea('notes'); + ?> + +Will output: + +:: + + + diff --git a/de/The-Manual/Core-Helpers/HTML.rst b/de/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..b97b74ea3c47f8f53b60be02574ca5984db7b34f --- /dev/null +++ b/de/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,573 @@ +HTML +#### + +Der HtmlHelper wird in CakePHP benutzt, um HTML-basierte Ausgabe +einfacher, schneller und stabiler gegenüber Änderungen zu machen. Wenn +du diesen Helfer benutzt, dann wird deine Anwendung leichtgewichtiger +und flexibler werden wenn sie unter einer Domain in Einsatz kommt. + +Die Aufgabe von HtmlHelper hat sich seit CakePHP 1.1 sehr stark +verändert. Die Methoden für Eingabeformulare wurden als veraltet +markiert und befinden sich nun im neuen FormHelper. Wenn du Fragen zu +HTML Eingabeformularen (*Forms*) hast, dann schau beim neuen FormHelper +nach. + +Bevor wir uns die Methoden von HtmlHelper anschauen ist es wichtig die +Konfigurationen und Anwendungssituation von HtmlHelper zu kennen. Für +diejenigen, die nicht so viele *Shorttags* () oder viele echo() +Aufrufe im Code mögen, wurde der HtmlHelper so geschrieben, dass alle +Methoden direkt der output() Methode übergeben werden. Wenn die Ausgabe +des HtmlHelpers direkt ausgegeben werden soll, dann kann man einfach die +output() methode der AppHelper-Klasse implementieren. + +:: + + function output($str) { + echo $str; + } + +Wenn man diese Methode implementiert muss man keine echo Aufrufe im +*View* mehr schreiben. + +Viele Methoden von HtmlHelper enthalten einen Parameter namens +$htmlAttributes. Der Parameter erlaubt es dem HTML-Tag welches +ausgegeben wird, beliebige weitere Attribute und Werte hinzuzufügen. +Hier ein paar Beispiele wie man diesen Parameter benutzt: + +:: + + Gewünschte Attribute: + $htmlAttributes Parameter: array('class'=>'someClass') + + Gewünschte Attribute: + $htmlAttributes Parameter: array('name' => 'foo', 'value' => 'bar') + +Der HtmlHelper ist in allen Views automatisch eingebunden und verfügbar. +Sollte trotzdem eine Fehlermeldung erscheinen, die behauptet, dass der +HtmlHelper fehlt, so liegt das meist daran, dass der HtmlHelper in der +$helpers Variablen des *Controller* nicht angegeben ist. + +Einfügen von wohlgeformtem HTML +=============================== + +Die wichtigste Aufgabe von HtmlHelper ist die Generierung von +wohlgeformtem HTML. Zögere nicht, ihn oft zu benutzten - die *Views* +können in CakePHP zwischengespeichert werden (*Cache*) um +Prozessorzyklen zu sparen wenn die *Views* gerendert und ausgeliefert +werden. Dieser Abschnitt wird einige der Methoden des HtmlHelpers +beschreiben. + +charset (Zeichensatz) +--------------------- + +``charset(string $charset=null)`` + +Diese Methode wird benutzt um ein Meta-Tag zu erzeugen, welches die +Zeichenkodierung des Dokuments angibt. Standard ist UTF-8. + +:: + + charset(); ?> + +Ausgabe: + +:: + + + +Um einen anderen Zeichensatz auszugeben, kann man folgendes verwenden: + +:: + + charset('ISO-8859-1'); ?> + +Ausgabe: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +Diese Methode erzeugt eine Verknüpfung zu CSS Dateien. Wenn der +Parameter $inline auf *false* gesetzt wird, dann werden die Link-Tags +der $scripts\_for\_layout Variable hinzugefügt, welche man im head-Tag +des Dokuments gesetzt werden kann. + +Diese Methode erwartet, dass sich die CSS-Dateien im Verzeichnis +/app/webroot/css befinden. + +:: + + css('forms'); ?> + +Ausgabe: + +:: + + + +Um mehrere Dateien einzubinden, kann ein Array als ersten Parameter +übergeben werden. + +:: + + css(array('forms','tables','menu')); ?> + +Ausgabe: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +Diese Methode ist nützlich um externe Ressourcen wie RSS/Atom-Feeds oder +Favicons zu einzubinden. Wie bei der css() Methode kann mit dem +Parameter $inline angezeigt werden, ob der Tag direkt an der Stelle +(*inline*) oder im Kopf (*head*) an der Stelle der Variablen +$scripts\_for\_layouts erscheinen soll. + +Der $type Parameter kann benutzt werden um das type-Attribut des Tags zu +erzeugen. Hierbei können ein paar Abkürzungen verwendet werden: + ++--------+------------------------+ +| type | Erzeugter Wert | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> + //Ausgabe (Zeilenumbrüche hinzugefügt)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + //Ausgabe (Zeilenumbrüche hinzugefügt) + + +Mit dieser Methode kann man auch meta-Schlüsselwörter (*keywords*) und +-Beschreibung (*description*) hinzufügen: + +:: + + meta( + 'keywords', + 'Hier Schlüsselworte eintragen' + );?> + //Ausgabe + + meta( + 'description', + 'Hier Beschreibung eintragen' + );?> + //Ausgabe + +doctype +------- + +``docType(string $type = 'xhtml-strict')`` + +Diese Methode schreibt einen (X)HTML doctype Tag. Als $type kann +folgendes angegeben werden: + ++----------------+-----------------------+ +| type | Ausgabewert | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Mithilfe dieser Methode kann man CSS Stildefinitionen erzeugen. Die +gewünschten Schlüssel/Wert-Paare können über das Array $data übergeben +werden. Diese Methode ist besonders nützlich, wenn die CSS-Datei +dynamisch erzeugt wird. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Ausgabe: + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Mit dieser Methode kann man ein Image-Tag erzeugen. Der Pfad in der +Variablen $path muss relativ zum Ordner /app/webroot/img/ angegeben +werden. + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Ausgabe: + +:: + + CakePHP + +Um eine Bildverknüpfung zu erzeugen kann die +``url Option des Parameter $htmlAttributes genutzt werden.`` + +:: + + image("recipes/6.jpg", array( + "alt" => "Kekse", + 'url' => array('controller' => 'recipes', 'action' => 'view', 6) + )); ?> + +Ausgabe: + +:: + + + Kekse + + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +Allgemeine Methode zum Erzeugen von HTML Links. Der Parameter +``$htmlAttributes`` kann benutzt werden um die Attribute des Link-Tags +zu setzen. + +:: + + link('Enter', '/pages/home', array('class'=>'button','target'=>'_blank')); ?> + +Ausgabe: + +:: + + + Enter + +Wenn eine Bestätigungsnachricht in ``$confirmMessage`` angegeben ist, +dann wird ein javascript ``confirm()`` Dialog mit dieser Nachricht +angezeigt. + +:: + + link( + 'Löschen', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "Sind sie sicher dass sie dieses Rezept löschen möchten?" + );?> + +Ausgabe: + +:: + + + Löschen + +``link()`` kann auch dazu benutzt werden Seitenanfragestrings zu +erzeugen: + +:: + + link('Bild anzeigen', array( + 'controller' => 'images', + 'action' => 'view', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Ausgabe: + +:: + + + Bild anzeigen + +HTML-Sonderzeichen die in ``$title`` vorkommen, werden automatisch in +HTML *entities* konvertiert. Die Konvertierung kann ausgeschalten werden +indem entweder die escape Option des Parameter ``$htmlAttributes`` oder +der Parameter ``$escapeTitle`` auf false gesetzt wird. + +:: + + link( + $html->image("recipes/6.jpg", array("alt" => "Kekse")), + "recipes/view/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recipes/6.jpg", array("alt" => "Kekse")), + "recipes/view/6", + null, null, false + ); + ?> + +Beide echo geben folgendes aus: + +:: + + + Kekse + + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Mit dieser Methode kann ein Tag mit beliebigem Inhalt erzeugt werden. +Wenn kein Text in ``$text`` angegeben ist, wird nur das öffnende Tag + zurückgegeben. + +:: + + tag('span', 'Hallo Welt!.', array('class' => 'willkommen'));?> + + // Ausgabe + Hallo Welt! + + // Kein Text angegeben + tag('span', null, array('class' => 'willkommen'));?> + + // Ausgabe + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Mithilfe dieser Funktion können Abschnitte erzeugt werden, die von einem +``div``-Tag umschlossen sind. Der erste Parameter gibt eine CSS-Klasse +an und der zweite Parameter den Text, der zwischen dem öffnenden und +schließenden Tag eingeschlossen werden soll. Wenn der letzte Parameter +auf true gesetzt wird, wird $text HTML-escaped dargestellt. + +Wird kein Text angegeben, wird nur das öffnende div Tag ausgegeben. + +:: + + + div('fehler', 'Bitte geben Sie Ihre Kreditkartennummer ein.');?> + + //Output +
Bitte geben Sie Ihre Kreditkartennummer ein.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Diese Funktion gibt einen Text zurück der in

-Tags eingeschlossen +ist. Ist kein Text angegeben, wird nur das öffnende

Tag +zurückgegeben.. + +:: + + para(null, 'Hallo Welt!');?> + + //Ausgabe +

Hallo Welt!

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Erzeugt eine Kopfzeile welche in einem Tag verwendet werden +kann. + +:: + + tableHeaders(array('Datum','Titel','Aktiv'));?> + + //Ausgabe + + + tableHeaders( + array('Datum','Titel','Aktiv'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + //Ausgabe + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Erzeugt eine Reihe von Zellen für Tabellen. Je nach gerader oder +ungerader Zeilennummer werden die -Tags mit den Attributen +$oddTrOptions (ungerade) bzw. $evenTrOptions (gerade) versehen. + +Um Attribute für die + + + + tableCells(array( + array('7. Juli 2007', array('Spitzenschokokuchen', array('class'=>'highlight')) , 'Ja'), + array('21. Juni 2007', 'Pfiffige Kekse', 'Ja'), + array('1. August 2006', 'Anti-Java Kuchen', array('Nein', array('id'=>'special'))), + )); + ?> + + //Ausgabe + + + + + tableCells( + array( + array('Rot', 'Apfel'), + array('Orange', 'Orange'), + array('Gelb', 'Banane'), + ), + array('class' => 'dunkler') + ); + ?> + + //Ausgabe + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Gibt eine CakePHP-typische URL zurück die auf einen *Controller* und +eine *Action* zeigt. Wenn $url leer ist, wird die Anfragende URL +(REQUEST\_URI) zurückgegeben. Ansonten wird die URL für die +Controller/Action Kombination erzeugt. Wird der Parameter full auf true +gesetzt, wird die URL inklusive Basisadresse zurückgeben. + +:: + + url(array("controller" => "posts", + "action" => "foo", + "bar" => 1));?> + + //Ausgabe + /posts/foo/bar:1 + +Das nächste Beispiel gibt auch eine URL die mit '/' startet zurück aber +die komplette Basisadresse wird vorne angehängt. + +:: + + url('/posts/foo/bar:1'); ?> + + //Output + /cakeinstall/posts/foo/bar:1 + +Verändern des Tag-Satzes mit dem HtmlHelper +=========================================== + +Die eingebauten Tag-Sätze für den ``HtmlHelper`` sind kompatibel zu +XHTML. Falls HTML für HTML4 erzeugt werden muss, muss man eine neue +Tag-Konfigurationsdatei mit den Tags erzeugen und laden, die man +verwenden möchte. Um die Tags zu wechseln, erzeugt man die Datei +``app/config/tags.php`` mit folgendem Inhalt: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +Durch ``$html->loadConfig('tags');`` wird dieser Tag-Satz geladen. diff --git a/de/The-Manual/Core-Helpers/Javascript.rst b/de/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..ed9309d04fd3484781efceb67435e1155038ce64 --- /dev/null +++ b/de/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,144 @@ +Javascript +########## + +Der Javascript-Helper wird verwendet, um die Erzegung von wohl geformten +Javascript-Tags und Codeblöcken zu unterstützen. Es gibt zahlreiche +Methoden, von denen einige erzeugt wuden, um mit der +`Prototype `_ Javascript-Bibliothek +zusammenzuarbeiten. + +Methods +======= + +``codeBlock($script, $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), $safe)`` + +- string $script - The JavaScript to be wrapped in SCRIPT tags +- array $options - Set of options: + + - allowCache: boolean, designates whether this block is cacheable + using the current cache settings. + - safe: boolean, whether this block should be wrapped in CDATA tags. + Defaults to helper's object configuration. + - inline: whether the block should be printed inline, or written to + cached for later output (i.e. $scripts\_for\_layout). + +- boolean $safe - DEPRECATED. Use $options['safe'] instead + +codeBlock returns a formatted script element containing $script. But can +also return null if Javascript helper is set to cache events. See +JavascriptHelper::cacheEvents(). And can write in +``$scripts_for_layout`` if you set $options['inline'] to false. + +``blockEnd()`` + +Ends a block of cached Javascript. Can return either a end script tag, +or empties the buffer, adding the contents to the cachedEvents array. +Its return value depends on the cache settings. See +JavascriptHelper::cacheEvents() + +``link($url, $inline = true)`` + +- mixed $url - String URL to JavaScript file, or an array of URLs. +- boolean $inline If true, the '; + echo Sanitize::html($badString); + // output: <font size="99" color="#FF0000">HEY</font><script>...</script> + echo Sanitize::html($badString, true); + // output: HEY... + +escape +====== + +escape(string $string, string $connection) + +Used to escape SQL statements by adding slashes, depending on the +system's current magic\_quotes\_gpc setting. $connection is the name of +the database to quote the string for, as named in your +app/config/database.php file. + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +This function is an industrial-strength, multi-purpose cleaner, meant to +be used on entire arrays (like $this->data, for example). The function +takes an array (or string) and returns the clean version. The following +cleaning operations are performed on each element in the array +(recursively): + +- Odd spaces (including 0xCA) are replaced with regular spaces. +- Double-checking special chars and removal of carriage returns for + increased SQL security. +- Adding of slashes for SQL (just calls the sql function outlined + above). +- Swapping of user-inputted backslashes with trusted backslashes. + +The $options argument can either be a string or an array. When a string +is provided it's the database connection name. If an array is provided +it will be merged with the following options: + +- connection +- odd\_spaces +- encode +- dollar +- carriage +- unicode +- escape +- backslash + +Usage of clean() with options looks something like the following: + +:: + + $this->data = Sanitize::clean($this->data, array('encode' => false)); + diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..0a51d6684ebed22d1748878a2d6e73606e467eed --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,1049 @@ +Data Validation +############### + +Data validation is an important part of any application, as it helps to +make sure that the data in a Model conforms to the business rules of the +application. For example, you might want to make sure that passwords are +at least eight characters long, or ensure that usernames are unique. +Defining validation rules makes form handling much, much easier. + +There are many different aspects to the validation process. What we’ll +cover in this section is the model side of things. Essentially: what +happens when you call the save() method of your model. For more +information about how to handle the displaying of validation errors, +check out :doc:`/The-Manual/Core-Helpers/Form`. + +The first step to data validation is creating the validation rules in +the Model. To do that, use the Model::validate array in the Model +definition, for example: + +:: + + + +In the example above, the $validate array is added to the User Model, +but the array contains no validation rules. Assuming that the users +table has login, password, email and born fields, the example below +shows some simple validation rules that apply to those fields: + +:: + + 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + ?> + +This last example shows how validation rules can be added to model +fields. For the login field, only letters and numbers will be accepted, +the email should be valid, and born should be a valid date. Defining +validation rules enables CakePHP’s automagic showing of error messages +in forms if the data submitted does not follow the defined rules. + +CakePHP has many validation rules and using them can be quite easy. Some +of the built-in rules allow you to verify the formatting of emails, +URLs, and credit card numbers – but we’ll cover these in detail later +on. + +Here is a more complex validation example that takes advantage of some +of these built-in validation rules: + +:: + + array( + 'alphaNumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Alphabets and numbers only' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Between 5 to 15 characters' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Mimimum 8 characters long' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date', + 'allowEmpty' => true + ) + ); + } + ?> + +Two validation rules are defined for login: it should contain letters +and numbers only, and its length should be between 5 and 15. The +password field should be a minimum of 8 characters long. The email +should be a valid email address, and born should be a valid date. Also, +notice how you can define specific error messages that CakePHP will use +when these validation rules fail. + +As the example above shows, a single field can have multiple validation +rules. And if the built-in rules do not match your criteria, you can +always add your own validation rules as required. + +Now that you’ve seen the big picture on how validation works, let’s look +at how these rules are defined in the model. There are three different +ways that you can define validation rules: simple arrays, single rule +per field, and multiple rules per field. + +Simple Rules +============ + +As the name suggests, this is the simplest way to define a validation +rule. The general syntax for defining rules this way is: + +:: + + var $validate = array('fieldName' => 'ruleName'); + +Where, 'fieldName' is the name of the field the rule is defined for, and +‘ruleName’ is a pre-defined rule name, such as 'alphaNumeric', 'email' +or 'isUnique'. + +For example, to ensure that the user is giving a well formatted email +address, you could use this rule: + +:: + + var $validate = array('user_email' => 'email'); + +One Rule Per Field +================== + +This definition technique allows for better control of how the +validation rules work. But before we discuss that, let’s see the general +usage pattern adding a rule for a single field: + +:: + + var $validate = array( + 'fieldName1' => array( + 'rule' => 'ruleName', // or: array('ruleName', 'param1', 'param2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // or: 'update' + 'message' => 'Your Error Message' + ) + ); + +The 'rule' key is required. If you only set 'required' => true, the form +validation will not function correctly. This is because 'required' is +not actually a rule. + +As you can see here, each field (only one field shown above) is +associated with an array that contains five keys: ‘rule’, ‘required’, +‘allowEmpty’, ‘on’ and ‘message’. Let’s have a closer look at these +keys. + +rule +---- + +The 'rule' key defines the validation method and takes either a single +value or an array. The specified 'rule' may be the name of a method in +your model, a method of the core Validation class, or a regular +expression. For more information on the rules available by default, see +:doc:`/The-Manual/Common-Tasks-With-CakePHP/Data-Validation`. + +If the rule does not require any parameters, 'rule' can be a single +value e.g.: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +If the rule requires some parameters (like the max, min or range), +'rule' should be an array: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +Remember, the 'rule' key is required for array-based rule definitions. + +required +-------- + +This key should be assigned to a boolean value. If ‘required’ is true, +the field must be present in the data array. For example, if the +validation rule has been defined as follows: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +The data sent to the model’s save() method must contain data for the +login field. If it doesn’t, validation will fail. The default value for +this key is boolean false. + +``required => true`` does not mean the same as the validation rule +``notEmpty()``. ``required => true`` indicates that the array *key* must +be present - it does not mean it must have a value. Therefore validation +will fail if the field is not present in the dataset, but may (depending +on the rule) succeed if the value submitted is empty (''). + +allowEmpty +---------- + +If set to ``false``, the field value must be "nonempty", where +"nonempty" is defined as ``!empty($value) || is_numeric($value)``. The +numeric check is so that CakePHP does the right thing when ``$value`` is +zero. + +The difference between ``required`` and ``allowEmpty`` can be confusing. +``'required' => true`` means that you cannot save the model without the +key for this field being present in ``$this->data`` (the check is +performed with ``isset``); whereas, ``'allowEmpty' => false`` makes sure +that the current field *value* is "nonempty", as described above. + +on +-- + +The ‘on’ key can be set to either one of the following values: ‘update’ +or ‘create’. This provides a mechanism that allows a certain rule to be +applied either during the creation of a new record, or during update of +a record. + +If a rule has defined ‘on’ => ‘create’, the rule will only be enforced +during the creation of a new record. Likewise, if it is defined as ‘on’ +=> ‘update’, it will only be enforced during the updating of a record. + +The default value for ‘on’ is null. When ‘on’ is null, the rule will be +enforced during both creation and update. + +message +------- + +The ‘message’ key allows you to define a custom validation error message +for the rule: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'Password must be at least 8 characters long' + ) + ); + +last +---- + +Setting the ``'last'`` key to ``true`` will cause the validator to stop +on the rule if it fails instead of continuing with the next rule. This +is handy if you want validation to stop if the field is notEmpty in a +:doc:`/The-Manual/Common-Tasks-With-CakePHP/Data-Validation`. + +:: + + var $validate = array( + 'username' => array( + 'usernameRule-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Please enter a username.', + 'last' => true + ), + 'usernameRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters.' + ) + ) + ); + +The default value for ``'last'`` is ``false``. + +Multiple Rules per Field +======================== + +The technique outlined above gives us much more flexibility than simple +rules assignment, but there’s an extra step we can take in order to gain +more fine-grained control of data validation. The next technique we’ll +outline allows us to assign multiple validation rules per model field. + +If you would like to assign multiple validation rules to a single field, +this is basically how it should look: + +:: + + + var $validate = array( + 'fieldName' => array( + 'ruleName' => array( + 'rule' => 'ruleName', + // extra keys like on, required, etc. go here... + ), + 'ruleName2' => array( + 'rule' => 'ruleName2', + // extra keys like on, required, etc. go here... + ) + ) + ); + +As you can see, this is quite similar to what we did in the previous +section. There, for each field we had only one array of validation +parameters. In this case, each ‘fieldName’ consists of an array of rule +indices. Each ‘ruleName’ contains a separate array of validation +parameters. + +This is better explained with a practical example: + +:: + + var $validate = array( + 'login' => array( + 'loginRule-1' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Only alphabets and numbers allowed', + 'last' => true + ), + 'loginRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters' + ) + ) + ); + +The above example defines two rules for the login field: loginRule-1 and +loginRule-2. As you can see, each rule is identified with an arbitrary +name. + +By default CakePHP tries to validate a field using all the validation +rules declared for it and returns the error message for the last failing +rule. But if the key ``last`` is set to ``true`` for a rule and it +fails, then the error message for that rule is returned and further +rules are not validated. So if you prefer to show the error message for +the first failing rule then set ``'last' => true`` for each rule. + +If you plan on using internationalized error messages, you may want to +specify error messages in your view instead: + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'loginRule-1' => __('Only alphabets and numbers allowed', true), + 'loginRule-2' => __('Minimum length of 8 characters', true) + ) + ) + ); + +The field is now fully internationalized, and you are able to remove the +messages from the model. For more information on the \_\_() function, +see :doc:`/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization` + +Core Validation Rules +===================== + +The Validation class in CakePHP contains many validation rules that can +make model data validation much easier. This class contains many +oft-used validation techniques you won’t need to write on your own. +Below, you'll find a complete list of all the rules, along with usage +examples. + +alphaNumeric +------------ + +The data for the field must only contain letters and numbers. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Usernames must only contain letters and numbers.' + ) + ); + +between +------- + +The length of the data for the field must fall within the specified +numeric range. Both minimum and maximum values must be supplied. Uses <= +not < . + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Passwords must be between 5 and 15 characters long.' + ) + ); + +The length of data is "the number of bytes in the string representation +of the data". Be careful that it may be larger than the number of +characters when handling non-ASCII characters. + +blank +----- + +This rule is used to make sure that the field is left blank or only +white space characters are present in its value. White space characters +include space, tab, carriage return, and newline. + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +The data for the field must be a boolean value. Valid values are true or +false, integers 0 or 1 or strings '0' or '1'. + +:: + + var $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'Incorrect value for myCheckbox' + ) + ); + +cc +-- + +This rule is used to check whether the data is a valid credit card +number. It takes three parameters: ‘type’, ‘deep’ and ‘regex’. + +The ‘type’ key can be assigned to the values of ‘fast’, ‘all’ or any of +the following: + +- amex +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +If ‘type’ is set to ‘fast’, it validates the data against the major +credit cards’ numbering formats. Setting ‘type’ to ‘all’ will check with +all the credit card types. You can also set ‘type’ to an array of the +types you wish to match. + +The ‘deep’ key should be set to a boolean value. If it is set to true, +the validation will check the Luhn algorithm of the credit card +(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_). +It defaults to false. + +The ‘regex’ key allows you to supply your own regular expression that +will be used to validate the credit card number. + +:: + + var $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'The credit card number you supplied was invalid.' + ) + ); + +comparison +---------- + +Comparison is used to compare numeric values. It supports “is greater”, +“is less”, “greater or equal”, “less or equal”, “is less”, “equal to”, +and “not equal”. Some examples are shown below: + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Must be at least 18 years old to qualify.' + ) + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Must be at least 18 years old to qualify.' + ) + ); + +date +---- + +This rule ensures that data is submitted in valid date formats. A single +parameter (which can be an array) can be passed that will be used to +check the format of the supplied date. The value of the parameter can be +one of the following: + +- ‘dmy’ e.g. 27-12-2006 or 27-12-06 (separators can be a space, period, + dash, forward slash) +- ‘mdy’ e.g. 12-27-2006 or 12-27-06 (separators can be a space, period, + dash, forward slash) +- ‘ymd’ e.g. 2006-12-27 or 06-12-27 (separators can be a space, period, + dash, forward slash) +- ‘dMy’ e.g. 27 December 2006 or 27 Dec 2006 +- ‘Mdy’ e.g. December 27, 2006 or Dec 27, 2006 (comma is optional) +- ‘My’ e.g. (December 2006 or Dec 2006) +- ‘my’ e.g. 12/2006 or 12/06 (separators can be a space, period, dash, + forward slash) + +If no keys are supplied, the default key that will be used is ‘ymd’. + +:: + + var $validate = array( + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date in YY-MM-DD format.', + 'allowEmpty' => true + ) + ); + +While many data stores require a certain date format, you might consider +doing the heavy lifting by accepting a wide-array of date formats and +trying to convert them, rather than forcing users to supply a given +format. The more work you can do for your users, the better. + +decimal +------- + +This rule ensures that the data is a valid decimal number. A parameter +can be passed to specify the number of digits required after the decimal +point. If no parameter is passed, the data will be validated as a +scientific float, which will cause validation to fail if no digits are +found after the decimal point. + +:: + + var $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +This checks whether the data is a valid email address. Passing a boolean +true as the second parameter for this rule will also attempt to verify +that the host for the address is valid. + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Please supply a valid email address.' + ) + ); + +equalTo +------- + +This rule will ensure that the value is equal to, and of the same type +as the given value. + +:: + + var $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'This value must be the string cake' + ) + ); + +extension +--------- + +This rule checks for valid file extensions like .jpg or .png. Allow +multiple extensions by passing them in array form. + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), + 'message' => 'Please supply a valid image.' + ) + ); + +file +---- + +This rule ensures that the value is a valid file name. This validation +rule is currently non-functional. + +ip +-- + +This rule will ensure that a valid IPv4 address has been submitted. + +:: + + var $validate = array( + 'clientip' => array( + 'rule' => 'ip', + 'message' => 'Please supply a valid IP address.' + ) + ); + +isUnique +-------- + +The data for the field must be unique, it cannot be used by any other +rows. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'This username has already been taken.' + ) + ); + +minLength +--------- + +This rule ensures that the data meets a minimum length requirement. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', 8), + 'message' => 'Usernames must be at least 8 characters long.' + ) + ); + +The length here is "the number of bytes in the string representation of +the data". Be careful that it may be larger than the number of +characters when handling non-ASCII characters. + +maxLength +--------- + +This rule ensures that the data stays within a maximum length +requirement. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', 15), + 'message' => 'Usernames must be no larger than 15 characters long.' + ) + ); + +The length here is "the number of bytes in the string representation of +the data". Be careful that it may be larger than the number of +characters when handling non-ASCII characters. + +money +----- + +This rule will ensure that the value is in a valid monetary amount. + +Second parameter defines where symbol is located (left/right). + +:: + + var $validate = array( + 'salary' => array( + 'rule' => array('money', 'left'), + 'message' => 'Please supply a valid monetary amount.' + ) + ); + +`View this section in 1.3 +cookbook `_ + +multiple +-------- + +Use this for validating a multiple select input. It supports parameters +"in", "max" and "min". + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('do', 'ray', 'me', 'fa', 'so', 'la', 'ti'), 'min' => 1, 'max' => 3)), + 'message' => 'Please select one, two or three options' + ) + ); + +inList +------ + +This rule will ensure that the value is in a given set. It needs an +array of values. The field is valid if the field's value matches one of +the values in the given array. + +Example: + +:: + + var $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Enter either Foo or Bar.' + ) + ) + ); + +numeric +------- + +Checks if the data passed is a valid number. + +:: + + var $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => 'Please supply the number of cars.' + ) + ); + +notEmpty +-------- + +The basic rule to ensure that a field is not empty. + +:: + + var $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'This field cannot be left blank' + ) + ); + +Do not use this for a multiple select input as it will cause an error. +Instead, use "multiple". + +phone +----- + +Phone validates US phone numbers. If you want to validate non-US phone +numbers, you can provide a regular expression as the second parameter to +cover additional number formats. + +:: + + var $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +Postal is used to validate ZIP codes from the U.S. (us), Canada (ca), +U.K (uk), Italy (it), Germany (de) and Belgium (be). For other ZIP code +formats, you may provide a regular expression as the second parameter. + +:: + + var $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +This rule ensures that the value is in a given range. If no range is +supplied, the rule will check to ensure the value is a legal finite on +the current platform. + +:: + + var $validate = array( + 'number' => array( + 'rule' => array('range', -1, 11), + 'message' => 'Please enter a number between 0 and 10' + ) + ); + +The above example will accept any value which is larger than 0 (e.g., +0.01) and less than 10 (e.g., 9.99). Note: The range lower/upper are not +inclusive!!! + +ssn +--- + +Ssn validates social security numbers from the U.S. (us), Denmark (dk), +and the Netherlands (nl). For other social security number formats, you +may provide a regular expression. + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +This rule checks for valid URL formats. Supports http(s), ftp(s), file, +news, and gopher protocols. + +:: + + var $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + +To ensure that a protocol is in the url, strict mode can be enabled like +so. + +:: + + var $validate = array( + 'website' => array( + 'rule' => array('url', true) + ) + ); + +Custom Validation Rules +======================= + +If you haven’t found what you need thus far, you can always create your +own validation rules. There are two ways you can do this: by defining +custom regular expressions, or by creating custom validation methods. + +Custom Regular Expression Validation +------------------------------------ + +If the validation technique you need to use can be completed by using +regular expression matching, you can define a custom expression as a +field validation rule. + +:: + + var $validate = array( + 'login' => array( + 'rule' => '/^[a-z0-9]{3,}$/i', + 'message' => 'Only letters and integers, min 3 characters' + ) + ); + +The example above checks if the login contains only letters and +integers, with a minimum of three characters. + +The regular expression in the ``rule`` must be delimited by slashes. The +optional trailing 'i' after the last slash means the reg-exp is case +*i*\ nsensitive. + +Adding your own Validation Methods +---------------------------------- + +Sometimes checking data with regular expression patterns is not enough. +For example, if you want to ensure that a promotional code can only be +used 25 times, you need to add your own validation function, as shown +below: + +:: + + array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'This code has been used too many times.' + ) + ); + + function limitDuplicates($check, $limit){ + //$check will have value: array('promomotion_code' => 'some-value') + //$limit will have value: 25 + $existing_promo_count = $this->find( 'count', array('conditions' => $check, 'recursive' => -1) ); + return $existing_promo_count < $limit; + } + } + ?> + +The current field to be validated is passed into the function as first +parameter as an associated array with field name as key and posted data +as value. + +If you want to pass extra parameters to your validation function, add +elements onto the ‘rule’ array, and handle them as extra params (after +the main ``$check`` param) in your function. + +Your validation function can be in the model (as in the example above), +or in a behavior that the model implements. This includes mapped +methods. + +Model/behavior methods are checked first, before looking for a method on +the ``Validation`` class. This means that you can override existing +validation methods (such as ``alphaNumeric()``) at an application level +(by adding the method to ``AppModel``), or at model level. + +When writing a validation rule which can be used by multiple fields, +take care to extract the field value from the $check array. The $check +array is passed with the form field name as its key and the field value +as its value. The full record being validated is stored in $this->data +member variable. + +:: + + array( + 'rule' => 'alphaNumericDashUnderscore', + 'message' => 'Slug can only be letters, numbers, dash and underscore' + ) + ); + + function alphaNumericDashUnderscore($check) { + // $data array is passed using the form field name as the key + // have to extract the value to make the function generic + $value = array_values($check); + $value = $value[0]; + + return preg_match('|^[0-9a-zA-Z_-]*$|', $value); + } + } + ?> + +Validating Data from the Controller +=================================== + +While normally you would just use the save method of the model, there +may be times where you wish to validate the data without saving it. For +example, you may wish to display some additional information to the user +before actually saving the data to the database. Validating data +requires a slightly different process than just saving the data. + +First, set the data to the model: + +:: + + $this->ModelName->set( $this->data ); + +Then, to check if the data validates, use the validates method of the +model, which will return true if it validates and false if it doesn't: + +:: + + if ($this->ModelName->validates()) { + // it validated logic + } else { + // didn't validate logic + } + +It may be desirable to validate your model only using a subset of the +validations specified in your model. For example say you had a User +model with fields for first\_name, last\_name, email and password. In +this instance when creating or editing a user you would want to validate +all 4 field rules. Yet when a user logs in you would validate just email +and password rules. To do this you can pass an options array specifying +the fields to validate. e.g. + +:: + + if ($this->User->validates(array('fieldList' => array('email', 'password')))) { + // valid + } else { + // invalid + } + +The validates method invokes the invalidFields method which populates +the validationErrors property of the model. The invalidFields method +also returns that data as the result. + +:: + + $errors = $this->ModelName->invalidFields(); // contains validationErrors array + +It is important to note that the data must be set to the model before +the data can be validated. This is different from the save method which +allows the data to be passed in as a parameter. Also, keep in mind that +it is not required to call validates prior to calling save as save will +automatically validate the data before actually saving. + +To validate multiple models, either of the following approaches should +be used: + +:: + + if ($this->ModelName->saveAll($this->data, array('validate' => 'first'))) { + // validates and saves + } else { + // does not save or does not validate + } + +Or + +:: + + if ($this->ModelName->saveAll($this->data, array('validate' => 'only'))) { + // validates + } else { + // does not validate + } + +If you have validated data before save, you can turn off validation to +avoid second check. + +:: + + if ($this->ModelName->saveAll($this->data, array('validate' => false))) { + // saving wihout validation + } + diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..ce16d3e10ca879ac6cc669d7e53d8f8110afade4 --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,146 @@ +Debugging +######### + +Debugging is an inevitable and necessary part of any development cycle. +While CakePHP doesn't offer any tools that directly connect with any IDE +or editor, CakePHP does provide several tools to assist in debugging and +exposing what is running under the hood of your application. + +Basic Debugging +=============== + +debug($var, $showHTML = false, $showFrom = true) + +The debug() function is a globally available function that works +similarly to the PHP function print\_r(). The debug() function allows +you to show the contents of a variable in a number of different ways. +First, if you'd like data to be shown in an HTML-friendly way, set the +second parameter to true. The function also prints out the line and file +it is originating from by default. + +Output from this function is only shown if the core debug variable has +been set to a value greater than 0. + +Using the Debugger Class +======================== + +To use the debugger, first ensure that Configure::read('debug') is set +to a value greater than 0. + +dump($var) + +Dump prints out the contents of a variable. It will print out all +properties and methods (if any) of the supplied variable. + +:: + + $foo = array(1,2,3); + + Debugger::dump($foo); + + //outputs + array( + 1, + 2, + 3 + ) + + //simple object + $car = new Car(); + + Debugger::dump($car); + + //outputs + Car:: + Car::colour = 'red' + Car::make = 'Toyota' + Car::model = 'Camry' + Car::mileage = '15000' + Car::acclerate() + Car::decelerate() + Car::stop() + +log($var, $level = 7) + +Creates a detailed stack trace log at the time of invocation. The log() +method prints out data similar to that done by Debugger::dump(), but to +the debug.log instead of the output buffer. Note your app/tmp directory +(and its contents) must be writable by the web server for log() to work +correctly. + +trace($options) + +Returns the current stack trace. Each line of the trace includes the +calling method, including which file and line the call originated from. + +:: + + //In PostsController::index() + pr( Debugger::trace() ); + + //outputs + PostsController::index() - APP/controllers/downloads_controller.php, line 48 + Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 265 + Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 + [main] - APP/webroot/index.php, line 84 + +Above is the stack trace generated by calling Debugger::trace() in a +controller action. Reading the stack trace bottom to top shows the order +of currently running functions (stack frames). In the above example, +index.php called Dispatcher::dispatch(), which in-turn called +Dispatcher::\_invoke(). The \_invoke() method then called +PostsController::index(). This information is useful when working with +recursive operations or deep stacks, as it identifies which functions +are currently running at the time of the trace(). + +excerpt($file, $line, $context) + +Grab an excerpt from the file at $path (which is an absolute filepath), +highlights line number $line with $context number of lines around it. + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + //will output the following. + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +Although this method is used internally, it can be handy if you're +creating your own error messages or log entries for custom situations. + +exportVar($var, $recursion = 0) + +Converts a variable of any type to a string for use in debug output. +This method is also used by most of Debugger for internal variable +conversions, and can be used in your own Debuggers as well. + +invoke($debugger) + +Replace the CakePHP Debugger with a new Error Handler. + +Debugger Class +============== + +The debugger class is new in CakePHP 1.2 and offers even more options +for obtaining debugging information. It has several functions which are +invoked statically, and provide dumping, logging, and error handling +functions. + +The Debugger Class overrides PHP's default error handling, replacing it +with far more useful error reports. The Debugger's error handling is +used by default in CakePHP. As with all debugging functions, +Configure::debug must be set to a value higher than 0. + +When an error is raised, Debugger both outputs information to the page +and makes an entry in the error.log file. The error report that is +generated has both a stack trace and a code excerpt from where the error +was raised. Click on the "Error" link type to reveal the stack trace, +and on the "Code" link to reveal the error-causing lines. diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..926346717c2cacef699992f06b625fde88557961 --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,83 @@ +Error Handling +############## + +In the event of an unrecoverable error in your application, it is common +to stop processing and show an error page to the user. To save you from +having to code error handling for this in each of your controllers and +components, you can use the provided method: + +``$this->cakeError(string $errorType [, array $parameters]);`` + +Calling this method will show an error page to the user and halt any +further processing in your application. + +``parameters`` must be an array of strings. If the array contains +objects (including Exception objects), they will be cast into strings. + +CakePHP pre-defines a set of error-types, but at the time of writing, +most are only really useful by the framework itself. One that is more +useful to the application developer is the good old 404 error. This can +be called with no parameters as follows: + +:: + + $this->cakeError('error404'); + +Or alternatively, you can cause the page to report the error was at a +specific URL by passing the ``url`` parameter: + +:: + + $this->cakeError('error404', array('url' => 'some/other.url')); + +This all starts being a lot more useful by extending the error handler +to use your own error-types. Application error handlers are largely like +controller actions; You typically will set() any passed parameters to be +available to the view and then render a view file from your +``app/views/errors`` directory. + +Create a file ``app/app_error.php`` with the following definition. + +:: + + + +Handlers for new error-types can be implemented by adding methods to +this class. Simply create a new method with the name you want to use as +your error-type. + +Let's say we have an application that writes a number of files to disk +and that it is appropriate to report write errors to the user. We don't +want to add code for this all over the different parts of our +application, so this is a great case for using a new error type. + +Add a new method to your ``AppError`` class. We'll take one parameter +called ``file`` that will be the path to the file we failed to write. + +:: + + function cannotWriteFile($params) { + $this->controller->set('file', $params['file']); + $this->_outputMessage('cannot_write_file'); + } + +Create the view in ``app/views/errors/cannot_write_file.ctp`` + +:: + +

Unable to write file

+

Could not write file to the disk.

+ +and throw the error in your controller/component + +:: + + $this->cakeError('cannotWriteFile', array('file'=>'somefilename')); + +The default implementation of ``$this->_outputMessage()`` +will just display the view in ``views/errors/.ctp``. If +you wish to override this behaviour, you can redefine +``_outputMessage($template)`` in your AppError class. diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..025023546c47e34021b47aaf48d9b7e96cfd147c --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,198 @@ +Internationalization & Localization +################################### + +One of the best ways for your applications to reach a larger audience is +to cater for multiple languages. This can often prove to be a daunting +task, but the internationalization and localization features in CakePHP +make it much easier. + +First, it’s important to understand some terminology. +*Internationalization* refers to the ability of an application to be +localized. The term *localization* refers to the adaptation of an +application to meet specific language (or culture) requirements (i.e., a +"locale"). Internationalization and localization are often abbreviated +as i18n and l10n respectively; 18 and 10 are the number of characters +between the first and last character. + +Internationalizing Your Application +=================================== + +There are only a few steps to go from a single-language application to a +multi-lingual application, the first of which is to make use of the +```__()`` `_ +function in your code. Below is an example of some code for a +single-language application: + +:: + +

Posts

+ +To internationalize your code, all you need to do is to wrap strings in +`the translate +function `_ +like so: + +:: + +

+ +If you do nothing further, these two code examples are functionally +identical - they will both send the same content to the browser. The +```__()`` +function `_ +will translate the passed string if a translation is available, or +return it unmodified. It works similar to other +`Gettext `_ implementations (as do +the other translate functions, such as +```__d()`` `_, +```__n()`` `_ +etc) + +With your code ready to be multilingual, the next step is to create your +`pot file `_, which is the +template for all translatable strings in your application. To generate +your pot file(s), all you need to do is run the `i18n console +task `_, +which will look for where you've used a translate function in your code +and generate your pot file(s) for you. You can and should re-run this +console task any time you change the translations in your code. + +The pot file(s) themselves are not used by CakePHP, they are the +templates used to create or update your `po +files `_, which contain the +translations. Cake will look for your po files in the following +location: + +:: + + /app/locale//LC_MESSAGES/.po + +The default domain is 'default', therefore your locale folder would look +something like this: + +:: + + /app/locale/eng/LC_MESSAGES/default.po (English) + /app/locale/fre/LC_MESSAGES/default.po (French) + /app/locale/por/LC_MESSAGES/default.po (Portuguese) + +To create or edit your po files it's recommended that you do *not* use +your favorite editor. To create a po file for the first time it is +possible to copy the pot file to the correct location and change the +extension *however* unless you're familiar with their format, it's quite +easy to create an invalid po file or to save it as the wrong charset (if +you're editing manually, use UTF-8 to avoid problems). There are free +tools such as `PoEdit `_ which make editing and +updating your po files an easy task; especially for updating an existing +po file with a newly updated pot file. + +The three-character locale codes conform to the `ISO +639-2 `_ +standard, although if you create regional locales (en\_US, en\_GB, etc.) +cake will use them if appropriate. + +there is a 1014-character limit for each msgstr value (source needed). + +Remember that po files are useful for short messages, if you find you +want to translate long paragraphs, or even whole pages - you should +consider implementing a different solution. e.g.: + +:: + + // App Controller Code. + function beforeFilter() { + $locale = Configure::read('Config.language'); + if ($locale && file_exists(VIEWS . $locale . DS . $this->viewPath)) { + // e.g. use /app/views/fre/pages/tos.ctp instead of /app/views/pages/tos.ctp + $this->viewPath = $locale . DS . $this->viewPath; + } + } + +or + +:: + + // View code + echo $this->element(Configure::read('Config.language') . '/tos') + +Localization in CakePHP +======================= + +To change or set the language for your application, all you need to do +is the following: + +:: + + Configure::write('Config.language', 'fre'); + +This tells Cake which locale to use (if you use a regional locale, such +as fr\_FR, it will use the `ISO +639-2 `_ locale +as a fallback if it doesn't exist), you can change the language at any +time, e.g. in your bootstrap if you're setting the application default +language, in your (app) controller beforeFilter if it's specific to the +request or user, or in fact anytime at all before you want a message in +a different language. + +It's a good idea to serve up public content available in multiple +languages from a unique url - this makes it easy for users (and search +engines) to find what they're looking for in the language they are +expecting. There are several ways to do this, it can be by using +language specific subdomains (en.example.com, fra.example.com, etc.), or +using a prefix to the url such as is done with this application. You may +also wish to glean the information from the browser’s user-agent, among +other things. + +As mentioned in the previous section, displaying localized content is +done using the \_\_() convenience function, or one of the other +translation functions all of which are globally available, but probably +be best utilized in your views. The first parameter of the function is +used as the msgid defined in the .po files. + +Remember to use the return parameter for the various ``__*`` methods if +you don't want the string echo'ed directly. For example: + +:: + + error( + 'Card.cardNumber', + __("errorCardNumber", true), + array('escape' => false) + ); + ?> + +If you would like to have all of your validation error messages +translated by default, a simple solution would be to add the following +code in you app\_model.php: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + +The i18n console task will not be able to determine the message id from +the above example, which means you'll need to add the entries to your +pot file manually (or via your own script). To prevent the need to edit +your default.po(t) file every time you run the i18n console task, you +can use a different domain such as: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __d('validation_errors', $value, true)); + } + +This will look for ``$value`` in the validation\_errors.po file. + +There's one other aspect of localizing your application which is not +covered by the use of the translate functions, and that is date/money +formats. Don't forget that CakePHP is PHP :), therefore to set the +formats for these things you need to use +```setlocale`` `_. + +If you pass a locale that doesn't exist on your computer to +```setlocale`` `_ it will have no effect. +You can find the list of available locales by running the command +$locale -a in a terminal. diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..e1c3d77db0e1a39f1434121f5d09b26f46ff8c39 --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,67 @@ +Logging +####### + +While CakePHP core Configure Class settings can really help you see +what's happening under the hood, there are certain times that you'll +need to log data to the disk in order to find out what's going on. In a +world that is becoming more dependent on technologies like SOAP and +AJAX, debugging can be rather difficult. + +Logging can also be a way to find out what's been going on in your +application over time. What search terms are being used? What sorts of +errors are my users being shown? How often is a particular query being +executed? + +Logging data in CakePHP is easy - the log() function is a part of the +Object class, which is the common ancestor for almost all CakePHP +classes. If the context is a CakePHP class (Model, Controller, +Component... almost anything), you can log your data. + +Using the log function +====================== + +The log() function takes two parameters. The first is the message you'd +like written to the log file. By default, this error message is written +to the error log found in app/tmp/logs/error.log. + +:: + + //Executing this inside a CakePHP class: + + $this->log("Something didn't work!"); + + //Results in this being appended to app/tmp/logs/error.log + + 2007-11-02 10:22:02 Error: Something didn't work! + +The second parameter is used to define the log type you wish to write +the message to. If not supplied, it defaults to LOG\_ERROR, which writes +to the error log previously mentioned. You can set this second parameter +to LOG\_DEBUG to write your messages to an alternate debug log found at +app/tmp/logs/debug.log: + +:: + + //Executing this inside a CakePHP class: + + $this->log('A debugging message.', LOG_DEBUG); + + //Results in this being appended to app/tmp/logs/debug.log (rather than error.log) + + 2007-11-02 10:22:02 Error: A debugging message. + +You can also specify a different name for the log file, by setting the +second parameter to the name of the file. + +:: + + //Executing this inside a CakePHP class: + + $this->log('A special message for activity logging', 'activity'); + + //Results in this being appended to app/tmp/logs/activity.log (rather than error.log) + + 2007-11-02 10:22:02 Activity: A special message for activity logging + +Your app/tmp directory must be writable by the web server user in order +for logging to work correctly. diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..e8ed5e8febea5eae850469bad7ef4a929c3d767b --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,378 @@ +Pagination +########## + +One of the main obstacles of creating flexible and user-friendly web +applications is designing an intuitive UI. Many applications tend to +grow in size and complexity quickly, and designers and programmers alike +find they are unable to cope with displaying hundreds or thousands of +records. Refactoring takes time, and performance and user satisfaction +can suffer. + +Displaying a reasonable number of records per page has always been a +critical part of every application and used to cause many headaches for +developers. CakePHP eases the burden on the developer by providing a +quick, easy way to paginate data. + +The PaginatorHelper offers a great solution because it's so easy to use. +Apart from pagination, it bundles some very easy-to-use sorting +features. Last but not least, Ajax sorting and pagination are supported +as well. + +Controller Setup +================ + +In the controller, we start by defining the pagination defaults in the +*$paginate* controller variable. It is important to note here that the +order key must be defined in the array structure given. + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +You can also include other find() options, such as *fields*: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +Other keys that can be included in the *$paginate* array are similar to +the parameters of the *Model->find('all')* method, that is: +*conditions*, *fields*, *order*, *limit*, *page*, *contain*, *joins*, +and *recursive*. In fact, you can define more than one set of pagination +defaults in the controller, you just name the pieces of the array after +the model you wish to configure: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'Recipe' => array (...), + 'Author' => array (...) + ); + } + +Example of syntax using Containable Behavior: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +Once the *$paginate* variable has been defined, we can call the +*paginate()* method in controller actions. This method returns paged +*find()* results from the model, and grabs some additional paging +statistics, which are passed to the View behind the scenes. This method +also adds PaginatorHelper to the list of helpers in your controller, if +it has not been added already. + +:: + + function list_recipes() { + // similar to findAll(), but fetches paged results + $data = $this->paginate('Recipe'); + $this->set('data', $data); + } + +You can filter the records by passing conditions as second parameter to +the ``paginate()`` function. + +:: + + $data = $this->paginate('Recipe', array('Recipe.title LIKE' => 'a%')); + +Or you can also set *conditions* and other keys in the ``$paginate`` +array inside your action. + +:: + + function list_recipes() { + $this->paginate = array( + 'conditions' => array('Recipe.title LIKE' => 'a%'), + 'limit' => 10 + ); + $data = $this->paginate('Recipe'); + $this->set(compact('data')); + ); + +Pagination in Views +=================== + +It's up to you to decide how to show records to the user, but most often +this will be done inside HTML tables. The examples below assume a +tabular layout, but the PaginatorHelper available in views doesn't +always need to be restricted as such. + +See the details on +`PaginatorHelper `_ in +the API. + +As mentioned, the PaginatorHelper also offers sorting features which can +be easily integrated into your table column headers: + +:: + + // app/views/recipes/list_recipes.ctp +
DateTitleActive
DatumTitelAktiv
-Tags anzugeben, muss ein array() mit +Attributwerten zusätzlich zum Textwert der Zelle in ein Array gesetzt +werden (siehe 2. Beispiel unten). + +:: + + tableCells(array( + array('7. Juli 2007', 'Spitzenschokokuchen', 'Ja'), + array('21. Juni 2007', 'Pfiffige Kekse', 'Ja'), + array('1. August 2006', 'Anti-Java Kuchen', 'Nein'), + )); + ?> + + //Ausgabe +
7. Juli 2007SpitzenschokokuchenJa
21. Juni 2007Pfiffige KekseJa
1. August 2006Anti-Java KuchenNein
7. Juli 2007SpitzenschokokuchenJa
21. Juni 2007Pfiffige KekseJa
1. August 2006Anti-Java KuchenNein
RotApfel
OrangeOrange
GelbBanane
+ + + + + + + + + + +
sort('ID', 'id'); ?>sort('Title', 'title'); ?>
+ +The links output from the sort() method of the PaginatorHelper allow +users to click on table headers to toggle the sorting of the data by a +given field. + +It is also possible to sort a column based on associations: + +:: + + + + + + + + + + + + +
sort('Title', 'title'); ?>sort('Author', 'Author.name'); ?>
+ +The final ingredient to pagination display in views is the addition of +page navigation, also supplied by the PaginationHelper. + +:: + + + numbers(); ?> + + prev('« Previous ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Next »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +The wording output by the counter() method can also be customized using +special markers: + +:: + + counter(array( + 'format' => 'Page %page% of %pages%, showing %current% records out of + %count% total, starting on record %start%, ending on %end%' + )); + ?> + +To pass all URL arguments to paginator functions, add the following to +your view: + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +Route elements that are not named arguments should manually be merged +with ``$this->passedArgs``: + +:: + + //for urls like http://www.example.com/en/controller/action + //that are routed as Router::connect('/:lang/:controller/:action/*', array(),array('lang'=>'ta|en')); + $paginator->options(array('url'=>array_merge(array('lang'=>$lang),$this->passedArgs))); + +Or you can specify which params to pass manually: + +:: + + $paginator->options(array('url' => array("0", "1"))); + +AJAX Pagination +=============== + +It's very easy to incorporate Ajax functionality into pagination. The +only extra coding required is the inclusion of the the Prototype +JavaScript library, setting the indicator (loading icon inside of DIV) +and the specifying of a DIV to be updated (instead of reloading the +page). + +Do not forget to add the RequestHandler component to use Ajax calls to +your controller: + +:: + + var $components = array('RequestHandler'); + +If you include PaginatorHelper in your $helpers array, and want to use a +specific JsHelper adapter, be sure to put Paginator after JsHelper. +Failing to do so will cause JsHelper to use the default adapter which is +jQuery. + +Layout Changes +-------------- + +First, we'll include the Prototype library in the header, set up our +status indicator image (spinner.gif), and set up our main content +wrapper DIV, "content". + +Here’s what a layout including those elements might look like +(partially): + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +View Changes +------------ + +The only extra configuration for Ajax pagination is done using the +options() method of the PaginationHelper, which specifies required Ajax +parameters. In this case, we're specifying that all pagination links +should update the element with the ID 'content' with the resulting data, +and we want to show 'spinner' as the loading indicator. + +If the ‘update’ key is not specifed, the PaginationHelper will output +non-Ajax pagination sorting and paging links. + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Previous', null, null, array('class' => 'disabled')); + + echo $paginator->next('Next >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +Custom Query Pagination +======================= + +Fix me: Please add an example where overriding paginate is justified + +A good example of when you would need this is if the underlying DB does +not support the SQL LIMIT syntax. This is true of IBM's DB2. You can +still use the CakePHP pagination by adding the custom query to the +model. + +Should you need to create custom queries to generate the data you want +to paginate, you can override the ``paginate()`` and ``paginateCount()`` +model methods used by the pagination controller logic. + +Before continuing check you can't achieve your goal with the core model +methods. + +The ``paginate()`` method uses the same parameters as ``Model::find()``. +To use your own method/logic override it in the model you wish to get +the data from. + +:: + + /** + * Overridden paginate method - group by week, away_team_id and home_team_id + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $recursive = -1; + $group = $fields = array('week', 'away_team_id', 'home_team_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')); + } + +You also need to override the core ``paginateCount()``, this method +expects the same arguments as ``Model::find('count')``. The example +below uses some Postgres-specifc features, so please adjust accordingly +depending on what database you are using. + +:: + + /** + * Overridden paginateCount method + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +The observant reader will have noticed that the paginate method we've +defined wasn't actually necessary - All you have to do is add the +keyword in controller's ``$paginate`` class variable. + +:: + + /** + * Add GROUP BY clause + */ + var $paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + /** + * Or on-the-fly from within the action + */ + function index() { + $this->paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + +However, it will still be necessary to override the ``paginateCount()`` +method to get an accurate value. diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/en/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..6811d8cb1125ba617a0706749ac0038b27e90f2e --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,178 @@ +REST +#### + +Many newer application programmers are realizing the need to open their +core functionality to a greater audience. Providing easy, unfettered +access to your core API can help get your platform accepted, and allows +for mashups and easy integration with other systems. + +While other solutions exist, REST is a great way to provide easy access +to the logic you've created in your application. It's simple, usually +XML-based (we're talking simple XML, nothing like a SOAP envelope), and +depends on HTTP headers for direction. Exposing an API via REST in +CakePHP is simple. + +The Simple Setup +================ + +The fastest way to get up and running with REST is to add a few lines to +your routes.php file, found in app/config. The Router object features a +method called mapResources(), that is used to set up a number of default +routes for REST access to your controllers. If we wanted to allow REST +access to a recipe database, we'd do something like this: + +:: + + //In app/config/routes.php... + + Router::mapResources('recipes'); + Router::parseExtensions(); + +The first line sets up a number of default routes for easy REST access. +These routes are HTTP Request Method sensitive. + ++---------------+----------------+----------------------------------+ +| HTTP Method | URL | Controller action invoked | ++===============+================+==================================+ +| GET | /recipes | RecipesController::index() | ++---------------+----------------+----------------------------------+ +| GET | /recipes/123 | RecipesController::view(123) | ++---------------+----------------+----------------------------------+ +| POST | /recipes | RecipesController::add() | ++---------------+----------------+----------------------------------+ +| POST | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ +| PUT | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ +| DELETE | /recipes/123 | RecipesController::delete(123) | ++---------------+----------------+----------------------------------+ + +CakePHP's Router class uses a number of different indicators to detect +the HTTP method being used. Here they are in order of preference: + +#. The *\_method* POST variable +#. The X\_HTTP\_METHOD\_OVERRIDE +#. The REQUEST\_METHOD header + +The *\_method* POST variable is helpful in using a browser as a REST +client (or anything else that can do POST easily). Just set the value of +\_method to the name of the HTTP request method you wish to emulate. + +Once the router has been set up to map REST requests to certain +controller actions, we can move on to creating the logic in our +controller actions. A basic controller might look something like this: + +:: + + // controllers/recipes_controller.php + + class RecipesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recipes = $this->Recipe->find('all'); + $this->set(compact('recipes')); + } + + function view($id) { + $recipe = $this->Recipe->findById($id); + $this->set(compact('recipe')); + } + + function edit($id) { + $this->Recipe->id = $id; + if ($this->Recipe->save($this->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recipe->delete($id)) { + $message = 'Deleted'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + } + +Since we've added a call to Router::parseExtensions(), the CakePHP +router is already primed to serve up different views based on different +kinds of requests. Since we're dealing with REST requests, the view type +is XML. We place the REST views for our RecipesController inside +app/views/xml. We can also use the XmlHelper for quick-and-easy XML +output in those views. Here's what our index view might look like: + +:: + + // app/views/recipes/xml/index.ctp + + + serialize($recipes); ?> + + +Experienced CakePHP users might notice that we haven't included the +XmlHelper in our RecipesController $helpers array. This is on purpose - +when serving up a specific content type using parseExtensions(), CakePHP +automatically looks for a view helper that matches the type. Since we're +using XML as the content type, the XmlHelper is automatically loaded up +for our use in those views. + +The rendered XML will end up looking something like this: + +:: + + + + + + + + + + + + +Creating the logic for the edit action is a bit trickier, but not by +much. Since you're providing an API that outputs XML, it's a natural +choice to receive XML as input. Not to worry, however: the +RequestHandler and Router classes make things much easier. If a POST or +PUT request has an XML content-type, then the input is taken and passed +to an instance of Cake's Xml object, which is assigned to the $data +property of the controller. Because of this feature, handling XML and +POST data in parallel is seamless: no changes are required to the +controller or model code. Everything you need should end up in +$this->data. + +Custom REST Routing +=================== + +If the default routes created by mapResources() don't work for you, use +the Router::connect() method to define a custom set of REST routes. The +connect() method allows you to define a number of different options for +a given URL. The first parameter is the URL itself, and the second +parameter allows you to supply those options. The third parameter allows +you to specify regex patterns to help CakePHP identify certain markers +in the specified URL. + +We'll provide a simple example here, and allow you to tailor this route +for your other RESTful purposes. Here's what our edit REST route would +look like, without using mapResources(): + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9]+") + ) + +Advanced routing techniques are covered elsewhere, so we'll focus on the +most important point for our purposes here: the [method] key of the +options array in the second parameter. Once that key has been set, the +specified route works only for that HTTP request method (which could +also be GET, DELETE, etc.) diff --git a/en/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/en/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..c4f964f18111051d51ee0d441ad4bcd72ef31f27 --- /dev/null +++ b/en/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,1069 @@ +Testing +####### + +As of CakePHP 1.2 there is support for a comprehensive testing framework +built into CakePHP. The framework is an extension of the SimpleTest +framework for PHP. This section will discuss how to prepare for testing +and how to build and run your tests. + +Preparing for testing +===================== + +Ready to start testing? Good! Lets get going then! + +Installing SimpleTest +--------------------- + +Installing SimpleTest + +The testing framework provided with CakePHP 1.2 is built upon the +SimpleTest testing framework. SimpleTest is not shipped with the default +CakePHP installation, so we need to download it first. You can find it +here: +`http://simpletest.sourceforge.net/ `_. + +Fetch the latest version, and unzip the code to your vendors folder, or +your app/vendors folder, depending on your preference. You should now +have a vendors/simpletest directory with all SimpleTest files and +folders inside. Remember to have a DEBUG level of at least 1 in your +app/config/core.php file before running any tests! + +If you have no test database connection defined in your +app/config/database.php, test tables will be created with a +``test_suite_`` prefix. You can create a ``$test`` database connection +to contain any test tables like the one below: + +:: + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'dbhost', + 'login' => 'dblogin', + 'password' => 'dbpassword', + 'database' => 'databaseName' + ); + +If the test database is available and CakePHP can connect to it, all +tables will be created in this database. + +Running Core test cases +----------------------- + +The release packages of CakePHP 1.2 do not ship with the core test +cases. In order to get these tests, you need to download from the +repository. All versions of CakePHP are currently located at the website +`http://code.cakephp.org/ `_. You will need to +create a user account with personal key, and use Git to access the +repository. + +To add the core tests to your existing application, uncompress the +downloaded nightly package into a temporary directory. Locate the +``/cake/tests`` directory from the repository and copy it (recursively) +into your ``/cake/tests`` folder. + +The tests can then be accessed by browsing to +http://your.cake.domain/test.php - depending on how your specific setup +looks. Try executing one of the core test groups by clicking on the +corresponding link. Executing a test group might take a while, but you +should eventually see something like "2/2 test cases complete: 49 +passes, 0 fails and 0 exceptions.". + +Congratulations, you are now ready to start writing tests! + +If you run all of the core tests at once or run core test groups most of +them will fail. This is known by the CakePHP developers and is normal so +don't panic. Instead, try running each of the core test cases +individually. + +Testing overview - Unit testing vs. Web testing +=============================================== + +The CakePHP test framework supports two types of testing. One is Unit +Testing, where you test small parts of your code, such as a method in a +component or an action in a controller. The other type of testing +supported is Web Testing, where you automate the work of testing your +application through navigating pages, filling forms, clicking links and +so on. + +Preparing test data +=================== + +About fixtures +-------------- + +When testing code that depends on models and data, one can use +**fixtures** as a way to generate temporary data tables loaded with +sample data that can be used by the test. The benefit of using fixtures +is that your test has no chance of disrupting live application data. In +addition, you can begin testing your code prior to actually developing +live content for an application. + +CakePHP attempts to use the connection named ``$test`` in your +app/config/database.php configuration file. If this connection is not +usable, it will use the ``$default`` database configuration and create +the test tables in the database defined in that configuration. In either +case, it will add "test\_suite\_" to your own table prefix (if any) to +prevent collision with your existing tables. + +CakePHP performs the following during the course of a fixture based test +case: + +#. Creates tables for each of the fixtures needed +#. Populates tables with data, if data is provided in fixture +#. Runs test methods +#. Empties the fixture tables +#. Removes fixture tables from database + +Creating fixtures +----------------- + +When creating a fixture you will mainly define two things: how the table +is created (which fields are part of the table), and which records will +be initially populated to the test table. Let's then create our first +fixture, that will be used to test our own Article model. Create a file +named **article\_fixture.php** in your **app/tests/fixtures** directory, +with the following content: + +If you are testing a plugin, see the section :doc:`/The-Manual/Common-Tasks-With-CakePHP/Testing`. + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'title' => array('type' => 'string', 'length' => 255, 'null' => false), + 'body' => 'text', + 'published' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +The ``$name`` variable is extremely significant. If you omit it, cake +will use the wrong table names when it sets up your test database, and +you'll get strange errors that are difficult to debug. If you use PHP +5.2, you might be used to writing model classes without ``$name``, but +you must remember to include it in your fixture files. You can also +specify the table name to be created by including a ``$table`` variable +in the fixture. + +We use $fields to specify which fields will be part of this table, on +how they are defined. The format used to define these fields is the same +used in the function **generateColumnSchema()** defined on Cake's +database engine classes (for example, on file dbo\_mysql.php.) Let's see +the available attributes a field can take and their meaning: + +type + CakePHP internal data type. Currently supported: string (maps to + VARCHAR), text (maps to TEXT), integer (maps to INT), float (maps to + FLOAT), datetime (maps to DATETIME), timestamp (maps to TIMESTAMP), + time (maps to TIME), date (maps to DATE), and binary (maps to BLOB) +key + set to primary to make the field AUTO\_INCREMENT, and a PRIMARY KEY + for the table. +length + set to the specific length the field should take. +null + set to either true (to allow NULLs) or false (to disallow NULLs) +default + default value the field takes. + +We lastly can set a set of records that will be populated after the test +table is created. The format is fairly straight forward and needs little +further explanation. Just keep in mind that each record in the $records +array must have a key for **every** field specified in the $fields +array. If a field for a particular record needs to have a NULL value, +just specify the value of that key as NULL. + +Importing table information and records +--------------------------------------- + +Your application may have already working models with real data +associated to them, and you might decide to test your model with that +data. It would be then a duplicate effort to have to define the table +definition and/or records on your fixtures. Fortunately, there's a way +for you to define that table definition and/or records for a particular +fixture come from an existing model or an existing table. + Let's start with an example. Assuming you have a model named Article +available in your application (that maps to a table named articles), +change the example fixture given in the previous section +(**app/tests/fixtures/article\_fixture.php**) to: + +:: + + + + +This statement tells the test suite to import your table definition from +the table linked to the model called Article. You can use any model +available in your application. The statement above does not import +records, you can do so by changing it to: + +:: + + 'Article', 'records' => true); + } + ?> + +If on the other hand you have a table created but no model available for +it, you can specify that your import will take place by reading that +table information instead. For example: + +:: + + 'articles'); + } + ?> + +Will import table definition from a table called 'articles' using your +CakePHP database connection named 'default'. If you want to change the +connection to use just do: + +:: + + 'articles', 'connection' => 'other'); + } + ?> + +Since it uses your CakePHP database connection, if there's any table +prefix declared it will be automatically used when fetching table +information. The two snippets above do not import records from the +table. To force the fixture to also import its records, change it to: + +:: + + 'articles', 'records' => true); + } + ?> + +You can naturally import your table definition from an existing +model/table, but have your records defined directly on the fixture as it +was shown on previous section. For example: + +:: + + 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Creating tests +============== + +First, lets go through a number of rules, or guidelines, concerning +tests: + +#. PHP files containing tests should be in your + **app/tests/cases/[some\_folder]**. +#. The filenames of these files should end in **.test.php** instead of + just .php. +#. The classes containing tests should extend **CakeTestCase** or + **CakeWebTestCase**. +#. The name of any method containing a test (i.e. containing an + assertion) should begin with **test**, as in **testPublished()**. + +When you have created a test case, you can execute it by browsing to +**http://your.cake.domain/cake\_folder/test.php** (depending on how your +specific setup looks) and clicking App test cases, and then click the +link to your specific file. + +CakeTestCase Callback Methods +----------------------------- + +If you want to sneak in some logic just before or after an individual +CakeTestCase method, and/or before or after your entire CakeTestCase, +the following callbacks are available: + +**start()** + First method called in a *test case*. + +**end()** + Last method called in a *test case*. + +**startCase()** + called before a *test case* is started. + +**endCase()** + called after a *test case* has run. + +**before($method)** + Announces the start of a *test method*. + +**after($method)** + Announces the end of a *test method*. + +**startTest($method)** + Called just before a *test method* is executed. + +**endTest($method)** + Called just after a *test method* has completed. + +Testing models +============== + +Creating a test case +-------------------- + +Let's say we already have our Article model defined on +app/models/article.php, which looks like this: + +:: + + name . '.published' => 1 + ); + + return $this->findAll($conditions, $fields); + } + + } + ?> + +We now want to set up a test that will use this model definition, but +through fixtures, to test some functionality in the model. CakePHP test +suite loads a very minimum set of files (to keep tests isolated), so we +have to start by loading our parent model (in this case the Article +model which we already defined), and then inform the test suite that we +want to test this model by specifying which DB configuration it should +use. CakePHP test suite enables a DB configuration named **test** that +is used for all models that rely on fixtures. Setting $useDbConfig to +this configuration will let CakePHP know that this model uses the test +suite database connection. + +CakePHP Models will only use the test DB config if they rely on fixtures +in your testcase! + + Since we also want to reuse all our existing model code we will create +a test model that will extend from Article, set $useDbConfig and $name +appropiately. Let's now create a file named **article.test.php** in your +**app/tests/cases/models** directory, with the following contents: + +:: + + + +We have created the ArticleTestCase. In variable **$fixtures** we define +the set of fixtures that we'll use. + +If your model is associated with other models, you will need to include +ALL the fixtures for each associated model even if you don't use them. +For example: A hasMany B hasMany C hasMany D. In ATestCase you will have +to include fixtures for a, b, c and d. + +Creating a test method +---------------------- + +Let's now add a method to test the function published() in the Article +model. Edit the file **app/tests/cases/models/article.test.php** so it +now looks like this: + +:: + + Article =& ClassRegistry::init('Article'); + + $result = $this->Article->published(array('id', 'title')); + $expected = array( + array('Article' => array( 'id' => 1, 'title' => 'First Article' )), + array('Article' => array( 'id' => 2, 'title' => 'Second Article' )), + array('Article' => array( 'id' => 3, 'title' => 'Third Article' )) + ); + + $this->assertEqual($result, $expected); + } + } + ?> + + You can see we have added a method called **testPublished()**. We start +by creating an instance of our fixture based **Article** model, and then +run our **published()** method. In **$expected** we set what we expect +should be the proper result (that we know since we have defined which +records are initally populated to the article table.) We test that the +result equals our expectation by using the **assertEqual** method. See +the section Creating Tests for information on how to run the test. + +Testing controllers +=================== + +Creating a test case +-------------------- + +Say you have a typical articles controller, with its corresponding +model, and it looks like this: + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $result = $this->Article->findAll(null, array('id', + 'title')); + } else { + $result = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $result; + } + + $this->set('title', 'Articles'); + $this->set('articles', $result); + } + } + ?> + +Create a file named articles\_controller.test.php in your +app/tests/cases/controllers directory and put the following inside: + +:: + + Starting Test Case'; + } + function endCase() { + echo '

Ending Test Case

'; + } + function startTest($method) { + echo '

Starting method ' . $method . '

'; + } + function endTest($method) { + echo '
'; + } + function testIndex() { + $result = $this->testAction('/articles/index'); + debug($result); + } + function testIndexShort() { + $result = $this->testAction('/articles/index/short'); + debug($result); + } + function testIndexShortGetRenderedHtml() { + $result = $this->testAction('/articles/index/short', + array('return' => 'render')); + debug(htmlentities($result)); + } + function testIndexShortGetViewVars() { + $result = $this->testAction('/articles/index/short', + array('return' => 'vars')); + debug($result); + } + function testIndexFixturized() { + $result = $this->testAction('/articles/index/short', + array('fixturize' => true)); + debug($result); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'published' + => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body')); + $result = $this->testAction('/articles/index', + array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($result); + } + } + ?> + +The testAction method +--------------------- + +The new thing here is the **testAction** method. The first argument of +that method is the Cake url of the controller action to be tested, as in +'/articles/index/short'. + +The second argument is an array of parameters, consisting of: + +return + Set to what you want returned. + Valid values are: + + - 'vars' - You get the view vars available after executing action + - 'view' - You get The rendered view, without the layout + - 'contents' - You get the rendered view's complete html, including + the layout + - 'result' - You get the returned value when action uses + $this->params['requested']. + + The default is 'result'. +fixturize + Set to true if you want your models auto-fixturized (so your + application tables get copied, along with their records, to test + tables so if you change data it does not affect your real + application.) If you set 'fixturize' to an array of models, then + only those models will be auto-fixturized while the other will + remain with live tables. If you wish to use your fixture files with + testAction() do not use fixturize, and instead just use fixtures as + you normally would. +method + set to 'post' or 'get' if you want to pass data to the controller +data + the data to be passed. Set it to be an associative array consisting + of fields => value. Take a look at + ``function testIndexPostFixturized()`` in above test case to see how + we emulate posting form data for a new article submission. + +Pitfalls +-------- + +If you use testAction to test a method in a controller that does a +redirect, your test will terminate immediately, not yielding any +results. + See +`https://trac.cakephp.org/ticket/4154 `_ +for a possible fix. + +For an in-depth explanation of controller testing please see this blog +post by Mark Story `Testing CakePHP Controllers the hard +way `_. + +Testing Helpers +=============== + +Since a decent amount of logic resides in Helper classes, it's important +to make sure those classes are covered by test cases. + +Helper testing is a bit similar to the same approach for Components. +Suppose we have a helper called CurrencyRendererHelper located in +``app/views/helpers/currency_renderer.php`` with its accompanying test +case file located in +``app/tests/cases/helpers/currency_renderer.test.php`` + +Creating Helper test, part I +---------------------------- + +First of all we will define the responsibilities of our +CurrencyRendererHelper. Basically, it will have two methods just for +demonstration purpose: + +function usd($amount) + +This function will receive the amount to render. It will take 2 decimal +digits filling empty space with zeros and prefix 'USD'. + +function euro($amount) + +This function will do the same as usd() but prefix the output with +'EUR'. Just to make it a bit more complex, we will also wrap the result +in span tags: + +:: + + + +Let's create the tests first: + +:: + + currencyRenderer = new CurrencyRendererHelper(); + } + + //testing usd() function. + public function testUsd() { + $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); + //We should always have 2 decimal digits. + $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); + $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); + //Testing the thousands separator + $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70)); + } + } + +Here, we call ``usd()`` with different parameters and tell the test +suite to check if the returned values are equal to what is expected. + +Executing the test now will result in errors (because +currencyRendererHelper doesn't even exist yet) showing that we have 3 +fails. + +Once we know what our method should do, we can write the method itself: + +:: + + Transporter = $controller->Transporter; + } + +then we can just design a really simple fake class: + +:: + + class FakeTransporterController {} + +and assign values into it like this: + +:: + + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + +Creating a test method +---------------------- + +Just create a class that extends CakeTestCase and start writing tests! + +:: + + class TransporterTestCase extends CakeTestCase { + var $fixtures = array('transporter'); + function testGetTransporter() { + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); + $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); + + $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); + $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); + + $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); + $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); + $this->assertEqual($result, 0, "Noone can service Norway"); + } + } + + +Web testing - Testing views +=========================== + +Most, if not all, CakePHP projects result in a web application. While +unit tests are an excellent way to test small parts of functionality, +you might also want to test the functionality on a large scale. The +**CakeWebTestCase** class provides a good way of doing this testing from +a user point-of-view. + +About CakeWebTestCase +--------------------- + +**CakeWebTestCase** is a direct extension of the SimpleTest WebTestCase, +without any extra functionality. All the functionality found in the +`SimpleTest documentation for Web +testing `_ +is also available here. This also means that no functionality other than +that of SimpleTest is available. This means that you cannot use +fixtures, and **all web test cases involving updating/saving to the +database will permanently change your database values**. Test results +are often based on what values the database holds, so making sure the +database contains the values you expect is part of the testing +procedure. + +Creating a test +--------------- + +In keeping with the other testing conventions, you should create your +view tests in tests/cases/views. You can, of course, put those tests +anywhere but following the conventions whenever possible is always a +good idea. So let's create the file +tests/cases/views/complete\_web.test.php + +First, when you want to write web tests, you must remember to extend +**CakeWebTestCase** instead of CakeTestCase: + +:: + + class CompleteWebTestCase extends CakeWebTestCase + +If you need to do some preparation before you start the test, create a +constructor: + +:: + + function CompleteWebTestCase(){ + //Do stuff here + } + +When writing the actual test cases, the first thing you need to do is +get some output to look at. This can be done by doing a **get** or +**post** request, using **get()**\ or **post()** respectively. Both +these methods take a full url as the first parameter. This can be +dynamically fetched if we assume that the test script is located under +http://your.domain/cake/folder/webroot/test.php by typing: + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +You can then do gets and posts using Cake urls, like this: + +:: + + $this->get($this->baseurl."/products/index/"); + $this->post($this->baseurl."/customers/login", $data); + +The second parameter to the post method, **$data**, is an associative +array containing the post data in Cake format: + +:: + + $data = array( + "data[Customer][mail]" => "user@user.com", + "data[Customer][password]" => "userpass"); + +When you have requested the page you can do all sorts of asserts on it, +using standard SimpleTest web test methods. + +Walking through a page +---------------------- + +CakeWebTest also gives you an option to navigate through your page by +clicking links or images, filling forms and clicking buttons. Please +refer to the SimpleTest documentation for more information on that. + +Testing plugins +=============== + +Tests for plugins are created in their own directory inside the plugins +folder. + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +They work just like normal tests but you have to remember to use the +naming conventions for plugins when importing classes. This is an +example of a testcase for the PizzaOrder model from the plugins chapter +of this manual. A difference from other tests is in the first line where +'Pizza.PizzaOrder' is imported. You also need to prefix your plugin +fixtures with '``plugin.plugin_name.``\ '. + +:: + + PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); + + // do some useful test here + $this->assertTrue(is_object($this->PizzaOrderTest)); + } + } + ?> + +If you want to use plugin fixtures in the app tests you can reference +them using 'plugin.pluginName.fixtureName' syntax in the $fixtures +array. + +That is all there is to it. + +Miscellaneous +============= + +Customizing the test reporter +----------------------------- + +The standard test reporter is **very** minimalistic. If you want more +shiny output to impress someone, fear not, it is actually very easy to +extend. + The only danger is that you have to fiddle with core Cake code, +specifically **/cake/tests/libs/cake\_reporter.php**. + +To change the test output you can override the following methods: + +paintHeader() + Prints before the test is started. +paintPass() + Prints everytime a test case has passed. Use $this->getTestList() to + get an array of information pertaining to the test, and $message to + get the test result. Remember to call parent::paintPass($message). +paintFail() + Prints everytime a test case has failed. Remember to call + parent::paintFail($message). +paintFooter() + Prints when the test is over, i.e. when all test cases has been + executed. + +If, when running paintPass and paintFail, you want to hide the parent +output, enclose the call in html comment tags, as in: + +:: + + echo "\n\n"; + +A sample **cake\_reporter.php**\ setup that creates a table to hold the +test results follows: + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
Res.Test caseMessage
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tPass: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\tFail: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +Grouping tests +-------------- + +If you want several of your test to run at the same time, you can try +creating a test group. Create a file in **/app/tests/groups/** and name +it something like **your\_test\_group\_name.group.php**. In this file, +extend **GroupTest** and import test as follows: + +:: + + + +The code above will group all test cases found in the +**/app/tests/cases/models/** folder. To add an individual file, use +**TestManager::addTestFile**\ ($this, filename). + +Running tests in the Command Line +================================= + +If you have simpletest installed you can run your tests from the command +line of your application. + +from **app/** + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +As the help menu suggests, you'll be able to run all, part, or just a +single test case from your app, plugin, or core, right from the command +line. + +If you have a model test of **test/models/my\_model.test.php** you'd run +just that test case by running: + +:: + + cake testsuite app case models/my_model + diff --git a/en/The-Manual/Core-Behaviors.rst b/en/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..4f0f56fd48ac365401b39a4a057cb92d7253e7d3 --- /dev/null +++ b/en/The-Manual/Core-Behaviors.rst @@ -0,0 +1,14 @@ +Core Behaviors +############## + +Behaviors add extra functionality to your models. CakePHP comes with a +number of built-in behaviors such as Tree and Containable. + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/en/The-Manual/Core-Behaviors/ACL.rst b/en/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..6f3ce649e38ee3261b9f5f9a1e85a892c68fe06e --- /dev/null +++ b/en/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,103 @@ +ACL +### + +The Acl behavior provides a way to seamlessly integrate a model with +your ACL system. It can create both AROs or ACOs transparently. + +To use the new behavior, you can add it to the $actsAs property of your +model. When adding it to the actsAs array you choose to make the related +Acl entry an ARO or an ACO. The default is to create AROs. + +:: + + class User extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +This would attach the Acl behavior in ARO mode. To join the ACL behavior +in ACO mode use: + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +You can also attach the behavior on the fly like so: + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +Using the AclBehavior +===================== + +Most of the AclBehavior works transparently on your Model's afterSave(). +However, using it requires that your Model has a parentNode() method +defined. This is used by the AclBehavior to determine parent->child +relationships. A model's parentNode() method must return null or return +a parent Model reference. + +:: + + function parentNode() { + return null; + } + +If you want to set an ACO or ARO node as the parent for your Model, +parentNode() must return the alias of the ACO or ARO node. + +:: + + function parentNode() { + return 'root_node'; + } + +A more complete example. Using an example User Model, where User +belongsTo Group. + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['User']['group_id']) { + return null; + } else { + $this->Group->id = $data['User']['group_id']; + $groupNode = $this->Group->node(); + return array('Group' => array('id' => $groupNode[0]['Aro']['foreign_key'])); + } + } + +In the above example the return is an array that looks similar to the +results of a model find. It is important to have the id value set or the +parentNode relation will fail. The AclBehavior uses this data to +construct its tree structure. + +node() +====== + +The AclBehavior also allows you to retrieve the Acl node associated with +a model record. After setting $model->id. You can use $model->node() to +retrieve the associated Acl node. + +You can also retrieve the Acl Node for any row, by passing in a data +array. + +:: + + $this->User->id = 1; + $node = $this->User->node(); + + $user = array('User' => array( + 'id' => 1 + )); + $node = $this->User->node($user); + +Will both return the same Acl Node information. diff --git a/en/The-Manual/Core-Behaviors/Containable.rst b/en/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..3d956cb58a5134776b5bd7270032826d4cbb8c7c --- /dev/null +++ b/en/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,374 @@ +Containable +########### + +A new addition to the CakePHP 1.2 core is the ``ContainableBehavior``. +This model behavior allows you to filter and limit model find +operations. Using Containable will help you cut down on needless wear +and tear on your database, increasing the speed and overall performance +of your application. The class will also help you search and filter your +data for your users in a clean and consistent way. + +Containable allows you to streamline and simplify operations on your +model bindings. It works by temporarily or permanently altering the +associations of your models. It does this by using the supplied +containments to generate a series of ``bindModel`` and ``unbindModel`` +calls. + +To use the new behavior, you can add it to the $actsAs property of your +model: + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +You can also attach the behavior on the fly: + +:: + + $this->Post->Behaviors->attach('Containable'); + +Using Containable +================= + +To see how Containable works, let's look at a few examples. First, we'll +start off with a find() call on a model named Post. Let's say that Post +hasMany Comment, and Post hasAndBelongsToMany Tag. The amount of data +fetched in a normal find() call is rather extensive: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => Awesome + ) + [1] => Array + ( + [id] => 2 + [name] => Baking + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +For some interfaces in your application, you may not need that much +information from the Post model. One thing the ``ContainableBehavior`` +does is help you cut down on what find() returns. + +For example, to get only the post-related information, you can do the +following: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +You can also invoke Containable's magic from inside the find() call: + +:: + + $this->Post->find('all', array('contain' => false)); + +Having done that, you end up with something a lot more concise: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +This sort of help isn't new: in fact, you can do that without the +``ContainableBehavior`` doing something like this: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable really shines when you have complex associations, and you +want to pare down things that sit at the same level. The model's +``$recursive`` property is helpful if you want to hack off an entire +level of recursion, but not when you want to pick and choose what to +keep at each level. Let's see how it works by using the ``contain()`` +method. + +The contain method's first argument accepts the name, or an array of +names, of the models to keep in the find operation. If we wanted to +fetch all posts and their related tags (without any comment +information), we'd try something like this: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Again, we can use the contain key inside a find() call: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Without Containable, you'd end up needing to use the ``unbindModel()`` +method of the model, multiple times if you're paring off multiple +models. Containable creates a cleaner way to accomplish this same task. + +Containing deeper associations +============================== + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors — and +nothing else — you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the ``ContainableBehavior`` when you've got +deep and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that ``contain`` key is only used once in the main model, +you don't need to use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must be attached to all models used +in containment, you may consider attaching it to your AppModel. + +Using Containable with pagination +================================= + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +By including the 'contain' parameter in the ``$paginate`` property it +will apply to both the find('count') and the find('all') done on the +model + +ContainableBehavior options +=========================== + +The ``ContainableBehavior`` has a number of options that can be set when +the Behavior is attached to a model. The settings allow you to fine tune +the behavior of Containable and work with other behaviors more easily. + +- **recursive** (boolean, optional) set to true to allow containable to + automatically determine the recursiveness level needed to fetch + specified models, and set the model recursiveness to this level. + setting it to false disables this feature. The default value is + ``true``. +- **notices** (boolean, optional) issues E\_NOTICES for bindings + referenced in a containable call that are not valid. The default + value is ``true``. +- **autoFields**: (boolean, optional) auto-add needed fields to fetch + requested bindings. The default value is ``true``. + +You can change ContainableBehavior settings at run time by reattaching +the behavior as seen in :doc:`/The-Manual/Developing-with-CakePHP/Behaviors` + +ContainableBehavior can sometimes cause issues with other behaviors or +queries that use aggregate functions and/or GROUP BY statements. If you +get invalid SQL errors due to mixing of aggregate and non-aggregate +fields, try disabling the ``autoFields`` setting. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/en/The-Manual/Core-Behaviors/Translate.rst b/en/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..3aa96f9ca5bc359f50b50054a1a16decf9b3fb30 --- /dev/null +++ b/en/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,374 @@ +Translate +######### + +TranslateBehavior is actually quite easy to setup and works out of the +box with very little configuration. In this section, you will learn how +to add and setup the behavior to use in any model. + +If you are using TranslateBehavior in alongside containable issue, be +sure to set the 'fields' key for your queries. Otherwise you could end +up with invalid SQL generated. + +Initializing the i18n Database Tables +===================================== + +You can either use the CakePHP console or you can manually create it. It +is advised to use the console for this, because it might happen that the +layout changes in future versions of CakePHP. Sticking to the console +will make sure that you have the correct layout. + +:: + + ./cake i18n + +Select ``[I]`` which will run the i18n database intialization script. +You will be asked if you want to drop any existing and if you want to +create it. Answer with yes if you are sure there is no i18n table +already, and answer with yes again to create the table. + +Attaching the Translate Behavior to your Models +=============================================== + +Add it to your model by using the ``$actsAs`` property like in the +following example. + +:: + + + +This will do nothing yet, because it expects a couple of options before +it begins to work. You need to define which fields of the current model +should be tracked in the translation table we've created in the first +step. + +Defining the Fields +=================== + +You can set the fields by simply extending the ``'Translate'`` value +with another array, like so: + +:: + + array( + 'fieldOne', 'fieldTwo', 'and_so_on' + ) + ); + } + ?> + +After you have done that (for example putting "name" as one of the +fields) you already finished the basic setup. Great! According to our +current example the model should now look something like this: + +:: + + array( + 'name' + ) + ); + } + ?> + +When defining fields for TranslateBehavior to translate, be sure to omit +those fields from the translated model's schema. If you leave the fields +in, there can be issues when retrieving data with fallback locales. + +Conclusion +========== + +From now on each record update/creation will cause TranslateBehavior to +copy the value of "name" to the translation table (default: i18n) along +with the current locale. A locale is the identifier of the language, so +to speak. + +The *current locale* is the current value of +``Configure::read('Config.language')``. The value of *Config.language* +is assigned in the L10n Class - unless it is already set. However, the +TranlateBehavior allows you to override this on-the-fly, which allows +the user of your page to create multiple versions without the need to +change his preferences. More about this in the next section. + +Retrieve all translation records for a field +============================================ + +If you want to have all translation records attached to the current +model record you simply extend the *field array* in your behavior setup +as shown below. The naming is completely up to you. + +:: + + array( + 'name' => 'nameTranslation' + ) + ); + } + ?> + +With this setup the result of $this->Post->find() should look something +like this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +**Note**: The model record contains a *virtual* field called "locale". +It indicates which locale is used in this result. + +Note that only fields of the model you are directly doing \`find\` on +will be translated. Models attached via associations won't be translated +because triggering callbacks on associated models is currently not +supported. + +Using the bindTranslation method +-------------------------------- + +You can also retrieve all translations, only when you need them, using +the bindTranslation method + +``bindTranslation($fields, $reset)`` + +``$fields`` is a named-key array of field and association name, where +the key is the translatable field and the value is the fake association +name. + +:: + + $this->Post->bindTranslation(array ('name' => 'nameTranslation')); + $this->Post->find('all', array ('recursive'=>1)); // need at least recursive 1 for this to work. + +With this setup the result of your find() should look something like +this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +Saving in another language +========================== + +You can force the model which is using the TranslateBehavior to save in +a language other than the one detected. + +To tell a model in what language the content is going to be you simply +change the value of the ``$locale`` property on the model before you +save the data to the database. You can do that either in your controller +or you can define it directly in the model. + +**Example A:** In your controller + +:: + + data) { + $this->Post->locale = 'de_de'; // we are going to save the german version + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**Example B:** In your model + +:: + + array( + 'name' + ) + ); + + // Option 1) just define the property directly + var $locale = 'en_us'; + + // Option 2) create a simple method + function setLanguage($locale) { + $this->locale = $locale; + } + } + ?> + +Multiple Translation Tables +=========================== + +If you expect a lot of entries you probably wonder how to deal with a +rapidly growing database table. There are two properties introduced by +TranslateBehavior that allow you to specify which "Model" to bind as the +model containing the translations. + +These are **$translateModel** and **$translateTable**. + +Lets say we want to save our translations for all posts in the table +"post\_i18ns" instead of the default "i18n" table. To do so you need to +setup your model like this: + +:: + + array( + 'name' + ) + ); + + // Use a different model (and table) + var $translateModel = 'PostI18n'; + } + ?> + +**Important** to note is that you have to pluralize the table. It is now +a usual model and can be treated as such and thus comes with the +conventions involved. The table schema itself must be identical with the +one generated by the CakePHP console script. To make sure it fits one +could just initialize an empty i18n table using the console and rename +the table afterwards. + +Create the TranslateModel +------------------------- + +For this to work you need to create the actual model file in your models +folder. The reason is that there is no property to set the displayField +directly in the model using this behavior yet. + +Make sure that you change the ``$displayField`` to ``'field'``. + +:: + + + +That's all it takes. You can also add all other model stuff here like +$useTable. But for better consistency we could do that in the model +which actually uses this translation model. This is where the optional +``$translateTable`` comes into play. + +Changing the Table +------------------ + +If you want to change the name of the table you simply define +$translateTable in your model, like so: + +:: + + array( + 'name' + ) + ); + + // Use a different model + var $translateModel = 'PostI18n'; + + // Use a different table for translateModel + var $translateTable = 'post_translations'; + } + ?> + +Please note that **you can't use $translateTable alone**. If you don't +intend to use a custom ``$translateModel`` then leave this property +untouched. Reason is that it would break your setup and show you a +"Missing Table" message for the default I18n model which is created in +runtime. diff --git a/en/The-Manual/Core-Behaviors/Tree.rst b/en/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..0d50649b0301c31cacfc3069609d90cb6b4bcc23 --- /dev/null +++ b/en/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,623 @@ +Tree +#### + +It's fairly common to want to store hierarchical data in a database +table. Examples of such data might be categories with unlimited +subcategories, data related to a multilevel menu system or a literal +representation of hierarchy such as is used to store access control +objects with ACL logic. + +For small trees of data, or where the data is only a few levels deep it +is simple to add a parent\_id field to your database table and use this +to keep track of which item is the parent of what. Bundled with cake +however, is a powerful behavior which allows you to use the benefits of +`MPTT +logic `_ +without worrying about any of the intricacies of the technique - unless +you want to ;). + +Requirements +============ + +To use the tree behavior, your database table needs 3 fields as listed +below (all are ints): + +- parent - default fieldname is parent\_id, to store the id of the + parent object +- left - default fieldname is lft, to store the left value of the + current row. +- right - default fieldname is rght, to store the right value of the + current row. + +If you are familiar with MPTT logic you may wonder why a parent field +exists - quite simply it's easier to do certain tasks if a direct parent +link is stored on the database - such as finding direct children. + +Basic Usage +=========== + +The tree behavior has a lot packed into it, but let's start with a +simple example - create the following database table and put some data +in it: + +:: + + CREATE TABLE categories ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + name VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(1, 'My Categories', NULL, 1, 30); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(2, 'Fun', 1, 2, 15); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(3, 'Sport', 2, 3, 8); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(4, 'Surfing', 3, 4, 5); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(5, 'Extreme knitting', 3, 6, 7); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(6, 'Friends', 2, 9, 14); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(7, 'Gerald', 6, 10, 11); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(8, 'Gwendolyn', 6, 12, 13); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(9, 'Work', 1, 16, 29); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(10, 'Reports', 9, 17, 22); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(11, 'Annual', 10, 18, 19); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(12, 'Status', 10, 20, 21); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(13, 'Trips', 9, 23, 28); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(14, 'National', 13, 24, 25); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(15, 'International', 13, 26, 27); + +For the purpose of checking that everything is setup correctly, we can +create a test method and output the contents of our category tree to see +what it looks like. With a simple controller: + +:: + + data = $this->Category->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +and an even simpler model definition: + +:: + + + +We can check what our category tree data looks like by visiting +/categories You should see something like this: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +Adding data +----------- + +In the previous section, we used existing data and checked that it +looked hierarchal via the method ``generatetreelist``. However, usually +you would add your data in exactly the same way as you would for any +model. For example: + +:: + + // pseudo controller code + $data['Category']['parent_id'] = 3; + $data['Category']['name'] = 'Skating'; + $this->Category->save($data); + +When using the tree behavior its not necessary to do any more than set +the parent\_id, and the tree behavior will take care of the rest. If you +don't set the parent\_id, the tree behavior will add to the tree making +your new addition a new top level entry: + +:: + + // pseudo controller code + $data = array(); + $data['Category']['name'] = 'Other People\'s Categories'; + $this->Category->save($data); + +Running the above two code snippets would alter your tree as follows: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating **New** + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories **New** + +Modifying data +-------------- + +Modifying data is as transparent as adding new data. If you modify +something, but do not change the parent\_id field - the structure of +your data will remain unchanged. For example: + +:: + + // pseudo controller code + $this->Category->id = 5; // id of Extreme knitting + $this->Category->save(array('name' =>'Extreme fishing')); + +The above code did not affect the parent\_id field - even if the +parent\_id is included in the data that is passed to save if the value +doesn't change, neither does the data structure. Therefore the tree of +data would now look like: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme fishing **Updated** + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + +Moving data around in your tree is also a simple affair. Let's say that +Extreme fishing does not belong under Sport, but instead should be +located under Other People's Categories. With the following code: + +:: + + // pseudo controller code + $this->Category->id = 5; // id of Extreme fishing + $newParentId = $this->Category->field('id', array('name' => 'Other People\'s Categories')); + $this->Category->save(array('parent_id' => $newParentId)); + +As would be expected the structure would be modified to: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + + - Extreme fishing **Moved** + +Deleting data +------------- + +The tree behavior provides a number of ways to manage deleting data. To +start with the simplest example; let's say that the reports category is +no longer useful. To remove it *and any children it may have* just call +delete as you would for any model. For example with the following code: + +:: + + // pseudo controller code + $this->Category->id = 10; + $this->Category->delete(); + +The category tree would be modified as follows: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Trips + + - National + - International + +- Other People's Categories + + - Extreme fishing + +Querying and using your data +---------------------------- + +Using and manipulating hierarchical data can be a tricky business. In +addition to the core find methods, with the tree behavior there are a +few more tree-orientated permutations at your disposal. + +Most tree behavior methods return and rely on data being sorted by the +``lft`` field. If you call ``find()`` and do not order by ``lft``, or +call a tree behavior method and pass a sort order, you may get +undesirable results. + +Children +~~~~~~~~ + +The ``children`` method takes the primary key value (the id) of a row +and returns the children, by default in the order they appear in the +tree. The second optional parameter defines whether or not only direct +children should be returned. Using the example data from the previous +section: + +:: + + $allChildren = $this->Category->children(1); // a flat array with 11 items + // -- or -- + $this->Category->id = 1; + $allChildren = $this->Category->children(); // a flat array with 11 items + + // Only return direct children + $directChildren = $this->Category->children(1, true); // a flat array with 2 items + +If you want a recursive array use ``find('threaded')`` + +Counting children +~~~~~~~~~~~~~~~~~ + +As with the method ``children``, ``childCount`` takes the primary key +value (the id) of a row and returns how many children it has. The second +optional parameter defines whether or not only direct children are +counted. Using the example data from the previous section: + +:: + + $totalChildren = $this->Category->childCount(1); // will output 11 + // -- or -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // will output 11 + + // Only counts the direct descendants of this category + $numChildren = $this->Category->childCount(1, true); // will output 2 + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist ($conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)`` + +This method will return data similar to find('list'), with an indented +prefix to show the structure of your data. Below is an example of what +you can expect this method to return see the api for the other find-like +parameters. + +:: + + array( + [1] => "My Categories", + [2] => "_Fun", + [3] => "__Sport", + [4] => "___Surfing", + [16] => "___Skating", + [6] => "__Friends", + [7] => "___Gerald", + [8] => "___Gwendolyn", + [9] => "_Work", + [13] => "__Trips", + [14] => "___National", + [15] => "___International", + [17] => "Other People's Categories", + [5] => "_Extreme fishing" + ) + +getparentnode +~~~~~~~~~~~~~ + +This convenience function will, as the name suggests, return the parent +node for any node, or *false* if the node has no parent (its the root +node). For example: + +:: + + $parent = $this->Category->getparentnode(2); //<- id for fun + // $parent contains All categories + +getpath +~~~~~~~ + +The 'path' when refering to hierachial data is how you get from where +you are to the top. So for example the path from the category +"International" is: + +- My Categories + + - ... + - Work + + - Trips + + - ... + - International + +Using the id of "International" getpath will return each of the parents +in turn (starting from the top). + +:: + + $parents = $this->Category->getpath(15); + +:: + + // contents of $parents + array( + [0] => array('Category' => array('id' => 1, 'name' => 'My Categories', ..)), + [1] => array('Category' => array('id' => 9, 'name' => 'Work', ..)), + [2] => array('Category' => array('id' => 13, 'name' => 'Trips', ..)), + [3] => array('Category' => array('id' => 15, 'name' => 'International', ..)), + ) + +Advanced Usage +============== + +The tree behavior doesn't only work in the background, there are a +number of specific methods defined in the behavior to cater for all your +hierarchical data needs, and any unexpected problems that might arise in +the process. + +moveDown +-------- + +Used to move a single node down the tree. You need to provide the ID of +the element to be moved and a positive number of how many positions the +node should be moved down. All child nodes for the specified node will +also be moved. + +Here is an example of a controller action (in a controller named +Categories) that moves a specified node down the tree: + +:: + + function movedown($name = null, $delta = null) { + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('There is no category named ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Please provide the number of positions the field should be moved down.'); + } + + $this->redirect(array('action' => 'index'), null, true); + } + +For example, if you'd like to move the "Sport" category one position +down, you would request: /categories/movedown/Sport/1. + +moveUp +------ + +Used to move a single node up the tree. You need to provide the ID of +the element to be moved and a positive number of how many positions the +node should be moved up. All child nodes will also be moved. + +Here's an example of a controller action (in a controller named +Categories) that moves a node up the tree: + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('There is no category named ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Please provide a number of positions the category should be moved up.'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +For example, if you would like to move the category "Gwendolyn" up one +position you would request /categories/moveup/Gwendolyn/1. Now the order +of Friends will be Gwendolyn, Gerald. + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Using this method wil either delete or move a node but retain its +sub-tree, which will be reparented one level higher. It offers more +control than ```delete()`:doc:`/The-Manual/Developing-with-CakePHP/Models`, which for a model using +the tree behavior will remove the specified node and all of its +children. + +Taking the following tree as a starting point: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating + +Running the following code with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id); + +The Sport node will be become a top level node: + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +- Sport **Moved** + +This demonstrates the default behavior of ``removeFromTree`` of moving +the node to have no parent, and re-parenting all children. + +If however the following code snippet was used with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id,true); + +The tree would become + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +This demonstrates the alternate use for ``removeFromTree``, the children +have been reparented and 'Sport' has been deleted. + +reorder +------- + +This method can be used to sort hierarchical data. + +Data Integrity +============== + +Due to the nature of complex self referential data structures such as +trees and linked lists, they can occasionally become broken by a +careless call. Take heart, for all is not lost! The Tree Behavior +contains several previously undocumented features designed to recover +from such situations. + +These functions that may save you some time are: + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +The mode parameter is used to specify the source of info that is +valid/correct. The opposite source of data will be populated based upon +that source of info. E.g. if the MPTT fields are corrupt or empty, with +the $mode 'parent' the values of the parent\_id field will be used to +populate the left and right fields. The missingParentAction parameter +only applies to "parent" mode and determines what to do if the parent +field contains an id that is not present. + +reorder(&$model, $options = array()) + +Reorders the nodes (and child nodes) of the tree according to the field +and direction specified in the parameters. This method does not change +the parent of any node. + +The options array contains the values 'id' => null, 'field' => +$model->displayField, 'order' => 'ASC', and 'verify' => true, by +default. + +verify(&$model) + +Returns true if the tree is valid otherwise an array of (type, incorrect +left/right index, message). diff --git a/en/The-Manual/Core-Components.rst b/en/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..3f9c5fb2b0f6124faca42cba910cb33203b89855 --- /dev/null +++ b/en/The-Manual/Core-Components.rst @@ -0,0 +1,58 @@ +Core Components +############### + +CakePHP has a number of built-in components. They provide out of the box +functionality for several commonly used tasks. + +:doc:`/The-Manual/Core-Components/Access-Control-Lists` + +The Acl component provides an easy to use interface for database and ini +based access control lists. + +:doc:`/The-Manual/Core-Components/Authentication` + +The auth component provides an easy to use authentication system using a +variety of authentication processes, such as controller callbacks, Acl, +or Object callbacks. + +:doc:`/The-Manual/Core-Components/Cookies` + +The cookie component behaves in a similar fashion to the +SessionComponent in that it provides a wrapper for PHP's native cookie +support. + +:doc:`/The-Manual/Core-Components/Email` + +An interface that can be used to send emails using one of several mail +transfer agents including php's mail() and smtp. + +:doc:`/The-Manual/Core-Components/Request-Handling` + +The request handler allows you to introspect further into the requests +your visitors and inform your application about the content types and +requested information. + +:doc:`/The-Manual/Core-Components/Security-Component` + +The security component allows you to set tighter security and use and +manage HTTP authentication. + +:doc:`/The-Manual/Core-Components/Sessions` + +The session component provides a storage independent wrapper to PHP's +sessions. + +To learn more about each component see the menu on the left, or learn +more about :doc:`/The-Manual/Developing-with-CakePHP/Components`. + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/en/The-Manual/Core-Components/Access-Control-Lists.rst b/en/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..d95cbabc97821ca79bd1181d43f2e3c4c82e512a --- /dev/null +++ b/en/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,862 @@ +Access Control Lists +#################### + +CakePHP's access control list functionality is one of the most +oft-discussed, most likely because it is the most sought after, but also +because it can be the most confusing. If you're looking for a good way +to get started with ACLs in general, read on. + +Be brave and stick with it, even if the going gets rough. Once you get +the hang of it, it's an extremely powerful tool to have on hand when +developing your application. + +Understanding How ACL Works +=========================== + +Powerful things require access control. Access control lists are a way +to manage application permissions in a fine-grained, yet easily +maintainable and manageable way. + +Access control lists, or ACL, handle two main things: things that want +stuff, and things that are wanted. In ACL lingo, things (most often +users) that want to use stuff are called access request objects, or +AROs. Things in the system that are wanted (most often actions or data) +are called access control objects, or ACOs. The entities are called +'objects' because sometimes the requesting object isn't a person - +sometimes you might want to limit the access certain Cake controllers +have to initiate logic in other parts of your application. ACOs could be +anything you want to control, from a controller action, to a web +service, to a line on your grandma's online diary. + +To review: + +- ACO - Access Control Object - Something that is wanted +- ARO - Access Request Object - Something that wants something + +Essentially, ACL is what is used to decide when an ARO can have access +to an ACO. + +In order to help you understand how everything works together, let's use +a semi-practical example. Imagine, for a moment, a computer system used +by a familiar group of fantasy novel adventurers from the *Lord of the +Rings*. The leader of the group, Gandalf, wants to manage the party's +assets while maintaining a healthy amount of privacy and security for +the other members of the party. The first thing he needs to do is create +a list of the AROs involved: + +- Gandalf +- Aragorn +- Bilbo +- Frodo +- Gollum +- Legolas +- Gimli +- Pippin +- Merry + +Realize that ACL is *not* the same as authentication. ACL is what +happens *after* a user has been authenticated. Although the two are +usually used in concert, it's important to realize the difference +between knowing who someone is (authentication) and knowing what they +can do (ACL). + +The next thing Gandalf needs to do is make an initial list of things, or +ACOs, the system will handle. His list might look something like: + +- Weapons +- The One Ring +- Salted Pork +- Diplomacy +- Ale + +Traditionally, systems were managed using a sort of matrix, that showed +a basic set of users and permissions relating to objects. If this +information were stored in a table, it might look like the following +table: + +Weapons + +The Ring + +Salted Pork + +Diplomacy + +Ale + +Gandalf + +Allow + +Allow + +Allow + +Aragorn + +Allow + +Allow + +Allow + +Allow + +Bilbo + +Allow + +Frodo + +Allow + +Allow + +Gollum + +Allow + +Legolas + +Allow + +Allow + +Allow + +Allow + +Gimli + +Allow + +Allow + +Pippin + +Allow + +Allow + +Merry + +Allow + +At first glance, it seems that this sort of system could work rather +well. Assignments can be made to protect security (only Frodo can access +the ring) and protect against accidents (keeping the hobbits out of the +salted pork and weapons). It seems fine grained enough, and easy enough +to read, right? + +For a small system like this, maybe a matrix setup would work. But for a +growing system, or a system with a large amount of resources (ACOs) and +users (AROs), a table can become unwieldy rather quickly. Imagine trying +to control access to the hundreds of war encampments and trying to +manage them by unit. Another drawback to matrices is that you can't +really logically group sections of users or make cascading permissions +changes to groups of users based on those logical groupings. For +example, it would sure be nice to automatically allow the hobbits access +to the ale and pork once the battle is over: Doing it on an individual +user basis would be tedious and error prone. Making a cascading +permissions change to all 'hobbits' would be easy. + +ACL is most usually implemented in a tree structure. There is usually a +tree of AROs and a tree of ACOs. By organizing your objects in trees, +permissions can still be dealt out in a granular fashion, while still +maintaining a good grip on the big picture. Being the wise leader he is, +Gandalf elects to use ACL in his new system, and organizes his objects +along the following lines: + +- Fellowship of the Ring™ + + - Warriors + + - Aragorn + - Legolas + - Gimli + + - Wizards + + - Gandalf + + - Hobbits + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitors + + - Gollum + +Using a tree structure for AROs allows Gandalf to define permissions +that apply to entire groups of users at once. So, using our ARO tree, +Gandalf can tack on a few group-based permissions: + +- Fellowship of the Ring + (**Deny**: all) + + - Warriors + (**Allow**: Weapons, Ale, Elven Rations, Salted Pork) + + - Aragorn + - Legolas + - Gimli + + - Wizards + (**Allow**: Salted Pork, Diplomacy, Ale) + + - Gandalf + + - Hobbits + (**Allow**: Ale) + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitors + (**Allow**: Salted Pork) + + - Gollum + +If we wanted to use ACL to see if the Pippin was allowed to access the +ale, we'd first get his path in the tree, which is +Fellowship->Hobbits->Pippin. Then we see the different permissions that +reside at each of those points, and use the most specific permission +relating to Pippin and the Ale. + ++--------------------------+-------------------+---------------------------+ +| ARO Node | Permission Info | Result | ++==========================+===================+===========================+ +| Fellowship of the Ring | Deny all | Denying access to ale. | ++--------------------------+-------------------+---------------------------+ +| Hobbits | Allow 'ale' | Allowing access to ale! | ++--------------------------+-------------------+---------------------------+ +| Pippin | -- | Still allowing ale! | ++--------------------------+-------------------+---------------------------+ + +Since the 'Pippin' node in the ACL tree doesn't specifically deny access +to the ale ACO, the final result is that we allow access to that ACO. + +The tree also allows us to make finer adjustments for more granular +control - while still keeping the ability to make sweeping changes to +groups of AROs: + +- Fellowship of the Ring + (**Deny**: all) + + - Warriors + (**Allow**: Weapons, Ale, Elven Rations, Salted Pork) + + - Aragorn + (Allow: Diplomacy) + - Legolas + - Gimli + + - Wizards + (**Allow**: Salted Pork, Diplomacy, Ale) + + - Gandalf + + - Hobbits + (**Allow**: Ale) + + - Frodo + (Allow: Ring) + - Bilbo + - Merry + (Deny: Ale) + - Pippin + (Allow: Diplomacy) + + - Visitors + (**Allow**: Salted Pork) + + - Gollum + +This approach allows us both the ability to make wide-reaching +permissions changes, but also fine-grained adjustments. This allows us +to say that all hobbits can have access to ale, with one +exception—Merry. To see if Merry can access the Ale, we'd find his path +in the tree: Fellowship->Hobbits->Merry and work our way down, keeping +track of ale-related permissions: + ++--------------------------+-------------------+---------------------------+ +| ARO Node | Permission Info | Result | ++==========================+===================+===========================+ +| Fellowship of the Ring | Deny all | Denying access to ale. | ++--------------------------+-------------------+---------------------------+ +| Hobbits | Allow 'ale' | Allowing access to ale! | ++--------------------------+-------------------+---------------------------+ +| Merry | Deny 'ale' | Denying ale. | ++--------------------------+-------------------+---------------------------+ + +Defining Permissions: Cake's INI-based ACL +========================================== + +Cake's first ACL implementation was based on INI files stored in the +Cake installation. While it's useful and stable, we recommend that you +use the database backed ACL solution, mostly because of its ability to +create new ACOs and AROs on the fly. We meant it for usage in simple +applications - and especially for those folks who might not be using a +database for some reason. + +By default, CakePHP's ACL is database-driven. To enable INI-based ACL, +you'll need to tell CakePHP what system you're using by updating the +following lines in app/config/core.php + +:: + + //Change these lines: + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + //To look like this: + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +ARO/ACO permissions are specified in **/app/config/acl.ini.php**. The +basic idea is that AROs are specified in an INI section that has three +properties: groups, allow, and deny. + +- groups: names of ARO groups this ARO is a member of. +- allow: names of ACOs this ARO has access to +- deny: names of ACOs this ARO should be denied access to + +ACOs are specified in INI sections that only include the allow and deny +properties. + +As an example, let's see how the Fellowship ARO structure we've been +crafting would look like in INI syntax: + +:: + + ;------------------------------------- + ; AROs + ;------------------------------------- + [aragorn] + groups = warriors + allow = diplomacy + + [legolas] + groups = warriors + + [gimli] + groups = warriors + + [gandalf] + groups = wizards + + [frodo] + groups = hobbits + allow = ring + + [bilbo] + groups = hobbits + + [merry] + groups = hobbits + deny = ale + + [pippin] + groups = hobbits + + [gollum] + groups = visitors + + ;------------------------------------- + ; ARO Groups + ;------------------------------------- + [warriors] + allow = weapons, ale, salted_pork + + [wizards] + allow = salted_pork, diplomacy, ale + + [hobbits] + allow = ale + + [visitors] + allow = salted_pork + +Now that you've got your permissions defined, you can skip along to :doc:`/The-Manual/Core-Components/Access-Control-Lists` using the ACL +component. + +Defining Permissions: Cake's Database ACL +========================================= + +Now that we've covered INI-based ACL permissions, let's move on to the (more commonly used) database ACL. +========================================================================================================= + +Getting Started +--------------- + +The default ACL permissions implementation is database powered. Cake's +database ACL consists of a set of core models, and a console application +that comes with your Cake installation. The models are used by Cake to +interact with your database in order to store and retrieve nodes in tree +format. The console application is used to initialize your database and +interact with your ACO and ARO trees. + +To get started, first you'll need to make sure your +``/app/config/database.php`` is present and correctly configured. See +section 4.1 for more information on database configuration. + +Once you've done that, use the CakePHP console to create your ACL +database tables: + +:: + + $ cake schema run create DbAcl + +Running this command will drop and re-create the tables necessary to +store ACO and ARO information in tree format. The output of the console +application should look something like the following: + +:: + + --------------------------------------------------------------- + Cake Schema Shell + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +This replaces an older deprecated command, "initdb". + +You can also use the SQL file found in ``app/config/sql/db_acl.sql``, +but that's nowhere near as fun. + +When finished, you should have three new database tables in your system: +acos, aros, and aros\_acos (the join table to create permissions +information between the two trees). + +If you're curious about how Cake stores tree information in these +tables, read up on modified database tree traversal. The ACL component +uses CakePHP's :doc:`/The-Manual/Core-Behaviors/Tree` to manage the +trees' inheritances. The model class files for ACL are all compiled in a +single file +`db\_acl.php `_. + +Now that we're all set up, let's work on creating some ARO and ACO +trees. + +Creating Access Request Objects (AROs) and Access Control Objects (ACOs) +------------------------------------------------------------------------ + +In creating new ACL objects (ACOs and AROs), realize that there are two +main ways to name and access nodes. The *first* method is to link an ACL +object directly to a record in your database by specifying a model name +and foreign key value. The *second* method can be used when an object +has no direct relation to a record in your database - you can provide a +textual alias for the object. + +In general, when you're creating a group or higher level object, use an +alias. If you're managing access to a specific item or record in the +database, use the model/foreign key method. + +You create new ACL objects using the core CakePHP ACL models. In doing +so, there are a number of fields you'll want to use when saving data: +``model``, ``foreign_key``, ``alias``, and ``parent_id``. + +The ``model`` and ``foreign_key`` fields for an ACL object allows you to +link up the object to its corresponding model record (if there is one). +For example, many AROs will have corresponding User records in the +database. Setting an ARO's ``foreign_key`` to the User's ID will allow +you to link up ARO and User information with a single User model find() +call if you've set up the correct model associations. Conversely, if you +want to manage edit operation on a specific blog post or recipe listing, +you may choose to link an ACO to that specific model record. + +The ``alias`` for an ACL object is just a human-readable label you can +use to identify an ACL object that has no direct model record +correlation. Aliases are usually useful in naming user groups or ACO +collections. + +The ``parent_id`` for an ACL object allows you to fill out the tree +structure. Supply the ID of the parent node in the tree to create a new +child. + +Before we can create new ACL objects, we'll need to load up their +respective classes. The easiest way to do this is to include Cake's ACL +Component in your controller's $components array: + +:: + + var $components = array('Acl'); + +Once we've got that done, let's see what some examples of creating these +objects might look like. The following code could be placed in a +controller action somewhere: + +While the examples here focus on ARO creation, the same techniques can +be used to create an ACO tree. + +Keeping with our Fellowship setup, let's first create our ARO groups. +Because our groups won't really have specific records tied to them, +we'll use aliases to create these ACL objects. What we're doing here is +from the perspective of a controller action, but could be done +elsewhere. What we'll cover here is a bit of an artificial approach, but +you should feel comfortable using these techniques to build AROs and +ACOs on the fly. + +This shouldn't be anything drastically new - we're just using models to +save data like we always do: + +:: + + function anyAction() + { + $aro =& $this->Acl->Aro; + + //Here's all of our group info in an array we can iterate through + $groups = array( + 0 => array( + 'alias' => 'warriors' + ), + 1 => array( + 'alias' => 'wizards' + ), + 2 => array( + 'alias' => 'hobbits' + ), + 3 => array( + 'alias' => 'visitors' + ), + ); + + //Iterate and create ARO groups + foreach($groups as $data) + { + //Remember to call create() when saving in loops... + $aro->create(); + + //Save data + $aro->save($data); + } + + //Other action logic goes here... + } + +Once we've got them in there, we can use the ACL console application to +verify the tree structure. + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [2]wizards + + [3]hobbits + + [4]visitors + + --------------------------------------------------------------- + +I suppose it's not much of a tree at this point, but at least we've got +some verification that we've got four top-level nodes. Let's add some +children to those ARO nodes by adding our specific user AROs under these +groups. Every good citizen of Middle Earth has an account in our new +system, so we'll tie these ARO records to specific model records in our +database. + +When adding child nodes to a tree, make sure to use the ACL node ID, +rather than a foreign\_key value. + +:: + + function anyAction() + { + $aro = new Aro(); + + //Here are our user records, ready to be linked up to new ARO records + //This data could come from a model and modified, but we're using static + //arrays here for demonstration purposes. + + $users = array( + 0 => array( + 'alias' => 'Aragorn', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'Legolas', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'Gimli', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'Gandalf', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'Frodo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'Bilbo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'Merry', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'Pippin', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'Gollum', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + //Iterate and create AROs (as children) + foreach($users as $data) + { + //Remember to call create() when saving in loops... + $aro->create(); + + //Save data + $aro->save($data); + } + + //Other action logic goes here... + } + +Typically you won't supply both an alias and a model/foreign\_key, but +we're using both here to make the structure of the tree easier to read +for demonstration purposes. + +The output of that console application command should now be a little +more interesting. Let's give it a try: + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [5]Aragorn + + [6]Legolas + + [7]Gimli + + [2]wizards + + [8]Gandalf + + [3]hobbits + + [9]Frodo + + [10]Bilbo + + [11]Merry + + [12]Pippin + + [4]visitors + + [13]Gollum + + --------------------------------------------------------------- + +Now that we've got our ARO tree setup properly, let's discuss a possible +approach for structuring an ACO tree. While we can structure more of an +abstract representation of our ACO's, it's often more practical to model +an ACO tree after Cake's Controller/Action setup. We've got five main +objects we're handling in this Fellowship scenario, and the natural +setup for that in a Cake application is a group of models, and +ultimately the controllers that manipulate them. Past the controllers +themselves, we'll want to control access to specific actions in those +controllers. + +Based on that idea, let's set up an ACO tree that will mimic a Cake app +setup. Since we have five ACOs, we'll create an ACO tree that should end +up looking something like the following: + +- Weapons +- Rings +- PorkChops +- DiplomaticEfforts +- Ales + +One nice thing about a Cake ACL setup is that each ACO automatically +contains four properties related to CRUD (create, read, update, and +delete) actions. You can create children nodes under each of these five +main ACOs, but using Cake's built in action management covers basic CRUD +operations on a given object. Keeping this in mind will make your ACO +trees smaller and easier to maintain. We'll see how these are used later +on when we discuss how to assign permissions. + +Since you're now a pro at adding AROs, use those same techniques to +create this ACO tree. Create these upper level groups using the core Aco +model. + +Assigning Permissions +--------------------- + +After creating our ACOs and AROs, we can finally assign permissions +between the two groups. This is done using Cake's core Acl component. +Let's continue on with our example. + +Here we'll work in the context of a controller action. We do that +because permissions are managed by the Acl Component. + +:: + + class SomethingsController extends AppController + { + // You might want to place this in the AppController + // instead, but here works great too. + + var $components = array('Acl'); + + } + +Let's set up some basic permissions using the AclComponent in an action +inside this controller. + +:: + + function index() + { + //Allow warriors complete access to weapons + //Both these examples use the alias syntax + $this->Acl->allow('warriors', 'Weapons'); + + //Though the King may not want to let everyone + //have unfettered access + $this->Acl->deny('warriors/Legolas', 'Weapons', 'delete'); + $this->Acl->deny('warriors/Gimli', 'Weapons', 'delete'); + + die(print_r('done', 1)); + } + +The first call we make to the AclComponent allows any user under the +'warriors' ARO group full access to anything under the 'Weapons' ACO +group. Here we're just addressing ACOs and AROs by their aliases. + +Notice the usage of the third parameter? That's where we use those handy +actions that are in-built for all Cake ACOs. The default options for +that parameter are ``create``, ``read``, ``update``, and ``delete`` but +you can add a column in the ``aros_acos`` database table (prefixed with +\_ - for example ``_admin``) and use it alongside the defaults. + +The second set of calls is an attempt to make a more fine-grained +permission decision. We want Aragorn to keep his full-access privileges, +but deny other warriors in the group the ability to delete Weapons +records. We're using the alias syntax to address the AROs above, but you +might want to use the model/foriegn key syntax yourself. What we have +above is equivalent to this: + +:: + + // 6342 = Legolas + // 1564 = Gimli + + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342), 'Weapons', 'delete'); + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564), 'Weapons', 'delete'); + +Addressing a node using the alias syntax uses a slash-delimited string +('/users/employees/developers'). Addressing a node using model/foreign +key syntax uses an array with two parameters: +``array('model' => 'User', 'foreign_key' => 8282)``. + +The next section will help us validate our setup by using the +AclComponent to check the permissions we've just set up. + +Checking Permissions: The ACL Component +--------------------------------------- + +Let's use the AclComponent to make sure dwarves and elves can't remove +things from the armory. At this point, we should be able to use the +AclComponent to make a check between the ACOs and AROs we've created. +The basic syntax for making a permissions check is: + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +Let's give it a try inside a controller action: + +:: + + function index() + { + //These all return true: + $this->Acl->check('warriors/Aragorn', 'Weapons'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'create'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'read'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'update'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'delete'); + + //Remember, we can use the model/foreign key syntax + //for our user AROs + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), 'Weapons'); + + //These also return true: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'create'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'read'); + + //But these return false: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'delete'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'delete'); + } + +The usage here is demonstrational, but hopefully you can see how +checking like this can be used to decide whether or not to allow +something to happen, show an error message, or redirect the user to a +login. diff --git a/en/The-Manual/Core-Components/Authentication.rst b/en/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..42969210b5ca34c0320a46e7738e2937a960e606 --- /dev/null +++ b/en/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,857 @@ +Authentication +############## + +User authentication systems are a common part of many web applications. +In CakePHP there are several systems for authenticating users, each of +which provides different options. At its core the authentication +component will check to see if a user has an account with a site. If +they do, the component will give access to that user to the complete +site. + +This component can be combined with the ACL (access control lists) +component to create more complex levels of access within a site. The ACL +Component, for example, could allow you to grant one user access to +public site areas, while granting another user access to protected +administrative portions of the site. + +CakePHP's AuthComponent can be used to create such a system easily and +quickly. Let's take a look at how you would build a very simple +authentication system. + +Like all components, you use it by adding 'Auth' to the list of +components in your controller: + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +Or add it to your AppController so all of your controllers will use it: + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +Now, there are a few conventions to think about when using +AuthComponent. By default, the AuthComponent expects you to have a table +called 'users' with fields called 'username' and 'password' to be used. + +In some situations, databases don't let you use 'password' as a column +name. See :doc:`/The-Manual/Core-Components/Authentication` for an example +how to change the default field names to work with your own environment. + +Let's set up our users table using the following SQL: + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(40), + first_name varchar(32), + last_name varchar(32), + PRIMARY KEY (id) + ); + +Something to keep in mind when creating a table to store all your user +authentication data is that the AuthComponent expects the password value +stored in the database to be hashed instead of being stored in +plaintext. Make sure that the field you will be using to store passwords +is long enough to store the hash (40 characters for SHA1, for example). + +If you want to add a user manually to the db - the simplest method to +get the right data is to attempt to login and look at the sql log. + +For the most basic setup, you'll only need to create two actions in your +controller: + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); // Not necessary if declared in your app controller + + /** + * The AuthComponent provides the needed functionality + * for login, so you can leave this function blank. + */ + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +While you can leave the login() function blank, you do need to create +the login view template (saved in app/views/users/login.ctp). This is +the only UsersController view template you need to create, however. The +example below assumes you are already using the Form helper: + +:: + + flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +This view creates a simple login form where you enter a username and +password. Once you submit this form, the AuthComponent takes care of the +rest for you. The session flash message will display any notices +generated by the AuthComponent for example when the username and +password combination do not match. Upon successful login the database +record of the current logged in user is saved to session under the key +Auth.User. + +It is common that on successful login the user should be redirected to +some place else, such as a dashboard. To achieve this Auth's +loginRedirect property should be set to this location using either +string or array URL notation. For example if your UsersController uses +the Auth component, we can configure this property in a beforeFilter +override method + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); + + function beforeFilter() { + parent::beforeFilter(); + $this->Auth->loginRedirect = array('controller' => 'dashboard', 'action' => 'index'); + } + + function login() { + } + + + /** delegate /users/logout request to Auth->logout method */ + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +Now, on success, we would arrive at our dashboard view. It is typical +that you show a mast header on your page with the logged-in user's name +and a logout link. To achieve this you can echo out a property of the +Auth.User session variable. + +:: + +
+ read('Auth.User.first_name'); ?> + link('Logout', array('controller' => 'users', 'action' => 'logout')); ?> +
+ +That's how to implement an incredibly simple, database-driven +authentication system using the Auth component. However, there is a lot +more we can do. Let's take a look at some more advanced usage of the +component in the subsequent sections. + +Setting Auth Component Variables +================================ + +Whenever you want to alter a default option for AuthComponent, you do +that by creating a beforeFilter() method for your controller, and then +calling various built-in methods or setting component variables. + +For example, to change the field name used for passwords from 'password' +to 'secretword', you would do the following: + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'secretword' + ); + } + } + +In this particular situation, you would also need to remember to change +the field name in the view template! + +Another common use of Auth component variables is to allow access to +certain methods without the user being logged in (by default Auth +restricts access to every action except the login and logout methods). + +For example if we want to allow all users access to the index and view +methods ( but not any other), we would do the following: + +:: + + function beforeFilter() { + $this->Auth->allow('index','view'); + } + +Displaying Auth Error Messages +============================== + +In order to display the error messages that Auth spits out you need to +add the following code to your view. In this case, the message will +appear below the regular flash messages: + +In order to show all normal flash messages and auth flash messages for +all views add the following two lines to the views/layouts/default.ctp +file in the body section preferable before the content\_for\_layout +line. + +:: + + flash(); + $session->flash('auth'); + ?> + +Troubleshooting Auth Problems +============================= + +It can sometimes be quite difficult to diagnose problems when it's not +behaving as expected, so here are a few pointers to remember. + +*Password hashing* + +When posting information to an action via a form, the Auth component +automatically hashes the contents of your password input field if you +also have data in the username field. So, if you are trying to create +some sort of registration page, make sure to have the user fill out a +'confirm password' field so that you can compare the two. Here's some +sample code: + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +Change Hash Function +==================== + +The AuthComponent uses the Security class to hash a password. The +Security class uses the SHA1 scheme by default. To change another hash +function used by the Auth component, use the ``setHash`` method passing +it ``md5``, ``sha1`` or ``sha256`` as its first and only parameter in +your login function. + +:: + + Security::setHash('md5'); // or sha1 or sha256. + +The Security class uses a salt value (set in /app/config/core.php) to +hash the password. + +If you want to use different password hashing logic beyond md5/sha1 with +the application salt, you will need to override the standard +hashPassword mechanism - You may need to do this if for example you have +an existing database that previously used a hashing scheme without a +salt. To do this, create the method ``hashPasswords`` in the class you +want to be responsible for hashing your passwords (usually the User +model) and set ``authenticate`` to the object you're authenticating +against (usually this is User) like so: + +:: + + function beforeFilter() { + $this->Auth->authenticate = ClassRegistry::init('User'); + ... + parent::beforeFilter(); + } + +With the above code, the User model hashPasswords() method will be +called each time Cake calls AuthComponent::hashPasswords(). Here's an +example hashPassword function, appropriate if you've already got a users +table full of plain md5-hashed passwords: + +:: + + class User extends AppModel { + function hashPasswords($data) { + if (isset($data['User']['password'])) { + $data['User']['password'] = md5($data['User']['password']); + return $data; + } + return $data; + } + } + +AuthComponent Methods +===================== + +action +------ + +``action (string $action = ':controller/:action')`` + +If you are using ACO's as part of your ACL structure, you can get the +path to the ACO node bound to a particular controller/action pair: + +:: + + $acoNode = $this->Auth->action('users/delete'); + +If you don't pass in any values, it uses the current controller / action +pair + +allow +----- + +If you have some actions in your controller that you don't have to +authenticate against (such as a user registration action), you can add +methods that the AuthComponent should ignore. The following example +shows how to allow an action named 'register'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('register'); + } + +If you wish to allow multiple actions to skip authentication, you supply +them as parameters to the allow() method: + +:: + + function beforeFilter() { + ... + $this->Auth->allow('foo', 'bar', 'baz'); + } + +Shortcut: you may also allow all the actions in a controller by using +'\*'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('*'); + } + +If you are using requestAction in your layout or elements you should +allow those actions in order to be able to open login page properly. + +The auth component assumes that your actions names :doc:`/The-Manual/Basic-Principles-of-CakePHP/CakePHP-Conventions` and +are underscored. + +deny +---- + +There may be times where you will want to remove actions from the list +of allowed actions (set using $this->Auth->allow()). Here's an example: + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +This method checks if the ``$data`` contains the username and password +fields as specified by the variable ``$fields`` indexed by the model +name as specified by ``$userModel``. If the ``$data`` array contains +both the username and password, it hashes the password field in the +array and returns the ``data`` array in the same format. This function +should be used prior to insert or update calls of the user when the +password field is affected. + +:: + + $data['User']['username'] = 'me@me.com'; + $data['User']['password'] = 'changeme'; + $hashedPasswords = $this->Auth->hashPasswords($data); + pr($hashedPasswords); + /* returns: + Array + ( + [User] => Array + ( + [username] => me@me.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +The *$hashedPasswords['User']['password']* field would now be hashed +using the ``password`` function of the component. + +If your controller uses the Auth component and posted data contains the +fields as explained above, it will automatically hash the password field +using this function. + +mapActions +---------- + +If you are using Acl in CRUD mode, you may want to assign certain +non-default actions to each part of CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('someAction'), + 'read' => array('someAction', 'someAction2'), + 'update' => array('someAction'), + 'delete' => array('someAction') + ) + ); + +login +----- + +``login($data = null)`` + +If you are doing some sort of Ajax-based login, you can use this method +to manually log someone into the system. If you don't pass any value for +``$data``, it will automatically use POST data passed into the +controller. + +for example, in an application you may wish to assign a user a password +and auto log them in after registration. In an over simplified example: + +View: + +:: + + echo $form->create('User',array('action'=>'register')); + echo $form->input('username'); + echo $form->end('Register'); + +Controller: + +:: + + function register() { + if(!empty($this->data)) { + $this->User->create(); + $assigned_password = "password"; + $this->data['User']['password'] = $this->Auth->password($assigned_password); + if($this->User->save($this->data)) { + // send signup email containing password to the user + $this->Auth->login($this->data); + $this->redirect("home"); + } + } + +One thing to note is that you must manually redirect the user after +login as loginRedirect is not called. + +``$this->Auth->login($data)`` returns 1 on successful login, 0 on a +failure + +logout +------ + +Provides a quick way to de-authenticate someone, and redirect them to +where they need to go. This method is also useful if you want to provide +a 'Log me out' link inside a members' area of your application. + +Example: + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +Pass in a string, and you can get what the hashed password would look +like. This is an essential functionality if you are creating a user +registration screen where you have users enter their password a second +time to confirm it. + +:: + + if ($this->data['User']['password'] == + $this->Auth->password($this->data['User']['password2'])) { + + // Passwords match, continue processing + ... + } else { + $this->flash('Typed passwords did not match', 'users/register'); + } + +The auth component will automatically hash the password field if the +username field is also present in the submitted data + +Cake appends your password string to a salt value and then hashes it. +The hashing function used depends on the one set by the core utility +class ``Security`` (sha1 by default). You can use the +``Security::setHash`` function to change the hashing method. The salt +value is used from your application's configuration defined in your +``core.php`` + +user +---- + +``user(string $key = null)`` + +This method provides information about the currently authenticated user. +The information is taken from the session. For example: + +:: + + if ($this->Auth->user('role') == 'admin') { + $this->flash('You have admin access'); + } + +It can also be used to return the whole user session data like so: + +:: + + $data['User'] = $this->Auth->user(); + +If this method returns null, the user is not logged in. + +In the view you can use the Session helper to retrieve the currently +authenticated user's information: + +:: + + $session->read('Auth.User'); // returns complete user record + $session->read('Auth.User.first_name') //returns particular field value + +The session key can be different depending on which model Auth is +configured to use. Eg. If you use model ``Account`` instead of ``User``, +then the session key would be ``Auth.Account`` + +AuthComponent Variables +======================= + +Now, there are several Auth-related variables that you can use as well. +Usually you add these settings in your Controller's beforeFilter() +method. Or, if you need to apply such settings site-wide, you would add +them to App Controller's beforeFilter() + +userModel +--------- + +Don't want to use a User model to authenticate against? No problem, just +change it by setting this value to the name of the model you want to +use. + +:: + + Auth->userModel = 'Member'; + ?> + +fields +------ + +Overrides the default username and password fields used for +authentication. + +:: + + Auth->fields = array('username' => 'email', 'password' => 'passwd'); + ?> + +userScope +--------- + +Use this to provide additional requirements for authentication to +succeed. + +:: + + Auth->userScope = array('User.active' => true); + ?> + +loginAction +----------- + +You can change the default login from */users/login* to be any action of +your choice. + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'members', 'action' => 'login'); + ?> + +loginRedirect +------------- + +The AuthComponent remembers what controller/action pair you were trying +to get to before you were asked to authenticate yourself by storing this +value in the Session, under the ``Auth.redirect`` key. However, if this +session value is not set (if you're coming to the login page from an +external link, for example), then the user will be redirected to the URL +specified in loginRedirect. + +Example: + +:: + + Auth->loginRedirect = array('controller' => 'members', 'action' => 'home'); + ?> + +logoutRedirect +-------------- + +You can also specify where you want the user to go after they are logged +out, with the default being the login action. + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'members', 'action' => 'logout'); + ?> + +loginError +---------- + +Change the default error message displayed when someone does not +successfully log in. + +:: + + Auth->loginError = "No, you fool! That's not the right password!"; + ?> + +authError +--------- + +Change the default error message displayed when someone attempts to +access an object or action to which they do not have access. + +:: + + Auth->authError = "Sorry, you are lacking access."; + ?> + +autoRedirect +------------ + +Normally, the AuthComponent will automatically redirect you as soon as +it authenticates. Sometimes you want to do some more checking before you +redirect users: + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- code inside this function will execute only when autoRedirect was set to false (i.e. in a beforeFilter). + if ($this->Auth->user()) { + if (!empty($this->data['User']['remember_me'])) { + $cookie = array(); + $cookie['username'] = $this->data['User']['username']; + $cookie['password'] = $this->data['User']['password']; + $this->Cookie->write('Auth.User', $cookie, true, '+2 weeks'); + unset($this->data['User']['remember_me']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.User'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // Clear auth message, just in case we use it. + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +The code in the login function will not execute *unless* you set +$autoRedirect to false in a beforeFilter. The code present in the login +function will only execute *after* authentication was attempted. This is +the best place to determine whether or not a successful login occurred +by the AuthComponent (should you desire to log the last successful login +timestamp, etc.). + +With autoRedirect set to false, you can also inject additional code such +as keeping track of the last successful login timestamp + +:: + + data)) && $this->Auth->user() ){ + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('last_login', date('Y-m-d H:i:s') ); + $this->redirect($this->Auth->redirect()); + } + } + ?> + +authorize +--------- + +Normally, the AuthComponent will attempt to verify that the login +credentials you've entered are accurate by comparing them to what's been +stored in your user model. However, there are times where you might want +to do some additional work in determining proper credentials. By setting +this variable to one of several different values, you can do different +things. Here are some of the more common ones you might want to use. + +:: + + Auth->authorize = 'controller'; + ?> + +When authorize is set to 'controller', you'll need to add a method +called isAuthorized() to your controller. This method allows you to do +some more authentication checks and then return either true or false. + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } + } + if ($this->action == 'view') { + return true; + } + ... + return false; + } + ?> + +Remember that this method will be checked after you have already passed +the basic authentication check against the user model. + +:: + + Auth->authorize = 'model'; + ?> + +Don't want to add anything to your controller and might be using ACO's? +You can get the AuthComponent to call a method in your user model called +isAuthorized() to do the same sort of thing: + +:: + + + +Lastly, you can use authorize with actions such as below + +:: + + Auth->authorize = 'actions'; + ?> + +By using actions, Auth will make use of ACL and check with +AclComponent::check(). An isAuthorized function is not needed. + +:: + + Auth->authorize = 'crud'; + ?> + +By using crud, Auth will make use of ACL and check with +AclComponent::check(). Actions should be mapped to CRUD (see +`mapActions `_). + +sessionKey +---------- + +Name of the session array key where the record of the current authed +user is stored. + +Defaults to "Auth", so if unspecified, the record is stored in +"Auth.{$userModel name}". + +:: + + Auth->sessionKey = 'Authorized'; + ?> + +ajaxLogin +--------- + +If you are doing Ajax or Javascript based requests that require +authenticated sessions, set this variable to the name of a view element +you would like to be rendered and returned when you have an invalid or +expired session. + +As with any part of CakePHP, be sure to take a look at `AuthComponent +class `_ for a more +in-depth look at the AuthComponent. + +authenticate +------------ + +This variable holds a reference to the object responsible for hashing +passwords if it is necessary to change/override the default password +hashing mechanism. See :doc:`/The-Manual/Core-Components/Authentication` for more info. + +actionPath +---------- + +If using action-based access control, this defines how the paths to +action ACO nodes is computed. If, for example, all controller nodes are +nested under an ACO node named 'Controllers', $actionPath should be set +to 'Controllers/'. diff --git a/en/The-Manual/Core-Components/Cookies.rst b/en/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..6da04e1eb9152ece42179191fd897537751c18ea --- /dev/null +++ b/en/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,181 @@ +Cookies +####### + +The CookieComponent is a wrapper around the native PHP setcookie method. +It also includes a host of delicious icing to make coding cookies in +your controllers very convenient. Before attempting to use the +CookieComponent, you must make sure that 'Cookie' is listed in your +controllers' $components array. + +Controller Setup +================ + +There are a number of controller variables that allow you to configure +the way cookies are created and managed. Defining these special +variables in the beforeFilter() method of your controller allows you to +define how the CookieComponent works. + +Cookie variable + +default + +description + +string $name + +'CakeCookie' + +The name of the cookie. + +string $key + +null + +This string is used to encrypt the value written to the cookie. This +string should be random and difficult to guess. + +string $domain + +'' + +The domain name allowed to access the cookie. e.g. Use '.yourdomain.com' +to allow access from all your subdomains. + +int or string $time + +'5 Days' + +The time when your cookie will expire. Integers are interpreted as +seconds and a value of 0 is equivalent to a 'session cookie': i.e. the +cookie expires when the browser is closed. If a string is set, this will +be interpreted with PHP function strtotime(). You can set this directly +within the write() method. + +string $path + +'/' + +The server path on which the cookie will be applied. If $cookiePath is +set to '/foo/', the cookie will only be available within the /foo/ +directory and all sub-directories such as /foo/bar/ of your domain. The +default value is the entire domain. You can set this directly within the +write() method. + +boolean $secure + +false + +Indicates that the cookie should only be transmitted over a secure HTTPS +connection. When set to true, the cookie will only be set if a secure +connection exists. You can set this directly within the write() method. + +The following snippet of controller code shows how to include the +CookieComponent and set up the controller variables needed to write a +cookie named 'baker\_id' for the domain 'example.com' which needs a +secure connection, is available on the path ‘/bakers/preferences/’, and +expires in one hour. + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'baker_id'; + $this->Cookie->time = 3600; // or '1 hour' + $this->Cookie->path = '/bakers/preferences/'; + $this->Cookie->domain = 'example.com'; + $this->Cookie->secure = true; //i.e. only sent if using secure HTTPS + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +Next, let’s look at how to use the different methods of the Cookie +Component. + +Using the Component +=================== + +The CookieComponent offers a number of methods for working with Cookies. + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +The write() method is the heart of cookie component, $key is the cookie +variable name you want, and the $value is the information to be stored. + +:: + + $this->Cookie->write('name','Larry'); + +You can also group your variables by supplying dot notation in the key +parameter. + +:: + + $this->Cookie->write('User.name', 'Larry'); + $this->Cookie->write('User.role','Lead'); + +If you want to write more than one value to the cookie at a time, you +can pass an array: + +:: + + $this->Cookie->write( + array('name'=>'Larry','role'=>'Lead') + ); + +All values in the cookie are encrypted by default. If you want to store +the values as plain-text, set the third parameter of the write() method +to false. The encryption performed on cookie values is fairly +uncomplicated encryption system. It uses Security.salt and a predefined +``CIPHER_SEED`` constant to encrypt values. To make your cookies more +secure you should define ``CIPHER_SEED`` in your bootstrap to ensure a +better encryption. The default value of ``CIPHER_SEED`` is +``76859309657453542496749683645`` + +:: + + $this->Cookie->write('name','Larry',false); + +The last parameter to write is $expires – the number of seconds before +your cookie will expire. For convenience, this parameter can also be +passed as a string that the php strtotime() function understands: + +:: + + //Both cookies expire in one hour. + $this->Cookie->write('first_name','Larry',false, 3600); + $this->Cookie->write('last_name','Masters',false, '1 hour'); + +**read(mixed $key)** + +This method is used to read the value of a cookie variable with the name +specified by $key. + +:: + + // Outputs “Larry” + echo $this->Cookie->read('name'); + + //You can also use the dot notation for read + echo $this->Cookie->read('User.name'); + + //To get the variables which you had grouped + //using the dot notation as an array use something like + $this->Cookie->read('User'); + + // this outputs something like array('name' => 'Larry', 'role'=>'Lead') + +**del(mixed $key)** + +Deletes a cookie variable of the name in $key. Works with dot notation. + +:: + + //Delete a variable + $this->Cookie->del('bar') + + //Delete the cookie variable bar, but not all under foo + $this->Cookie->del('foo.bar') + + +**destroy()** + +Destroys the current cookie. diff --git a/en/The-Manual/Core-Components/Email.rst b/en/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..7f9da167df0dedb82b66fa1fb668099662bf5c8f --- /dev/null +++ b/en/The-Manual/Core-Components/Email.rst @@ -0,0 +1,257 @@ +Email +##### + +The emailComponent is a way for you to add simple email sending +functionality to your CakePHP application. Using the same concepts of +layouts and view ctp files to send formated messages as text, html or +both. It supports sending via the built in mail functions of PHP, via +smtp server or a debug mode where it writes the message out to a session +flash message. It supports file attachments and does some basic header +injection checking/ filtering for you. There is a lot that it doesn't do +for you but it will get you started. + +Class Attributes and Variables +============================== + +These are the values that you can set before you call +``EmailComponent::send()`` + +to + +Address the message is going to (string). Separate the addresses with a +comma if you want to send the email to more than one recipient. + +cc + +array of addresses to cc the message to + +bcc + +array of addresses to bcc (blind carbon copy) the message to + +replyTo + +reply to address (string) + +return + +Return mail address that will be used in case of any errors(string) (for +mail-daemon/errors) + +from + +from address (string) + +subject + +subject for the message (string) + +template + +The email element to use for the message (located in +``app/views/elements/email/html/`` and +``app/views/elements/email/text/``) + +layout + +The layout used for the email (located in +``app/views/layouts/email/html/`` and ``app/views/layouts/email/text/``) + +lineLength + +Length at which lines should be wrapped. Defaults to 70. (integer) + +sendAs + +how do you want message sent string values of ``text``, ``html`` or +``both`` + +attachments + +array of files to send (absolute and relative paths) + +delivery + +how to send the message (``mail``, ``smtp`` [would require smtpOptions +set below] and ``debug``) + +smtpOptions + +associative array of options for smtp mailer (``port``, ``host``, +``timeout``, ``username``, ``password``, ``client``) + +There are some other things that can be set but you should refer to the +api documentation for more information + +Sending Multiple Emails in a loop +--------------------------------- + +If you wish to send multiple emails using a loop, you'll need to reset +the email fields using the reset method of the Email component. You'll +need to reset before setting the email properties again. + +:: + + $this->Email->reset() + +Sending a basic message +======================= + +To send a message without using a template, simply pass the body of the +message as a string (or an array of lines) to the send() method. For +example: + +:: + + $this->Email->from = 'Somebody '; + $this->Email->to = 'Somebody Else '; + $this->Email->subject = 'Test'; + $this->Email->send('Hello message body!'); + +Setting up the Layouts +---------------------- + +To use both text and html mailing message you need to create layout +files for them, just like in setting up your default layouts for the +display of your views in a browser, you need to set up default layouts +for your email messages. In the ``app/views/layouts/`` directory you +need to set up (at a minimum) the following structure + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +These are the files that hold the layout templates for your default +messages. Some example content is below + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +Setup an email element for the message body +------------------------------------------- + +In the ``app/views/elements/email/`` directory you need to set up +folders for ``text`` and ``html`` unless you plan to just send one or +the other. In each of these folders you need to create templates for +both types of messages referring to the content that you send to the +view either by using $this->set() or using the $contents parameter of +the send() method. Some simple examples are shown below. It is +worthwhile to note that $this->set() should be done before invoking +Email's send(), a little break in mindset of the usual CakePHP view +conventions. For this example we will call the templates +simple\_message.ctp + +``text`` + +:: + + Dear , + Thank you for your interest. + +``html`` + +:: + +

Dear ,
+    Thank you for your interest.

+ +Controller +---------- + +In your controller you need to add the component to your ``$components`` +array or add a $components array to your controller like: + +:: + + + +In this example we will set up a private method to handle sending the +email messages to a user identified by an $id. In our controller (let's +use the User controller in this example) + +:: + + + User->read(null,$id); + $this->Email->to = $User['User']['email']; + $this->Email->bcc = array('secret@example.com'); + $this->Email->subject = 'Welcome to our really cool thing'; + $this->Email->replyTo = 'support@example.com'; + $this->Email->from = 'Cool Web App '; + $this->Email->template = 'simple_message'; // note no '.ctp' + //Send as 'html', 'text' or 'both' (default is 'text') + $this->Email->sendAs = 'both'; // because we like to send pretty mail + //Set view variables as normal + $this->set('User', $User); + //Do not pass any args to send() + $this->Email->send(); + } + ?> + +You have sent a message, you could call this from another method like + +:: + + + $this->_sendNewUserMail( $this->User->id ); + +Sending A Message Using SMTP +============================ + +To send an email using an SMTP server, the steps are similar to sending +a basic message. Set the delivery method to ``smtp`` and assign any +options to the Email object's ``smtpOptions`` property. You may also +retrieve SMTP errors generated during the session by reading the +``smtpError`` property of the component. + +:: + + /* SMTP Options */ + $this->Email->smtpOptions = array( + 'port'=>'25', + 'timeout'=>'30', + 'host' => 'your.smtp.server', + 'username'=>'your_smtp_username', + 'password'=>'your_smtp_password', + 'client' => 'smtp_helo_hostname' + ); + + /* Set delivery method */ + $this->Email->delivery = 'smtp'; + + /* Do not pass any args to send() */ + $this->Email->send(); + + /* Check for SMTP errors. */ + $this->set('smtp-errors', $this->Email->smtpError); + +If your SMTP server requires authentication, be sure to specify the +username and password parameters for ``smtpOptions`` as shown in the +example. + +If you don't know what an SMTP HELO is, then you most likely will not +need to set the ``client`` parameter for the ``smtpOptions``. This is +only needed for compatibility with SMTP servers which do not fully +respect RFC 821 (SMTP HELO). diff --git a/en/The-Manual/Core-Components/Request-Handling.rst b/en/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..f381586bc1f4b931f3913b58d9c0d8d5a1535189 --- /dev/null +++ b/en/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,265 @@ +Request Handling +################ + +The Request Handler component is used in CakePHP to obtain additional +information about the HTTP requests that are made to your applications. +You can use it to inform your controllers about Ajax as well as gain +additional insight into content types that the client accepts and +automatically changes to the appropriate layout when file extensions are +enabled. + +By default RequestHandler will automatically detect Ajax requests based +on the HTTP-X-Requested-With header that many javascript libraries use. +When used in conjunction with Router::parseExtensions() RequestHandler +will automatically switch the layout and view files to those that match +the requested type. Furthermore, if a helper with the same name as the +requested extension exists, it will be added to the Controllers Helper +array. Lastly, if XML data is POST'ed to your Controllers, it will be +parsed into an XML object which is assigned to Controller::data, and can +then be saved as model data. In order to make use of Request Handler it +must be included in your $components array. + +:: + + + +Obtaining Request Information +============================= + +Request Handler has several methods that provide information about the +client and its request. + +accepts ( $type = null) + +$type can be a string, or an array, or null. If a string, accepts will +return true if the client accepts the content type. If an array is +specified, accepts return true if any one of the content types is +accepted by the client. If null returns an array of the content-types +that the client accepts. For example: + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // Execute code only if client accepts an HTML (text/html) response + } elseif ($this->RequestHandler->accepts('xml')) { + // Execute XML-only code + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // Executes if the client accepts any of the above: XML, RSS or Atom + } + } + } + +Other request 'type' detection methods include: + +isAjax() + +Returns true if the request contains the X-Requested-Header equal to +XMLHttpRequest. + +isSSL() + +Returns true if the current request was made over an SSL connection. + +isXml() + +Returns true if the current request accepts XML as a response. + +isRss() + +Returns true if the current request accepts RSS as a response. + +isAtom() + +Returns true if the current call accepts an Atom response, false +otherwise. + +isMobile() + +Returns true if user agent string matches a mobile web browser, or if +the client accepts WAP content. The supported Mobile User Agent strings +are: + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +Returns true if the client accepts WAP content. + +All of the above request detection methods can be used in a similar +fashion to filter functionality intended for specific content types. For +example when responding to Ajax requests, you often will want to disable +browser caching, and change the debug level. However, you want to allow +caching for non-ajax requests. The following would accomplish that: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + //Continue Controller action + +You could also disable caching with the functionally analogous +``Controller::disableCache`` + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + //Continue Controller action + +Request Type Detection +====================== + +RequestHandler also provides information about what type of HTTP request +has been made and allowing you to respond to each Request Type. + +isPost() + +Returns true if the request is a POST request. + +isPut() + +Returns true if the request is a PUT request. + +isGet() + +Returns true if the request is a GET request. + +isDelete() + +Returns true if the request is a DELETE request. + +Obtaining Additional Client Information +======================================= + +getClientIP() + +Get the remote client IP address + +getReferrer() + +Returns the domain name from which the request originated + +getAjaxVersion() + +Gets Prototype version if call is Ajax, otherwise empty string. The +Prototype library sets a special "Prototype version" HTTP header. + +Responding To Requests +====================== + +In addition to request detection RequestHandler also provides easy +access to altering the output and content type mappings for your +application. + +setContent($name, $type = null) + +- $name string - The name of the Content-type ie. html, css, json, xml. +- $type mixed - The mime-type(s) that the Content-type maps to. + +setContent adds/sets the Content-types for the given name. Allows +content-types to be mapped to friendly aliases and or extensions. This +allows RequestHandler to automatically respond to requests of each type +in its startup method. Furthermore, these content types are used by +prefers() and accepts(). + +setContent is best used in the beforeFilter() of your controllers, as +this will best leverage the automagicness of content-type aliases. + +The default mappings are: + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +Determines which content-types the client prefers. If no parameter is +given the most likely content type is returned. If $type is an array the +first type the client accepts will be returned. Preference os determined +primarily by the file extension parsed by Router if one has been +provided. and secondly by the list of content-types in HTTP\_ACCEPT. + +renderAs($controller, $type) + +- $controller - Controller Reference +- $type - friendly content type name to render content for ex. xml, + rss. + +Change the render mode of a controller to the specified type. Will also +append the appropriate helper to the controller's helper array if +available and not already in the array. + +respondAs($type, $options) + +- $type - Friendly content type name ex. xml, rss or a full content + type like application/x-shockwave +- $options - If $type is a friendly type name that has more than one + content association, $index is used to select the content type. + +Sets the response header based on content-type map names. If DEBUG is +greater than 1, the header is not set. + +responseType() + +Returns the current response type Content-type header or null if one has +yet to be set. + +mapType($ctype) + +Maps a content-type back to an alias diff --git a/en/The-Manual/Core-Components/Security-Component.rst b/en/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..eef4068499fd1eac9ce6ad2f630444cd69205d90 --- /dev/null +++ b/en/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,280 @@ +Security Component +################## + +The Security Component creates an easy way to integrate tighter security +in your application. An interface for managing HTTP-authenticated +requests can be created with Security Component. It is configured in the +beforeFilter() of your controllers. It has several configurable +parameters. All of these properties can be set directly or through +setter methods of the same name. + +If an action is restricted using the Security Component it is +black-holed as an invalid request which will result in a 404 error by +default. You can configure this behavior by setting the +$this->Security->blackHoleCallback property to a callback function in +the controller. Keep in mind that black holes from all of the Security +Component's methods will be ran through this callback method. + +By using the Security Component you automatically get +`CSRF `_ and +form tampering protection. Hidden token fields will automatically be +inserted into forms and checked by the Security component. Among other +things, a form submission will not be accepted after a certain period of +inactivity, which depends on the setting of ``Security.level``. On +'high', this timeout is as short as 10 minutes. Other token fields +include a randomly generated nonce (one-time id) and a hash of fields +which (and only which) must be present in the submitted POST data. + +If you are using Security component's form protection features and other +components that process form data in their ``startup()`` callbacks, be +sure to place Security Component before those components in your +``$components`` array. + +When using the Security Component you **must** use the FormHelper to +create your forms. The Security Component looks for certain indicators +that are created and managed by the FormHelper (especially those created +in create() and end()). Dynamically altering the fields that are +submitted in a POST request (e.g. disabling, deleting or creating new +fields via JavaScript) is likely to trigger a black-holing of the +request. See the ``$validatePost`` or ``$disabledFields`` configuration +parameters. + +Configuration +============= + +$blackHoleCallback + A Controller callback that will handle any requests that are + blackholed. +$requirePost + A List of controller actions that require a POST request to occur. + An array of controller actions or '\*' to force all actions to + require a POST. +$requireSecure + List of actions that require an SSL connection to occur. An array of + controller actions or '\*' to force all actions to require a SSL + connection. +$requireAuth + List of actions that requires a valid authentication key. This + validation key is set by Security Component. +$requireLogin + List of actions that require HTTP-Authenticated logins (basic or + digest). Also accepts '\*' indicating that all actions of this + controller require HTTP-authentication. +$loginOptions + Options for HTTP-Authenticate login requests. Allows you to set the + type of authentication and the controller callback for the + authentication process. +$loginUsers + An associative array of usernames => passwords that are used for + HTTP-authenticated logins. If you are using digest authentication, + your passwords should be MD5-hashed. +$allowedControllers + A List of Controller from which the actions of the current + controller are allowed to receive requests from. This can be used to + control cross controller requests. +$allowedActions + Actions from which actions of the current controller are allowed to + receive requests. This can be used to control cross controller + requests. +$disabledFields + List of form fields that shall be ignored when validating POST - The + value, presence or absence of these form fields will not be taken + into account when evaluating whether a form submission is valid. + Specify fields as you do for the Form Helper (``Model.fieldname``). +$validatePost + Set to ``false`` to completely skip the validation of POST requests, + essentially turning CSRF protection off. + +Methods +======= + +requirePost() +------------- + +Sets the actions that require a POST request. Takes any number of +arguments. Can be called with no arguments to force all actions to +require a POST. + +requireSecure() +--------------- + +Sets the actions that require a SSL-secured request. Takes any number of +arguments. Can be called with no arguments to force all actions to +require a SSL-secured. + +requireAuth() +------------- + +Sets the actions that require a valid Security Component generated +token. Takes any number of arguments. Can be called with no arguments to +force all actions to require a valid authentication. + +requireLogin() +-------------- + +Sets the actions that require a valid HTTP-Authenticated request. Takes +any number of arguments. Can be called with no arguments to force all +actions to require valid HTTP-authentication. + +loginCredentials(string $type) +------------------------------ + +Attempt to validate login credentials for a HTTP-authenticated request. +$type is the type of HTTP-Authentication you want to check. Either +'basic', or 'digest'. If left null/empty both will be tried. Returns an +array with login name and password if successful. + +loginRequest(array $options) +---------------------------- + +Generates the text for an HTTP-Authenticate request header from an array +of $options. + +$options generally contains a 'type', 'realm' . Type indicate which +HTTP-Authenticate method to use. Realm defaults to the current HTTP +server environment. + +parseDigestAuthData(string $digest) +----------------------------------- + +Parse an HTTP digest authentication request. Returns and array of digest +data as an associative array if succesful, and null on failure. + +generateDigestResponseHash(array $data) +--------------------------------------- + +Creates a hash that to be compared with an HTTP digest-authenticated +response. $data should be an array created by +SecurityComponent::parseDigestAuthData(). + +blackHole(object $controller, string $error) +-------------------------------------------- + +Black-hole an invalid request with a 404 error or a custom callback. +With no callback, the request will be exited. If a controller callback +is set to SecurityComponent::blackHoleCallback, it will be called and +passed any error information. + +Usage +===== + +Using the security component is generally done in the controller +beforeFilter(). You would specify the security restrictions you want and +the Security Component will enforce them on its startup. + +:: + + Security->requirePost('delete'); + } + } + ?> + +In this example the delete action can only be successfully triggered if +it receives a POST request. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +This example would force all actions that had admin routing to require +secure SSL requests. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->blackHoleCallback = 'forceSSL'; + $this->Security->requireSecure(); + } + } + + function forceSSL() { + $this->redirect('https://' . env('SERVER_NAME') . $this->here); + } + } + ?> + +This example would force all actions that had admin routing to require +secure SSL requests. When the request is black holed, it will call the +nominated forceSSL() callback which will redirect non-secure requests to +secure requests automatically. + +Basic HTTP Authentication +========================= + +The SecurityComponent has some very powerful authentication features. +Sometimes you may need to protect some functionality inside your +application using `HTTP Basic +Authentication `_. +One common usage for HTTP Auth is protecting a REST or SOAP API. + +This type of authentication is called basic for a reason. Unless you're +transferring information over SSL, credentials will be transferred in +plain text. + +Using the SecurityComponent for HTTP authentication is easy. The code +example below includes the SecurityComponent and adds a few lines of +code inside the controller's beforeFilter method. + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MyRealm' + ); + $this->Security->loginUsers = array( + 'john'=>'johnspassword', + 'jane'=>'janespassword' + ); + $this->Security->requireLogin(); + } + + function index() { + //protected application logic goes here... + } + } + +The loginOptions property of the SecurityComponent is an associative +array specifying how logins should be handled. You only need to specify +the **type** as **basic** to get going. Specify the **realm** if you +want display a nice message to anyone trying to login or if you have +several authenticated sections (= realms) of your application you want +to keep separate. + +The loginUsers property of the SecurityComponent is an associative array +containing users and passwords that should have access to this realm. +The examples here use hard-coded user information, but you'll probably +want to use a model to make your authentication credentials more +manageable. + +Finally, requireLogin() tells SecurityComponent that this Controller +requires login. As with requirePost(), above, providing method names +will protect those methods while keeping others open. diff --git a/en/The-Manual/Core-Components/Sessions.rst b/en/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..e210582d2e90abf94ee585bc0511344c16d9c20a --- /dev/null +++ b/en/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,187 @@ +Sessions +######## + +The CakePHP session component provides a way to persist client data +between page requests. It acts as a wrapper for the $\_SESSION as well +as providing convenience methods for several $\_SESSION related +functions. + +Sessions can be persisted in a few different ways. The default is to use +the settings provided by PHP; however, other options exist. + +cake + Saves the session files in your app's tmp/sessions directory. +database + Uses CakePHP's database sessions. +cache + Use the caching engine configured by Cache::config(). Very useful in + conjunction with Memcache (in setups with multiple application + servers) to store both cached data and sessions. +php + The default setting. Saves session files as indicated by php.ini + +To change the default Session handling method alter the Session.save +Configuration to reflect the option you desire. If you choose 'database' +you should also uncomment the Session.database settings and run the +database session SQL file located in app/config + +To provide a custom configuration, set Session.save Configuration to a +filename. CakePHP will use your file in the CONFIGS directory for the +settings. + +:: + + // app/config/core.php + Configure::write('Session.save','my_session'); + +This will allow you to customize the session handling. + +:: + + // app/config/my_session.php + // + // Revert value and get rid of the referrer check even when, + // Security.level is medium + ini_restore('session.referer_check'); + + ini_set('session.use_trans_sid', 0); + ini_set('session.name', Configure::read('Session.cookie')); + + // Cookie is now destroyed when browser is closed, doesn't + // persist for days as it does by default for security + // low and medium + ini_set('session.cookie_lifetime', 0); + + // Cookie path is now '/' even if you app is within a sub + // directory on the domain + $this->path = '/'; + ini_set('session.cookie_path', $this->path); + + // Session cookie now persists across all subdomains + ini_set('session.cookie_domain', env('HTTP_BASE')); + +Methods +======= + +The Session component is used to interact with session information. It +includes basic CRUD functions as well as features for creating feedback +messages to users. + +It should be noted that Array structures can be created in the Session +by using dot notation. So User.username would reference the following: + +:: + + array('User' => + array('username' => 'clarkKent@dailyplanet.com') + ); + +Dots are used to indicate nested arrays. This notation is used for all +Session component methods wherever a $name is used. + +write +----- + +``write($name, $value)`` + +Write to the Session puts $value into $name. $name can be a dot +separated array. For example: + +:: + + $this->Session->write('Person.eyeColor', 'Green'); + +This writes the value 'Green' to the session under Person => eyeColor. + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +Used to set a session variable that can be used for output in the View. +$layout allows you to control which layout (located in +``/app/views/layouts``) should be used to render the message in. If you +leave the ``$layout`` set to 'default', the message will be wrapped with +the following: + +:: + +
[message]
+ +$params allows you to pass additional view variables to the rendered +layout. $key sets the $messages index in the Message array. Default is +'flash'. + +Parameters can be passed affecting the rendered div, for example padding +"class" in the $params array will apply a class to the ``div`` output +using ``$session->flash()`` in your layout or view. + +:: + + $this->Session->setFlash('Example message text', 'default', array('class' => 'example_class')) + +The output from using ``$session->flash()`` with the above example would +be: + +:: + +
Example message text
+ +read +---- + +``read($name)`` + +Returns the value at $name in the Session. If $name is null the entire +session will be returned. E.g. + +:: + + $green = $this->Session->read('Person.eyeColor'); + +Retrieve the value Green from the session. + +check +----- + +``check($name)`` + +Used to check if a Session variable has been set. Returns true on +existence and false on non-existence. + +delete +------ + +``delete($name)del($name) `` + +Clear the session data at $name. del($name) is deprecated from 1.3. + +:: + + $this->Session->delete('Person.eyeColor'); + +Our session data no longer has the value 'Green', or the index eyeColor +set. However, Person is still in the Session. To delete the entire +Person information from the session use. + +:: + + $this->Session->delete('Person'); + +destroy +------- + +The ``destroy`` method will delete the session cookie and all session +data stored in the temporary file system. It will then destroy the PHP +session and then create a fresh session. + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +Used to determine the last error in a session. diff --git a/en/The-Manual/Core-Console-Applications.rst b/en/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..1f5bcfd5a4babe158bce07f2f2232817da5ac938 --- /dev/null +++ b/en/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,22 @@ +Core Console Applications +######################### + +CakePHP features a number of console applications out of the box. Some +of these applications are used in concert with other CakePHP features +(like ACL or i18n), and others are for general use in getting you to +launch quicker. + +This section explains how to use the core console applications packaged +with CakePHP. + +Before you dive in here, you may want to check out the :doc:`/The-Manual/Developing-with-CakePHP/The-CakePHP-Console` covered earlier. Console setup +isn't covered here, so if you've never used the console before, check it +out. + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/en/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/en/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..38fd3a24d01b6b837cb6592db4917501919ffc65 --- /dev/null +++ b/en/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,58 @@ +Code Generation with Bake +######################### + +You’ve already learned about scaffolding in CakePHP: a simple way to get +up and running with only a database and some bare classes. CakePHP’s +Bake console is another effort to get you up and running in CakePHP – +fast. The Bake console can create any of CakePHP’s basic ingredients: +models, views and controllers. And we aren’t just talking skeleton +classes: Bake can create a fully functional application in just a few +minutes. In fact, Bake is a natural step to take once an application has +been scaffolded. + +Those new to Bake (especially Windows users) may find the `Bake +screencast `_ helpful in setting +things up before continuing. + +Depending on the configuration of your setup, you may have to set +execute rights on the cake bash script or call it using ./cake bake. The +cake console is run using the PHP CLI (command line interface). If you +have problems running the script, ensure that you have the PHP CLI +installed and that it has the proper modules enabled (eg: MySQL). + +When running Bake for the first time, you'll be prompted to create a +Database Configuration file, if you haven't created one already. + +After you've created a Database Configuration file, running Bake will +present you with the following options: + +:: + + --------------------------------------------------------------- + App : app + Path: /path-to/project/app + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake? (D/M/V/C/P/Q) + > + +Alternatively, you can run any of these commands directly from the +command line: + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake all + $ cake bake project + $ cake bake test + diff --git a/en/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/en/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..bc473c4a60caedb4195074baaf023677feacefcc --- /dev/null +++ b/en/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,38 @@ +Modify default HTML produced by "baked" templates +################################################# + +If you wish to modify the default HTML output produced by the "bake" +command, follow these simple steps: + +**For baking custom views:** + +#. Go into: cake/console/libs/templates/views +#. Notice the 4 files there +#. Copy them to your: app/vendors/shells/templates/views. +#. Make changes to the HTML output to control the way "bake" builds your + views + +**For baking custom projects:** + +#. Go into: cake/console/libs/templates/skel +#. Notice the base application files there +#. Copy them to your: app/vendors/shells/templates/skel +#. Make changes to the HTML output to control the way "bake" builds your + views +#. Pass the skeleton path parameter to the project task + + :: + + cake bake project -skel vendors/shells/templates/skel + +Notes + +- You must run the specific project task ``cake bake project`` so that + the path parameter can be passed. +- The template path is relative to the current path of the Command Line + Interface. +- Since the full path to the skeleton needs to be manually entered, you + can specify any directory holding your template build you want, + including using multiple templates. (Unless Cake starts supporting + overriding the skel folder like it does for views) + diff --git a/en/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/en/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..d38e4d766e1dae8bf8f497b01410a24245ab84fe --- /dev/null +++ b/en/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,87 @@ +Schema management and migrations +################################ + +The SchemaShell provides a functionality to create schema objects, +schema sql dumps as well as create snapshots and restore database +snapshots. + +Generating and using Schema files +================================= + +A generated schema file allows you to easily transport a database +agnostic schema. You can generate a schema file of your database using: + +:: + + $ cake schema generate + +This will generate a schema.php file in you ``app/config/sql`` +directory. + +The schema shell will only process tables for which there are models +defined. To force the schema shell to process all the tables, you must +add the ``-f`` option in the command line. + +To later rebuild the database schema from your previously made +schema.php file run: + +:: + + $ cake schema run create + +This will drop and create the tables based on the contents of the +schema.php. + +Schema files can also be used to generate sql dump files. To generate a +sql file containing the ``CREATE TABLE`` statements, run: + +:: + + $ cake schema dump filename.sql + +Where filename.sql is the desired filename for the sql dump. If you omit +filename.sql the sql dump will be output to the console but not written +to a file. + +Migrations with CakePHP schema shell +==================================== + +Migrations allow for versioning of your database schema, so that as you +develop features you have an easy and database agnostic way to +distribute database changes. Migrations are achieved through either SCM +controlled schema files or schema snapshots. Versioning a schema file +with the schema shell is quite easy. If you already have a schema file +created running + +:: + + $ cake schema generate + +Will bring up the following choices: + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +Choosing [s] (snapshot) will create an incremented schema.php. So if you +have schema.php, it will create schema\_2.php and so on. You can then +restore to any of these schema files at any time by running: + +:: + + $ cake schema run update -s 2 + +Where 2 is the snapshot number you wish to run. The schema shell will +prompt you to confirm you wish to perform the ``ALTER`` statements that +represent the difference between the existing database the currently +executing schema file. + +You can perform a dry run by adding a ``-dry`` to your command. + +A schema file generated using the -f option should be updated using the +-f option. diff --git a/en/The-Manual/Core-Helpers.rst b/en/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..b3f94ce01106da1e3c07c846a6f40ab83cdc1d6d --- /dev/null +++ b/en/The-Manual/Core-Helpers.rst @@ -0,0 +1,29 @@ +Core Helpers +############ + +Helpers are the component-like classes for the presentation layer of +your application. They contain presentational logic that is shared +between many views, elements, or layouts. + +This section describes each of the helpers that come with CakePHP such +as Form, Html, JavaScript and RSS. + +Read :doc:`/The-Manual/Developing-with-CakePHP/Helpers` to learn more about helpers and how +you can build your own helpers. + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/en/The-Manual/Core-Helpers/AJAX.rst b/en/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..149aa7b9fb55761ee8f6a65f2f23a6ae954ee664 --- /dev/null +++ b/en/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,776 @@ +AJAX +#### + +The AjaxHelper utilizes the ever-popular Prototype and script.aculo.us +libraries for Ajax operations and client side effects. To use the +AjaxHelper, you must have a current version of the JavaScript libraries +from `www.prototypejs.org `_ and +`http://script.aculo.us `_ placed in +/app/webroot/js/. In addition, you must include the Prototype and +script.aculo.us JavaScript libraries in any layouts or views that +require AjaxHelper functionality. + +You'll need to include the Ajax and Javascript helpers in your +controller: + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +Once you have the javascript helper included in your controller, you can +use the javascript helper link() method to include Prototype and +Scriptaculous: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +Now you can use the Ajax helper in your view: + +:: + + $ajax->whatever(); + +If the :doc:`/The-Manual/Core-Components/Request-Handling` is +included in the controller then CakePHP will automatically apply the +Ajax layout when an action is requested via AJAX + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +AjaxHelper Options +================== + +Most of the methods of the AjaxHelper allow you to supply an $options +array. You can use this array to configure how the AjaxHelper behaves. +Before we cover the specific methods in the helper, let’s look at the +different options available through this special array. You’ll want to +refer to this section as you start using the methods in the AjaxHelper +later on. + +General Options +--------------- + +``$option`` keys + +Description + +``$options['evalScripts']`` + +Determines if script tags in the returned content are evaluated. Set to +*true* by default. + +``$options['frequency']`` + +The number of seconds between interval based checks. + +``$options['indicator']`` + +The DOM id of an element to show while a request is loading and to hide +when a request is completed. + +``$options['position']`` + +To insert rather than replace, use this option to specify an insertion +position of *top*, *bottom*, *after*, or *before*. + +``$options['update']`` + +The id of the DOM element to be updated with returned content. + +``$options['url']`` + +The url of the controller/action that you want to call. + +``$options['type']`` + +Indicate whether the request should be 'synchronous' or 'asynchronous' +(default). + +``$options['with']`` + +A URL-encoded string which will be added to the URL for get methods or +in to the post body for any other method. Example: ``x=1&foo=bar&y=2``. +The parameters will be available in ``$this->params['form']`` or +available in ``$this->data`` depending on formatting. For more +information see the `Prototype +Serialize `_ method. + +Callback Options +---------------- + +Callback options allow you to call JavaScript functions at specific +points in the request process. If you’re looking for a way to inject a +bit of logic before, after, or during your AjaxHelper operations, use +these callbacks to set things up. + +$options keys + +Description + +$options['condition'] + +JavaScript code snippet that needs to evaluate to *true* before request +is initiated. + +$options['before'] + +Executed before request is made. A common use for this callback is to +enable the visibility of a progress indicator. + +$options['confirm'] + +Text to display in a JavaScript confirmation alert before proceeding. + +$options['loading'] + +Callback code to be executed while data is being fetched from server. + +$options['after'] + +JavaScript called immediately after request has run; fires before the +$options['loading'] callback runs. + +$options['loaded'] + +Callback code to be executed when the remote document has been received +by client. + +$options['interactive'] + +Called when the user can interact with the remote document, even though +it has not finished loading. + +$options['complete'] + +JavaScript callback to be run when XMLHttpRequest is complete. + +Methods +======= + +link +---- + +``link(string $title, mixed $href, array $options, string $confirm, boolean $escapeTitle)`` + +Returns a link to a remote action defined by ``$options['url']`` or +``$href`` that's called in the background using XMLHttpRequest when the +link is clicked. The result of that request can then be inserted into a +DOM object whose id can be specified with ``$options['update']``. + +If ``$options['url']`` is blank the href is used instead + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +By default, these remote requests are processed asynchronously during +which various callbacks can be triggered + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Hello World" )' ) + ); + ?> + +To use synchronous processing specify +``$options['type'] = 'synchronous'``. + +To automatically set the ajax layout include the *RequestHandler* +component in your controller + +By default the contents of the target element are replaced. To change +this behaviour set the ``$options['position']`` + +Example: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +``$confirm`` can be used to call up a JavaScript confirm() message +before the request is run. Allowing the user to prevent execution. + +Example: + +:: + +
+
+ link( + 'Delete Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Do you want to delete this post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +This function creates the JavaScript needed to make a remote call. It is +primarily used as a helper for link(). This is not used very often +unless you need to generate some custom scripting. + +The ``$options`` for this function are the same as for the ``link`` +method + +Example: + +:: + +
+
+ + +It can also be assigned to HTML Event Attributes: + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Mouse Over This +
+ +If ``$options['update']`` is not passed, the browser will ignore the +server response. + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +Periodically calls the action at ``$options['url']``, every +``$options['frequency']`` seconds. Usually used to update a specific div +(specified by ``$options['update']``) with the result of the remote +call. Callbacks can be used. + +``remoteTimer`` is the same as the ``remoteFunction`` except for the +extra ``$options['frequency']`` + +Example: + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post', 'complete' => 'alert( "request completed" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +The default ``$options['frequency']`` is 10 seconds + +form +---- + +``form(string $action, string $type, array $options)`` + +Returns a form tag that submits to $action using XMLHttpRequest instead +of a normal HTTP request via $type ('post' or 'get'). Otherwise, form +submission will behave exactly like normal: data submitted is available +at $this->data inside your controllers. If $options['update'] is +specified, it will be updated with the resulting document. Callbacks can +be used. + +The options array should include the model name e.g. + +:: + + $ajax->form('edit','post',array('model'=>'User','update'=>'UserInfoDiv')); + +Alternatively, if you need to cross post to another controller from your +form: + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'User', + 'update'=>'UserInfoDiv', + 'url' => array( + 'controller' => 'comments', + 'action' => 'edit' + ) + ) + )); + +You should not use the ``$ajax->form()`` and ``$ajax->submit()`` in the +same form. If you want the form validation to work properly use the +``$ajax->submit()`` method as shown below. + +submit +------ + +``submit(string $title, array $options)`` + +Returns a submit button that submits the form to ``$options['url']`` and +updates the div specified in ``$options['update']`` + +:: + +
+ create('User'); + echo $form->input('email'); + echo $form->input('name'); + echo $ajax->submit('Submit', array('url'=> array('controller'=>'users', 'action'=>'add'), 'update' => 'testdiv')); + echo $form->end(); + ?> +
+ +Use the ``$ajax->submit()`` method if you want form validation to work +properly. i.e. You want the messages you specify in your validation +rules to show up correctly. + +observeField +------------ + +``observeField(string $field, array $options)`` + +Observes the field with the DOM id specified by $field (every +$options['frequency'] seconds) and makes an XMLHttpRequest when its +contents have changed. + +When no frequency or a small frequency interval (between 0 and 1) is +specified, a prototype ``Form.Element.EventObserver`` will be used +instead of a ``Form.Element.Observer``. The +``Form.Element.EventObserver`` is not timed and will execute at the same +time the value of the element has changed. + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'title', array( 'options' => $titles ) ) ?> + + + observeField( 'PostTitle', + array( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +``observeField`` uses the same options as ``link`` + +The field to send up can be set using ``$options['with']``. This +defaults to ``Form.Element.serialize('$field')``. Data submitted is +available at ``$this->data`` inside your controllers. Callbacks can be +used with this function. + +To send up the entire form when the field changes use +``$options['with'] = Form.serialize( $('Form ID') )`` + +observeForm +----------- + +``observeForm(string $form, array $options)`` + +Similar to observeField(), but operates on an entire form identified by +the DOM id $form. The supplied $options are the same as observeField(), +except the default value of the $options['with'] option evaluates to the +serialized (request string) value of the form. + +autoComplete +------------ + +``autoComplete(string $field, string $url, array $options)`` + +Renders a text field with $field with autocomplete. The remote action at +$url should return a suitable list of autocomplete terms. Often an +unordered list is used for this. First, you need to set up a controller +action that fetches and organizes the data you'll need for your list, +based on user input: + +:: + + function autoComplete() { + //Partial strings will come from the autocomplete field as + //$this->data['Post']['subject'] + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.subject LIKE' => $this->data['Post']['subject'].'%' + ), + 'fields' => array('subject') + ))); + $this->layout = 'ajax'; + } + +Next, create ``app/views/posts/auto_complete.ctp`` that uses that data +and creates an unordered list in (X)HTML: + +:: + +
    + +
  • + +
+ +Finally, utilize autoComplete() in a view to create your auto-completing +form field: + +:: + + create('User', array('url' => '/users/index')); ?> + autoComplete('Post.subject', '/posts/autoComplete')?> + end('View Post')?> + +Once you've got the autoComplete() call working correctly, use CSS to +style the auto-complete suggestion box. You might end up using something +similar to the following: + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +If you want the user to enter a minimum number of characters before the +autocomplete starts, you can use the minChars-Option as follows: + +:: + + $ajax->autoComplete('Post.subject', '/posts/autoComplete',array('minChars' => 3)); + +isAjax +------ + +``isAjax()`` + +Allows you to check if the current request is a Prototype Ajax request +inside a view. Returns a boolean. Can be used for presentational logic +to show/hide blocks of content. + +drag & drop +----------- + +``drag(string $id, array $options)`` + +Makes a Draggable element out of the DOM element specified by $id. For +more information on the parameters accepted in $options see +`http://github.com/madrobby/scriptaculous/wikis/draggable `_. + +Common options might include: + ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++==========================+=======================================================================================================================================================================================================================================================================================================+ +| $options['handle'] | Sets whether the element should only be draggable by an embedded handle. The value must be an element reference or element id or a string referencing a CSS class value. The first child/grandchild/etc. element found within the element that has this CSS class value will be used as the handle. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | If set to true, the element returns to its original position when the drags ends. Revert can also be an arbitrary function reference, called when the drag ends. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | Constrains the drag to either 'horizontal' or 'vertical', leave blank for no constraints. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +Makes the DOM element specified by $id able to accept dropped elements. +Additional parameters can be specified with $options. For more +information see +`http://github.com/madrobby/scriptaculous/wikis/droppables `_. + +Common options might include: + ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++===========================+==========================================================================================================================================================================================+ +| $options['accept'] | Set to a string or javascript array of strings describing CSS classes that the droppable element will accept. The drop element will only accept elements of the specified CSS classes. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | The droppable element will only accept the dragged element if it is contained in the given elements (element ids). Can be a string or a javascript array of id references. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | If set to 'horizontal' or 'vertical', the droppable element will only react to a draggable element if it is overlapping the droparea by more than 50% in the given axis. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | A javascript call back that is called when the dragged element is dropped on the droppable element. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +Makes a drop target that creates an XMLHttpRequest when a draggable +element is dropped on it. The $options array for this function are the +same as those specified for drop() and link(). + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +Creates a directional slider control. For more information see +`http://wiki.github.com/madrobby/scriptaculous/slider `_. + +Common options might include: + +$options keys + +Description + +$options['axis'] + +Sets the direction the slider will move in. 'horizontal' or 'vertical'. +Defaults to horizontal + +$options['handleImage'] + +The id of the image that represents the handle. This is used to swap out +the image src with disabled image src when the slider is enabled. Used +in conjunction with handleDisabled. + +$options['increment'] + +Sets the relationship of pixels to values. Setting to 1 will make each +pixel adjust the slider value by one. + +$options['handleDisabled'] + +The id of the image that represents the disabled handle. This is used to +change the image src when the slider is disabled. Used in conjunction +handleImage. + +$options['change'] + $options['onChange'] + +JavaScript callback fired when the slider has finished moving, or has +its value changed. The callback function receives the slider's current +value as a parameter. + +$options['slide'] + $options['onSlide'] + +JavaScript callback that is called whenever the slider is moved by +dragging. It receives the slider's current value as a parameter. + +editor +------ + +``editor(string $id, string $url, array $options)`` + +Creates an in-place editor at DOM id. The supplied ``$url`` should be an +action that is responsible for saving element data. For more information +and demos see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_. + +Common options might include: + +$options keys + +Description + +``$options['collection']`` + +Activate the 'collection' mode of in-place editing. +$options['collection'] takes an array which is turned into options for +the select. To learn more about collection see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_. + +``$options['callback']`` + +A function to execute before the request is sent to the server. This can +be used to format the information sent to the server. The signature is +``function(form, value)`` + +``$options['okText']`` + +Text of the submit button in edit mode + +``$options['cancelText']`` + +The text of the link that cancels editing + +``$options['savingText']`` + +The text shown while the text is sent to the server + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +The row height of the input field + +``$options['cols']`` + +The number of columns the text area should span + +``$options['size']`` + +Synonym for ‘cols’ when using single-line + +``$options['highlightcolor']`` + +The highlight color + +``$options['highlightendcolor']`` + +The color which the highlight fades to + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +Example + +:: + +
Text To Edit
+ editor( + "in_place_editor_id", + array( + 'controller' => 'Posts', + 'action' => 'update_title', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +Makes a list or group of floated objects contained by $id sortable. The +options array supports a number of parameters. To find out more about +sortable see +`http://wiki.github.com/madrobby/scriptaculous/sortable `_. + +:: + +
+
+ Element 1 +
+
+ Element 2 +
+
+ Element 3 +
+
+ + sortable('sortableContainer',array('tag'=>'div','only'=>'sortableItem','onUpdate'=>'writeupdate')); + ?> + +Make sure that you do not include the parenthesis on the onUpdate +callback, or it will not execute. + +Common options might include: + +$options keys + +Description + +$options['tag'] + +Indicates what kind of child elements of the container will be made +sortable. Defaults to 'li'. + +$options['only'] + +Allows for further filtering of child elements. Accepts a CSS class. + +$options['overlap'] + +Either 'vertical' or 'horizontal'. Defaults to vertical. + +$options['constraint'] + +Restrict the movement of the draggable elements. accepts 'horizontal' or +'vertical'. Defaults to vertical. + +$options['handle'] + +Makes the created Draggables use handles, see the handle option on +Draggables. + +$options['onUpdate'] + +Called when the drag ends and the Sortable's order is changed in any +way. When dragging from one Sortable to another, the callback is called +once on each Sortable. + +$options['hoverclass'] + +Give the created droppable a hoverclass. + +$options['ghosting'] + +If set to true, dragged elements of the sortable will be cloned and +appear as a ghost, instead of directly manipulating the original +element. diff --git a/en/The-Manual/Core-Helpers/Cache.rst b/en/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..398aaa6096603f1bf4ff3ff050e4088f3d5232e4 --- /dev/null +++ b/en/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,188 @@ +Cache +##### + +The Cache helper assists in caching entire layouts and views, saving +time repetitively retrieving data. View Caching in Cake temporarily +stores parsed layouts and views with the storage engine of choice. It +should be noted that the Cache helper works quite differently than other +helpers. It does not have methods that are directly called. Instead a +view is marked with cache tags indicating which blocks of content should +not be cached. + +When a URL is requested, Cake checks to see if that request string has +already been cached. If it has, the rest of the url dispatching process +is skipped. Any nocache blocks are processed normally and the view is +served. This creates a big savings in processing time for each request +to a cached URL as minimal code is executed. If Cake doesn't find a +cached view, or the cache has expired for the requested URL it continues +to process the request normally. + +General Caching +=============== + +Caching is intended to be a means of temporary storage to help reduce +load on the server. For example you could store the results of a +time-expensive database query so that it is not required to run on every +page load. + +With this in mind caching is not permanent storage and should never be +used to permanently store anything. And only cache things that can be +regenerated when needed. + +Cache Engines in Cake +===================== + +New in 1.2 are several cache engines or cache backends. These interface +transparently with the cache helper, allowing you to store view caches +in a multitude of media without worrying about the specifics of that +media. The choice of cache engine is controlled through the +app/config/core.php config file. Most options for each caching engine +are listed in the core.php config file and more detailed information on +each caching engine can be found in the Caching Section. + +File + +The File Engine is the default caching engine used by cake. It writes +flat files to the filesystem and it has several optional parameters but +works well with the defaults. + +APC + +The APC engine implements the `Alternative PHP +Cache `_ opcode Cacher. Like XCache, this engine +caches the compiled PHP opcode. + +XCache + +The XCache caching engine is functionally similar to APC other than it +implements the `XCache `_ opcode caching +engine. It requires the entry of a user and password to work properly. + +Memcache + +The Memcache engine works with a memcaching server allowing you to +create a cache object in system memory. More information on memcaching +can be found on `php.net `_ and +`memcached `_ + +Cache Helper Configuration +========================== + +View Caching and the Cache Helper have several important configuration +elements. They are detailed below. + +To use the cache helper in any view or controller, you must first +uncomment and set Configure::Cache.check to true in ``core.php`` of your +app/config folder. If this is not set to true, then the cache will not +be checked or created. + +:: + + Configure::write('Cache.check', true); + +Caching in the Controller +========================= + +Any controllers that utilize caching functionality need to include the +CacheHelper in their $helpers array. + +:: + + var $helpers = array('Cache'); + +You also need to indicate which actions need caching, and how long each +action will be cached. This is done through the $cacheAction variable in +your controllers. $cacheAction should be set to an array which contains +the actions you want cached, and the duration in seconds you want those +views cached. The time value can be expressed in a strtotime() format. +(ie. "1 hour", or "3 minutes"). + +Using the example of an ArticlesController, that receives a lot of +traffic that needs to be cached. + +Cache frequently visited Articles for varying lengths of time + +:: + + var $cacheAction = array( + 'view/23' => 21600, + 'view/48' => 36000, + 'view/52' => 48000 + ); + +Remember to use your routes in the $cacheAction if you have any. + +Cache an entire action in this case a large listing of articles + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +Cache every action in the controller using a strtotime() friendly time +to indicate Controller wide caching time. + +:: + + var $cacheAction = "1 hour"; + +You can also enable controller/component callbacks for cached views +created with ``CacheHelper``. To do so you must use the array format for +``$cacheAction`` and create an array like the following: + +:: + + var $cacheAction = array( + 'view' => array('callbacks' => true, 'duration' => 21600), + 'add' => array('callbacks' => true, 'duration' => 36000), + 'index' => array('callbacks' => true, 'duration' => 48000) + ); + +By setting ``callbacks => true`` you tell CacheHelper that you want the +generated files to create the components and models for the controller. +As well as, fire the component initialize, controller beforeFilter, and +component startup callbacks. + +callbacks => true partly defeats the purpose of caching. This is also +the reason it is disabled by default. + +Marking Non-Cached Content in Views +=================================== + +There will be times when you don't want an *entire* view cached. For +example, certain parts of the page may look different whether a user is +currently logged in or browsing your site as a guest. + +To indicate blocks of content that are *not* to be cached, wrap them in +`` `` like so: + +:: + + + check('User.name')) : ?> + Welcome, read('User.name')?>. + + link('Login', 'users/login')?> + + + +It should be noted that once an action is cached, the controller method +for the action will not be called - otherwise what would be the point of +caching the page. Therefore, it is not possible to wrap +`` `` around variables which are set from +the controller as they will be *null*. + +Clearing the Cache +================== + +It is important to remember that the Cake will clear a cached view if a +model used in the cached view is modified. For example, if a cached view +uses data from the Post model, and there has been an INSERT, UPDATE, or +DELETE query made to a Post, the cache for that view is cleared, and new +content is generated on the next request. + +If you need to manually clear the cache, you can do so by calling +Cache::clear(). This will clear **all** cached data, excluding cached +view files. If you need to clear the cached view files, use +``clearCache()``. diff --git a/en/The-Manual/Core-Helpers/Form.rst b/en/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..f4b1a76ed3fe70fd05f620aa1da8687a7d6878c6 --- /dev/null +++ b/en/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,1378 @@ +Form +#### + +The FormHelper is a new addition to CakePHP. Most of the heavy lifting +in form creation is now done using this new class, rather than (now +deprecated) methods in the HtmlHelper. The FormHelper focuses on +creating forms quickly, in a way that will streamline validation, +re-population and layout. The FormHelper is also flexible - it will do +almost everything for you automagically, or you can use specific methods +to get only what you need. + +Creating Forms +============== + +The first method you’ll need to use in order to take advantage of the +FormHelper is ``create()``. This special method outputs an opening form +tag. + +``create(string $model = null, array $options = array())`` + +All parameters are optional. If ``create()`` is called with no +parameters supplied, it assumes you are building a form that submits to +the current controller, via either the ``add()`` or ``edit()`` action. +The default method for form submission is POST. The form element also is +returned with a DOM ID. The ID is generated using the name of the model, +and the name of the controller action, CamelCased. If I were to call +``create()`` inside a UsersController view, I’d see something like the +following output in the rendered view: + +:: + +
+ +You can also pass ``false`` for ``$model``. This will place your form +data into the array: ``$this->data`` (instead of in the sub-array: +``$this->data['Model']``). This can be handy for short forms that may +not represent anything in your database. + +The ``create()`` method allows us to customize much more using the +parameters, however. First, you can specify a model name. By specifying +a model for a form, you are creating that form's *context*. All fields +are assumed to belong to this model (unless otherwise specified), and +all models referenced are assumed to be associated with it. If you do +not specify a model, then it assumes you are using the default model for +the current controller. + +:: + + create('Recipe'); ?> + + //Output: + + +This will POST the form data to the ``add()`` action of +RecipesController. However, you can also use the same logic to create an +edit form. The FormHelper uses the ``$this->data`` property to +automatically detect whether to create an add or edit form. If +``$this->data`` contains an array element named after the form's model, +and that array contains a non-empty value of the model's primary key, +then the FormHelper will create an edit form for that record. For +example, if we browse to http://site.com/recipes/edit/5, we might get +the following: + +:: + + // controllers/recipes_controller.php: + data)) { + $this->data = $this->Recipe->findById($id); + } else { + // Save logic goes here + } + } + ?> + + // views/recipes/edit.ctp: + + // Since $this->data['Recipe']['id'] = 5, we should get an edit form + create('Recipe'); ?> + + //Output: + + + +Since this is an edit form, a hidden input field is generated to +override the default HTTP method. + +The ``$options`` array is where most of the form configuration happens. +This special array can contain a number of different key-value pairs +that affect the way the form tag is generated. + +$options[‘type’] +---------------- + +This key is used to specify the type of form to be created. Valid values +include ‘post’, ‘get’, ‘file’, ‘put’ and ‘delete’. + +Supplying either ‘post’ or ‘get’ changes the form submission method +accordingly. + +:: + + create('User', array('type' => 'get')); ?> + + //Output: + + +Specifying ‘file’ changes the form submission method to ‘post’, and +includes an enctype of “multipart/form-data” on the form tag. This is to +be used if there are any file elements inside the form. The absence of +the proper enctype attribute will cause the file uploads not to +function. + +:: + + create('User', array('type' => 'file')); ?> + + //Output: + + +When using ‘put’ or ‘delete’, your form will be functionally equivalent +to a 'post' form, but when submitted, the HTTP request method will be +overridden with 'PUT' or 'DELETE', respectively. This allows CakePHP to +emulate proper REST support in web browsers. + +$options[‘action’] +------------------ + +The action key allows you to point the form to a specific action in your +current controller. For example, if you’d like to point the form to the +login() action of the current controller, you would supply an $options +array like the following: + +:: + + create('User', array('action' => 'login')); ?> + + //Output: + +
+ +$options[‘url’] +--------------- + +If the desired form action isn’t in the current controller, you can +specify a URL for the form action using the ‘url’ key of the $options +array. The supplied URL can be relative to your CakePHP application, or +can point to an external domain. + +:: + + create(null, array('url' => '/recipes/add')); ?> + // or + create(null, array('url' => array('controller' => 'recipes', 'action' => 'add'))); ?> + + + //Output: +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + //Output: + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +$options[‘default’] +------------------- + +If ‘default’ has been set to boolean false, the form’s submit action is +changed so that pressing the submit button does not submit the form. If +the form is meant to be submitted via AJAX, setting ‘default’ to false +suppresses the form’s default behavior so you can grab the data and +submit it via AJAX instead. + +Closing the Form +================ + +The FormHelper also includes an end() method that completes the form +markup. Often, end() only outputs a closing form tag, but using end() +also allows the FormHelper to insert needed hidden form elements other +methods may be depending on. + +:: + + create(); ?> + + + + end(); ?> + +If a string is supplied as the first parameter to end(), the FormHelper +outputs a submit button named accordingly along with the closing form +tag. + +:: + + end('Finish'); ?> + + +Will output: + +:: + + +
+ +
+ + +You can specify detail settings by passing an array to end(). + +:: + + 'Update', + 'label' => 'Update!', + 'div' => array( + 'class' => 'glass-pill', + ) + ); + echo $form->end($options); + +Will output: + +:: + +
+ +See the API for further details. + +Automagic Form Elements +======================= + +First, let’s look at some of the more automatic form creation methods in +the FormHelper. The main method we’ll look at is input(). This method +will automatically inspect the model field it has been supplied in order +to create an appropriate input for that field. + +input(string $fieldName, array $options = array()) + ++--------------------------------------------------+--------------------------------------------------------+ +| Column Type | Resulting Form Field | ++==================================================+========================================================+ +| string (char, varchar, etc.) | text | ++--------------------------------------------------+--------------------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++--------------------------------------------------+--------------------------------------------------------+ +| text | textarea | ++--------------------------------------------------+--------------------------------------------------------+ +| text, with name of password, passwd, or psword | password | ++--------------------------------------------------+--------------------------------------------------------+ +| date | day, month, and year selects | ++--------------------------------------------------+--------------------------------------------------------+ +| datetime, timestamp | day, month, year, hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ +| time | hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ + +For example, let’s assume that my User model includes fields for a +username (varchar), password (varchar), approved (datetime) and quote +(text). I can use the input() method of the FormHelper to create +appropriate inputs for all of these form fields. + +:: + + create(); ?> + + input('username'); //text + echo $form->input('password'); //password + echo $form->input('approved'); //day, month, year, hour, minute, meridian + echo $form->input('quote'); //textarea + ?> + + end('Add'); ?> + +A more extensive example showing some options for a date field: + +:: + + echo $form->input('birth_dt', array( 'label' => 'Date of birth' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +Besides the specific input options found below you can specify any html +attribute (for instance onfocus). For more information on $options and +$htmlAttributes see :doc:`/The-Manual/Core-Helpers/HTML`. + +And to round off, here's an example for creating a hasAndBelongsToMany +select. Assume that User hasAndBelongsToMany Group. In your controller, +set a camelCase plural variable (group -> groups in this case, or +ExtraFunkyModel -> extraFunkyModels) with the select options. In the +controller action you would put the following: + +:: + + $this->set('groups', $this->User->Group->find('list')); + +And in the view a multiple select can be expected with this simple code: + +:: + + echo $form->input('Group'); + +If you want to create a select field while using a belongsTo- or +hasOne-Relation, you can add the following to your Users-controller +(assuming your User belongsTo Group): + +:: + + $this->set('groups', $this->User->Group->find('list')); + +Afterwards, add the following to your form-view: + +:: + + echo $form->input('group_id'); + +If your model name consists of two or more words, e.g., "UserGroup", +when passing the data using set() you should name your data in a +pluralised and camelCased format as follows: + +:: + + $this->set('userGroups', $this->UserGroup->find('list')); + // or + $this->set('reallyInappropriateModelNames', $this->ReallyInappropriateModelName->find('list')); + +Field naming convention +----------------------- + +The Form helper is pretty smart. Whenever you specify a field name with +the form helper methods, it'll automatically use the current model name +to build an input with a format like the following: + +:: + + + +You can manually specify the model name by passing in +Modelname.fieldname as the first parameter. + +:: + + echo $form->input('Modelname.fieldname'); + +If you need to specify multiple fields using the same field name, thus +creating an array that can be saved in one shot with saveAll(), use the +following convention: + +:: + + input('Modelname.0.fieldname'); + echo $form->input('Modelname.1.fieldname'); + ?> + + + + +$options[‘type’] +---------------- + +You can force the type of an input (and override model introspection) by +specifying a type. In addition to the field types found in the :doc:`/The-Manual/Core-Helpers/Form`, you can also create ‘file’, +and ‘password’ inputs. + +:: + + input('field', array('type' => 'file')); ?> + + Output: + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +Use these keys if you need to inject some markup inside the output of +the input() method. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + ));?> + + Output: + +
+ --before-- + + --between--- + + --after-- +
+ +For radio type input the 'separator' attribute can be used to inject +markup to separate each input/label pair. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ +For ``date`` and ``datetime`` type elements the 'separator' attribute +can be used to change the string between select elements. Defaults to +'-'. + +$options[‘options’] +------------------- + +This key allows you to manually specify options for a select input, or +for a radio group. Unless the ‘type’ is specified as ‘radio’, the +FormHelper will assume that the target output is a select input. + +:: + + input('field', array('options' => array(1,2,3,4,5))); ?> + +Output: + +:: + +
+ + +
+ +Options can also be supplied as key-value pairs. + +:: + + input('field', array('options' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2', + 'Value 3'=>'Label 3' + ))); ?> + +Output: + +:: + +
+ + +
+ +If you would like to generate a select with optgroups, just pass data in +hierarchical format. Works on multiple checkboxes and radio buttons too, +but instead of optgroups wraps elements in fieldsets. + +:: + + input('field', array('options' => array( + 'Label1' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2' + ), + 'Label2' => array( + 'Value 3'=>'Label 3' + ) + ))); ?> + +Output: + +:: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +If ‘multiple’ has been set to true for an input that outputs a select, +the select will allow multiple selections. Alternatively set ‘multiple’ +to ‘checkbox’ to output a list of related check boxes. + +:: + + $form->input('Model.field', array( 'type' => 'select', 'multiple' => true )); + $form->input('Model.field', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +Defines the maximum number of characters allowed in a text input. + +$options[‘div’] +--------------- + +Use this option to set attributes of the input's containing div. Using a +string value will set the div's class name. An array will set the div's +attributes to those specified by the array's keys/values. Alternatively, +you can set this key to false to disable the output of the div. + +Setting the class name: + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +Output: + +:: + +
+ + +
+ +Setting multiple attributes: + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +Output: + +:: + +
+ + +
+ +Disabling div output: + +:: + + input('User.name', array('div' => false));?> + +Output: + +:: + + + + +$options[‘label’] +----------------- + +Set this key to the string you would like to be displayed within the +label that usually accompanies the input. + +:: + + input( 'User.name', array( 'label' => 'The User Alias' ) );?> + +Output: + +:: + +
+ + +
+ +Alternatively, set this key to false to disable the output of the label. + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Output: + +:: + +
+ +
+ +Set this to an array to provide additional options for the ``label`` +element. If you do this, you can use a ``text`` key in the array to +customize the label text. + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'The User Alias') ) ); ?> + +Output: + +:: + +
+ + +
+ +$options['legend'] +------------------ + +Some inputs like radio buttons will be automatically wrapped in a +fieldset with a legend title derived from the fields name. The title can +be overridden with this option. Setting this option to false will +completely eliminate the fieldset. + +$options[‘id’] +-------------- + +Set this key to force the value of the DOM id for the input. + +$options['error'] +----------------- + +Using this key allows you to override the default model error messages +and can be used, for example, to set i18n messages. It has a number of +suboptions which control the wrapping element, wrapping element class +name, and whether HTML in the error message will be escaped. + +To disable error message output set the error key to false. + +:: + + $form->input('Model.field', array('error' => false)); + +To modify the wrapping element type and its class, use the following +format: + +:: + + $form->input('Model.field', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +To prevent HTML being automatically escaped in the error message output, +set the escape suboption to false: + +:: + + $form->input('Model.field', array('error' => array('escape' => false))); + +To override the model error messages use an associate array with the +keyname of the validation rule: + +:: + + $form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough', true) ))); + +As seen above you can set the error message for each validation rule you +have in your models. In addition you can provide i18n messages for your +forms. + +$options['default'] +------------------- + +Used to set a default value for the input field. The value is used if +the data passed to the form does not contain a value for the field (or +if no data is passed at all). + +Example usage: + +:: + + input('ingredient', array('default'=>'Sugar')); + ?> + +Example with select field (Size "Medium" will be selected as default): + +:: + + 'Small', 'm'=>'Medium', 'l'=>'Large'); + echo $form->input('size', array('options'=>$sizes, 'default'=>'m')); + ?> + +You cannot use ``default`` to check a checkbox - instead you might set +the value in ``$this->data`` in your controller, ``$form->data`` in your +view, or set the input option ``checked`` to true. + +Date and datetime fields' default values can be set by using the +'selected' key. + +$options[‘selected’] +-------------------- + +Used in combination with a select-type input (i.e. For types select, +date, time, datetime). Set ‘selected’ to the value of the item you wish +to be selected by default when the input is rendered. + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +The selected key for date and datetime inputs may also be a UNIX +timestamp. + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +These two keys specify the number of rows and columns in a textarea +input. + +:: + + echo $form->input('textarea', array('rows' => '5', 'cols' => '5')); + +Output: + +:: + +
+ + +
+ +$options[‘empty’] +----------------- + +If set to true, forces the input to remain empty. + +When passed to a select list, this creates a blank option with an empty +value in your drop down list. If you want to have a empty value with +text displayed instead of just a blank option, pass in a string to +empty. + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(choose one)')); ?> + +Output: + +:: + +
+ + +
+ +If you need to set the default value in a password field to blank, use +'value' => '' instead. + +Options can also supplied as key-value pairs. + +$options[‘timeFormat’] +---------------------- + +Used to specify the format of the select inputs for a time-related set +of inputs. Valid values include ‘12’, ‘24’, and ‘none’. + +$options[‘dateFormat’] +---------------------- + +Used to specify the format of the select inputs for a date-related set +of inputs. Valid values include ‘DMY’, ‘MDY’, ‘YMD’, and ‘NONE’. + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +Used in combination with a date/datetime input. Defines the lower and/or +upper end of values shown in the years select field. + +$options['interval'] +-------------------- + +This option specifies the number of minutes between each option in the +minutes select box. + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +Would create 4 options in the minute select. One for each 15 minutes. + +$options['class'] +----------------- + +You can set the classname for an input field using ``$options['class']`` + +:: + + echo $form->input('title', array('class' => 'custom-class')); + +File Fields +=========== + +To add a file upload field to a form, you must first make sure that the +form enctype is set to "multipart/form-data", so start off with a create +function such as the following. + +:: + + echo $form->create('Document', array('enctype' => 'multipart/form-data') ); + // OR + echo $form->create('Document', array('type' => 'file')); + +Next add either of the two lines to your form view file. + +:: + + echo $form->input('Document.submittedfile', array('between'=>'
','type'=>'file')); + + // or + + echo $form->file('Document.submittedfile'); + +Due to the limitations of HTML itself, it is not possible to put default +values into input fields of type 'file'. Each time the form is +displayed, the value inside will be empty. + +Upon submission, file fields provide an expanded data array to the +script receiving the form data. + +For the example above, the values in the submitted data array would be +organized as follows, if the CakePHP was installed on a Windows server. +'tmp\_name' will have a different path in a Unix environment. + +:: + + + $this->data['Document']['submittedfile'] = array( + 'name' => conference_schedule.pdf + 'type' => application/pdf + 'tmp_name' => C:/WINDOWS/TEMP/php1EE.tmp + 'error' => 0 + 'size' => 41737 + ); + +This array is generated by PHP itself, so for more detail on the way PHP +handles data passed via file fields `read the PHP manual section on file +uploads `_. + +Validating Uploads +------------------ + +Below is an example validation method you could define in your model to +validate whether a file has been successfully uploaded. + +:: + + // Based on comment 8 from: http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty( $val['tmp_name']) && $val['tmp_name'] != 'none')) { + return is_uploaded_file($val['tmp_name']); + } + return false; + } + +Form Element-Specific Methods +============================= + +The rest of the methods available in the FormHelper are for creating +specific form elements. Many of these methods also make use of a special +$options parameter. In this case, however, $options is used primarily to +specify HTML tag attributes (such as the value or DOM id of an element +in the form). + +:: + + text('username', array('class' => 'users')); ?> + +Will output: + +:: + + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +Creates a checkbox form element. This method also generates an +associated hidden form input to force the submission of data for the +specified field. + +:: + + checkbox('done'); ?> + +Will output: + +:: + + + + +button +------ + +``button(string $title, array $options = array())`` + +Creates an HTML button with the specified title and a default type of +"button". Setting ``$options['type']`` will output one of the three +possible button types: + +#. button: Creates a standard push button (the default). +#. reset: Creates a form reset button. +#. submit: Same as the ``$form->submit`` method. + +:: + + button('A Button'); + echo $form->button('Another Button', array('type'=>'button')); + echo $form->button('Reset the Form', array('type'=>'reset')); + echo $form->button('Submit Form', array('type'=>'submit')); + ?> + +Will output: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, mixed $showEmpty)`` + +Creates a select element populated with the years from ``$minYear`` to +``$maxYear``, with the ``$selected`` year selected by default. +``$selected`` can either be a four-digit year (e.g. 2004) or string +``'now'``. HTML attributes may be supplied in ``$attributes``. + +:: + + year('purchased', 2005, 2009); + ?> + +Will output: + +:: + + + +If ``$showEmpty`` is false, the select will not include an empty option. +If ``$showEmpty`` is a string, it will be used as empty option's name. + +:: + + year('returned', 2008, 2010, null, null, 'Select a year'); + ?> + +Will output: + +:: + + + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with month names. + +:: + + month('mob'); + ?> + +Will output: + +:: + + + +You can pass in your own array of months to be used by setting the +'monthNames' attribute (CakePHP 1.3 only), or have months displayed as +numbers by passing false. (Note: the default months are +internationalized and can be translated using localization.) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a set of select inputs for date and time. Valid values for +$dateformat are ‘DMY’, ‘MDY’, ‘YMD’ or ‘NONE’. Valid values for +$timeFormat are ‘12’, ‘24’, and ‘NONE’. + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the (numerical) days of the +month. + +To create an empty option with prompt text of your choosing (e.g. the +first option is 'Day'), you can supply the text as the final parameter +as follows: + +:: + + day('created'); + ?> + +Will output: + +:: + + + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the hours of the day. + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the minutes of the hour. + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with ‘am’ and ‘pm’. + +error +----- + +``error(string $fieldName, string $text, array $options)`` + +Shows a validation error message, specified by $text, for the given +field, in the event that a validation error has occurred. + +Options: + +- 'escape' bool Whether or not to html escape the contents of the + error. +- 'wrap' mixed Whether or not the error message should be wrapped in a + div. If a string, will be used as the HTML tag to use. +- 'class' string The classname for the error message + +file +---- + +``file(string $fieldName, array $options)`` + +Creates a file input. + +:: + + create('User',array('type'=>'file')); + echo $form->file('avatar'); + ?> + +Will output: + +:: + +
+ + +When using ``$form->file()``, remember to set the form encoding-type, by +setting the type option to 'file' in ``$form->create()`` + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +Creates a hidden form input. Example: + +:: + + hidden('id'); + ?> + +Will output: + +:: + + + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +Returns true if the supplied $fieldName has an active validation error. + +:: + + isFieldError('gender')){ + echo $form->error('gender'); + } + ?> + +When using ``$form->input()``, errors are rendered by default. + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +Creates a label tag, populated with $text. + +:: + + label('status'); + ?> + +Will output: + +:: + + + +password +-------- + +``password(string $fieldName, array $options)`` + +Creates a password field. + +:: + + password('password'); + ?> + +Will output: + +:: + + + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +Creates a radio button input. Use ``$attributes['value']`` to set which +value should be selected default. + +Use ``$attributes['separator']`` to specify HTML in between radio +buttons (e.g.
). + +Radio elements are wrapped with a label and fieldset by default. Set +``$attributes['legend']`` to false to remove them. + +:: + + 'Male','F'=>'Female'); + $attributes=array('legend'=>false); + echo $form->radio('gender',$options,$attributes); + ?> + +Will output: + +:: + + + + + + + +If for some reason you don't want the hidden input, setting +``$attributes['value']`` to a selected value or boolean false will do +just that. + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element, populated with the items in ``$options``, with +the option specified by ``$selected`` shown as selected by default. Set +``$showEmpty`` to false if you do not want an empty select option to be +displayed. + +:: + + 'Male','F'=>'Female'); + echo $form->select('gender',$options) + ?> + +Will output: + +:: + + + +submit +------ + +``submit(string $caption, array $options)`` + +Creates a submit button with caption ``$caption``. If the supplied +``$caption`` is a URL to an image (it contains a ‘.’ character), the +submit button will be rendered as an image. + +It is enclosed between ``div`` tags by default; you can avoid this by +declaring ``$options['div'] = false``. + +:: + + submit(); + ?> + +Will output: + +:: + +
+ +You can also pass a relative or absolute url to an image for the caption +parameter instead of caption text. + +:: + + submit('ok.png'); + ?> + +Will output: + +:: + +
+ +text +---- + +``text(string $fieldName, array $options)`` + +Creates a text input field. + +:: + + text('first_name'); + ?> + +Will output: + +:: + + + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +Creates a textarea input field. + +:: + + textarea('notes'); + ?> + +Will output: + +:: + + + diff --git a/en/The-Manual/Core-Helpers/HTML.rst b/en/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..f65aba6544e4d6df8156511bf8eb16c9da5db4d8 --- /dev/null +++ b/en/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,634 @@ +HTML +#### + +The role of the HtmlHelper in CakePHP is to make HTML-related options +easier, faster, and more resilient to change. Using this helper will +enable your application to be more light on its feet, and more flexible +on where it is placed in relation to the root of a domain. + +The HtmlHelper's role has changed significantly since CakePHP 1.1. Form +related methods have been deprecated and moved to the new FormHelper. If +you're looking for help with HTML forms, check out the new FormHelper. + +Before we look at HtmlHelper's methods, you'll need to know about a few +configuration and usage situations that will help you use this class. +First in an effort to assuage those who dislike short tags () or +many echo() calls in their view code all methods of HtmlHelper are +passed to the output() method. If you wish to enable automatic output of +the generated helper HTML you can simply implement output() in your +AppHelper. + +:: + + function output($str) { + echo $str; + } + +Doing this will remove the need to add echo statements to your view +code. + +Many HtmlHelper methods also include a $htmlAttributes parameter, that +allow you to tack on any extra attributes on your tags. Here are a few +examples of how to use the $htmlAttributes parameter: + +:: + + Desired attributes: + Array parameter: array('class'=>'someClass') + + Desired attributes: + Array parameter: array('name' => 'foo', 'value' => 'bar') + +The HtmlHelper is available in all views by default. If you're getting +an error informing you that it isn't there, it's usually due to its name +being missing from a manually configured $helpers controller variable. + +Inserting Well-Formatted elements +================================= + +The most important task the HtmlHelper accomplishes is creating well +formed markup. Don't be afraid to use it often - you can cache views in +CakePHP in order to save some CPU cycles when views are being rendered +and delivered. This section will cover some of the methods of the +HtmlHelper and how to use them. + +charset +------- + +``charset(string $charset=null)`` + +Used to create a meta tag specifying the document's character. Defaults +to UTF-8. + +:: + + + charset(); ?> + +Will output: + +:: + + + +Alternatively, + +:: + + charset('ISO-8859-1'); ?> + +Will output: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +Creates a link(s) to a CSS style-sheet. If $inline is set to false, the +link tags are added to the $scripts\_for\_layout variable which you can +print inside the head tag of the document. + +This method of CSS inclusion assumes that the CSS file specified resides +inside the /app/webroot/css directory. + +:: + + css('forms'); ?> + +Will output: + +:: + + + +The first parameter can be an array to include multiple files. + +:: + + css(array('forms','tables','menu')); ?> + +Will output: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +This method is handy for linking to external resources like RSS/Atom +feeds and favicons. Like css(), you can specify whether or not you'd +like this tag to appear inline or in the head tag using the fourth +parameter. + +If you set the "type" attribute using the $htmlAttributes parameter, +CakePHP contains a few shortcuts: + ++--------+------------------------+ +| type | translated value | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> //Output (line breaks added)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + + //Output (line breaks added) + + +This method can also be used to add the meta keywords and descriptions. +Example: + +:: + + meta( + 'keywords', + 'enter any meta keyword here' + );?> + //Output + // + + meta( + 'description', + 'enter any meta description here' + );?> + + //Output + +If you want to add a custom meta tag then the first parameter should be +set to an array. To output a robots noindex tag use the following code: + +:: + + echo $html->meta(array('name' => 'robots', 'content' => 'noindex')); + +docType +------- + +``docType(string $type = 'xhtml-strict')`` + +Returns a (X)HTML doctype tag. Supply the doctype according to the +following table: + ++----------------+-----------------------+ +| type | translated value | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Builds CSS style definitions based on the keys and values of the array +passed to the method. Especially handy if your CSS file is dynamic. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Will output: + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Creates a formatted image tag. The path supplied should be relative to +/app/webroot/img/. + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Will output: + +:: + + CakePHP + +To create an image link specify the link destination using the ``url`` +option in ``$htmlAttributes``. + +:: + + image("recipes/6.jpg", array( + "alt" => "Brownies", + 'url' => array('controller' => 'recipes', 'action' => 'view', 6) + )); ?> + +Will output: + +:: + + + Brownies + + +You can also use this alternate method to create an image link, by +assigning the image to a variable (e.g. $image), and passing it to +``$html->link()`` as the first argument: + +:: + + image('recipes/6.jpg', array( + 'alt' => 'Brownies', + )); + + //$image is passed as the first argument instead of link text + echo $html->link($image, array( + 'controller' => 'recipies', + 'action' => 'view', + 6 + ), + array( + 'escape' => false //important so htmlHelper doesn't escape you image link + ) + ); + ?> + +This is useful if you want to keep your link and image a bit more +separate, or if you want to sneak some markup into your link. Be sure to +pass ``'escape' => false`` in the options array for +`` $html->link($string, $url, $options)`` to prevent htmlHelper from +escaping the code. + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +General purpose method for creating HTML links. Use ``$htmlAttributes`` +to specify attributes for the element. + +:: + + link('Enter', '/pages/home', array('class'=>'button','target'=>'_blank')); ?> + +Will output: + +:: + + + Enter + +Specify ``$confirmMessage`` to display a javascript ``confirm()`` +dialog. + +:: + + link( + 'Delete', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "Are you sure you wish to delete this recipe?" + );?> + +Will output: + +:: + + + Delete + +Query strings can also be created with ``link()``. + +:: + + link('View image', array( + 'controller' => 'images', + 'action' => 'view', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Will output: + +:: + + + View image + +HTML special characters in ``$title`` will be converted to HTML +entities. To disable this conversion, set the escape option to false in +the ``$htmlAttributes``, or set ``$escapeTitle`` to false. + +:: + + link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + null, null, false + ); + ?> + +Both will output: + +:: + + + Brownies + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns text wrapped in a specified tag. If no text is specified then +only the opening is returned. + +:: + + tag('span', 'Hello World.', array('class' => 'welcome'));?> + + //Output + Hello World + + //No text specified. + tag('span', null, array('class' => 'welcome'));?> + + //Output + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Used for creating div-wrapped sections of markup. The first parameter +specifies a CSS class, and the second is used to supply the text to be +wrapped by div tags. If the last parameter has been set to true, $text +will be printed HTML-escaped. + +If no text is specified, only an opening div tag is returned. + +:: + + + div('error', 'Please enter your credit card number.');?> + + //Output +
Please enter your credit card number.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns a text wrapped in a CSS-classed

tag. If no text is supplied, +only a starting

tag is returned. + +:: + + para(null, 'Hello World.');?> + + //Output +

Hello World.

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Creates a row of table header cells to be placed inside of tags. + +:: + + tableHeaders(array('Date','Title','Active'));?> //Output + + + tableHeaders( + array('Date','Title','Active'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + //Output + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Creates table cells, in rows, assigning attributes differently for +odd- and even-numbered rows. Wrap a single table cell within an array() +for specific + + + + tableCells(array( + array('Jul 7th, 2007', array('Best Brownies', array('class'=>'highlight')) , 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', array('No', array('id'=>'special'))), + )); + ?> + + //Output + + + + + tableCells( + array( + array('Red', 'Apple'), + array('Orange', 'Orange'), + array('Yellow', 'Banana'), + ), + array('class' => 'darker') + ); + ?> + + //Output + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Returns an URL pointing to a combination of controller and action. If +$url is empty, it returns the REQUEST\_URI, otherwise it generates the +url for the controller and action combo. If full is true, the full base +URL will be prepended to the result. + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "bar"));?> + + // Output + /posts/view/bar + +Here are a few more usage examples: + +URL with named parameters + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "foo" => "bar")); + ?> + + // Output + /posts/view/foo:bar + +URL with extension + +:: + + url(array( + "controller" => "posts", + "action" => "list", + "ext" => "rss")); + ?> + + // Output + /posts/list.rss + +URL (starting with '/') with the full base URL prepended. + +:: + + url('/posts', true); ?> + + //Output + http://somedomain.com/posts + +URL with GET params and named anchor + +:: + + url(array( + "controller" => "posts", + "action" => "search", + "?" => array("foo" => "bar"), + "#" => "first")); + ?> + + //Output + /posts/search?foo=bar#first + +For further information check +`Router::url `_ in +the API. + +Changing the tags output by HtmlHelper +====================================== + +The built in tag sets for ``HtmlHelper`` are XHTML compliant, however if +you need to generate HTML for HTML4 you will need to create and load a +new tags config file containing the tags you'd like to use. To change +the tags used create ``app/config/tags.php`` containing: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +You can then load this tag set by calling ``$html->loadConfig('tags');`` diff --git a/en/The-Manual/Core-Helpers/Javascript.rst b/en/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..48cffc5840a8459557673b0531e6e2a1708d45e4 --- /dev/null +++ b/en/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,143 @@ +Javascript +########## + +The Javascript helper is used to aid in creating well formatted related +javascript tags and codeblocks. There are several methods some of which +are designed to work with the `Prototype `_ +Javascript library. + +Methods +======= + +``codeBlock($script, $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), $safe)`` + +- string $script - The JavaScript to be wrapped in SCRIPT tags +- array $options - Set of options: + + - allowCache: boolean, designates whether this block is cacheable + using the current cache settings. + - safe: boolean, whether this block should be wrapped in CDATA tags. + Defaults to helper's object configuration. + - inline: whether the block should be printed inline, or written to + cached for later output (i.e. $scripts\_for\_layout). + +- boolean $safe - DEPRECATED. Use $options['safe'] instead + +codeBlock returns a formatted script element containing $script. But can +also return null if Javascript helper is set to cache events. See +JavascriptHelper::cacheEvents(). And can write in +``$scripts_for_layout`` if you set $options['inline'] to false. + +``blockEnd()`` + +Ends a block of cached Javascript. Can return either a end script tag, +or empties the buffer, adding the contents to the cachedEvents array. +Its return value depends on the cache settings. See +JavascriptHelper::cacheEvents() + +``link($url, $inline = true)`` + +- mixed $url - String URL to JavaScript file, or an array of URLs. +- boolean $inline If true, the '; + echo Sanitize::html($badString); + // salida: <font size="99" color="#FF0000">HEY</font><script>...</script> + echo Sanitize::html($badString, true); + // salida: HEY... + +escape +====== + +escape(string $string, string $connection) + +Usado para escapar sentencias SQL agregándole barras invertidas, +dependiendo de la configuración de magic\_quotes\_gpc del sistema. +$connection es el nombre de la base de datos para la cual escapar el +string, según el nombre definido en app/config/database.php. + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +Esta función es un limpiador multi-propósito y de potencia industrial, y +sirve para ser usado en arreglos (como $this->data, por ejemplo). La +función recibe un arreglo (o string) y retorna su versión limpia. Las +siguientes operaciones de limpieza son realizadas en cada elemento del +arreglo (recursivamente): + +- Los espacios raros (incluyendo 0xCA) son reemplazados con espacios + regulares. +- Verificación doble de caracteres especiales y remoción de retornos de + carro para una mayor seguridad SQL. +- Se agregan barras para SQL (sólo llama a la función sql explicada + anteriormente). +- Se reemplazan las barras invertidas ingresadas por el usuario por + barras invertidas confiables. + diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..7613faf3933e2aa593970fc44a26705fcb7a9820 --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,944 @@ +Validación de Datos +################### + +La validación de los datos es una parte importante de cualquier +aplicación, ya que asegura que los datos en un modelo están conformes a +las reglas de negocio de la aplicación. Por ejemplo, tu podrias querer +que los passwords tengan a lo menos un largo de ocho caracteres, o +asegurar que los username sean únicos. Definir reglas de validación hace +que el manejo de los formularios sea muchísimo más fácil. + +Hay muchos diferentes aspectos del proceso de validación. En esta +sección cubriremos el lado del modelo, es decir, lo que ocurre cuando tu +llamas al método save() de tu modelo. Para más información acerca de +cómo manejar el despliegue de errores de validación, revisa la sección +que cubre el FormHelper. + +El primer paso en la validación de datos es la creación de las reglas de +validación en el Modelo. Para hacer eso, usa el arreglo Model::validate +en la definición del Modelo, por ejemplo: + +:: + + + +En el ejemplo de arriba, el arreglo $validate se agrega al modelo User, +pero el arreglo no contiene reglas de validación. Asumiendo que la tabla +*users* tiene los campos *login*, *password*, *email* y *born*, el +ejemplo de abajo muestra algunas simples reglas de validación que se +aplican a esos campos: + +:: + + 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + ?> + +El ejemplo muestra cómo se pueden agregar reglas de validación a los +campos de un modelo. Para el campo *login* serán aceptadas sólo letras y +números, el *email* debe ser válido, y *born* debe ser una fecha válida. +La definición de reglas de validación habilitan en CakePHP el despliegue +automático de mensajes de error en formularos si los datos enviados no +cumplen las reglas de validación. + +CakePHP incluye muchas reglas de validación y usarlas puede ser bastante +simple. Algunas de las reglas incluidas permiten verificar el formato de +los emails, URLs, y números de tarjeta de crédito - las cubriremos en +detalle más adelante. + +Acá tenemos un ejemplo de validación más complejo que aprovecha algunas +de las reglas incluidas: + +:: + + array( + 'alphaNumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Sólo letras y números' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Entre 5 y 15 caracteres' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Largo mínimo de 8 caracteres' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Ingrese una fecha válida', + 'allowEmpty' => true + ) + ); + } + ?> + +Dos reglas de validación son definidas para *login*: debería contener +sólo letras y números, y su largo debe ser de 5 a 15. El campo +*password* debe tener un largo mínimo de 8 caracteres. El *email* debe +contener una dirección de correo válida, y *born* debe ser una fecha +válida. Además, notar que puedes agregar mensajes de error propios que +CakePHP mostrará cuando estas reglas de validación no se cumplan. + +Como lo muestra el ejemplo de arriba, un único campo puede tener +múltiples reglas de validación. Y si las reglas incluidas no coinciden +con lo que necesitas, puedes agregar tus propias reglas de validación +según tus requerimientos. + +Ahora que viste a grandes rasgos cómo funciona la validación, veamos +cómo estas reglas son definidas en el modelo. Hay tres diferentes +maneras para definir reglas de validación: arreglos simples, una única +regla por campo, y múltiples reglas por campo. + +Reglas Simples +============== + +Tal como el nombre lo sugiere, esta es la manera más simple de definir +una regla de validación. La sintaxis para la definición de reglas usando +esta manera es: + +:: + + var $validate = array('fieldName' => 'ruleName'); + +Donde, 'fieldName' es el nombre del campo para el cual se está +definiendo una regla, y 'ruleName' es el nombre de una regla +pre-definida (cubriremos esto en la siguiente sección). + +Una regla por campo +=================== + +Ésta técnica de definición permite un mejor control del funcionamiento +de las reglas de validación. Pero antes de su discusión, veamos el +patrón de uso general para agregar una regla a un solo campo: + +:: + + var $validate = array( + 'fieldName1' => array( + 'rule' => 'ruleName', // ó: array('ruleName', 'param1', 'param2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // ó: 'update' + 'message' => 'Su mensaje de error' + ) + ); + +El índice 'rule' es requerido. Si sólo se setea 'required' => true la +validación del formulario no funcionará correctamente. Esto debido a que +'required' no es en realidad una regla. + +Como puedes ver, cada campo (arriba se está mostrando sólo un campo) es +asociado con un arreglo que contiene cinco índice: ‘rule’, ‘required’, +‘allowEmpty’, ‘on’ y ‘message’. Veamos con más detalle cada uno de estos +índices. + +rule +---- + +El índice ‘rule’ define el método de validación y acepta un sólo valor o +un arreglo. El valor para ‘rule’ especificado puede ser el nombre de un +método en tu modelo, un método de la clase core Validation, o una +expresión regular. Para un completo listado de todas las reglas +incorporadas ver la sección llamada "Reglas de Validación Incorporadas". + +Si la regla no requiere parámetros, ‘rule’ puede ser un sólo valor, por +ejemplo: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +Si la regla requiere algunos parámetros (como max, min o range), +entonces ‘rule’ debería ser un arreglo: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +Recuerda, el índice ‘rule’ es requerido para la definición de reglas +basadas en arreglos. + +required +-------- + +Este índice debería tener asignado un valor booleano. Si ‘required’ es +true, el campo debe estar presente en el arreglo de datos. Por ejemplo, +si la regla de validación ha sido definida de la siguiente manera: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +Los datos enviados al método save() del modelo deben contener un valor +para el campo *login*. Si no es así, la validación falla (la regla no se +cumple). El valor por defecto para este índice es un false booleano. + +Si el índice login están presente pero no tiene un valor asignado, la +validación será exitosa. Setear ‘required’ a true sólo verifica que el +índice del campo este presente. + +allowEmpty +---------- + +Al índice ``allowEmpty`` se le debería asignar un valor booleano. Si +``allowEmpty`` es false, los datos pasados al método ``save()`` del +modelo deben incluir el campo a un valor no-vacío para ese campo. Cuando +se deja en true, un campo vacío pasará exitosamente la validación de ese +campo. + +El valor por defecto de ``allowEmpty`` es null. Esto significa que el +campo siempre procesará las reglas de validación incluyendo la ejecución +de funciones de validación propias. + +on +-- + +El índice ‘on’ puede ser seteado con uno de los siguientes valores: +‘update’ o ‘create’. Esto provee un mecanismo que permite que cierta +regla sea aplicada ya sea durante la creación de un nuevo registro, o +durante la actualización de un registro. + +Si la regla fue definida como ‘on’ => ‘create’, entonces la regla sólo +será verificada durante la creación de un nuevo registro. De igual +manera, si es definida como ‘on’ => ‘update’, la regla sólo será +verificada durante la actualización de un registro. + +El valor por defecto de ‘on’ es null. Cuando ‘on’ es null, la regla será +verificada durante la creación y actualización de un registro. + +message +------- + +El índice ‘message’ permite definir un mensaje de error de validación +para la regla: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'Password debe tener a lo menos 8 caracteres' + ) + ); + +last +---- + +Asignar ``'last'`` a ``true`` provocará que el validador se detenga en +la regla si llegara a fallar en vez de continuar con la siguiente regla. +Ésto es útil si quieres que la validación se detenga si el campos es +notEmpty en un `campo +multi-regla `_. + +:: + + var $validate = array( + 'username' => array( + 'usernameRule-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Por favor, introduce el nombre de usuario.', + 'last' => true + ), + 'usernameRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Longitud mímima de 8 caracteres.' + ) + ) + ); + +El valor predeterminado de ``'last'`` es ``false``. + +Múltiples Reglas por Campo +========================== + +La técnica descrita anteriormente nos entrega mayor flexibilidad que la +asignación de reglas simples, pero hay un paso adicional que podemos +tomar para lograr un control más fino de la validación de datos. La +siguiente técnica que revisaremos nos permite asignar múltiples reglas +de validación por cada campo de un modelo. + +Si quieres asignar múltiples reglas de validación a un sólo campo, +básicamente así es cómo se verá: + +:: + + + var $validate = array( + 'nombreCampo' => array( + 'nombreRegla' => array( + 'rule' => 'nombreRegla', + // acá van índices extra como on, required, etc. + ), + 'nombreRegla2' => array( + 'rule' => 'nombreRegla2', + // acá van índices extra como on, required, etc. + ) + ) + ); + +Como puedes ver, esto es bastante similar a lo que hicimos en la sección +previa. Anteriormente, por cada campo teníamos un sólo arreglo con +parámetros de validación. En este caso, cada ‘nombreCampo’ consiste en +un arreglo de índices de reglas. Cada ‘nombreRegla’ contiene un arreglo +distinto con parámetros de validación. + +Esto se entiende mejor con un ejemplo práctico: + +:: + + var $validate = array( + 'login' => array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Se permiten sólo letras y números', + 'last' => true + ), + 'minlength' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Largo mínimo de 8 caracteres' + ), + ) + ); + +El ejemplo de arriba define dos reglas para el campo login: alphanumeric +y minLength. Como puedes ver, cada regla se identifica con un nombre de +índice. En este caso particular, los nombres de índice son similares a +las reglas que usan, pero el nombre de índice puede ser cualquier +nombre. + +Por defecto CakePHP trata de validar un campo usando todas las reglas de +validación declaradas para él y retorna un mensaje de error para la +última regla no satisfecha. Pero si el índice ``last`` es dejado como +``true`` y la regla no es satisfecha, entonces se mostrará el mensaje de +error para esa regla y no se validará ninguna regla adicional. Asi que +si prefieres mostrar un mensaje de error para la primera regla no +satisfecha entonces debes dejar ``'last' => true`` por cada regla. + +Si vas a usar mensajes de error internacionalizados podrias quierer +especificar los mensajes de error en las vistas: + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'alphanumeric' => __('Se permiten sólo letras y números', true), + 'minlength' => __('Largo mínimo de 8 caracteres', true) + ) + ) + ); + +El campo ahora está totalmente internacionalizado, y puedes eliminar los +mensajes del modelo. Para más información acerca de la función \_\_() +ver "Localization & Internationalization" + +Reglas de Validación Incorporadas +================================= + +La clase Validation de CakePHP contiene muchas reglas de validación +incorporadas que pueden hacer mucho más fácil la validación de datos. +Esta clase contiene muchas técnicas de validación frecuentemente usadas +que no necesitarás escribir por tu cuenta. Abajo encontrarás una lista +completa de todas las reglas, junto ejemplos de uso. + +alphaNumeric +------------ + +Los datos para el campo deben contener sólo letras y números. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Los nombres de usuario deben contener sólo letras y números.' + ) + ); + +between +------- + +El largo de los datos para el campo debe estar dentro de un rango +numérico específico. Se debe indicar un valor mínimo y máximo. + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Las contraseñas deben tener un largo entre 5 y 15 caracteres.' + ) + ); + +blank +----- + +Esta regla es usada para asegurar que el campo es dejado en blanco o con +sólo espacios en blanco como su valor. Los espacios en blanco incluyen +los caracteres de la barra espaciadora, tabulador, retorno de carro y +nueva línea. + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +El campo debe contener un valor booleano. Los valores aceptados son +“true” o “false”, los enteros 0 o 1 y las cadenas "0" o "1". + +:: + + var $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'Valor incorrecto en myCheckbox' + ) + ); + +cc +-- + +Esta regla es usada para verificar si los datos corresponden a un número +de tarjeta de credito válido. Acepta tres parámetros: ‘type’, ‘deep’ y +‘regex’. + +El ‘type’ puede ser ‘fast’, ‘all’ o cualquiera de los siguientes: + +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +Si ‘type’ es dejado en ‘fast’, se validan los datos contra el formato +numérico de las principales tarjetas de crédito. También se puede dejar +‘type’ como un arreglo con todos los tipos de validaciones que se quiere +satisfacer. + +El índice ‘deep’ debería dejarse con un valor booleano. Si es verdadero, +la validación usará el algoritmo de Luhn para tarjetas de crédito +(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_). +Por defecto el valor se asume como falso. + +El índice ‘regex’ permite indicar una expersión regular propia que será +usada para validar el número de tarjeta de credito. + +:: + + var $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'El número de tarjeta de crédito que ha suministrado no es válido.' + ) + ); + +comparison +---------- + +Esta regla es usada para comparar valores numéricos. Soporta “is +greater”, “is less”, “greater or equal”, “less or equal”, “is less”, +“equal to”, y “not equal”. A continuación algunos ejemplos: + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Debe tener al menos 18 años para calificar.' + ) + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Debe tener al menos 18 años para calificar.' + ) + ); + +date +---- + +Esta regla asegura que los datos enviados esten en un formato de fecha +válido. Un único parámetro (que puede ser un arreglo) puede ser pasado y +que será usado para verificar el formato de la fecha indicada. El valor +del parámetro puede ser uno de los siguientes formatos: + +- ‘dmy’ por ejemplo 27-12-2006 o 27-12-06 (los separadores pueden ser + espacio, punto, guion, slash) +- ‘mdy’ por ejemplo 12-27-2006 or 12-27-06 (los separadores pueden ser + espacio, punto, guion, slash) +- ‘ymd’ por ejemplo 2006-12-27 or 06-12-27 (los separadores pueden ser + espacio, punto, guion, slash) +- ‘dMy’ por ejemplo 27 December 2006 o 27 Dec 2006 +- ‘Mdy’ por ejemplo December 27, 2006 o Dec 27, 2006 (la coma es + opcional) +- ‘My’ por ejemplo (December 2006 o Dec 2006) +- ‘my’ por ejemplo 12/2006 o 12/06 (los separadores pueden ser espacio, + punto, guion, slash) + +Si no especifica ningún índice, se usará el índice por defecto ‘ymd’. + +:: + + var $validate = array( + 'born' => array( + 'rule' => 'date', + 'message' => 'Ingrese una fecha válida usando el formato AA-MM-DD.', + 'allowEmpty' => true + ) + ); + +Mientras que muchos almacenes de datos (motores de bases de datos) +requieren cierto formato de datos, podrias considerar aceptar una amplia +variedad de formatos de fechas y luego convertirlos, en vez de forzar a +los usuarios a ingresar cierto formato. Entre más trabajo puedas hacer +por tus usuarios, mejor. + +decimal +------- + +Esta regla asegura que el dato es un número decimal válido. Se puede +pasar un parámetro para especificar la cantidad de dígitos requeridos +después del punto decimal. Si no se pasa ningún parámetro, el dato será +validado como un número de punto flotante científico, que causará que la +validación no sea satisfecha si es que no se encuentra ningún dígito +después del punto decimal. + +:: + + var $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +Esta regla verifica que el dato sea una dirección de correo electrónico +válida. Al pasar un valor booleano verdadero como segundo parámetro se +tratará también de verificar que el host de la dirección sea válido. + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Por favor indique una dirección de correo electrónico válida.' + ) + ); + +equalTo +------- + +Esta regla asegura que el valor sea equivalente a, y del mismo tipo que +el valor indicado. + +:: + + var $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'El valor debe ser el string cake' + ) + ); + +extension +--------- + +Esta regla verifica que la extensión de archivo sea como .jpg o .png. +Para permitir múltiples extensiones estas se deben pasar dentro de un +arreglo. + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg'), + 'message' => 'Por favor indique una imágen válida.' + ) + ); + +file +---- + +Esta sección aún tiene que ser escrita, si tienes una idea de qué poner +aqui, por favor usa los links y déjanos saber tu sugerencia! + +ip +-- + +Esta regla asegura que haya sido ingresada una dirección IPv4 válida. + +:: + + var $validate = array( + 'clientip' => array( + 'rule' => 'ip', + 'message' => 'Por favor ingrese una dirección IP válida.' + ) + ); + +isUnique +-------- + +El dato para este campo debe ser único, no puede ser usado por ningún +otro registro. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'Este nombre de usuario ya ha sido asignado.' + ) + ); + +minLength +--------- + +Esta regla asegura que el dato cumple con un requisito de largo mínimo. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Los nombres de usuario deben tener un largo de al menos 8 caracteres.' + ) + ); + +maxLength +--------- + +Esta regla asegura que el dato siempre esté dentro del requisito de +largo máximo. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', '15'), + 'message' => 'Los nombres de usuario no pueden tener un largo mayor a 15 caracteres.' + ) + ); + +money +----- + +Esta regla asegura que el valor sea una cantidad en formato monetario +válido. + +El segundo parámetro define dónde se ubica el símbolo: left/right +(izquierda/derecha). + +:: + + var $validate = array( + 'salary' => array( + 'rule' => array('money', 'left'), + 'message' => 'Por favor ingrere una cantidad monetaria válida.' + ) + ); + +multiple +-------- + +Empleado para validar campos input select multiple. Soporta los +paramentros "in", "max" y "min". + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('foo', 'bar'), 'min' => 1, 'max' => 3)), + 'message' => 'Por favor seleccione una, dos o tres opciones' + ) + ); + +inList +------ + +Esta regla asegura que el valor está dentro de un conjunto dado. +Necesita de un arreglo de valores. El valor es válido si coincide con +uno de los valores del arreglo indicado. + +Example: + +:: + + var $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Ingreso Foo o ingrese Bar.' + ) + ) + ); + +numeric +------- + +Verifica si el dato ingresado es un número válido. + +:: + + var $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => 'Por favor indique la cantidad de vehículos.' + ) + ); + +notEmpty +-------- + +Regla básica para asegurar que un campo no este vacío. + +:: + + var $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'Este campo no puede quedar vacío.' + ) + ); + +phone +----- + +Phone valida números telefónicos de EE.UU. Si quieres validar números +telefónicos que no sean de EE.UU. puedes proveer una expresión regular +como segundo parámetro para cubrir formatos adicionales. + +:: + + var $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +Postal es usado para validar códigos ZIP de EE.UU. (us), Canada (ca), +Reino Unido (uk), Italia (it), Alemania (de) y Bélgica (be). Para otros +formatos ZIP puedes proveer una expersión regular como segundo +parámetro. + +:: + + var $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +Esta regla asegura que el valor esté dentro de un rango dado. Si no se +indica un rango, la regla va a verificar si el valor es un número finito +válido en la actual plataforma. + +:: + + var $validate = array( + 'number' => array( + 'rule' => array('range', 0, 10), + 'message' => 'Por favor ingrese un número entre 0 y 10' + ) + ); + +El ejemplo de arriba aceptará cualquier valor mayor a 0 (por ejemplo +0.01) y menor a 10 (por ejemplo 9.99). + +ssn +--- + +Ssn valida los números de seguridad social de EE.UU. (us), Dinamarca +(dk), y los Paises Bajos (nl). Para otros formatos de números de +seguridad social puedes proveer una expersión regular. + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +Esta regla verifica formatos de URL válidos. Soporta los protocolos +http(s), ftp(s), file, news, y gopher. + +:: + + var $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + +Reglas de Validación Personalizadas +=================================== + +Si hasta el momento no has encontrado lo que buscabas, siempre podrás +crear tus propias reglas de validación personalizadas. Hay dos maneras +de hacer esto: definiendo expresiones regulares personalizadas, o +creando métodos de validación personalizados. + +Validación Personalizada Mediante Expresiones Relugares +------------------------------------------------------- + +Si la técnica de validación que necesitas usar puede ser completada +usando expresiones regulares, puedes definir una expresión personalizada +como una regla de validación de un campo. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('custom', '/^[a-z0-9]{3,}$/i'), + 'message' => 'Sólo letras y enteros, mínimo 3 caracteres' + ) + ); + +El ejemplo de arriba verifica si login contiene sólo letras y enteros, +con un largo mínimo de tres caracteres. + +Validación Mediante Métodos Personalizados +------------------------------------------ + +Algunas veces revisar los datos usando expresiones regulares no es +suficiente. Por ejemplo, si quieres asegurar que un código promocional +sólo pueda ser usado 25 veces, necesitas agregar una función de +validación personalizada, como se muestra más abajo: + +:: + + array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'Este código ha sido usado demasiadas veces.' + ) + ); + + function limitDuplicates($data, $limit){ + $existing_promo_count = $this->find( 'count', array('conditions' => $data, 'recursive' => -1) ); + return $existing_promo_count < $limit; + } + } + ?> + +Si quieres pasar parámetros a tu función de validación personalizada, +agrega elementos extra al arreglo ‘rule’ y trátalos como parámetros +extra (despúes del parámetro principal ``$data``) en tu función +personalizada. + +Tu función de validación personalizada puede estar en el modelo (como en +el ejemplo de arriba), o en un behavior implementado por el modelo. Esto +incluye los modelos mapeados. + +Notar que los métodos del model/behavior son verificados primero, antes +de buscar un método en la clase ``Validation``. Esto significa que +puedes sobreescribir métodos de validación existentes (como por ejemplo +``alphaNumeric()``) en el nivel de aplicación (agregando el método a +``AppModel``), o en el nivel de modelo. + +Validando datos desde el Controlador +==================================== + +Mientras que normalmente sólo usarás el método save del modelo, habrán +veces que te gustaría validar los datos sin guardarlos. Por ejemplo, +podrías querer mostrar algo de información adicional al usuario antes de +guardar los datos a la base de datos. Validar datos requiere de un +proceso ligeramente distinto al de sólo guardar los datos. + +Primero, debes setear los datos al modelo: + +:: + + $this->ModelName->set( $this->data ); + +Luego, para verificar si los datos se validan correctamente, usa el +método validates del modelo, que retornará true si es que se valida y +false si es que no: + +:: + + if ($this->ModelName->validates()) { + // paso la lógica de validación + } else { + // no paso la lógica de validadición + } + +El método validates invoca el método invalidFields que le asignará un +valor a la propiedad validationErrors del modelo. El método +invalidFields también retorna los datos como su resultado. + +:: + + $errors = $this->ModelName->invalidFields(); // contiene el arrego validationErrors + +Es importante notar que los datos se deben primero setear al modelo +antes de poder validarlos. Esto es diferente al método save que permite +pasar los datos como un parámetro. También, ten en cuenta que no es +necesario llamar a validates antes de llamar a save ya que save validará +automáticamente los datos antes realmente de guardarlos. diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..78e86794d058f9dc7c0da8f49aee4897c36dfe59 --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,156 @@ +Depuración +########## + +La depuración es una parte necesaria e inevitable de cualquier ciclo de +desarrollo. Mientras que CakePHP no ofrece ninguna herramienta que se +conecte directamente con ningún editor o IDE, provee de varias +herramientas para ayudar en la depuración y exponer lo que se esta +ejecutando dentro de su aplicación. + +Depuración básica +================= + +debug($var, $showHTML = false, $showFrom = true) + +La función debug() está disponible de forma global, esta trabaja similar +a la función de PHP print\_r(). La función debug() permite mostrar el +contenido de una variable en diferente número de formas. Primero, si +quieres que la data se muestre en formato HTML amigable, establece el +segundo parámetro a true. Por defecto la función muestra la línea y el +archivo donde se origina. + +La salida de esta función solo se muestra si la variable debug del core +(app/config/core.php, línea 43) se ha establecido a un valor mayor que +0. + +Usando la clase Debugger +======================== + +Para usar el depurador hay que primero asegurarse que +Configure::read('debug') este seteado a un valor mayor a 0. + +dump($var) + +Dump muestra el contenido de una variable. Desplegará todas las +propiedades y métodos (si existen) de la variable que se indique. + +:: + + $foo = array(1,2,3); + + Debugger::dump($foo); + + //outputs + array( + 1, + 2, + 3 + ) + + //objeto simple + $car = new Car(); + + Debugger::dump($car); + + //despliegue + Car:: + Car::colour = 'red' + Car::make = 'Toyota' + Car::model = 'Camry' + Car::mileage = '15000' + Car::acclerate() + Car::decelerate() + Car::stop() + +log($var, $level = 7) + +Crea un log detallado de la traza de ejecución al momento de su +invocación. El método log() genera datos similares a los de +Debugger::dump() pero los envía al archivo debug.log en vez del buffer +de salida. Notar que el directorio app/tmp (y su contenido) debe tener +permisos de escritura para el servidor web para que log() funcione +correctamente. + +trace($options) + +Retorna la traza de ejecución actual. Cada línea de la traza incluye el +método que fue llamado, incluyendo desde cuál archivo y desde que línea +la llamada se originó. + +:: + + //En PostsController::index() + pr( Debugger::trace() ); + + //despliege + PostsController::index() - APP/controllers/downloads_controller.php, line 48 + Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 265 + Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 + [main] - APP/webroot/index.php, line 84 + +Arriba se muestra una traza de ejecución generada llamando a +Debugger::trace() desde una acción de un controlador. Al leer la traza +de ejecución desde abajo hacia arriba se muestra el orden de las +funciones actualmente en ejecución (stack frames). En el ejemplo de +arriba, index.php llamó a Dispatcher::dispatch(), que a su vez llamó a +Dispatcher::\_invoke(). Luego el método \_invoke() llamó a +PostsController::index(). Esta información es útil cuando se trabaja con +operaciones recursivas, ya que se identifican las funciones que estaban +en ejecución al momento de llamar a trace(). + +excerpt($file, $line, $context) + +Toma un extracto desde un archivo en $path (que es una ruta absoluta), +destaca la línea número $line con la cantidad de $context líneas a su +alrededor. + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + //se despliegará lo siguiente + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +Aunque este método es usado internamente, te puede ser práctico si estas +creando tus propios mensajes de error o entradas de log en ocasiones +especiales. + +exportVar($var, $recursion = 0) + +Convierte una variable de cualquier tipo a un string para su uso en el +despliegue del depurador. Este método también es usado mucho por +Debugger para conversiones internas de variables, y puede ser usado +también en tus propios Debuggers. + +invoke($debugger) + +Reemplazar el Debugger de CakePHP con un nuevo Administrador de Errores. + +Clase Debugger +============== + +La clase Debugger es nueva en CakePHP 1.2, ofrece muchas opciones para +obtener información de depuración. Tiene muchos métodos que pueden ser +invocados de forma estática, proveyendo volcado, trazabilidad, y +funciones de gestión de errores. + +La clase Debugger sobreescribe el manejo de errores por defecto de PHP, +reemplazándolo con información de errores mucho más útil. La depuración +de errores está activa por defecto en CakePHP. Al igual que con todas +las funciones de depuración, se debe establecer Configure::debug a un +valor mayor que 0. + +Cuando ocurre un error, el depurador genera dos salidas de información, +una a la página y la otra crea una entrada en el archivo error.log. El +reporte de errores generado contiene tanto la pila de llamadas como un +extracto del código donde ocurrió el error. Haga clic en el enlace +"Error" para ver la pila de llamadas, y el enlace "Code" para ver las +líneas de código causantes del error. diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..efe4115923ad5e68c9630bcaad5019c41ad167fc --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,85 @@ +Manejo de Errores +################# + +En caso de un error irrecuperable en tu aplicación, es común detener el +procesamiento y mostrar una página de error al usuario. Para ahorrarte +tener que codificar el manejo de esto en cada uno de tus controladores y +componentes, puedes usar el método: + +:: + + $this->cakeError(, [array parameters]); + +Al llamar este método se mostrará una página de error al usuario y se +detendrá cualquier tipo de procesamiento en tu aplicacion. + +CakePHP pre-define un conjunto de tipos de error, pero en estos momentos +(escritura de este manual), la mayoría son realmente útiles para el +mismo framework. Uno que es más útil para el desarrollador de +aplicaciones es el viejo y querido error 404. Puede ser llamado sin +ningún parámetro de la siguiente manera: + +:: + + $this->cakeError('error404'); + +De manera alternativa, puedes hacer que la página reporte que el error +fue en una URL específica pasando el parámetro ``url``: + +:: + + $this->cakeError('error404', array('url' => 'some/other.url')); + +Todo esto comienza a ser mucho más útil al extender el administrador de +errores para que use tus propios tipos de error. Los administradores de +error customizados son principalmente como acciones de un controlador. +Típicamente vas a usar set() para dejar disponibles sus parámetros en la +vista y luego mostrar (render) un fichero tipo vista desde el directorio +``app/views/errors``. + +Crea un fichero ``app/app_error.php`` con la siguiente definición. + +:: + + + +Se pueden implementar administradores (handlers) para nuevos tipos de +error agregando métodos a esta clase. Simplemente crea un nuevo método +con el nombre que quieres usar como tu tipo de error. + +Digamos que tenemos una aplicación que escribe cierta cantidad de +ficheros a disco y que es apropiado mostrale al usuario los errores de +escritura. No quieremos agregar código para esto en diferentas partes de +la aplicación, así que es un buen caso para usar un nuevo tipo de error. + +Agrega un nuevo método a tu clase ``AppError``. Vamos a aceptar un +parámetro llamado ``file`` que será la ruta al fichero cuya escritura +fallo. + +:: + + function cannotWriteFile($params) { + $this->controller->set('file', $params['file']); + $this->__outputMessage('cannot_write_file'); + } + +Crea la vista en ``app/views/errors/cannot_write_file.ctp`` + +:: + +

No fue posible escribir en el fichero

+

No se pudo escribir el fichero en el disco.

+ +y lanza el error en tu controllador/componente + +:: + + $this->cakeError('cannotWriteFile', array('file'=>'somefilename')); + +La implementación por defecto de ``$this->__outputMessage()`` sólo +mostrará la vista en ``views/errors/.ctp``. Si quieres cambiar este +comportamiento, puedes redefinir ``__outputMessage($template)`` en tu +clase AppError. diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..7626494980e61649ab282fc874f972495e856323 --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,198 @@ +Internacionalización & Localización +################################### + +Una de las mejores maneras para que tus aplicaciones lleguen a un +público más amplio es brindarlo en varios idiomas. Esto a menudo puede +resultar ser una tarea de enormes proporciones, pero las funciones de +internacionalización y localización en CakePHP lo hace mucho más fácil. + +En primer lugar, es importante comprender algunos términos. +*Internacionalización* se refiere a la capacidad de una aplicación para +ser localizado. El término *localización* se refiere a la adaptación de +una aplicación para responder a los requerimientos de un lenguaje (o +cultura) específico (es decir, un "lugar"). La internacionalización y +localización son a menudo abreviados como i18n y l10n, respectivamente, +18 y 10 son el número de caracteres entre el primero y el último +carácter. + +Internacionalizando su aplicación +================================= + +Hay sólo unos pocos pasos para pasar de una aplicación de un solo idioma +a una aplicación multi-idioma, la primera de ellas es hacer uso de la +función ```__()`` `_ +en su código. A continuación se muestra un ejemplo de código para una +aplicación de un solo idioma: + +:: + +

Posts

+ +Para internacionalizar su código todo lo que necesitas hacer es envolver +las cadenas de texto en la función +`translate `_ como +se muestra a continuación: + +:: + +

+ +Si no hace nada más, estos dos ejemplos de código son funcionalmente +idénticos – ambos envían el mismo contenido al navegador. La `función +``__()`` `_ +traducirá la cadena de texto que se pasa si la traducción está +disponible, si no, devolverá la cadena sin modificar. Funciona de manera +similar a otras implementaciones de +`Gettext `_ (igual que otras +funciones de traducción como +```__d()`` `_, +```__n()`` `_ etc) + +Con el código listo para ser multi-idioma, el siguiente paso es crear su +`archivo pot `_, que es el modelo +para todas las cadenas de texto traducibles en su aplicación. Para +generar archivos pot(s) todo lo que necesita es ejecutar la `tarea i18n +en la +consola `_, +que buscará las funciones translate utilizadas en su código y creará los +archivos por usted. Usted puede y debe volver a ejecutar esta tarea cada +vez que se produzca algún cambio de las traducciones en el código. + +Los archivos pot(s) en si mismos no son utilizados por CakePHP, son las +plantillas utilizadas para crear o actualizar los `archivos +po `_, que contienen las +traducciones. Cake buscará los archivos po en la siguiente ubicación: + +:: + + /app/locale//LC_MESSAGES/.po + +El dominio por defecto es 'default', por lo tanto en su carpeta locale +se verá algo como esto: + +:: + + /app/locale/eng/LC_MESSAGES/default.po (English) + /app/locale/fre/LC_MESSAGES/default.po (French) + /app/locale/por/LC_MESSAGES/default.po (Portuguese) + +Para crear o editar su archivo po no se recomienda que utilice su editor +favorito. Para crear un archivo po por primera vez es recomendable +copiar el archivo pot a la ubicación correcta y cambiar la extensión, a +menos que usted esté familiarizado con su formato. Es muy fácil crear un +archivo po inválido o guardarlos con una codificación errónea (si está +editando manualmente el archivo po use UTF-8 para evitar problemas). +Existen herramientas gratuitas como `PoEdit `_ +que hacen de la edición y actualización de sus archivos po una tarea +fácil. + +Los códigos de localización correctos son los de tres caracteres +conforme al estandar `ISO +639-2 `_ aunque +si crea locales regionales (en\_US, en\_GB, etc.) Cake los utiliza si +procede. + +hay un límite de 1.014 caracteres para cada valor msgstr. + +Recuerde que los archivos po son útiles para mensajes cortos, si +necesita traducir párrafos largos, o incluso páginas completas, debe +considerar aplicar una solución diferente. Por ejemplo: + +:: + + // App Controller Code. + function beforeFilter() { + $locale = Configure::read('Config.language'); + if ($locale && file_exists(VIEWS . $locale . DS . $this->viewPath)) { + // e.g. use /app/views/fre/pages/tos.ctp instead of /app/views/pages/tos.ctp + $this->viewPath = $locale . DS . $this->viewPath; + } + } + +o + +:: + + // View code + echo $this->element(Configure::read('Config.language') . '/tos') + +Localización en CakePHP +======================= + +Para cambiar o definir el idioma para su aplicación sólo necesita hacer +lo siguiente: + +:: + + Configure::write('Config.language', 'fre'); + +Esto le dice a Cake qué localización debe usar (si usa una localización +regional como fr\_FR, como alternativa en caso que no exista, se +utilizará la localización de la norma `ISO +639-2 `_). +Puede cambiar el idioma en cualquier momento, por ejemplo en el +bootstrap si desea definir el idioma por defecto para su aplicación, en +el beforeFilter del controlador si el idioma es específico para una +petición o un usuario o en cualquier otro momento si desea mostrar un +mensaje en un idioma diferente. + +Es una buena idea mostrar contenido disponible en varios idiomas a +partir de una URL diferente – esto hace que sea fácil para los usuarios +(y los motores de búsqueda) encontrar lo que están buscando en el idioma +esperado. Hay varias formas de hacer esto, puede ser utilizando +subdominios específicos para cada idioma, (en.example.com, +fra.example.com, etc), o usando un prefijo en la URL, como se hace en +esta aplicación. Usted también podría obtener la información del +navegador del usuario, entre otras cosas. + +Como se menciona en la sección anterior, para mostrar el contenido +localizado se utiliza la función \_\_() o una de las funciones de +traducción disponibles a nivel mundial. El primer parámetro de la +función se utiliza como msgid definidos en los archivos .po. + +Recuerde que debe usar el parámetro return de la función \_\_() si no +desea que se muestre la cadena de texto directamente. Por ejemplo: + +:: + + error( + 'Card.cardNumber', + __("errorCardNumber", true), + array('escape' => false) + ); + ?> + +Si a usted le gusta tener todos los mensajes de error de validación +traducidos por defecto, una solución simple sería añadir el siguiente +código en el app\_model.php: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + +La tarea i18n de la consola no será capaz de determinar el id del +mensaje del ejemplo anterior, lo que significa que tendrá que añadir las +entradas a su archivo po manualmente (o a través de su propio script). +Para evitar la necesidad de editar los archivos default.po cada vez que +ejecute la tarea i18n de la consola, puede utilizar un dominio +diferente, tal como: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __d('validation_errors', $value, true)); + } + +Hay otro aspecto de la localización de su aplicación que no está +cubierto por el uso de las funciones de traducción, estos son los +formatos de fecha y moneda. No olvide que CakePHP es PHP :), por lo +tanto para establecer los formatos para este tipo de cosas deberá +utilizar ```setlocale`` `_. + +Si pasa una localización que no existe en su computadora a +```setlocale`` `_, no tendrá ningún +efecto. Puede encontrar la lista de localizaciones disponibles +ejecutando el comando $locale -a diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..f1518dc8da4b8901114af2fa3f08495eec7a0fc7 --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,55 @@ +Logging +####### + +Aunque los ajustes de Configuración de la clase desde el corazón de +CakePHP realmente puede ayudarle a ver qué pasa en el fondo, usted +necesitará algún tiempo para grabar datos en el disco para saber lo que +pasa. En un mundo cada vez más dependientes de tecnologías como SOAP y +AJAX, la depuración puede ser difíícil. . + +La grabación (registro) puede ser también una manera de descubrir es que +ocurrió en su solicitud en cualquier momento. ¿Qué términos de búsqueda +se utilizaron? ¿Qué tipo de errores de mis usuarios que han visto? ¿Con +qué frecuencia se ejecuta una consulta? + +En CakePHP la grabación (registro) es fácil - la función log () es un +elemento de la clase Object, que es el ancestro común de la mayoría de +las clases CakePHP. Si el contexto es una clase CakePHP (Modelo, +Controlador, Componente ... lo que sea), puede guardar sus datos. + +Uso de la función log +===================== + +La función log() toma dos parámetros. El primero es el mensaje que se +desea escribir en el archivo de log. Por defecto, este mensaje de error +es escrito en el log de errores ubicado en app/tmp/logs/error.log. + +:: + + //Ejecutando esto dentro de una clase CakePHP: + + $this->log("Algo que no hace nada!"); + + //El resultado de esto se agrega a app/tmp/logs/error.log + + 2007-11-02 10:22:02 Error: Algo que no hace nada! + +El segundo parámetro es usado para definir el tipo de log con el se +quiere escribir el mensaje. Si no se suministra, el valor por defecto es +LOG\_ERROR, el cual escribe en el log de errores previamente mensionado. +Como alternativa, Se puede establecer este segundo parámetro a +LOG\_DEBUG, para escribir su mensaje en el log de depuración ubicado en +app/tmp/logs/debug.log: + +:: + + ///Ejecutando esto dentro de una clase CakePHP: + + $this->log('Un mensaje de depuración.', LOG_DEBUG); + + //El resultado de esto se agrega a app/tmp/logs/debug.log (en lugar de error.log) + + 2007-11-02 10:22:02 Error: Un mensaje de depuración. + +El usuario del servidor web debe poder escribir en el directorio app/tmp +para que el log pueda funcionar correctamente. diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..bb68a41b128b343127b8940163379225f641848b --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,353 @@ +Paginación +########## + +Uno de los obstáculos principales al crear aplicaciones web flexibles y +amigables al usuario (*user-friendly*) es diseñar una Interfaz de +Usuario intuitiva. Muchas aplicaciones tienden a crecer en tamaño y +complejidad rápidamente, y tanto diseñadores como programadores se +encuentran conque no pueden arreglárselas para visualizar cientos o +miles de registros. Refactorizar lleva tiempo, y el rendimiento y la +satisfacción del usuario pueden sufrir. + +Visualizar un número razonable de registros por página ha sido siempre +una parte crítica de toda aplicación y solía causar muchos dolores de +cabeza a los desarrolladores. CakePHP aligera la carga del desarrollador +proveyendo una manera rápida y fácil de paginar los datos. + +El ayudante ``PaginatorHelper`` ofrece una genial solución porque es +fácil de usar. Además de paginación, empaqueta algunas características +de ordenación muy fáciles de usar. Por último, pero no menos importante, +también están soportados el paginado y la ordenación Ajax. + +Preparación del controlador +=========================== + +En el controlador comenzamos definiendo los valores de paginación por +defecto en la variable $paginate. Es importante señalar que la clave +'order' debe estar definida en la estructura de array dada. + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +También puedes incluir otras opciones para find(), como *fields* + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +Pueden incluirse otras claves en el array *$paginate* similares a los +parámetos del método *Model->find('all')*, esto es: *conditons*, +*fields*, *order*, *limit*, *page*, *contain* y *recursive*. De hecho, +puedes definir más de un conjunto de valores de paginación por defecto +en el controllador, simplemente nombra cada parte del array según el +modelo que desees configurar: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'Recipe' => array (...), + 'Author' => array (...) + ); + } + +Ejemplo de sintaxis usando Containable Behavior: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +Una vez que la variable *$paginate* ha sido definida, podemos llamar al +método *paginate()* en las acciones del controlador. Este método +devuelve los resultados de *find('all')* del modelo (aplicándoles los +parámetros de la paginación), y obtiene algunas estadísticas de +paginación adicionales, que son pasadas a la Vista de forma invisible. +Este método también añade PaginatorHelper a la lista de helpers en tu +controlador, si es que no estaba ya. + +:: + + function list_recipes() { + // similar to findAll(), but fetches paged results + $data = $this->paginate('Recipe'); + $this->set(compact('data')); + } + +Puedes filtrar los registros pasando condiciones como segundo parámetro +al método ``paginate()`` + +:: + + $data = $this->paginate('Recipe', array('Recipe.title LIKE' => 'a%')); + +O también puedes ajustar la clave *conditions* en la variable +``paginate``. + +Pagination in Views +=================== + +Es cosa tuya decidir cómo mostrar los registros al usuario, aunque lo +más habitual es hacerlo mediante tablas HTML. Los ejemplos que siguen +asumen una disposición tabular, pero el PaginatorHelper, disponible en +las vistas, no siempre necesita restringirse de ese modo. + +Como ya se ha dicho, PaginatorHelper ofrece capacidades para ordenación +que pueden integrarse fácilmente en las cabeceras de las columnas de tus +tablas: + +:: + + // app/views/recipes/list_recipes.ctp +
DateTitleActive
DateTitleActive
-attributes. + +:: + + tableCells(array( + array('Jul 7th, 2007', 'Best Brownies', 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', 'No'), + )); + ?> + + //Output +
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
RedApple
OrangeOrange
YellowBanana
+ + + + + + + + + + +
sort('ID', 'id'); ?>sort('Title', 'title'); ?>
+ +Los enlaces generados por el método sort() de PaginatorHelper permiten a +los usuarios hacer click en las cabeceras de las tablas y alternar la +ordenación de los datos por un campo dado. + +También es posible ordenar una columna en base a asociaciones: + +:: + + + + + + + + + + + + +
sort('Title', 'title'); ?>sort('Author', 'Author.name'); ?>
+ +El ingrediente final de la paginación en las vistas es añadir la +navegación de páginas, que también viene proporcionada por +PaginationHelper. + +:: + + + numbers(); ?> + + prev('« Previous ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Next »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +El texto generado por el método counter() puede personalizarse usando +marcadores especiales: + +:: + + counter(array( + 'format' => 'Page %page% of %pages%, showing %current% records out of + %count% total, starting on record %start%, ending on %end%' + )); + ?> + +Para pasar todos los argumentos de la URL a las funciones del paginador, +añade lo siguiente a tu vista: + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +También puedes especificar qué parámetros pasar manualmente: + +:: + + $paginator->options(array('url' => array("0", "1"))); + +Paginación AJAX +=============== + +Es muy fácil incorporar funcionalidad Ajax en la paginación. El único +código extra que necesitas es incluir la librería JavaScript Prototype, +ajustar el indicador (el icono de carga dentro la DIV) y especificar la +DIV que será actualizada en lugar de recargar la página. + +No olvides añadir el componente RequestHandler para poder usar llamadas +Ajax en tu controlador: + +:: + + var $components = array('RequestHandler'); + +Cambios en el Layout +-------------------- + +Primero, incluiremos la biblioteca Prototype en la cabecera, ajustaremos +nuestra imagen para el indicador de estado (spinner.gif), y ajustaremos +nuestra DIV contenedora principal, "content" (cuyo contenido será +actualizado por la llamada Ajax). + +He aquí un ejemplo de cómo podría ser este layout (parcialmente): + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +Cambios en la Vista +------------------- + +La única configuración extra para la paginación Ajax se hace mediante el +método options() del PaginatorHelper, el cual especifica parámetros Ajax +requeridos. En este caso, estamos especificando que los enlaces de +paginación deberían actualizar el elemento con el ID 'content' con los +datos resultantes, y que queremos mostrar 'spinner' como indicador de +carga. + +Si no se especifica la clave 'update', PaginatorHelper generará enlaces +de paginación y ordenación no Ajax. + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Previous', null, null, array('class' => 'disabled')); + + echo $paginator->next('Next >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +Custom Query Pagination +======================= + +Fix me: Please add an example where overriding paginate is justified + +A good example of when you would need this is if the underlying DB does +not support the SQL LIMIT syntax. This is true of IBM's DB2. You can +still use the CakePHP pagination by adding the custom query to the +model. + +Should you need to create custom queries to generate the data you want +to paginate, you can override the ``paginate()`` and ``paginateCount()`` +model methods used by the pagination controller logic. + +Before continuing check you can't achieve your goal with the core model +methods. + +The ``paginate()`` method uses the same parameters as ``Model::find()``. +To use your own method/logic override it in the model you wish to get +the data from. + +:: + + /** + * Overridden paginate method - group by week, away_team_id and home_team_id + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $recursive = -1; + $group = $fields = array('week', 'away_team_id', 'home_team_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')); + } + +You also need to override the core ``paginateCount()``, this method +expects the same arguments as ``Model::find('count')``. The example +below uses some Postgres-specifc features, so please adjust accordingly +depending on what database you are using. + +:: + + /** + * Overridden paginateCount method + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +The observant reader will have noticed that the paginate method we've +defined wasn't actually necessary - All you have to do is add the +keyword in controller's ``$paginate`` class variable. + +:: + + /** + * Add GROUP BY clause + */ + var $paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + /** + * Or on-the-fly from within the action + */ + function index() { + $this->paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + +However, it will still be necessary to override the ``paginateCount()`` +method to get an accurate value. diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/es/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..4dbc8d177d4de8e3ab42f16b15566415a808044d --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,181 @@ +REST +#### + +Many newer application programmers are realizing the need to open their +core functionality to a greater audience. Providing easy, unfettered +access to your core API can help get your platform accepted, and allows +for mashups and easy integration with other systems. + +While other solutions exist, REST is a great way to provide easy access +to the logic you've created in your application. It's simple, usually +XML-based (we're talking simple XML, nothing like a SOAP envelope), and +depends on HTTP headers for direction. Exposing an API via REST in +CakePHP is simple. + +The Simple Setup +================ + +The fastest way to get up and running with REST is to add a few lines to +your routes.php file, found in app/config. The Router object features a +method called mapResources(), that is used to set up a number of default +routes for REST access to your controllers. If we wanted to allow REST +access to a recipe database, we'd do something like this: + +:: + + //In app/config/routes.php... + + Router::mapResources('recipes'); + Router::parseExtensions(); + +The first line sets up a number of default routes for easy REST access. +These routes are HTTP Request Method sensitive. + ++---------------+----------------+----------------------------------+ +| HTTP Method | URL | Controller action invoked | ++===============+================+==================================+ +| GET | /recipes | RecipesController::index() | ++---------------+----------------+----------------------------------+ +| GET | /recipes/123 | RecipesController::view(123) | ++---------------+----------------+----------------------------------+ +| POST | /recipes | RecipesController::add() | ++---------------+----------------+----------------------------------+ +| POST | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ +| PUT | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ +| DELETE | /recipes/123 | RecipesController::delete(123) | ++---------------+----------------+----------------------------------+ + +CakePHP's Router class uses a number of different indicators to detect +the HTTP method being used. Here they are in order of preference: + +#. The *\_method* POST variable +#. The X\_HTTP\_METHOD\_OVERRIDE +#. The REQUEST\_METHOD header + +The *\_method* POST variable is helpful in using a browser as a REST +client (or anything else that can do POST easily). Just set the value of +\_method to the name of the HTTP request method you wish to emulate. + +Once the router has been set up to map REST requests to certain +controller actions, we can move on to creating the logic in our +controller actions. A basic controller might look something like this: + +:: + + // controllers/recipes_controller.php + + class RecipesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recipes = $this->Recipe->find('all'); + $this->set(compact('recipes')); + } + + function view($id) { + $recipe = $this->Recipe->findById($id); + $this->set(compact('recipe')); + } + + function edit($id) { + $this->Recipe->id = $id; + if ($this->Recipe->save($this->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recipe->delete($id)) { + $message = 'Deleted'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + } + +Since we've added a call to Router::parseExtensions(), the CakePHP +router is already primed to serve up different views based on different +kinds of requests. Since we're dealing with REST requests, the view type +is XML. We place the REST views for our RecipesController inside +app/views/xml. We can also use the XmlHelper for quick-and-easy XML +output in those views. Here's what our index view might look like: + +:: + + // app/views/recipes/xml/index.ctp + + + serialize($recipes); ?> + + +Experienced CakePHP users might notice that we haven't included the +XmlHelper in our RecipesController $helpers array. This is on purpose - +when serving up a specific content type using parseExtensions(), CakePHP +automatically looks for a view helper that matches the type. Since we're +using XML as the content type, the XmlHelper is automatically loaded up +for our use in those views. + +The rendered XML will end up looking something like this: + +:: + + + + + + + + + + + + +Creating the logic for the edit action is a bit trickier, but not by +much. Since you're providing an API that outputs XML, it's a natural +choice to receive XML as input. Not to worry, however: the +RequestHandler and Router classes make things much easier. If a POST or +PUT request has an XML content-type, then the input is taken and passed +to an instance of Cake's Xml object, which is assigned to the $data +property of the controller. Because of this feature, handling XML and +POST data in parallel is seamless: no changes are required to the +controller or model code. Everything you need should end up in +$this->data. + +Asignación de rutas personalizadas REST +======================================= + +Si las rutas por defecto creadas a través de mapResources() se ajustan a +un desarrollado determinado, entonces utilizar el método +Router::connect() para definir un conjunto personalizado de rutas REST. +El método connect() permite definir un número de opciones diferentes +para una URL dada. El primer parámetro es la URL misma, y el segundo +parámetro permite suministrar aquellas opciones. El tercer parámetro +permite especificar los patrones de expresiones regulares para ayudar a +CakePHP a identificar determinados marcadores en la URL especificada. + +Aquí se provee un simple ejemplo, permite ajustar la ruta para sus +propios propósitos en el uso de RESTful. Lo que se muestra a +continuación es una posible ruta de edición personalizada, sin utilizar +mapResources(): + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9]+") + ) + +Técnicas avanzadas de asignación de rutas no son cubiertas aquí, así se +le otorga el foco al punto más importante para los propósitos del +ejemplo: la clave [method] del arreglo de opciones en el segundo +parámetro. Una vez que la clave ha sido establecida, la ruta +especificada funcionará solo para aquel método de solicitud HTTP (el +cual bien podría ser GET, DELETE, etc.) diff --git a/es/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/es/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..c44b7137184e83d54c1729046e4c25b55e812ff9 --- /dev/null +++ b/es/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,1066 @@ +Testing +####### + +A partir de CakePHP 1.2 disponemos de soporte para un completo entorno +de testing incorporado en CakePHP. Este entorno es una extensión del +entorno SimpleTest para PHP. En esta sección discutiremos cómo preparar +la aplicación para testing y cómo construir y ejecutar tus tests. + +Preparándose para el testing +============================ + +¿Preparado/a para empezar a hacer test? ¡Bien! ¡Vamos allá entonces! + +Installing SimpleTest +--------------------- + +Instalación de SimpleTest + +El entorno de testing provisto con CakePHP 1.2 está construido sobre el +entorno de testing SimpleTest. SimpleTest no se distribuye con la +instalación por defecto de CakePHP por lo que debemos descargarlo +primero. Lo puedes encontrar aquí: +`http://simpletest.sourceforge.net/ `_ + +Consigue la última versión y descomprime el código en tu carpeta +cake/vendors, o en tu carpeta app/vendors, según tus preferencias. Ahora +deberías tener un directorio vendors/simpletest con todos los archivos y +carpetas de SimpleTest dentro. ¡Recuerda tener el nivel de DEBUG al +menos a 1 en tu archivo app/config/core.php antes de ejecutar cualquier +test! + +Si no tienes una conexión de base de datos para test definida en +app/config/database.php, las tablas de test se crearán con un prefijo +``test_suite_``. Puedes crear una conexión de base de adtos ``$test`` +para que contenga sólo las tablas de test como la que te mostramos +debajo: + +:: + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'dbhost', + 'login' => 'dblogin', + 'password' => 'dbpassword', + 'database' => 'databaseName' + ); + +Si la base de datosd e test está disponible y CakePHP puede conectarse a +ella, todas las tablas serán creadas en esta base de datos. + +Ejecutando los test-cases incorporados +-------------------------------------- + +CakePHP 1.2 se distribuye con un gran paquete de test-cases sobre la +funcionalidad del núcleo de CakePHP + +Puedes acceder a estos test navegando a +http://your.cake.domain/cake\_folder/test.php - dependiendo de como sea +la disposición específica de tu aplicación. Intenta ejecutar alguno de +los grupos de test del núcleo haciendo click en el enlace +correspondiente. Ejecutar un test puede llevar un rato, pero deberías +ver algo parecido a "2/2 test casese complete: 49 passes, 0 fails and 0 +exceptions.". + +¡Felicidades, ya estás listo/a para empezar a escribir tests! + +Introducción a los test - Unit testing vs. Web testing +====================================================== + +El entorno de test de CakePHP soporta dos tipos de testing. Uno es el +Unit Testing, en el cual tú pruebas pequeñas partes de tu código, como +pueden ser un método en un componente o una acción en un controlador. El +otro tipo de testing soportado es el Web Testing, en el cual automatizas +el trabajo de evaluar tu aplicación mediante la navegación por las +páginas, relleno de formularios, hacer clic en enlaces y demás. + +Preparando datos de prueba +========================== + +Acerca de las fixtures +---------------------- + +Cuando pruebes código que dependa de modelos y datos, puedes usar +**fixtures** como una forma de generar tablas temporales de datos +cargados con datos de ejemplo que pueden ser utilizados por el test. El +beneficio de usar fixtures es que tus test no pueden de ningún modo +alterar los datos de la aplicación en marcha. Además, así puedes empezar +a probar tu código antes de desarrollar contenido en vivo para tu +aplicación. + +CakePHP intenta utilizar la conexión denominada ``$test`` en tu archivo +app/config/database.php. Si esta conexión no es utilizable, usará la +configuración de base de datos ``$default`` y creará las tablas de test +en la base de datos definida en esa configuración. En cualquier caso, +añadirá el prefijo "test\_suite\_" a tu propio prefijo para las tablas +(si es que hay alguno) para evitar colisiones con las tablas existentes. + +CakePHP realiza los siguientes pasos durante el curso de un test case +basado en fixture: + +#. Crea tablas para cada una de las fixtures necesarias +#. Rellena las tablas con datos, si es que se han proporcionado éstos en + la fixture +#. Ejecuta los métodos de los test +#. Vacía las tablas fixture +#. Elimina las tablas fixture de la base de datos + +Creando fixtures +---------------- + +Cuando se crea un fixture se deben definir 2 cosas: + 1) cómo se crea la tabla (que campos serán parte de la tabla) + 2) cómo se guardarán los registros en la tabla de prueba. Luego +podremos crear nuestro primer fixture, que utilizaremos para testear +nuestro modelo Article. Creamos un archivo llamado +**article\_fixture.php** en la carpeta **app/tests/fixtures**, con el +siguiente código: + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'title' => array('type' => 'string', 'length' => 255, 'null' => false), + 'body' => 'text', + 'published' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Usamos $fields para indicar los campos que serán parte de la tabla, y +cómo serán definidos. El formato que se usa para definir los campos es +el mismo que usamos en la funcion **generateColumnSchema()** definida en +el motor de base de datos de Cake (por ejemplo en dbo\_mysql.php.) Los +atributos que un campo puede tenes son los siguientes: + +type + es el tipo de dato de CakePHP. Actualmente los soportados son: + string (mapea como VARCHAR), text (mapea como TEXT), integer (mapea + como INT), float (mapea como FLOAT), datetime (mapea como DATETIME), + timestamp (mapea como TIMESTAMP), time (mapea como TIME), date + (mapea como DATE), y binary (mapea como BLOB) +key + setea el campo como primary para hacerlo auto-incrementable + (AUTO\_INCREMENT), y clave primaria (PRIMARY KEY) de la tabla. +length + setea el tamaño del campo. +null + setea true o false. Si puede ser nulo indicamos true, si no se + permiten nulos va false +default + el valor por defecto del campo. + +Finalmente podemos setear un conjunto de registros que seran cargados +luego de que la tabla de testeo se crea. El formato es bastante simple, +sin embargo necesita un poco más de expilcación. Solo ten en cuenta que +cada registro del array $records debe tener una key para **cada** campo +del array $fields. Si un campo para un registro en particular necesita +tener el valor nulo, solo especifica el valor de ese campo como nulo +(NULL true). + +Importar información de tabla y registros +----------------------------------------- + +Tu aplicación puede tener ya modelos funcionando con datos reales +asociados, y puedes decidir probar tu modelo con esos datos. Sería +entonces un esfuerzo doble tener que definir la tabla y/o los registros +en tus fixtures. Por suerte, hay una forma de hacer que la definición de +la tabla y/o los registros para una fixture en particular vengan de un +modelo o una tabla ya existentes. + +Comencemos con un ejemplo. Asumiento que tienes un modelo llamado +Article disponible en tu aplicación (que se corresponde con una tabla +llamada articles), cambiamos la fixture de ejemplo que dimos en la +sección anterior (**app/tests/fixtures/article\_fixture.php**) a: + +:: + + + + +Esta sentencia le dice a la test suite que importe tu definición de +tabla de la tabla asociada al modelo llamado Article. Puedes usar +cualquier modelo disponible en tu aplicación. La expresión anterior no +importa registros, pero puedes hacerlo cambiandola para que sea: + +:: + + 'Article', 'records' => true); + } + ?> + +Si, por otro lado, tienes una tabla creada pero no un modelo disponible +para ella, puedes especificar que tu importación consistirá en leer la +información de la tabla. Por ejemplo: + +:: + + 'articles'); + } + ?> + +Esto importará la definición de una tabla llamada 'articles' usando tu +conexión de base de datos denominada 'default'. Si quieres cambiar la +conexión sólo tienes que hacer: + +:: + + 'articles', 'connection' => 'other'); + } + ?> + +Ya que se usa tu conexión a la base de datos, si hay algún prefijo de +tabla declarado, este será usado automáticamente al recabar la +información de tabla. Los dos fragmentos anteriores no importan +registros de la tabla. Para forzar a la fixture a importar también los +registros, cambialo a: + +:: + + 'articles', 'records' => true); + } + ?> + +Naturalmente puedes importar tus definiciones de tabla de un modelo o +tabla existente, pero tener tus registros definidos directamente en la +fixture, como se mostraba en la sección anterior. Por ejemplo: + +:: + + 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Creando los tests +================= + +En primer lugar, revisar una serie de normas y directrices para los +tests: + +#. Los archivos de PHP que contiene los tests deben estar en : + app/tests/cases/[algun\_ archivo]. +#. Los nombres de estos archivos deben terminar con un **.test.php** en + lugar de sólo .php. +#. Las clases que contienen los tests debe extender o heredar de + **CakeTestCase** o **CakeWebTestCase**. +#. El nombre de cualquier método que contenga un test (por ejemplo, que + contiene una afirmación) debería comenzar con **test**, como en + **testPublished()**. + +Cuando se crea un caso test, puede ejecutarce por medio del navegador en +la siguiente dirección **http://tu.dominio.cake/carpeta\_cake/test.php** +(dependiendo de cómo se ve específicamente tu configuración) y haciendo +clic en App casos de test, y a continuación, haga clic en el enlace a su +archivo. + +CakeTestCase Callback Methods +----------------------------- + +If you want to sneak in some logic just before or after an individual +CakeTestCase method, and/or before or after your entire CakeTestCase, +the following callbacks are available: + +**start()** + First method called in a *test case*. + +**end()** + Last method called in a *test case*. + +**startCase()** + called before a *test case* is started. + +**endCase()** + called after a *test case* has run. + +**before($method)** + Announces the start of a *test method*. + +**after($method)** + Announces the end of a *test method*. + +**startTest($method)** + Called just before a *test method* is executed. + +**endTest($method)** + Called just after a *test method* has completed. + +Testing models +============== + +Creating a test case +-------------------- + +Let's say we already have our Article model defined on +app/models/article.php, which looks like this: + +:: + + name . '.published' => 1 + ); + + return $this->findAll($conditions, $fields); + } + + } + ?> + +We now want to set up a test that will use this model definition, but +through fixtures, to test some functionality in the model. CakePHP test +suite loads a very minimum set of files (to keep tests isolated), so we +have to start by loading our parent model (in this case the Article +model which we already defined), and then inform the test suite that we +want to test this model by specifying which DB configuration it should +use. CakePHP test suite enables a DB configuration named **test** that +is used for all models that rely on fixtures. Setting $useDbConfig to +this configuration will let CakePHP know that this model uses the test +suite database connection. + +CakePHP Models will only use the test DB config if they rely on fixtures +in your testcase! + + Since we also want to reuse all our existing model code we will create +a test model that will extend from Article, set $useDbConfig and $name +appropiately. Let's now create a file named **article.test.php** in your +**app/tests/cases/models** directory, with the following contents: + +:: + + + +We have created the ArticleTestCase. In variable **$fixtures** we define +the set of fixtures that we'll use. + +If your model is associated with other models, you will need to include +ALL the fixtures for each associated model even if you don't use them. +For example: A hasMany B hasMany C hasMany D. In ATestCase you will have +to include fixtures for a, b, c and d. + +Creating a test method +---------------------- + +Let's now add a method to test the function published() in the Article +model. Edit the file **app/tests/cases/models/article.test.php** so it +now looks like this: + +:: + + Article =& ClassRegistry::init('Article'); + + $result = $this->Article->published(array('id', 'title')); + $expected = array( + array('Article' => array( 'id' => 1, 'title' => 'First Article' )), + array('Article' => array( 'id' => 2, 'title' => 'Second Article' )), + array('Article' => array( 'id' => 3, 'title' => 'Third Article' )) + ); + + $this->assertEqual($result, $expected); + } + } + ?> + + You can see we have added a method called **testPublished()**. We start +by creating an instance of our fixture based **Article** model, and then +run our **published()** method. In **$expected** we set what we expect +should be the proper result (that we know since we have defined which +records are initally populated to the article table.) We test that the +result equals our expectation by using the **assertEqual** method. See +the section Creating Tests for information on how to run the test. + +Testing controllers +=================== + +Creando un test case +-------------------- + +Digamos que tienes un típico controlador de artículos, con su +correspondiente modelo, y que se parece a éste: + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $result = $this->Article->findAll(null, array('id', + 'title')); + } else { + $result = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $result; + } + + $this->set('title', 'Articles'); + $this->set('articles', $result); + } + } + ?> + +Crea un archivo llamado articles\_controller.test.php y pon lo siguiente +dentro: + +:: + + Comenzando Test Case'; + } + function endCase() { + echo '

Terminado Test Case

'; + } + function startTest($method) { + echo '

Comenzando método ' . $method . '

'; + } + function endTest($method) { + echo '
'; + } + function testIndex() { + $result = $this->testAction('/articles/index'); + debug($result); + } + function testIndexShort() { + $result = $this->testAction('/articles/index/short'); + debug($result); + } + function testIndexShortGetRenderedHtml() { + $result = $this->testAction('/articles/index/short', + array('return' => 'render')); + debug(htmlentities($result)); + } + function testIndexShortGetViewVars() { + $result = $this->testAction('/articles/index/short', + array('return' => 'vars')); + debug($result); + } + function testIndexFixturized() { + $result = $this->testAction('/articles/index/short', + array('fixturize' => true)); + debug($result); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'published' + => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body')); + $result = $this->testAction('/articles/index', + array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($result); + } + } + ?> + +El método testAction +-------------------- + +La novedad aquí es el método **testAction**. El primer argumento de este +método es la URL "en formato Cake" de la acción del controlador que se +quiere probar, como en '/articles/index/short'. + +El segundo argumento es un array de parámetros, consistente en: + +return + Indica lo que se va a devolver. + Los valores válidos son: + + - 'vars' - Obtienes las variables de la vista disponibles tras + ejecutar la acción + - 'view' - Obtienes la vista generada, sin layout + - 'contents' - Obtienes todo el html de la vista, incluyendo layout + - 'result' - Obtienes el valor de retorno de la acción como cuando + se usa $this->params['requested']. + + El valor por defecto es 'result'. +fixturize + Ponlo a true si quieres que tus modelos se "auto-simulen" (de modo + que las tablas de la aplicación se copian, junto con los registros, + para que al probar las tablas si cambias datos no afecten a tu + aplicación real.) Si en 'fixturize' pones un array de modelos, + entonces sólo esos modelos se auto-simularán mientras que los demás + utilizarán las tablas reales. Si quieres usar tus archivos de + fixtures con testAction() no uses fixturize, y en su lugar usa las + fixtures como harías normalmente. +method + Ajustalo a 'post' o 'get' si quieres pasarle datos al controlador +data + Los datos que se pasarán. Será un array asociativo consistente en + pares de campo => valor. Échale un vistazo a + ``function testIndexPostFixturized()`` en el case test de arriba + para ver cómo emulamos pasar datos de formulario como post para un + nuevo artículo. + +Pitfalls +-------- + +If you use testAction to test a method in a controller that does a +redirect, your test will terminate immediately, not yielding any +results. + See +`https://trac.cakephp.org/ticket/4154 `_ +for a possible fix. + +For an in-depth explanation of controller testing please see this blog +post by Mark Story `Testing CakePHP Controllers the hard +way `_. + +Testing Helpers +=============== + +Since a decent amount of logic resides in Helper classes, it's important +to make sure those classes are covered by test cases. + +Helper testing is a bit similar to the same approach for Components. +Suppose we have a helper called CurrencyRendererHelper located in +``app/views/helpers/currency_renderer.php`` with its accompanying test +case file located in +``app/tests/cases/helpers/currency_renderer.test.php`` + +Creating Helper test, part I +---------------------------- + +First of all we will define the responsibilities of our +CurrencyRendererHelper. Basically, it will have two methods just for +demonstration purpose: + +function usd($amount) + +This function will receive the amount to render. It will take 2 decimal +digits filling empty space with zeros and prefix 'USD'. + +function euro($amount) + +This function will do the same as usd() but prefix the output with +'EUR'. Just to make it a bit more complex, we will also wrap the result +in span tags: + +:: + + + +Let's create the tests first: + +:: + + currencyRenderer = new CurrencyRendererHelper(); + } + + //testing usd() function. + public function testUsd() { + $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); + //We should always have 2 decimal digits. + $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); + $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); + //Testing the thousands separator + $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70)); + } + } + +Here, we call ``usd()`` with different parameters and tell the test +suite to check if the returned values are equal to what is expected. + +Executing the test now will result in errors (because +currencyRendererHelper doesn't even exist yet) showing that we have 3 +fails. + +Once we know what our method should do, we can write the method itself: + +:: + + `_ necesitamos un controlador para +acceder a los datos en el mmodelo. + +Si el método startup() del componente tiene este aspecto: + +:: + + public function startup(&$controller){ + $this->Transporter = $controller->Transporter; + } + +entonces podemos simplemente crear una clase sencilla: + +:: + + class FakeTransporterController {} + +y asignarle valores dentro de ella como aquí: + +:: + + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + +Creando un método de prueba +--------------------------- + +Simplemente crea una clase que extienda CakeTestCase y ¡comienza a +escribir tests! + +:: + + class TransporterTestCase extends CakeTestCase { + var $fixtures = array('transporter'); + function testGetTransporter() { + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); + $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); + + $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); + $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); + + $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); + $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); + $this->assertEqual($result, 0, "Noone can service Norway"); + } + } + + +Web testing - Testeando las vistas +================================== + +La mayoria, si no es que lo son todos, los proyectos CakePHP son +aplicaciones web. Aunque el testeo unitario es una excelente manera de +testear pequeñas porciones de nuestro código, hay ocaciones en la que +querriamos hacer un testeo a gran escala. La clase **CakeWebTestCase** +nos brinda una muy buena manera de hacer éste tipo de testing, desde el +punto de vista del usuario. + +About CakeWebTestCase +--------------------- + +**CakeWebTestCase** es una extensión directa de SimpleTest WebTestCase, +sin ninguna funcionalidad extra. Toda la funcionalidad encontrada en `la +documentación de SimpleTest para Testeo Web (Web +testing) `_ +tambien están disponibles aqui. Esto quiere decir que no se pueden usar +los fixtures, y que **todos los casos de testeo involucrados en un ABM +(alta, baja o modificación) a la base de datos modificarán +permanentemente los valores**. Los resultados del Test son comparados +frecuentemente con los qe tiene la base de datos, por lo tanto, +asegurarse que la bd tenga los valores que se esperan, es parte del +proceso de construcción del test. + +Creando un test +--------------- + +Manteniendo las convenciones de los otros tests, los archivos de testeo +de vistas se deberán crear en la carpeta tests/cases/views. Claro que se +podrian guardar en otra ubicación, pero siempre es bueno seguir las +convenciones. Entonces, crearemos el archivo: +tests/cases/views/complete\_web.test.php + +Para escribir testeos web, deberás extender la clase **CakeWebTestCase** +y no CakeTestCase, tal como era en los otros tests: + +:: + + class CompleteWebTestCase extends CakeWebTestCase + +Si necesitas hacer alguna inicialización antes de que comience el test, +crea el constructor: + +:: + + function CompleteWebTestCase(){ + //Do stuff here + } + +Cuando escribes los test cases, lo primero que vas a necesitar hacer es +capturar algun tipo de salida o resultado donde ver y analizar. Ésto +puede ser realizado haciendo un request **get** o **post**, usando los +métodos **get()**\ o **post()** respectivamente. A ambos métodos se le +pasa como primer parámetro la url, aunque puede ser traida dinámicamente +si asumimos que script de testing está en +http://your.domain/cake/folder/webroot/test.php tipeando: + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +Entonces podremos hacer gets y posts usando las urls de Cake, por +ejemplo: + +:: + + $this->get($this->baseurl."/products/index/"); + $this->post($this->baseurl."/customers/login", $data); + +El segundo parámetro del método post, **$data**, es un array asociativo +que contiene post data en el formato de Cake: + +:: + + $data = array( + "data[Customer][mail]" => "user@user.com", + "data[Customer][password]" => "userpass"); + +Una vez que se hizo el request a la página, se pueden utilizar todos los +mismos asserts que veniamos usando en SimpleTest. + +Walking through a page +---------------------- + +CakeWebTest also gives you an option to navigate through your page by +clicking links or images, filling forms and clicking buttons. Please +refer to the SimpleTest documentation for more information on that. + +Testing plugins +=============== + +Tests for plugins are created in their own directory inside the plugins +folder. + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +They work just like normal tests but you have to remember to use the +naming conventions for plugins when importing classes. This is an +example of a testcase for the PizzaOrder model from the plugins chapter +of this manual. A difference from other tests is in the first line where +'Pizza.PizzaOrder' is imported. You also need to prefix your plugin +fixtures with '``plugin.plugin_name.``\ '. + +:: + + PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); + + // do some useful test here + $this->assertTrue(is_object($this->PizzaOrderTest)); + } + } + ?> + +If you want to use plugin fixtures in the app tests you can reference +them using 'plugin.pluginName.fixtureName' syntax in the $fixtures +array. + +That is all there is to it. + +Miscellaneous +============= + +Customizing the test reporter +----------------------------- + +The standard test reporter is **very** minimalistic. If you want more +shiny output to impress someone, fear not, it is actually very easy to +extend. + The only danger is that you have to fiddle with core Cake code, +specifically **/cake/tests/libs/cake\_reporter.php**. + +To change the test output you can override the following methods: + +paintHeader() + Prints before the test is started. +paintPass() + Prints everytime a test case has passed. Use $this->getTestList() to + get an array of information pertaining to the test, and $message to + get the test result. Remember to call parent::paintPass($message). +paintFail() + Prints everytime a test case has failed. Remember to call + parent::paintFail($message). +paintFooter() + Prints when the test is over, i.e. when all test cases has been + executed. + +If, when running paintPass and paintFail, you want to hide the parent +output, enclose the call in html comment tags, as in: + +:: + + echo "\n\n"; + +A sample **cake\_reporter.php**\ setup that creates a table to hold the +test results follows: + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
Res.Test caseMessage
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tPass: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\tFail: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +Grouping tests +-------------- + +If you want several of your test to run at the same time, you can try +creating a test group. Create a file in **/app/tests/groups/** and name +it something like **your\_test\_group\_name.group.php**. In this file, +extend **GroupTest** and import test as follows: + +:: + + + +The code above will group all test cases found in the +**/app/tests/cases/models/** folder. To add an individual file, use +**TestManager::addTestFile**\ ($this, filename). + +Running tests in the Command Line +================================= + +If you have simpletest installed you can run your tests from the command +line of your application. + +from **app/** + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +As the help menu suggests, you'll be able to run all, part, or just a +single test case from your app, plugin, or core, right from the command +line. + +If you have a model test of **test/models/my\_model.test.php** you'd run +just that test case by running: + +:: + + cake testsuite app case models/my_model + diff --git a/es/The-Manual/Core-Behaviors.rst b/es/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..70f851b9f741fa4d6bc66b55f4c59f41febc326a --- /dev/null +++ b/es/The-Manual/Core-Behaviors.rst @@ -0,0 +1,15 @@ +Core Behaviors (Comportamientos Basicos) +######################################## + +Los comportamientos (Behaviors) agregan funcionalidad extra a tus +modelos. CakePHP viene con un numero de comportamientos incorporados +tales como el Arbol (Tree) y Contenible (Containable). + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/es/The-Manual/Core-Behaviors/ACL.rst b/es/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..7c5e4c3af1eb20de1ac44544537e5080e766c7cd --- /dev/null +++ b/es/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,105 @@ +ACL +### + +El comportamiento Acl provee una forma de integrar un modelo con tu +sistema ACL. Puede crear tanto los AROs o los ACOs transparentemente. + +Para usar el nuevo comportamiento, puedes añadirlo a la propiedad +$actsAs de tu modelo. Cuando lo agregas al arreglo $actsAs, se pueden +elegir entre hacer la entrada actual como un ARO o un ACO. El valor por +defecto es para crear AROs. + +:: + + class User extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +Esto incluye el comportamiento de Acl en el modo ARO. Para que el +comportamiento ACL sea ACO, se debe usar: + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +Se puede agregar el comportamiento ACL al vuelo, de la siguiente forma: + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +Using the AclBehavior +===================== + +Muchos de los comportamientos ACL funcionan transparentemente en el +método afterSave() del Modelo. Sin embargo, usarlo requiere que tu +Modelo tenga el método parentNode() definido. Esto es usado por el +comportamiento ACL para determinar las relaciones padre-hijo. El método +parentNode() del modelo debe retornar null, o bien, retornar una +referencia al modelo padre. + +:: + + function parentNode() { + return null; + } + +Si se quiere setear un nodo ACO o ARO como padre del modelo, +parentNode() debe retornar el alias del nodo ACO o ARO. + +:: + + function parentNode() { + return 'root_node'; + } + +Un ejemplo más completo. Usando un modelo User, donde User tiene la +relación belongsTo con el modelo Group. + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['User']['group_id']) { + return null; + } else { + $this->Group->id = $data['User']['group_id']; + $groupNode = $this->Group->node(); + return array('Group' => array('id' => $groupNode[0]['Aro']['foreign_key'])); + } + } + +En el ejemplo de arriba el valor de retorno es un arreglo que tiene la +misma estructura que el resultado de la operación find() del modelo. Es +importante setear el valor del id o la relacion parentNode fallará. El +comportamiento Acl usa esta data para construir la estructura del árbol. + +node() +====== + +El comportamiento ACL también permite rescatar el nodo ACL asociado al +registro del modelo. Despues de setear $model->id se puede utilizar +$model->node() para rescatar el nodo ACL asociado. + +Tambien se puede rescatar el nodo ACL de cualquier fila, pasandolo +dentro de un arreglo. + +:: + + $this->User->id = 1; + $node = $this->User->node(); + + $user = array('User' => array( + 'id' => 1 + )); + $node = $this->User->node($user); + +Ambos retornaran la misma información del nodo ACL. diff --git a/es/The-Manual/Core-Behaviors/Containable.rst b/es/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..a10c6c9372cf7076cb81a17da639f8711812b485 --- /dev/null +++ b/es/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,374 @@ +Containable +########### + +A new addition to the CakePHP 1.2 core is the ``ContainableBehavior``. +This model behavior allows you to filter and limit model find +operations. Using Containable will help you cut down on needless wear +and tear on your database, increasing the speed and overall performance +of your application. The class will also help you search and filter your +data for your users in a clean and consistent way. + +Containable allows you to streamline and simplify operations on your +model bindings. It works by temporarily or permanently altering the +associations of your models. It does this by using the supplied +containments to generate a series of ``bindModel`` and ``unbindModel`` +calls. + +To use the new behavior, you can add it to the $actsAs property of your +model: + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +You can also attach the behavior on the fly: + +:: + + $this->Post->Behaviors->attach('Containable'); + +Using Containable +================= + +To see how Containable works, let's look at a few examples. First, we'll +start off with a find() call on a model named Post. Let's say that Post +hasMany Comment, and Post hasAndBelongsToMany Tag. The amount of data +fetched in a normal find() call is rather extensive: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => Awesome + ) + [1] => Array + ( + [id] => 2 + [name] => Baking + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +For some interfaces in your application, you may not need that much +information from the Post model. One thing the ``ContainableBehavior`` +does is help you cut down on what find() returns. + +For example, to get only the post-related information, you can do the +following: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +You can also invoke Containable's magic from inside the find() call: + +:: + + $this->Post->find('all', array('contain' => false)); + +Having done that, you end up with something a lot more concise: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +This sort of help isn't new: in fact, you can do that without the +``ContainableBehavior`` doing something like this: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable really shines when you have complex associations, and you +want to pare down things that sit at the same level. The model's +``$recursive`` property is helpful if you want to hack off an entire +level of recursion, but not when you want to pick and choose what to +keep at each level. Let's see how it works by using the ``contain()`` +method. + +The contain method's first argument accepts the name, or an array of +names, of the models to keep in the find operation. If we wanted to +fetch all posts and their related tags (without any comment +information), we'd try something like this: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Again, we can use the contain key inside a find() call: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Without Containable, you'd end up needing to use the ``unbindModel()`` +method of the model, multiple times if you're paring off multiple +models. Containable creates a cleaner way to accomplish this same task. + +Containing deeper associations +============================== + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors — and +nothing else — you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the ``ContainableBehavior`` when you've got +deep and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that ``contain`` key is only used once in the main model, +you don't need to use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must be attached to all models used +in containment, you may consider attaching it to your AppModel. + +Using Containable with pagination +================================= + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +By including the 'contain' parameter in the ``$paginate`` property it +will apply to both the find('count') and the find('all') done on the +model + +ContainableBehavior options +=========================== + +The ``ContainableBehavior`` has a number of options that can be set when +the Behavior is attached to a model. The settings allow you to fine tune +the behavior of Containable and work with other behaviors more easily. + +- **recursive** (boolean, optional) set to true to allow containable to + automatically determine the recursiveness level needed to fetch + specified models, and set the model recursiveness to this level. + setting it to false disables this feature. The default value is + ``true``. +- **notices** (boolean, optional) issues E\_NOTICES for bindings + referenced in a containable call that are not valid. The default + value is ``true``. +- **autoFields**: (boolean, optional) auto-add needed fields to fetch + requested bindings. The default value is ``true``. + +You can change ContainableBehavior settings at run time by reattaching +the behavior as seen in `Using behaviors `_ + +ContainableBehavior can sometimes cause issues with other behaviors or +queries that use aggregate functions and/or GROUP BY statements. If you +get invalid SQL errors due to mixing of aggregate and non-aggregate +fields, try disabling the ``autoFields`` setting. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/es/The-Manual/Core-Behaviors/Translate.rst b/es/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..debe733f56972c224d6b2435db2b4742de95e731 --- /dev/null +++ b/es/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,362 @@ +Translate +######### + +TranslateBehavior es bastante fácil de configurar y trabaja fuera de la +caja con muy poca configuración. En esta sección, usted aprenderá cómo +añadir y configurar el comportamiento (behavior) para usarlo en +cualquier modelo. + +Inicializando las tablas de la Base de datos i18n +================================================= + +Puede utilizar la consola de CakePHP o puede crearlo manualmente. Se +recomienda utilizar la consola para esto, por que podrían pasar que +hallan cambios de diseño en las futuras versiones de CakePHP. Si lo +haces por consola, puedes estar seguro que tiene el correcto diseño. + +:: + + ./cake i18n + +Seleccione ``[I]`` y se ejecuta el script e inicializa la Base de datos +i18n. Se le preguntará si desea eliminar cualquiera que exista y si +desea crearla.Responde un Sí, si usted esta seguro de que no existe una +tabla i18n, y responda con Sí para crear la tabla de nuevo. + +Adjuntando el Comportamiento de Traducción a tus Modelos +======================================================== + +Se debe incorporar al modelo haciendo uso de la propiedad ``$actsAs`` +como en el siguiente ejemplo. + +:: + + + +Esto no hará nada aún, pues es necesario configurar algunas opciones +antes de comenzar a funcionar. Se deben definir qué campos de el modelo +actual serán rastreados en la tabla de traducciones creada en el paso +anterior. + +Definiendo los Campos +===================== + +Se pueden establecer los campos simplemente extendiendo el valor +``'Translate'`` con otro array, por ejemplo: + +:: + + array( + 'campoUno', 'campoDos', 'campoN' + ) + ); + } + ?> + +Luego de haber hecho esto (por ejemplo poner "nombre" como uno de los +campos) ya has terminado la configuración básica. Genial! De acuerdo con +el ejemplo anterior, ahora el modelo debería verse algo así: + +:: + + array( + 'nombre' + ) + ); + } + ?> + +Conclusiones +============ + +Desde ahora en cada actualización/creación de un registro hará que +TranslateBehavior copie el valor "nombre" en la tabla de traducciones +(por defecto: i18n) de acuerdo a la localización actual. Una +localización corresponde al identificador de una lengua + +La *localización actual* es el valor actual de +``Configure::read('Config.language')``. El valor de *Config.language* es +establecido en la Clase L10n - a no ser que ya se haya establecido. Sin +embargo, TranlateBehavior te permite invalidar esto último 'al vuelo', +lo cual permite al usuario de tus páginas crear multiples versiones sin +la necesidad de que este cambie sus preferencias. Más sobre esto en la +siguiente sección. + +Obtener todos los registros de traducción para un campo determinado +=================================================================== + +Si se desea obtener todos los registros de traducción asociados al +modelo actual, simplemente se extiende el *arreglo de campos* en la +configuracion como se muestra abajo. El nombre se puede definir sin +restricciones + +:: + + array( + 'name' => 'nameTranslation' + ) + ); + } + ?> + +Con esta configuración el resultado de find() se verá similar a esto: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +**Nota**: El registro del modelo contiene un campo *virtual* llamado +"locale", el cual indica que "locale" es usado en el resultado. + +Using the bindTranslation method +-------------------------------- + +También puedes obtener todas las traducciones, sólo cuando las +necesites, utilizando el método bindTrasnlation + +``bindTranslation($fields, $reset)`` + +``$fields`` es un array asociativo, donde la clave es la celda a +traducir y el valor es el nombre falso de la asociación. + +:: + + $this->Post->bindTranslation(array ('name' => 'nameTranslation')); + $this->Post->find('all', array ('recursive'=>1)); // need at least recursive 1 for this to work. + +Con esta configuración el resultado de tu find() debería parecerse a +algo como esto: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +Saving in another language +========================== + +You can force the model which is using the TranslateBehavior to save in +a language other than the one detected. + +To tell a model in what language the content is going to be you simply +change the value of the ``$locale`` property on the model before you +save the data to the database. You can do that either in your controller +or you can define it directly in the model. + +**Example A:** In your controller + +:: + + data) { + $this->Post->locale = 'de_de'; // we are going to save the german version + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**Example B:** In your model + +:: + + array( + 'name' + ) + ); + + // Option 1) just define the property directly + var $locale = 'en_us'; + + // Option 2) create a simple method + function setLanguage($locale) { + $this->locale = $locale; + } + } + ?> + +Multiple Translation Tables +=========================== + +If you expect a lot of entries you probably wonder how to deal with a +rapidly growing database table. There are two properties introduced by +TranslateBehavior that allow you to specify which "Model" to bind as the +model containing the translations. + +These are **$translateModel** and **$translateTable**. + +Lets say we want to save our translations for all posts in the table +"post\_i18ns" instead of the default "i18n" table. To do so you need to +setup your model like this: + +:: + + array( + 'name' + ) + ); + + // Use a different model (and table) + var $translateModel = 'PostI18n'; + } + ?> + +**Important** to note is that you have to pluralize the table. It is now +a usual model and can be treated as such and thus comes with the +conventions involved. The table schema itself must be identical with the +one generated by the CakePHP console script. To make sure it fits one +could just initialize an empty i18n table using the console and rename +the table afterwards. + +Create the TranslateModel +------------------------- + +For this to work you need to create the actual model file in your models +folder. The reason is that there is no property to set the displayField +directly in the model using this behavior yet. + +Make sure that you change the ``$displayField`` to ``'field'``. + +:: + + + +That's all it takes. You can also add all other model stuff here like +$useTable. But for better consistency we could do that in the model +which actually uses this translation model. This is where the optional +``$translateTable`` comes into play. + +Changing the Table +------------------ + +If you want to change the name of the table you simply define +$translateTable in your model, like so: + +:: + + array( + 'name' + ) + ); + + // Use a different model + var $translateModel = 'PostI18n'; + + // Use a different table for translateModel + var $translateTable = 'post_translations'; + } + ?> + +Please note that **you can't use $translateTable alone**. If you don't +intend to use a custom ``$translateModel`` then leave this property +untouched. Reason is that it would break your setup and show you a +"Missing Table" message for the default I18n model which is created in +runtime. diff --git a/es/The-Manual/Core-Behaviors/Tree.rst b/es/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..70651b907494f162bc4b2ee065de436c2b32398d --- /dev/null +++ b/es/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,625 @@ +Arboles (Tree) +############## + +Es muy común que se desea almacenar datos jerárquicos en una tabla de +base de datos. Ejemplos de estos datos pueden ser: categorías con +subcategorías ilimitadas, los datos relativos a un sistema de menús +multinivel o una representación literal de la jerarquía tal como se +utiliza para almacenar objetos de control de acceso con la lógica ACL. + +Para pequeños árboles de datos, o cuando los datos son de sólo unos +pocos niveles de profundidad es simple añadir un campo parent\_id a su +tabla de base de datos y utilizar este para hacer un seguimiento de cual +item es padre de quien. Sin embargo, Cake Viene equipado con este +paquete, pero con un comportamiento de gran alcance que le permite +utilizar los beneficios de la lógica +`MPTT `_ +sin preocuparse de cualquiera de los entresijos de la técnica - a menos +que ud lo desee ;). + +Requerimientos +============== + +Para poder usar la funcionalidad de árbol, la tabla de la base de datos +debe tener los 3 campos listados a continuación (enteros todos): + +- padre - el nombre por defecto del campo es **parent\_id**, para + almacenar el id del objeto padre +- izquierda - el nombre por defecto del campo es **lft**, para + almacenar el valor lft de la fila actual. +- deracha - el nombre por defecto del campo es **rght**, para almacenar + el valor rght de la fila actual. + +Si esta familiarizado con la lógina MPTT ud puede preguntarse por que el +campos parent existe - simplemente es más facil de realizar ciertas +tareas si existe un enlace directo al padre almacenado en la base de +datos - tales como la búsquera de los hijos directos. + +Uso Básico +========== + +El comportamiento de arbol tiene muchas cosas incluidas, pero empecemos +con un simple ejemplo - crearemos una pequeña tabla en una base de datos +y le agregaremos algunos datos: + +:: + + CREATE TABLE categories ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + name VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(1, 'Mis Categorias', NULL, 1, 30); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(2, 'Diversion', 1, 2, 15); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(3, 'Deportes', 2, 3, 8); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(4, 'Surfing', 3, 4, 5); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(5, 'Alpinismo', 3, 6, 7); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(6, 'Amigos', 2, 9, 14); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(7, 'Gerald', 6, 10, 11); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(8, 'Gwendolyn', 6, 12, 13); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(9, 'Trabajo', 1, 16, 29); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(10, 'Reportes', 9, 17, 22); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(11, 'Anual', 10, 18, 19); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(12, 'Status', 10, 20, 21); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(13, 'Viajes', 9, 23, 28); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(14, 'Nacional', 13, 24, 25); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(15, 'Internacional', 13, 26, 27); + +Para el propósito de verificar que todo está configurado correctamente, +podemos crear un metodo de testeo y obtener los contenidos de nuestro +arbol de categorias para ver como queda. Con un simple controlador: + +:: + + data = $this->Category->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +y un metodo aun mas simple en el modelo: + +:: + + + +Podemos verificar como se ve que nuestro arbol de categoria visitando +/categories Deberias ver algo como: + +- Mis Categorias + + - Diversion + + - Deportes + + - Surfing + - Alpinismo + + - Amigos + + - Gerald + - Gwendolyn + + - Trabajo + + - Reportes + + - Anual + - Status + + - Viajes + + - Nacional + - Internacional + +Agregando datos +--------------- + +En la seccion anterior, usamos datos pre-existentes y chequeamos que se +vieran en forma jerarquica con el método ``generatetreelist``. Sin +embargo, usualmente agregaríamos los datos de la misma forma que lo +hariamos con cualquier modelo. Por ejemplo: + +:: + + // pseudo código del controlador + $data['Category']['parent_id'] = 3; + $data['Category']['name'] = 'Skating'; + $this->Category->save($data); + +Cuando se usa el comportamiento de arbol no es necesario hacer nada mas +que configurar el parent\_id, y el comportamiento de arbol se encargara +del resto. Si no se setea el parent\_id el comportamiento de arbol lo +agregara al arbol como una entrada en el nivel superior: + +:: + + // pseudo codigo controlador + $data = array(); + $data['Category']['name'] = 'Otra Categoria'; + $this->Category->save($data); + +Ejecutando estos dos trozos de código alterará el árbol como sigue: + +- Mis Categorias + + - Diversion + + - Deportes + + - Surfing + - Alpinismo + - Skating **New** + + - Amigos + + - Gerald + - Gwendolyn + + - Trabajo + + - Reportes + + - Anual + - Status + + - Viajes + + - Nacional + - Internacional + +- Otra Categoria **New** + +Modificando datos +----------------- + +Modificar datos es tan transparente como agregar nuevos datos. Si +modificas algo, pero no modificas el campo parent\_id - la estructura de +tus datos permanecera inalterada. Por ejemplo: + +:: + + // pseudo codigo de controlador + $this->Category->id = 5; // id de Alpinismo + $this->Category->save(array('name' =>'Pesca')); + +El codigo anterior no modifica el parent\_id - incluso si el parent\_id +es incluido en los datos que son pasados al método save, si el valor no +ha sido cambiado, tampoco lo hace la estructura de datos. Entonces, el +arbol de datos queda: + +- Mis Categorias + + - Diversion + + - Deportes + + - Surfing + - Pesca **Updated** + - Skating + + - Amigos + + - Gerald + - Gwendolyn + + - Trabajo + + - Reportes + + - Anual + - Status + + - Viajes + + - Nacional + - Internacional + +- Otra Categoria + +Mover un dato a traves del arbol tambien es simple. Digamos que Pesca no +pertenece a Deportes, sino que deberia estar en Otra Categoria. Con el +siguiente código: + +:: + + // pseudo codigo de controlador + $this->Category->id = 5; // id of Pesca + $newParentId = $this->Category->field('id', array('name' => 'Otra Categoria')); + $this->Category->save(array('parent_id' => $newParentId)); + +Como es de esperar la estructura queda modificada a: + +- Mis Categorias + + - Diversion + + - Deportes + + - Surfing + - Skating + + - Amigos + + - Gerald + - Gwendolyn + + - Trabajo + + - Reportes + + - Anual + - Status + + - Viajes + + - Nacional + - Internacional + +- Otra Categoria + + - Pesca **Movido** + +Borrando datos +-------------- + +El comportamiento de arbol provee algunas formas para manejar la +eliminación de datos. Para comenzar con un ejemplo simple, diremos que +la categoria deportes ya no es necesaria. Para eliminarla *y cualquier +hijo que ella tenga* basta llamar a delete tal como lo harias en +cualquier modelo. Por ejemplo, en el siguiente código: + +:: + + // pseudo codigo de controlador + $this->Category->id = 10; + $this->Category->delete(); + +El arbol de categorias sería modificado como sigue: + +- Mis Categorias + + - Diversion + + - Deportes + + - Surfing + - Skating + + - Amigos + + - Gerald + - Gwendolyn + + - Trabajo + + - Viajes + + - Nacional + - Internacional + +- Otras Categorias + + - Pesca + +Haciendo consultas y usando tus datos +------------------------------------- + +Usar y manipular datos jerarquicos puede ser algo complejo. Ademas de +los metodos de busqueda del nucleo como find(), con los arboles tenemos +unos cuantos metodos más para su manipulacion. + +Muchos metodos del comportamiento de arbol devuelven y se apoyan en el +orden del campo ``lft``. Si llamas a ``find()`` y no ordenas por el +campo ``lft``, o llamas algun metodo de arboles entregandole un tipo de +ordenamiento, quizas obtengas resultados no deseados. + +El método children +~~~~~~~~~~~~~~~~~~ + +El método ``children`` toma la llave primaria (id) de una fila y retorna +los hijos, por defecto en el roden en que aparecen en el árbol. El +segundo parámetro es opcional y define si se entregan o no sólo los +hijos directos. Usando el ejemplo de la sección anterior: + +:: + + $allChildren = $this->Category->children(1); // un arreglo plano con 11 valores + // -- o bien -- + $this->Category->id = 1; + $allChildren = $this->Category->children(); // un arreglo plano con 11 valores + + // Retornar solo los hijos directos + $directChildren = $this->Category->children(1, true); //un arreglo plano con 2 valores + +Si quieres un arreglo recursivo utiliza ``find('threaded')`` + +Contando los hijos +~~~~~~~~~~~~~~~~~~ + +Tal como el método ``children``, ``childCount`` toma la llave primaria +(id) y retorna cuantos hijos tiene. El segundo parámetro es opcional y +define si se contarán o no los hijos directos. Usando los datos del +ejemplo anterior: + +:: + + $totalChildren = $this->Category->childCount(1); // entrega 11 + // -- o bien -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // entrega 11 + + //Solo los hijos directos + $numChildren = $this->Category->childCount(1, true); // entrega 2 + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist($conditions = null, $keyPath = null, $valuePath = null, $spacer= '_', $recursive = null)`` + +Este método retorna la data de forma similar a find('list'), mostrando +la data identada mediante el uso de un prefijo. A continuación se +muestra un ejemplo de los que se debe esperar de este método. Vea el api +para más información acerca de los parámetros. + +:: + + array( + [1] => "My Categories", + [2] => "_Fun", + [3] => "__Sport", + [4] => "___Surfing", + [16] => "___Skating", + [6] => "__Friends", + [7] => "___Gerald", + [8] => "___Gwendolyn", + [9] => "_Work", + [13] => "__Trips", + [14] => "___National", + [15] => "___International", + [17] => "Other People's Categories", + [5] => "_Extreme fishing" + ) + +getparentnode +~~~~~~~~~~~~~ + +Esta función devolverá, como el nombre lo indica, el nodo padre de +cualquier nodo. En caso de que el nodo no tenga un padre, entonces +devolverá falso. (Por ejemplo, el nodo raiz). Ejemplo: + +:: + + $parent = $this->Category->getparentnode(2); //<- id de Diversión + // $parent contiene al padre del nodo Diversión, es decir, el nodo raíz. + +getpath +~~~~~~~ + +The 'path' when refering to hierachial data is how you get from where +you are to the top. So for example the path from the category +"International" is: + +- My Categories + + - ... + - Work + + - Trips + + - ... + - International + +Using the id of "International" getpath will return each of the parents +in turn (starting from the top). + +:: + + $parents = $this->Category->getpath(15); + +:: + + // contents of $parents + array( + [0] => array('Category' => array('id' => 1, 'name' => 'My Categories', ..)), + [1] => array('Category' => array('id' => 9, 'name' => 'Work', ..)), + [2] => array('Category' => array('id' => 13, 'name' => 'Trips', ..)), + [3] => array('Category' => array('id' => 15, 'name' => 'International', ..)), + ) + +Uso Avanzado +============ + +Este comportamiento de modelo no sólo trabaja en segundo plano, hay una +gran variedad de métodos específicos definidos en este comportamiento +para antender todas tus necesidades de datos jerárquicos, y cualquier +problema inesperado que pueda surgir en el proceso. + +moveDown +-------- + +Utilizado para mover un único nodo hacia abajo del árbol. Debes indicar +el *ID* del elemento a mover y un número entero positivo de cuantas +posiciones el nodo debería ser movido hacia abajo. Todos los nodos hijos +del nodo a mover también serán movidos + +Un ejemplo de una acción de controlador (en un controlador llamado +Categories) que mueve un nodo hacia abajo del arbol: + +:: + + function movedown($name = null, $delta = null) { + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('No hay una categoría de nombre ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Por favor indique el número de posiciones que el nodo debe ser movido hacia abajo.'); + } + + $this->redirect(array('action' => 'index'), null, true); + } + +Por ejemplo, si quicieras mover la categoría "Sport" un nivel hacia +abajo, deberías llamar a: /categories/movedown/Sport/1. + +moveUp +------ + +Used to move a single node up the tree. You need to provide the ID of +the element to be moved and a positive number of how many positions the +node should be moved up. All child nodes will also be moved. + +Here's an example of a controller action (in a controller named +Categories) that moves a node up the tree: + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('There is no category named ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Please provide a number of positions the category should be moved up.'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +For example, if you would like to move the category "Gwendolyn" up one +position you would request /categories/moveup/Gwendolyn/1. Now the order +of Friends will be Gwendolyn, Gerald. + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Using this method wil either delete or move a node but retain its +sub-tree, which will be reparented one level higher. It offers more +control than ```delete()`` `_, which for a model +using the tree behavior will remove the specified node and all of its +children. + +Taking the following tree as a starting point: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating + +Running the following code with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id); + +The Sport node will be become a top level node: + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +- Sport **Moved** + +This demonstrates the default behavior of ``removeFromTree`` of moving +the node to have no parent, and re-parenting all children. + +If however the following code snippet was used with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id,true); + +The tree would become + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +This demonstrates the alternate use for ``removeFromTree``, the children +have been reparented and 'Sport' has been deleted. + +reorder +------- + +This method can be used to sort hierarchical data. + +Data Integrity +============== + +Due to the nature of complex self referential data structures such as +trees and linked lists, they can occasionally become broken by a +careless call. Take heart, for all is not lost! The Tree Behavior +contains several previously undocumented features designed to recover +from such situations. + +These functions that may save you some time are: + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +The mode parameter is used to specify the source of info that is +valid/correct. The opposite source of data will be populated based upon +that source of info. E.g. if the MPTT fields are corrupt or empty, with +the $mode 'parent' the values of the parent\_id field will be used to +populate the left and right fields. The missingParentAction parameter +only applies to "parent" mode and determines what to do if the parent +field contains an id that is not present. + +reorder(&$model, $options = array()) + +Reorders the nodes (and child nodes) of the tree according to the field +and direction specified in the parameters. This method does not change +the parent of any node. + +The options array contains the values 'id' => null, 'field' => +$model->displayField, 'order' => 'ASC', and 'verify' => true, by +default. + +verify(&$model) + +Returns true if the tree is valid otherwise an array of (type, incorrect +left/right index, message). diff --git a/es/The-Manual/Core-Components.rst b/es/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..f509de9df8ce6959101e30ab2c724894dae63bdb --- /dev/null +++ b/es/The-Manual/Core-Components.rst @@ -0,0 +1,59 @@ +Componentes del Núcleo +###################### + +CakePHP posee una serie de componentes integrados. Éstos proveen +distintas funcionalidades para tareas realizadas comunmente. + +Acl + +El componente Acl provee una sencilla interfaz para listas de control de +acceso (*access control list*) basadas en archivos ini o base de datos. + +Auth + +El componente Auth provee un sistema de autenticación fácil de utilizar +usando diferentes procesos de validación, como ser *callbacks* en los +controladores, Acl u *callbacks* en los objetos. + +Session + +El componente Session provee un *wrapper* de almacenamiento +independiente a las sesiones de PHP. + +RequestHandler + +El componente RequestHandler permite analizar las peticiones HTTP para +informarle a la aplicación acerca del tipo de contenido y la información +requerida por el usuario. + +Security + +El componente Security permite aumentar la seguridad y gestionar +autenticación HTTP. + +Email + +Una interfaz que puede ser utilizada para enviar emails usando distintos +MTA (*mail transfer agent*) incluyendo la función mail() de PHP y el +protocolo SMTP. + +Cookie + +El componente Cookie se comporta en cierta forma similar al Session ya +que provee un *wrapper* para el soporte nativo de cookies en PHP. + +Para aprender más acerca de cada componente mira en el menu a la +izquierda, o aprende acerca de `cómo crear tus propios +componentes `_. + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/es/The-Manual/Core-Components/Access-Control-Lists.rst b/es/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..0f804cd4471614be4baf0330d55f3770ba12d63b --- /dev/null +++ b/es/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,882 @@ +Listas de Control de Acceso +########################### + +La funcionalidad de listas de control de acceso en CakePHP es una de las +más comentadas, en parte porque es una de las más solicitadas, y en +parte porque puede ser algo confusa al principio. Si estás buscando un +buen lugar para comenzar a utilizar ACL en general, continúa leyendo. + +Debes ser valiente, incluso cuando las cosas se compliquen. Una vez que +asimiles estos conceptos, las listas de control de acceso son una +herramienta extremadamente poderosa para tener a mano al desarrollar tu +aplicación. + +Entendiendo cómo funciona ACL +============================= + +Las listas de control de acceso permiten gestionar detalladamente los +permisos de una aplicación de forma sencilla y escalable. + +Las listas de control de acceso, o ACL, manejan principalmente dos +cosas: las entidades que solicitan el control de algo y las entidades +que se quiere controlar. En la jerga de ACL, las entidades que quieren +controlar algo, que la mayoría de las veces son los usuarios, son los +ARO (en inglés *access request objects*), y las entidades del sistema +que se quiere controlar, que normalmente son acciones o datos, son los +ACO (en inglés *access control objects*). A los ARO se les llama +'objetos' porque quien realiza la petición no siempre es una persona; +los ACO son cualquier cosa que desees controlar: desde la acción de un +controlador o un servicio Web, hasta el diario en línea íntimo de tu +abuela. + +En resumen: + +- ACO - Access Control Object - Objeto que se quiere controlar +- ARO - Access Request Object - Objeto que solicita el control de algo + +Esencialmente, las ACL se utilizan para decidir cuándo un ARO puede +acceder a un ACO. + +Vamos a utilizar un ejemplo práctico para ver cómo encajan todas estas +piezas. Imaginemos que el grupo de aventureros de la novela de fantasía +*El señor de los Anillos* trabaja con una aplicación CakePHP, y que el +líder, Gandalf, se encarga de gestionar los elementos del grupo. Para +garantizar la privacidad y la seguridad de los miembros del grupo, pues, +lo primero que hace Gandalf es crear la lista de AROs involucrados: + +- Gandalf +- Aragorn +- Bilbo +- Frodo +- Gollum +- Legolas +- Gimli +- Pippin +- Merry + +Fíjate que ACL *no* es lo mismo que la autenticación; ACL es lo que +ocurre *después* de que el usuario se autentica. Aunque suelen +utilizarse los dos a la vez, es importante ver la diferencia entre saber +quién es alguien (autenticación) y saber qué puede hacer (ACL). + +A continuación, Gandalf tiene que crear una lista con las cosas, o ACOs, +que el sistema maneja. Esta lista puede ser como la siguiente: + +- Armas +- El Anillo +- Jamón +- Diplomacia +- Cerveza + +Armas + +El Anillo + +Jamón + +Diplomacia + +Cerveza + +Gandalf + +Permitir + +Permitir + +Permitir + +Aragorn + +Permitir + +Permitir + +Permitir + +Permitir + +Bilbo + +Permitir + +Frodo + +Permitir + +Permitir + +Gollum + +Permitir + +Legolas + +Permitir + +Permitir + +Permitir + +Permitir + +Gimli + +Permitir + +Permitir + +Pippin + +Permitir + +Permitir + +Merry + +Permitir + +A simple vista, parece que este tipo de sistema funciona bastante bien. +En efecto, las asignaciones garantizan la seguridad (sólo Frodo puede +acceder al anillo) y previenen los accidentes (los Hobbits se mantienen +lejos del jamón y de las armas). Parece bastante detallado y fácil de +leer, ¿verdad? + +Sin embargo, una matriz como esta sólo funciona en un sistema pequeño; +en un sistema que tenga previsto crecer, o que tenga muchos recursos +(ACOs) y usuarios (AROs), es difícil mantenerla. Ciertamente, ¿te +imaginas, en este ejemplo, cómo se controlaría el acceso a cientos de +campamentos de guerra, gestionándolos por unidad? Otro inconveniente de +las matrices es que no permiten formar grupos lógicos de usuarios ni +aplicar cambios de permiso en cascada a estos grupos. En otras palabras, +estaría muy bien que, una vez terminada la batalla, los hobbits pudieran +acceder a la cerveza y al jamón; en otras palabras, otorgar +individualmente permisos a hobbits es tedioso y propenso a errores, +mientras que aplicarles un cambio en cascada es mucho más fácil. + +ACL se suele implementar en una estructura de árbol, y, generalmente, +existe un árbol de AROs y un árbol de ACOs. Organizando los objetos así, +los permisos se gestionan de forma granular y se mantiene una visión +general; en consecuencia, siendo el sabio líder que es, Gandalf decide +utilizar ACL en su sistema y organiza los objetos de la siguiente +manera: + +- Comunidad del Anillo™ + + - Guerreros + + - Aragorn + - Legolas + - Gimli + + - MAgos + + - Gandalf + + - Hobbits + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitantes + + - Gollum + +Utilizar una estructura de árbol en los AROs permite a Gandalf definir +permisos que se aplican a un grupo de usuarios, de una sola vez. Por lo +tanto, Gandalf añade ahora estos permisos a los grupos: + +- Comunidad del Anillo + (**Denegar**: todo) + + - Guerreros + (**Permitir**: Armas, Cerveza, Raciones, Jamón) + + - Aragorn + - Legolas + - Gimli + + - Magos + (**Permitir**: Jamón, Diplomacia, Cerveza) + + - Gandalf + + - Hobbits + (**Permitir**: Cerveza) + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitantes + (**Permitir**: Jamón) + + - Gollum + +Para ver si Pippin tiene acceso a la cerveza, lo primero que tenemos que +hacer es obtener su camino en el árbol: Comunidad del +Anillo->Hobbits->Pippin. A continuación, hay que ver los permisos que +residen en cada uno de esos puntos, y, finalmente, se utiliza el más +específico relacionado con Pippin y la Cerveza. + ++------------------------+---------------------------+-------------------------------------+ +| Nodo ARO | Información de Permisos | Resultado | ++========================+===========================+=====================================+ +| Comunidad del Anillo | Denegar todo | Acceso a la Cerveza denegado. | ++------------------------+---------------------------+-------------------------------------+ +| Hobbits | Permitir 'Cerveza' | Acceso a la Cerveza permitido! | ++------------------------+---------------------------+-------------------------------------+ +| Pippin | -- | Todavía se le permite la Cerveza! | ++------------------------+---------------------------+-------------------------------------+ + +Como el nodo de 'Pippin' del árbol ACL no deniega específicamente el +acceso al ACO 'Cerveza', el resultado final es que se permite el acceso +a ese ACO. + +Además, el árbol permite realizar ajustes más finos para tener un +control más granular y no pierda la capacidad de realizar cambios +importantes en los grupos de AROs: + +- Comunidad del Anillo + (**Denegar**: todo) + + - Guerreros + (**Permitir**: Armas, Cerveza, Raciones, Jamón) + + - Aragorn + (Permitir: Diplomacia) + - Legolas + - Gimli + + - MAgos + (**Permitir**: Jamón, Diplomacia, Cerveza) + + - Gandalf + + - Hobbits + (**Permitir**: Cerveza) + + - Frodo + (Permitir: Anillo) + - Bilbo + - Merry + (Denegar: Cerveza) + - Pippin + (Permitir: Diplomacia) + + - Visitantes + (**Permitir**: Jamón) + + - Gollum + +Esta aproximación permite realizar cambios de permisos globales y, al +mismo tiempo, ajustes granulares. Por lo tanto, podemos decir que, a +excepción de Merry, todos los hobbits tienen acceso a la cerveza. De +nuevo, para saber si Merry tiene acceso a la Cerveza, primero debemos +encontrar su camino en el árbol, Comunidad del Anillo->Hobbits->Merry, +bajar, y analizar los premisos relacionados con la Cerveza: + ++------------------------+---------------------------+----------------------------------+ +| Nodo ARO | Información de Permisos | Resultado | ++========================+===========================+==================================+ +| Comunidad del Anillo | Denegar todo | Acceso a la Cerveza denegado. | ++------------------------+---------------------------+----------------------------------+ +| Hobbits | Permitir 'Cerveza' | Acceso a la Cerveza permitido! | ++------------------------+---------------------------+----------------------------------+ +| Merry | Denegar 'Cerveza' | Cerveza denegada. | ++------------------------+---------------------------+----------------------------------+ + +Definiendo Permisos: ACL basado en INI +====================================== + +La primer implementación de ACL en Cake fue basada en archivos INI +almacenados en el directorio de instalación de Cake. Si bien es útil y +estable, recomendamos que utilices la solución de ACL apoyada en la base +de datos, sobre todo por la posibilidad de crear nuevos ACOs y AROs +dentro de la aplicación. La implementación con archivos INI fue pensada +para ser utilizada en aplicaciones simples, especialmente en aquellas +que por alguna razón podrían no utilizar una base de datos. + +Por defecto, CakePHP utiliza el sistema de ACL apoyado en la base de +datos. Para habilitar el uso de ACL con archivos INI, tiene que decirle +a CakePHP qué sistema vas a usar editando las siguientes líneas en +app/config/core.php + +:: + + //Cambiar éstas líneas: + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + //Para que se vean como éstas: + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +Los permisos ARO/ACO son especificados en **/app/config/acl.ini.php**. +Básicamente los AROs se deben especificar en la sección del INI que +tiene tres propiedades: grupos, permitir y denegar. + +- grupos: nombres de los grupos de AROs a los que pertenece este ARO. +- permitir: nombres de los ACOs a los que tiene acceso este ARO. +- denegar: nombres de los ACOs a los que este ARO no tiene acceso. + +Los ACOs se definen en la sección del INI que sólo incluye las +propiedades permitir y denegar. + +Como ejemplo, veamos cómo se vería la estructura de AROs de la Comunidad +del Anillo en la sintaxis del INI: + +:: + + ;------------------------------------- + ; AROs + ;------------------------------------- + [aragorn] + grupos = guerreros + permitir = diplomacia + + [legolas] + grupos = guerreros + + [gimli] + grupos = guerreros + + [gandalf] + grupos = magos + + [frodo] + grupos = hobbits + permitir = ring + + [bilbo] + grupos = hobbits + + [merry] + grupos = hobbits + deny = cerveza + + [pippin] + grupos = hobbits + + [gollum] + grupos = visitantes + + ;------------------------------------- + ; ARO Groups + ;------------------------------------- + [guerreros] + permitir = armas, cerveza, jamón + + [magos] + permitir = jamón, diplomacia, cerveza + + [hobbits] + permitir = cerveza + + [visitantes] + permitir = jamón + +Ahora que ya has definido los permisos, puede saltar a `la sección de +verificación de permisos `_ +utilizando el componente ACL. + +Definiendo Permisos: ACL en la base de datos +============================================ + +Ahora que ya hemos cubierto los permisos de ACL basados en archivos INI, +veamos los permisos (más comúnmente utilizados) basados en base de +datos. + +Comenzando +---------- + +La implementación de ACL por defecto está basada en la base de datos. +Ésta consiste en un conjunto de modelos y una aplicación de consola que +viene con la instalación de Cake. Los modelos son usados por Cake para +interactuar con la base de datos para poder almacenar y recuperar los +nodos en forma de árbol. La aplicación de consola se utiliza para +inicializar la base de datos e interacturar con los árboles de ACOs y +AROs + +Para comenzar, lo primero que deber asegurarte es que +``/app/config/database.php`` exista y esté correctamente configurado. +Referirse a la sección 4.1 para más información acerca de la +configuración de la base de datos. + +Una vez que hayas hecho esto, tienes que utilizar la consola de CakePHP +para crear las tablas de ACL: + +:: + + $ cake schema run create DbAcl + +Ejecutando este comando recreará las tablas necesarias para almacenar +los árbols de ACOs y AROs (si las tablas ya existían, este comando las +elimina y las vuelve a crear). La salida de la consola debería verse +algo así: + +:: + + --------------------------------------------------------------- + Cake Schema Shell + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +Ésto reemplaza al viejo comando "initdb", el cuál ya es obsoleto. + +También puedes utilizar el archivo SQL que se encuentra en +``app/config/sql/db_acl.sql``, pero está lejos de ser tan divertido. + +Cuando finalices, deberías tener tres nuevas tablas en tu sistema: acos, +aros, and aros\_acos (la tabla de la relación n a n donde se definen los +permisos entre los dos árboles). + +Si eres curioso en saber cómo Cake almacena la información de los +árboles en esas tablas, debes leer acerca del recorrido de árboles en +bases de datos (en inglés *modified database tree traversal*). El +componente ACL utiliza el `Comportamiento de +Árbol `_ para gestionar la herencia dentro +del árbol. Las clases pertenecientes a los modelos de ACL están +contenidas en un único archivo +`db\_acl.php `_. + +Ahora que ya hemos configurado todo, creemos algunos árboles de AROs y +ACOs + +Creando Access Request Objects (AROs) y Access Control Objects (ACOs) +--------------------------------------------------------------------- + +Al crear nuevos objetos ACL (ACOs y AROs), hay dos formas de nombrar y +acceder a los nodos. El *primer* método consiste en realizar un enlace +entre el objeto ACL directamente con el registro de la base de datos, +especificando el nombre del modelo y el valor de la clave externa. El +*segundo* método puede usarse cuando un objeto no tiene relación con un +registro en la base de datos; puedes proveer un alias para este tipo de +objetos. + +En general, cuando estás creando un grupo o un nivel más alto de +objetos, deber usar un alias. Si estás gestionando el acceso a un +registro específico de la base de datos, debes usar el método de +modelo/clave externa. + +Para crear nuevos objetos ACL debes utilizar los modelos de ACL +provistos por CakePHP, en los cuales existen algunos campos que +necesitas conocer para almacenar la información: ``model``, +``foreign_key``, ``alias``, y ``parent_id``. + +Los campos ``model`` y ``foreign_key`` de un objeto ACL te permiten +enlazar directamente el objeto con el correspondiente registro de la +base de datos (si existe alguno). Por ejemplo, muchos AROs tendrán su +correspondencia con registros de Usuarios en la base de datos. +Estableciendo el campo ``foreign_key`` del ARO con el ID del Usuario te +permitirá enlazar la información del ARO y del Usuario con una simple +llamada find() del modelo del Usuario si las relaciones fueron +configuradas correctamente. En cambio, si lo que quieres es gestionar +las operaciones de editar en un post específico en un blog o un listado +de recetas, podrías elegir enlazar un ACO a ese registro en particular. + +El campo ``alias`` de un objeto ACL es sólo una etiqueta que puede ser +fácilmente interpretada por un ser humano, y se utiliza para identificar +un objeto ACL que no tiene una correlación directa con algún registro de +un modelo. Los alias son muy útiles para nombrar grupos de usuarios en +colecciones de ACOs. + +El campo ``parent_id`` de un objeto ACL te permite completar la +estructura del árbol. Debes proveer el ID del nodo padre en el árbol +para crear un nuevo hijo. + +Antes de crear nuevos objetos ACL, necesitamos cargar las respectivas +clases. La forma más fácil de hacer esto es incluir el componente ACL en +el *array* ``$components`` de tu controlador: + +:: + + var $components = array('Acl'); + +Una vez que haz hecho esto, veamos ejemplos de cómo sería la creación de +algunos objetos. El código siguiente puede ser colocado en la acción de +algún controlador: + +Mientras los ejemplos se enfocan en la creación de AROs, las mismas +técnicas pueden ser usadas para crear el árbol de ACOs. + +Siguiendo con la configuración de la Comunidad, creemos primero nuestro +grupo de ACOs. Debido a que nuestros grupos no tendrán registros +específicos asociados a ellos, usaremos alias en la creación de los +objetos ACL. Lo que estamos haciendo aquí es desde la perspectiva de la +acción de un controlador, pero puede realizarse en otro lugar. Lo que +vamos a usar es una aproximación algo artificial, pero deberías sentirte +cómodo usando estas técnicas para crear AROs y ACOs al vuelo. + +Esto no debería ser algo drásticamente nuevo, sólo estamos utilizando +los modelos para guardar los datos como siempre hacemos: + +:: + + function algunaAccion() + { + $aro = new Aro(); + + //Aquí tenemos la información de nuestros grupos en un array sobre el cual iteraremos luego + $groups = array( + 0 => array( + 'alias' => 'guerreros' + ), + 1 => array( + 'alias' => 'magos' + ), + 2 => array( + 'alias' => 'hobbits' + ), + 3 => array( + 'alias' => 'visitantes' + ), + ); + + //Iterar para crear los ARO de los grupos + foreach($groups as $data) + { + //Recuerda llamar a create() cuando estés guardando información dentro de bucles... + $aro->create(); + + //Guardar datos + $aro->save($data); + } + + //Aquí va otra lógica de la acción... + } + +Una vez creados, podemos utilizar la aplicación de consola de ACL para +verificar la estructura de los árboles. + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]guerreros + + [2]magos + + [3]hobbits + + [4]visitantes + + --------------------------------------------------------------- + +Supongo que no se parece mucho a un árbol en este punto, pero al menos +pudimos verificar que tenemos los cuatro nodos de primer nivel. +Agreguemos algunos hijos a esos nodos agregando nuestros AROs +específicos de cada usuario dentro de esos grupos. Todo buen ciudadano +de la Tierra Media tiene una cuenta en nuestro nuevo sistema, entonces +nosotros referiremos esos AROs a los registros dentro del modelo +específico en la base de datos. + +Cuando agregue hijos al árbol, asegúrese de utilizar el ID del nodo ACL +y no un valor de foreign\_key. + +:: + + function algunaAccion() + { + $aro = new Aro(); + + //Aquí tenemos nuestros registros de usuario, listos para ser relacionados con nuevos registros ARO + //Estos datos pueden venir de un modelo, pero en este caso estamos usando arrays estáticos + //con propósitos de demostración. + + $users = array( + 0 => array( + 'alias' => 'Aragorn', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'Legolas', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'Gimli', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'Gandalf', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'Frodo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'Bilbo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'Merry', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'Pippin', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'Gollum', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + //Iterar y crear los AROs (como hijos) + foreach($users as $data) + { + ///Recuerda llamar a create() cuando estés guardando información dentro de bucles... + $aro->create(); + + //Guardar datos + $aro->save($data); + } + + //Aquí va otra lógica de la acción... + } + +Típicamente no usarás el alias y los campos model/foreing\_key al mismo +tiempo, pero aquí estamos utilizando los dos para que la estructura sea +más fácil de leer y para propósitos de demostración. + +La salida de la aplicación de consola ahora debería ser un poco más +interesante. Veamos: + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]guerreros + + [5]Aragorn + + [6]Legolas + + [7]Gimli + + [2]magos + + [8]Gandalf + + [3]hobbits + + [9]Frodo + + [10]Bilbo + + [11]Merry + + [12]Pippin + + [4]visitantes + + [13]Gollum + + --------------------------------------------------------------- + +Ahora que ya tenemos nuestro árbol de AROs configurado apropiadamente, +discutamos una posible aproximación para la estructura del árbol de +ACOs. Mientras que podemos estructurar más de una representación +abstracta de nuestros ACOs, a menudo es más práctico modelar un árbol de +ACOs después de configurar los controladores y acciones. Tenemos cindo +objetos principales que queremos manejar en este escenario, y la +configuración natural para una aplicación Cake es un grupo de modelos y +en segundo lugar, los controladores que los manipulan. Es en éstos +controladores donde queremos controlar el acceso a algunas acciones +específicas. + +Basándonos en esa idea, vamos a crear un árbol de ACOs que imite una +aplicación Cake. Como tenenos cinco ACOs, vamos a crear un árbol de ACOs +que se verá algo así: + +- Armas +- Anillo +- Jamón +- EsfuerzosDiplomáticos +- Cervezas + +Una buena característica de la implementación de ACL en Cake, es que +cada ACO automáticamente contiene cuatro propiedades relacionadas con +las acciones CRUD (en inglés *create, read, update, and delete*). Puedes +crear nodos dentro de cada uno de esos ACOs principales, pero usando la +gestión de acciones que provee Cake abarcarás las operaciones básicas de +CRUD sobre un objeto dado. Teniendo esto en mente hará que tu árbol de +ACOs sea más pequeño y fácil de mantener. Veremos cómo usadas esas +propiedades cuando hablemos acerca de cómo asignar permisos. + +Como ya eres un profesional creando AROs, usa las mismas técnicas para +crear este árbol de ACOs. Crea esos grupos de nivel superior usando el +modelo básico de Aco + +Asignando Permisos +------------------ + +Después de crear los ACOs y los AROs, finalmente podremos asignar los +permisos entre los dos grupos. Esto se realiza utilizando el componente +ACL de Cake. Sigamos con nuestro ejemplo + +En este ejemplo trabajaremos en el contexto de una acción de un +controlador. Tenemos que hacer esto debido a que los permisos son +manejados por el componente ACL + +:: + + class AlgunController extends AppController + { + // Podrías colocar esta declaración en AppController + // para que sea heredada por todos los controladores + + var $components = array('Acl'); + + } + +Asignemos algunos permisos básicos utilizando el componente ACL in la +acción dentro de este controlador + +:: + + function index() + { + //Permitirle a los guerreros acceso total a las armas + //Ambos ejemplos usan la sintaxis de alias vista anteriormente + $this->Acl->allow('guerreros', 'Armas'); + + //Aunque el Rey puede no querer que todo el mundo tenga + //acceso irrestricto + $this->Acl->deny('guerreros/Legolas', 'Armas', 'delete'); + $this->Acl->deny('guerreros/Gimli', 'Armas', 'delete'); + + die(print_r('hecho', 1)); + } + +En la primer llamada que hicimos al componente ACL permitimos que +cualquier usuario dentro del grupo ARO denominado "guerreros" tenga +acceso irrestricto a cualquier arma dentro del grupo ACO denominado +"Armas". Esto lo realizamos referenciando a ambos grupos por sus alias. + +¿Has notado el uso del tercer parámetro? Ahí es donde utilizamos esas +acciones pre-contruidos para todos los ACOs dentro de Cake. La opción +por defecto para ese parámetro son ``create``, ``read``, ``update``, y +``delete``, pero puedes agregar una columna en la tabla ``aros_acos`` +(comenzando con el prefijo "\_", por ejemplo ``_admin``) y utilizarla +junto con las otras acciones. + +La segunda llamada es un intento de realizar una asignación de permisos +mucho más granular. Nosotros queremos que Aragorn tenga acceso +irrestricto, pero no queremos que los otros guerreros pertenecientes al +grupo tengan la habilidad de borrar registros de Armas. En el ejemplo +utilizamos la sintaxis de alias, pero se puede usar también de la forma +model/foreign\_key. El ejemplo anterior es equivalente a este: + +:: + + // 6342 = Legolas + // 1564 = Gimli + + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342), 'Armas', 'delete'); + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564), 'Armas', 'delete'); + +Para acceder a un nodo utilizando la sintaxis de alias, debemos usar una +cadena de caracteres delimitada por barras +('/usuarios/empleados/desarrolladores'). Para acceder a un nodo +utilizando la sintaxis model/foreign\_key debes utilizar un arreglo con +dos parámetros: ``array('model' => 'Usuario', 'foreign_key' => 8282)``. + +La próxima sección nos ayudará a validar nuestra configuración +utilizando el componente ACL para verificar los permisos que acabamos de +asignar. + +Verificando Permisos: El Componente ACL +--------------------------------------- + +Vamos a utilizar el componente ACL para asegurarnos que ni los enanos ni +los elfos pueden quitas armas de la armería. En este punto, deberíamos +ser capaces de utilizar ``AclComponent`` para verificar los permisos +entre los ACOs y AROs que hemos creado. La sintaxis básica para realizar +una verificación de permisos es: + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +Vamos a intentarlo dentro de una acción en un controlador: + +:: + + function index() + { + //Todos estos devuelven true + $this->Acl->check('guerreros/Aragorn', 'Armas'); + $this->Acl->check('guerreros/Aragorn', 'Armas', 'create'); + $this->Acl->check('guerreros/Aragorn', 'Armas', 'read'); + $this->Acl->check('guerreros/Aragorn', 'Armas', 'update'); + $this->Acl->check('guerreros/Aragorn', 'Armas', 'delete'); + + //Recuerda que también podemos utilizar la sintaxis model/foreign_key + //para los AROs de nuestro usuario + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), 'Armas'); + + //Estos también deben devolver true: + $result = $this->Acl->check('guerreros/Legolas', 'Armas', 'create'); + $result = $this->Acl->check('guerreros/Gimli', 'Armas', 'read'); + + //Pero estos devuelven false: + $result = $this->Acl->check('guerreros/Legolas', 'Armas','delete'); + $result = $this->Acl->check('guerreros/Gimli', 'Armas', 'delete'); + } + +El uso que le dimos aquí es solamente con propósitos de demostración, +pero esperamos que puedas ver cómo la verificación de permisos de esta +forma puede ser utilizada para decidir cuándo permitir o no determinada +acción, mostrar un mensaje de error o redirigir al usuario a la pantalla +de autenticación. diff --git a/es/The-Manual/Core-Components/Authentication.rst b/es/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..432517036c6668acffbbbcd05b836e5a4cf75d59 --- /dev/null +++ b/es/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,819 @@ +Autenticación +############# + +Un sistema de autenticación de usuarios es una parte común de muchas +aplicaciones web. En CakePHP hay muchas formas para autenticar usuarios, +cada una de estas provee diferentes opciones. La esencia de componente +de autenticación es comprobar si el usuario tiene una cuenta con el +sitio. De ser así, el componente da al usuario acceso completo a sitio. + +Este componente se puede combinar con el componente ACL (access control +lists) para crear niveles más complejos de acceso al sitio. El +componente ACL, por ejemplo, podría permitir acceso a un usuario a áreas +publicas del sitio, mientras que concede a otro usuario acceso a +porciones administrativas protegidas del sitio. + +El AuthComponent de CakePHP se puede usar para crear un sistema fácil y +rápidamente. Veamos como construir un sistema de autenticación simple. + +Al igual que todos los componentes, se utiliza mediante la incorporación +de 'Auth' a la lista de componentes en el controlador: + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +O añadelo al AppController si todos tus controladores lo van a usar: + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +Ahora, hay unas pocas convenciones en las que pensar cuando se usa el +AuthComponent. Por defecto, el AuthComponent espera que se tenga una +tabla llamada 'users' con campos llamados 'username' y 'password'. *En +algunos casos, las bases de datos no permiten usar 'password' como +nombre de columna, mas tarde, veremos como cambiar el nombre por defecto +de los campos para trabajar con nuestro propio entorno.* + +Vamos a crear nuestra tabla 'users' usando el siguiente SQL: + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(50), + PRIMARY KEY (id) + ); + +Algo a tener en cuenta a la hora de crear una tabla para almacenar todos +los datos de autenticación del usuario es que el AuthComponent espera +que el valor del password almacenado esté encriptado en vez de estar +almacenado en texto plano. Asegúrese de que el campo que utilizará para +almacenar la contraseña sea suficientemente largo para almacenar el hash +(40 caracteres para SHA1, por ejemplo). + +Para la configuración más básica usted solo tiene que crear dos acciones +en el controlador: + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); //No es necesario si se declaro en el app controller + + /** + * El AuthComponent proporciona la funcionalidad necesaria + * para el acceso (login), por lo que se puede dejar esta función en blanco. + */ + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +Si bien usted puede dejar la función login() en blanco, necesitara crear +la vista para la acción login (guardela en app/views/users/login.ctp). +Esta es la única vista del UsersController que es necesario crear, sin +embargo. El siguiente ejemplo asume que ya está familiarizado con el uso +del Form helper: + +:: + + flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +Esta vista crea un simple formulario de login en el cual introducir el +nombre de usuario y la clave. Una vez enviado este formulario, el +AuthComponent se encargará del resto por usted. El session flash message +mostrara cualquier información generada por el AuthComponent. + +Lo creas o no, ya está! Esta es la manera de implementar una +increiblemente simple, base de datos de autenticación usando el +componente Auth. Sin embargo, hay mucho más que podemos hacer. Echemos +un vistazo a algunos usos más avanzados del componente. + +Configurando las variables del componente Auth +============================================== + +Para cambiar las opciones predeterminadas de AuthComponent tienes que +crear el método beforeFilter() en el controlador, llamar a varios +métodos predefinidos, y configurar algunas variables del componente. + +Para cambiar el nombre del campo que se utiliza para guardar las +contraseñas, 'password', a 'secretword', por ejemplo, haríamos lo +siguiente: + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'secretword' + ); + } + } + +En este caso, ¡no olvidemos que también hay que cambiar en la vista el +nombre del campo! + +Las variables del componente Auth también se utilizan para que los +usuarios que no han entrado en el sistema puedan acceder a determinados +métodos. + +Por ejemplo, si queremos que todos los usuarios puedan acceder solamente +a los métodos index y view, hacemos lo siguiente: + +:: + + function beforeFilter() { + $this->Auth->allow('index','view'); + } + +Mostrando Mensajes de Error en la Autentificación +================================================= + +Con el objetivo de desplegar los mensajes de error que la +Autentificacion muestra, necesitas añadir el siguiente codigo en tu +vista. En este caso, el mensaje aparecerá debajo de los mensajes flash +regulares: + +:: + + check('Message.flash')) { + $session->flash(); + } + if ($session->check('Message.auth')) { + $session->flash('auth'); + } + ?> + +Problemas comunes con Auth +========================== + +A veces puede ser difícil diagnosticar problemas cuando encuentras +comportamientos inesperados. Recordar estos puntos te puede ayudar. + +*Password hashing* + +Al enviar información a través de un formulario, el componente Auth +encripta automáticamente el contenido del campo contraseña, si también +hay datos en el campo nombre de usuario. Así que si estás intentando +crear algún tipo de página de registro de nuevo usuario, asegúrate de +que el usuario rellene un campo "confirmar contraseña" que puedas +comparar. Aquí va un código de ejemplo: + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +Cambiar la Función Hash +======================= + +AuthComponent usa la clase Security para encriptar una contraseña. La +clase Security usa el esquema SHA1 por defecto. Para cambiar a otra +función hash usada por el componente Auth, usa el método ``setHash`` +pasándole ``md5``, ``sha1`` o ``sha256`` como primer y único parámetro. + +:: + + Security::setHash('md5'); // o sha1 o sha256. + +La clase Security usa el valor de inicialización (*salt value*, que se +encuentras en /app/config/core.php) para el hashing de la contraseña. + +Si quieres usar una lógica diferente para el hashing de la contraseña +más allá de md5/sha1 con el valor *salt* de la aplicacion, necesitará +reescribir el mecanismo estandar hashPassword - podrías necesitar hacer +esto si, por ejemplo, tuvieses una base de datos existente que +anteriormente usaba un esquema de *hashing* sin un valor de *salt*. Para +hacer esto, crea el metodo ``hashPasswords`` en la clase que quieras que +se haga a cargo del hashing de las contraseñas (normalmente el modelo +User ) y establece el atributo ``authenticate`` de Auth al objeto contra +el que se está autenticando (normalmente es User) de este modo: + +:: + + function beforeFilter() { + $this->Auth->authenticate = ClassRegistry::init('User'); + ... + parent::beforeFilter(); + } + +Con el código anterior, el método ``hashPasswords()`` del modelo User +será llamado cada vez que Cake llame a +``AuthComponent::hashPasswords()``. Aquí está un ejemplo del método +``hashPasswords``, apropiado si ya tienes una tabla de usuarios repleta +de contraseñas de *hash* 'plain md5': + +:: + + class User extends AppModel { + function hasPasswords($data) { + if (isset($data['User']['password'])) { + $data['User']['password'] = md5($data['User']['password']); + return $data; + } + return $data; + } + } + +Métodos de AuthComponent +======================== + +action +------ + +``action (string $action = ':controller/:action')`` + +Si estas usando +`ACOs `_ +como parte de tu estructura +`ACL `_, +puedes obtener la ruta al nodo del +`ACO `_ +que está enlazado a un par controlador/acción particular. + +:: + + $acoNode = $this->Auth->action('users/delete'); + +Si no le pasas valores, utilizará el par controlador/acción actual (el +que se está ejecutando). + +allow +----- + +Si tienes acciones en tu controlador que no necesitas que se autentiquen +contra ellas (como una acción de registro de usuarios), puedes agregar +métodos que debe ignorar AuthComponent. El siguiente ejemplo muestra +como permitir una acción llamada 'register'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('register'); + } + +Si deseas permitir que múltiples acciones no usen autenticación, las +pasas como parámetros al método allow(): + +:: + + function beforeFilter() { + ... + $this->Auth->allow('foo', 'bar', 'baz'); + } + +Atajo: también puedes permitir todas las acciones en un controlador +usando '\*'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('*'); + } + +Si estás usando requestAction en tu layout o en tus elementos, deberías +permitir esas acciones para poder abrir la página de login +correctamente. + +El componente auth supone que tus nombres de acciones `siguen las +convenciones `_ y +usan guiones bajos. + +deny +---- + +Habrá algunas veces que quieras eliminar acciones de la lista de +acciones permitidas (añadidas usando $this->Auth->allow()). He aquí un +ejemplo: + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +Este método verifica si ``$data`` contiene los campos nombre de +usuario(\ *username*) y contraseña(\ *password*), tal y como está +especificado en la variable ``$fields`` indexados por el nombre del +modelo especificado en ``$userModel``. Si el *array* ``$data`` contiene +el nombre de usuario y la contraseña, realiza el *hash* del campo +contraseña en el *array* y devuelve el *array* ``$data`` con el mismo +formato. Esta función debe ser usada antes de realizar llamadas de +inserción o actualización de los datos del usuario cuando afecta al +campo contraseña. + +:: + + $data['User']['username'] = 'me@me.com'; + $data['User']['password'] = 'changeme'; + $hashedPasswords = $this->Auth->hashPasswords($data); + pr($hashedPasswords); + /* devuelve: + Array + ( + [User] => Array + ( + [username] => me@me.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +En el campo *$hashedPasswords['User']['password']* ahora debería ser +realizado el *'hash'* usando el método ``password`` del componente. + +Si tu controlador usa el compoente Auth y los datos recibidos por POST +contienen los campos explicados arriba, automáticamente realizará el +*hash* al campo contraseña usando esta función. + +mapActions +---------- + +Si estás utilizando Acl en modo CRUD, tal vez desees asignar ciertas +acciones no predeterminadas a cada parte de CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('ciertaAccion'), + 'read' => array('ciertaAccion', 'ciertaAccion2'), + 'update' => array('ciertaAccion'), + 'delete' => array('ciertaAccion') + ) + ); + +login +----- + +``login($data = null)`` + +Si estás haciendo algún tipo de *login* basada en Ajax, puedes usar este +método para identificar manualmente a alguien en el sistema. Si no pasas +ningún valor para ``$data``, automáticamente usará los datos enviados +mediante POST al controlador. + +Por ejemplo, en una aplicación tal vez desees asignar a un usuario una +contraseña y autoidentificarlo en el sistema tras el registro. En un +ejemplo muy simplificado: + +Vista: + +:: + + echo $form->create('User',array('action'=>'registrar')); + echo $form->input('username'); + echo $form->end('Regístrame'); + +Controlador: + +:: + + function registrar() { + if(!empty($this->data)) { + $this->User->create(); + $contrasena_asignada = "ConTr4senna"; + $this->data['User']['password'] = $contrasena_asignada; + if($this->User->save($this->data)) { + // enviar el email de registro conteniendo la contraseña al nuevo usuario + $this->Auth->login($this->data); + $this->redirect("inicio"); + } + } + +Una cosa a remarcar es que has de redirigir manualmente al usuario tras +el *login* ya que no se invoca ``loginRedirect()``. + +``$this->Auth->login($data)`` devuelve 1 tras un *login* exitoso, 0 en +caso de fallo. + +logout +------ + +Provee de una manera rápida de 'deautenticar' a alguien y redirigirlo a +donde necesite ir. Este método también es útil si deseas proporcionar un +enlace 'Cerrar sesión' dentro de una sección para usuarios registrados +de tu aplicación. + +Ejemplo: + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +Pásale una cadena de texto, y obtendrás la contraseña *'hasheada'*. Esta +es una funcionalidad esencial si estás creando una pantala de registro +de usuario donde los usuarios han de insertar sus contraseñas una +segunda vez para confirmarlas. if ($this->data['User']['password'] == +$this->Auth->password($this->data['User']['password2'])) { // Las +contraseñas concuerdan, continuar procesando ... } else { +$this->flash('Las contraseñas introducidas no concuerdan', +'users/registrar'); } + +El componente Auth automáticamente aplicará el *hash* al campo +contraseña (*password*) si también está presente el campo nombre de +usuario (*username*) en los datos recibidos en la petición. + +Cake añade tu cadena contraseña a un valor *salt* y después realiza el +*hash*. La función de *hash* utilizada depende de la seleccionada por la +clase utilidad del núcleo ``Security`` (sha1 por defecto). Puedes +utilizar la función ``Security::setHash`` para cambiar el método para +calcular el *hash*. El valor *salt* es el indicado en la configuración +de tu aplicación definido en tu ``core.php``. + +user +---- + +``user(string $key = null)`` + +Este método proporciona información sobre el usuario actualmente +identificado. La información es tomada de la sesión. Por ejemplo: + +:: + + if ($this->Auth->user('rol') == 'admin') { + $this->flash('Tienes acceso de administrador'); + } + +También puede ser usado para obtener todos los datos de sesión del +usuario así: + +:: + + $data['User'] = $this->Auth->user(); + +Si este método devuelve ``null`` es que el usuario no se ha identificado +(*logged in*). + +En la vista puedes utilizar el *helper* ``Session`` para obtener la +información del usuario actualmente autenticado: + +:: + + $session->read('Auth.User'); // devuelve el registro completo del usuario + $session->read('Auth.User.nombre') //devuelve el valor particular de un campo + +La clave de la sesión puede ser diferente dependiendo de qué modelo se +ha configurado para ser utilizado por Auth. P.e., si usas el modelo +``Cuenta`` en vez de ``User``, entonces la clave de sesión sería +``Auth.Cuenta``. + +Atributos de AuthComponent +========================== + +Ahora hay varias variables relacionadas con Auth que también puedes +utilizar. Normalmente añades esta configuración en el método +``beforeFilter() de tu controlador. Si necesitas aplicar dicha configuración a todo el sitio, deberías añadirla a beforeFilter()`` +de ``AppController``. + +userModel +--------- + +¿No deseas utilizar un modelo *User* contra el que autenticar? No hay +problema. Simplemente cámbialo configurando este valor con el nombre del +modelo que deseas usar. + +:: + + Auth->userModel = 'Miembro'; + ?> + +fields +------ + +Sobreescribe los campos de usuario y contraseña por defecto usados para +la autenticación. + +:: + + Auth->fields = array('username' => 'email', 'password' => 'passwd'); + ?> + +userScope +--------- + +Utiliza esto para añadir requisitos adicionales para que la +autenticación sea exitosa. + +:: + + Auth->userScope = array('User.activo' => true); + ?> + +loginAction +----------- + +Puedes cambiar el *login* por defecto de */users/login* para que sea +cualquier acción a tu elección. + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'miembros', 'action' => 'inicio_sesion'); + ?> + +loginRedirect +------------- + +El componente AuthComponent recuerda qué par controlador/acción estabas +tratando de ejecutar antes de que pedirte que te autenticaras, +almacenando el valor en Session bajo la clave ``Auth.redirect``. Sin +embargo, si este valor de la sesión no está definido (si vienes de la +página de login de un enlace externo, por ejemplo), entonces el usuario +será redirigido a la URL indicada en loginRedirect. + +Ejemplo: + +:: + + Auth->loginRedirect = array('controller' => 'miembros', 'action' => 'inicio'); + ?> + +logoutRedirect +-------------- + +Puedes especificar a donde ira el usuario luego de 'deautenticarse', el +inicio por defecto es la accion login + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'members', 'action' => 'logout'); + ?> + +loginError +---------- + +Cambia el mensaje de error por defecto que se mostrará, cuando el login +no sea exitoso. + +:: + + Auth->loginError = "No, you fool! That's not the right password!"; + ?> + +authError +--------- + +Cambia el mensaje de error por defecto que será mostrado, cuando +intenten acceder a un objeto o a una acción a la que no autorizada. + +:: + + Auth->authError = "Sorry, you are lacking access."; + ?> + +autoRedirect +------------ + +Normally, the AuthComponent will automatically redirect you as soon as +it authenticates. Sometimes you want to do some more checking before you +redirect users: + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- code inside this function will execute only when autoRedirect was set to false (i.e. in a beforeFilter). + if ($this->Auth->user()) { + if (!empty($this->data['User']['remember_me'])) { + $cookie = array(); + $cookie['username'] = $this->data['User']['username']; + $cookie['password'] = $this->data['User']['password']; + $this->Cookie->write('Auth.User', $cookie, true, '+2 weeks'); + unset($this->data['User']['remember_me']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.User'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // Clear auth message, just in case we use it. + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +The code in the login function will not execute *unless* you set +$autoRedirect to false in a beforeFilter. The code present in the login +function will only execute *after* authentication was attempted. This is +the best place to determine whether or not a successful login occurred +by the AuthComponent (should you desire to log the last successful login +timestamp, etc.). + +With autoRedirect set to false, you can also inject additional code such +as keeping track of the last successful login timestamp + +:: + + data)) && $this->Auth->user() ){ + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('last_login', date('Y-m-d H:i:s') ); + $this->redirect($this->Auth->redirect()); + } + } + ?> + +authorize +--------- + +Normally, the AuthComponent will attempt to verify that the login +credentials you've entered are accurate by comparing them to what's been +stored in your user model. However, there are times where you might want +to do some additional work in determining proper credentials. By setting +this variable to one of several different values, you can do different +things. Here are some of the more common ones you might want to use. + +:: + + Auth->authorize = 'controller'; + ?> + +When authorize is set to 'controller', you'll need to add a method +called isAuthorized() to your controller. This method allows you to do +some more authentication checks and then return either true or false. + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } + } + if ($this->action == 'view') { + return true; + } + ... + return false; + } + ?> + +Remember that this method will be checked after you have already passed +the basic authentication check against the user model. + +:: + + Auth->authorize = 'model'; + ?> + +Don't want to add anything to your controller and might be using ACO's? +You can get the AuthComponent to call a method in your user model called +isAuthorized() to do the same sort of thing: + +:: + + + +Lastly, you can use authorize with actions such as below + +:: + + Auth->authorize = 'actions'; + ?> + +By using actions, Auth will make use of ACL and check with +AclComponent::check(). An isAuthorized function is not needed. + +:: + + Auth->authorize = 'crud'; + ?> + +By using crud, Auth will make use of ACL and check with +AclComponent::check(). Actions should be mapped to CRUD (see +`mapActions `_). + +sessionKey +---------- + +Name of the session array key where the record of the current authed +user is stored. + +Defaults to "Auth", so if unspecified, the record is stored in +"Auth.{$userModel name}". + +:: + + Auth->sessionKey = 'Authorized'; + ?> + +ajaxLogin +--------- + +If you are doing Ajax or Javascript based requests that require +authenticated sessions, set this variable to the name of a view element +you would like to be rendered and returned when you have an invalid or +expired session. + +As with any part of CakePHP, be sure to take a look at `AuthComponent +class `_ for a more +in-depth look at the AuthComponent. + +authenticate +------------ + +This variable holds a reference to the object responsible for hashing +passwords if it is necessary to change/override the default password +hashing mechanism. See `Changing the Encryption +Type `_ for more info. + +actionPath +---------- + +If using action-based access control, this defines how the paths to +action ACO nodes is computed. If, for example, all controller nodes are +nested under an ACO node named 'Controllers', $actionPath should be set +to 'Controllers/'. diff --git a/es/The-Manual/Core-Components/Cookies.rst b/es/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..a3e3f3b62d3787a233a148f66fdf75fb7cef728e --- /dev/null +++ b/es/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,180 @@ +Cookies +####### + +El componente Cookie es una abstracción para acceder al método nativo de +PHP setcookie(). También incluye una serie de funcionalidades muy útiles +para agilizar la escritura de cookies. Antes de tratar de utilizar el +componente Cookie, debe asegurarse que se encuentra habilitado en el +arreglo $components: si está habilitado uno de los elementos del arreglo +debe ser 'Cookie' + +Configuración del Controlador +============================= + +Hay una serie de variables que se configuran en el controlador, y te +permiten modificar la forma en que las cookies son creadas y +gestionadas. Estas variables especiales generalmente se setean en el +método beforeFilter() de tu controlador. + +Variable Cookie + +por defecto + +descripción + +string $name + +'CakeCookie' + +El nombre de la cookie. + +string $key + +null + +Esta cadena es utilizada para encriptar el valor escrito en la cookie. +La cadena debería ser aleatoria y difícil de adivinar. + +string $domain + +'' + +El nombre de dominio habilitado para acceder a la cookie, ej. Use +'.tudominio.com' para permitir acceso desde todos los subdominios. + +int o string $time + +'5 Days' + +El tiempo en que expirará la cookie. Los enteros son interpretados como +segundos, y un valor 0 es equivalente a 'cookie de sesión', es decir, la +cookie expira cuando se cierra el navegador. Si el valor es una cadena, +será interpretada con la función de PHP strtotime(). Puedes configurar +esto directamente dentro del método de escritura write(). + +string $path + +'/' + +La ruta del servidor donde la cookie será aplicada. Si $cookiePath está +seteada a '/foo/', la cookie estará disponible sólo dentro del +directorio /foo/ y todos los subdirectorios (como por ejemplo /foo/bar/) +de tu dominio. La opción por defecto es en todo el dominio. Puedes +configurar esto directamente dentro del método de escritura write(). + +boolean $secure + +false + +Indica que la cookie deberá ser transmitida únicamente por una conexión +segura HTTPS. Cuando este valor sea true, la cookie será creada sólo si +existe una conexión segura. Puedes configurar esto directamente dentro +del método de escritura write() + +El siguiente recorte del código de un controlador muestra cómo incluir +el componente Cookie y cómo configurar las variables necesarias para +enviar una cookie llamada 'baker\_id' para el dominio 'example.com' que +a su vez necesita una conexión segura, debe estar disponible para la +ruta ‘/bakers/preferencias/’, y expira en una hora. + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'baker_id'; + $this->Cookie->time = 3600; // o '1 hour' + $this->Cookie->path = '/bakers/preferencias/'; + $this->Cookie->domain = 'example.com'; + $this->Cookie->secure = true; //enviar sólo por una conexión segura HTTPS + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +A continuación, veremos cómo utilizar los diferentes métodos del +componente Cookie. + +Utilizando el Componente +======================== + +Esta sección resume los métodos del componente Cookie. + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +El método write() es el corazón del componente, $key es la variable que +se quiere guardar en la cookie y $value es el dato para almacenar. + +:: + + $this->Cookie->write('nombre','Pepito'); + +También puedes agrupar variables utilizando la notación de 'punto' en el +parámetro $key. + +:: + + $this->Cookie->write('Usuario.nombre', 'Pepito'); + $this->Cookie->write('Usuario.rol','Lider'); + +Si deseas escribir más de un valor a la vez en la cookie, puedes pasar +un arreglo: + +:: + + $this->Cookie->write( + array('nombre'=>'Pepito','rol'=>'Lider') + ); + + +Todos los valores de las cookis son encriptados por defecto. Si desea +almacenar valores en texto puro, cambie el tecer parámetro del método +write() a false. + +:: + + $this->Cookie->write('nombre','Pepito',false); + +El último parámetro del método es $expires: el número de segundos antes +de que la cookie expire. Por convenienia, este parámetro puede ser +pasado como una cadena que entienda la función strtotime() de PHP: + +:: + + //Ambas cookies expiran en una hora. + $this->Cookie->write('nombre','Pepito',false, 3600); + $this->Cookie->write('Apellido','Gonzales',false, '1 hour'); + +**read(mixed $key)** + +Este método es utilizado para leer el valor de una variable almaenada en +una cookie. La variable a leer debe ser especificada en el parámetro +$key. + +:: + + //Muestra “Pepito” + echo $this->Cookie->read('name'); + + //También se puede utilizar la notación de 'punto' para leer + echo $this->Cookie->read('Usuario.nombre'); + + //Para obtener las variables que has agrupado utilizando + //la notación de 'punto' como un arreglo debes usar + $this->Cookie->read('Usuario'); + + //esto devuelve un arreglo similar a array('nombre' => 'Pepito', 'rol'=>'Lider') + +**del(mixed $key)** + +Borra el contenido de la variable $key almacenada en una cookie. También +funciona con la notación de 'punto'. + +:: + + //Borrar una variable + $this->Cookie->del('bar') + + //Borrar la variable bar, pero no todas las contenidas en foo + $this->Cookie->del('foo.bar') + +**destroy()** + +Destruye la cookie actual. diff --git a/es/The-Manual/Core-Components/Email.rst b/es/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..4e1a46ca915aacfa10e891257e04fb84415e3ffd --- /dev/null +++ b/es/The-Manual/Core-Components/Email.rst @@ -0,0 +1,247 @@ +Email +##### + +El componente Email es una manera simple de agregarle a tu aplicación +CakePHP la funcionalidad de envío de mails, usando los mismos conceptos +de layouts, vistas, archivos .ctp, etc, formateados como texto, html, o +ambos. Puede enviar mails por medio de las funciones propias de PHP, vía +servidor SMTP o en modo DEBUG en el que escribe el mensaje en un mensaje +flash de sesión. También soporta archivos adjuntados y +inclusión/filtrado simple de encabezados. Hay un montón de cosas que no +hace por tí, pero te pondrá en movimiento. + +Atributos y Variables de la clase +================================= + +Estos son los valores que puedes configurar antes de hacer la llamada +``EmailComponent::send()`` + +to + +dirección a la que se dirige el mensaje (string) + +cc + +arreglo de direcciones a enviar copias del mensaje (CC) + +bcc + +arreglo de direcciones a enviar las copias ocultas del mensaje (CCO) + +replyTo + +dirección de respuesta(string) + +from + +dirección remitente (string) + +subject + +asunto del mensaje (string) + +template + +Elemento email a usar para el mensaje(ubicado en +``app/views/elements/email/html/`` y en +``app/views/elements/email/text/``) + +layout + +Layout usado por el mail (ubicado en ``app/views/layouts/email/html/`` y +en ``app/views/layouts/email/text/``) + +lineLength + +Longitud (en caracteres) en la que corta las líneas largas. Por defecto +es 70. (integer) + +sendAs + +Como quieres mandar el mensaje: ``text``\ (texto), ``html``\ (código +HTML) o ``both``\ (ambos). + +attachments + +Arreglo de archivos a enviar (rutas absolutas y relativas) + +delivery + +Como enviar el mensaje (``mail``, ``smtp`` [requerirá el campo +smtpOptions explicado abajo] y ``debug``) + +smtpOptions + +Arreglo asociativo de opciones para el envío por SMTP. +(``port``\ (puerto), ``host``\ (servidor), ``timeout``\ (tiempo de +espera), ``username``\ (nombre de usuario), ``password``\ (contraseña)) + +Hay algunas opciones más para configurar, para mayor información +consulta la documentación de CakePHP. + +Envío múltiple de emails en bucle +--------------------------------- + +Si lo que quieres es enviar varios emails usando un bucle, deberás +resetear los campos de mails usando el método ``reset()`` del componente +Email. Necesitarás resetearlo antes de setear nuevamente las propiedades +del email. + +:: + + $this->Email->reset() + +Envío de un mensaje simple +========================== + +Para enviar un mensaje sin usar ningún template, sólo pasa el cuerpo del +mensaje como una cadena (string) o un arreglo de líneas al método +``send()``. Por ejemplo: + +:: + + $this->Email->from = 'Alguien '; + $this->Email->to = 'Alguien más '; + $this->Email->subject = 'Prueba'; + $this->Email->send('Hola cuerpo de mensaje!!!'); + +Configurando el Layout +---------------------- + +Para usar tanto texto como html en el email necesitarás crear los +archivos de layout para ellos. Tal como lo hayas hecho para tu layout +*default* para las vistas en un navegador, precisas establecer los +layouts *default* para tus emails. En la carpeta ``app/views/layouts/`` +precisas tener (como mínimo) esta estructura + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +Estos son los archivos que conservan los valores por defecto para los +templates de layout para tus mensajes. Un simple ejemplo de contenido: + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +Configurar un elemento Email para el cuerpo del mensaje +------------------------------------------------------- + +En el directorio ``app/views/elements/email/`` debes configurar carpetas +para ``text``\ (mails modo texto) y ``html``\ (mails modo HTML) a menos +que quieras usar sólo uno de ellos. En cada una de estas carpetas debes +crear templates para poder utilizar con el contenido que le envíes a la +vista ya sea usando $this->set() o usando el parámetro $contents del +método send(). Algunos ejemplos simples a continuación, usando el +template simple\_message.ctp + +``text`` + +:: + + Estimado , + Gracias por su interés. + +``html`` + +:: + +

Estimado ,
+    Gracias por su interés.

+ +Controlador +----------- + +En tu controlador necesitas agregar el componente a tu array +``$components`` o agregar un array $components a tu controlador de la +forma: + +:: + + + +En este ejemplo configuraremos un método privado para manejar el envío +de mensajes de email a un usuario identificado por un ``$id``. En +nuestro controlador (usemos el controlador User en este ejemplo): + +:: + + + User->read(null,$id); + $this->Email->to = $User['User']['email']; + $this->Email->bcc = array('secreto@ejemplo.com'); + $this->Email->subject = 'Bienvenido a nuestra cosa genial'; + $this->Email->replyTo = 'support@ejemplo.com'; + $this->Email->from = 'Cool Web App '; + $this->Email->template = 'simple_message'; // NOTAR QUE NO HAY '.ctp' + //Enviar como 'html', 'text' or 'both' (ambos) - (por defecto es 'text') + $this->Email->sendAs = 'both'; // queremos enviar un lindo email + //Variables de la vista + $this->set('User', $User); + //NO PASAMOS ARGUMENTOS A SEND() + $this->Email->send(); + } + ?> + +Has enviado un mensaje, podrías llamarlo desde otro método de esta +forma: + +:: + + + $this->_sendNewUserMail( $this->User->id ); + +Enviar un mail por SMTP +======================= + +Para enviar un mail usando servidor SMTP, los pasos a seguir son +similares a los del mensaje básico. Configurar el método de entrega +(*delivery*) a ``smtp`` y asignar las opciones a las propiedades del +objeto de Email ``smtpOptions``. También puedes obtener los errores SMTP +generados durante la sesión leyendo la propiedad ``smtpError`` del +componente. + +:: + + /* Opciones SMTP*/ + $this->Email->smtpOptions = array( + 'port'=>'25', + 'timeout'=>'30', + 'host' => 'tu.servidor.smtp', + 'username'=>'tu_nombre_usuario_smtp', + 'password'=>'tu_contraseña_smtp'); + + /* Configurar método de entrega */ + $this->Email->delivery = 'smtp'; + + /* No le pases ningún argumento a send() */ + $this->Email->send(); + + /* Chequeo de errores SMTP. */ + $this->set('smtp-errors', $this->Email->smtpError); + +Si tu servidor SMTP requiere autenticación, asegúrate de especificar los +parámetros de nombre de usuario y contraseña en ``smtpOptions`` como se +ve en el ejemplo. diff --git a/es/The-Manual/Core-Components/Request-Handling.rst b/es/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..ed8c115f9b8deb004d8622d8972793de33504be3 --- /dev/null +++ b/es/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,267 @@ +Request Handling +################ + +El componente *Request Handler* (manejador de la petición) es usado en +CakePHP para obtener información adicional sobre las peticiones HTTP que +son realizadas a tus aplicaciones. Puedes usarlo para informar a tus +controladores sobre Ajax, como también para obtener información +adicional sobre tipos de contenido que el cliente acepta y cambiar +automáticamente al *layout* apropiado cuando estén activadas las +extensiones de ficheros. + +Por defecto, RequestHandler detectará automáticamente las peticiones +Ajax basadas en la cabecera HTTP-X-Requested-With que utilizan muchas +librerías javascript. Cuando se utiliza junto con +Router::parseExtensions(), RequestHandler cambiará automáticamente los +ficheros de *layout* y vista a aquellos que coincidan con el tipo +pedido. Además, si existe un *helper* con el mismo nombre que la +extensión pedida, se añadirá al array de *helpers* del controlador. +Finalmente, si son "POSTeados" datos XML a tus controladores, se +parsearán en un objecto XML que se asigna a Controller::data, y que +puede entonces ser salvado como datos de modelo. Para poder hacer uso de +*Request handler*, debe ser incluído en tu array $components. + +:: + + + +Obtaining Request Information +============================= + +Request Handler has several methods that provide information about the +client and its request. + +accepts ( $type = null) + +$type can be a string, or an array, or null. If a string, accepts will +return true if the client accepts the content type. If an array is +specified, accepts return true if any one of the content types is +accepted by the client. If null returns an array of the content-types +that the client accepts. For example: + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // Execute code only if client accepts an HTML (text/html) response + } elseif ($this->RequestHandler->accepts('xml')) { + // Execute XML-only code + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // Executes if the client accepts any of the above: XML, RSS or Atom + } + } + } + +Other request 'type' detection methods include: + +isAjax() + +Returns true if the request contains the X-Requested-Header equal to +XMLHttpRequest. + +isSSL() + +Returns true if the current request was made over an SSL connection. + +isXml() + +Returns true if the current request accepts XML as a response. + +isRss() + +Returns true if the current request accepts RSS as a response. + +isAtom() + +Returns true if the current call accepts an Atom response, false +otherwise. + +isMobile() + +Returns true if user agent string matches a mobile web browser, or if +the client accepts WAP content. The supported Mobile User Agent strings +are: + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +Returns true if the client accepts WAP content. + +All of the above request detection methods can be used in a similar +fashion to filter functionality intended for specific content types. For +example when responding to Ajax requests, you often will want to disable +browser caching, and change the debug level. However, you want to allow +caching for non-ajax requests. The following would accomplish that: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + //Continue Controller action + +You could also disable caching with the functionally analogous +``Controller::disableCache`` + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + //Continue Controller action + +Request Type Detection +====================== + +RequestHandler also provides information about what type of HTTP request +has been made and allowing you to respond to each Request Type. + +isPost() + +Returns true if the request is a POST request. + +isPut() + +Returns true if the request is a PUT request. + +isGet() + +Returns true if the request is a GET request. + +isDelete() + +Returns true if the request is a DELETE request. + +Obtaining Additional Client Information +======================================= + +getClientIP() + +Get the remote client IP address + +getReferrer() + +Returns the domain name from which the request originated + +getAjaxVersion() + +Gets Prototype version if call is Ajax, otherwise empty string. The +Prototype library sets a special "Prototype version" HTTP header. + +Responding To Requests +====================== + +In addition to request detection RequestHandler also provides easy +access to altering the output and content type mappings for your +application. + +setContent($name, $type = null) + +- $name string - The name of the Content-type ie. html, css, json, xml. +- $type mixed - The mime-type(s) that the Content-type maps to. + +setContent adds/sets the Content-types for the given name. Allows +content-types to be mapped to friendly aliases and or extensions. This +allows RequestHandler to automatically respond to requests of each type +in its startup method. Furthermore, these content types are used by +prefers() and accepts(). + +setContent is best used in the beforeFilter() of your controllers, as +this will best leverage the automagicness of content-type aliases. + +The default mappings are: + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +Determines which content-types the client prefers. If no parameter is +given the most likely content type is returned. If $type is an array the +first type the client accepts will be returned. Preference os determined +primarily by the file extension parsed by Router if one has been +provided. and secondly by the list of content-types in HTTP\_ACCEPT. + +renderAs($controller, $type) + +- $controller - Controller Reference +- $type - friendly content type name to render content for ex. xml, + rss. + +Change the render mode of a controller to the specified type. Will also +append the appropriate helper to the controller's helper array if +available and not already in the array. + +respondAs($type, $options) + +- $type - Friendly content type name ex. xml, rss or a full content + type like application/x-shockwave +- $options - If $type is a friendly type name that has more than one + content association, $index is used to select the content type. + +Sets the response header based on content-type map names. If DEBUG is +greater than 1, the header is not set. + +responseType() + +Returns the current response type Content-type header or null if one has +yet to be set. + +mapType($ctype) + +Maps a content-type back to an alias diff --git a/es/The-Manual/Core-Components/Security-Component.rst b/es/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..a6111a3db59ca759d2d99e1769f8e2717ebdbc8c --- /dev/null +++ b/es/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,222 @@ +El Componente Security +###################### + +El componente Security provee una forma fácil de aumentar la seguridad +en tu aplicación. Con el componente Security se puede crear una intefaz +para manejar autenticaciones HTTP. Se configura en el método +beforeFilter() de tus controladores, y tiene distintos parámetros para +ajustar. Todas esas propiedades pueden configurarse directamente con los +métodos creados para tal fin, y que se llaman de la misma manera. + +Configuración +============= + +$blackHoleCallback + Un callback del controlador que se encargará de manejar las + peticiones que hayan sido enviadas al "agujero negro". +$requirePost + Una lista de acciones del controlador que requieren una petición + POST para ejecutarse. Un arreglo de acciones del controlador o '\*' + para forzar que todas las acciones requieran POST. +$requireSecure + Lista de acciones que requieren una conexión SSL para ejecutarse. Un + arreglo de acciones del controlador o '\*' para forzar que todas las + acciones requieran una conexión SSL. +$requireAuth + Una lista de acciones que requieren una clave de autenticación + válida. Esta clave es configurada por el componente Security. +$requireLogin + Una lista de acciones que requieren autenticación HTTP (basic o + diges). También acepta '\*' indicando que todas las acciones del + controlador requieren autenticación HTTP. +$loginOptions + Opciones para la autenticación HTTP. Permite definir que tipo de + autenticación utilizar, y los callbacks del controlador para el + proceso de autenticación. +$loginUsers + Un arreglo asociativo de usuarios => passords que sonusados para + autenticaciones HTTP. Si estás utilizando autenticacion digest, los + passwords deben ser encryptados con el algoritmo MD5. +$allowedControllers + Una lista de controladores desde los cuales la acción del + controlador actual puede recibir peticiones. También puede + utilizarse para peticiones entre controladores. +$allowedActions + Una lista de acciones desde las cuales las acciones del controlador + actual pueden para recibir peticiones. También puede utilizarse para + peticiones entre controladores. +$disabledFields + Campos del formulario que se necesitan deshabilitar para una + petición dada. + +Métodos +======= + +requirePost() +------------- + +Define las acciones que requieren una petición POST. Acepta cualquier +número de argumentos. Puede ser llamado sin argumentos para forzar a que +todas las acciones requieran una petición POST. + +requireSecure() +--------------- + +Define las acciones que requieren una conexión SSL presente. Acepta +cualquier cantidad de argumentos. Puede ser llamado sin argumentos para +forzar que todas las acciones requieran una conexión SSL. + +requireAuth() +------------- + +Define las acciones que requieren un token válido generado por el +componente Security. Acepta cualquier número de argumentos. Puede ser +llamado sin argumentos para forzar que todas las acciones requieran una +autenticación válida. + +requireLogin() +-------------- + +Define las acciones que requieren una autenticación HTTP válida. Acepta +cualquier número de argumentos. Puede ser llamada sin argumentos para +forzar que todas las acciones requieran una autenticación HTTP válida. + +loginCredentials(string $type) +------------------------------ + +Trata de validar las credenciales para una petición de autenticación +HTTP. $type es el tipo de autenticación que se desea verificar. Puede +ser 'basic', o 'digest'. Si se deja como null o vació, tratará de +verificar las dos. Devuelve un arreglo con el nombre de usuario y el +password si tuvo éxtito. + +loginRequest(array $options) +---------------------------- + +Generates the text for an HTTP-Authenticate request header from an array +of $options. + +$options generally contains a 'type', 'realm' . Type indicate which +HTTP-Authenticate method to use. Realm defaults to the current HTTP +server environment. + +parseDigestAuthData(string $digest) +----------------------------------- + +Parse an HTTP digest authentication request. Returns and array of digest +data as an associative array if succesful, and null on failure. + +generateDigestResponseHash(array $data) +--------------------------------------- + +Creates a hash that to be compared with an HTTP digest-authenticated +response. $data should be an array created by +SecurityComponent::parseDigestAuthData(). + +blackHole(object $controller, string $error) +-------------------------------------------- + +Black-hole an invalid request with a 404 error or a custom callback. +With no callback, the request will be exited. If a controller callback +is set to SecurityComponent::blackHoleCallback, it will be called and +passed any error information. + +Uso +=== + +EL uso del componente security generalmente se hace en el método +beforeFilter() del controlador. Usted especifica las restricciones de +seguridad que desee y el componente Security las hara cumplir en el +arranque. + +:: + + Security->requirePost('delete'); + } + } + ?> + +En este ejemplo la accion delete solo puede ser lanzada +satisfactoriamente si se recive una solicitud POST. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +Este ejemplo forzaría todas las acciones que tengan enrrutamiento admin +a requerir peticiones seguras del SSL. + +Basic HTTP Authentication +========================= + +The SecurityComponent has some very powerful authentication features. +Sometimes you may need to protect some functionality inside your +application using `HTTP Basic +Authentication `_. +One common usage for HTTP Auth is protecting a REST or SOAP API. + +This type of authentication is called basic for a reason. Unless you're +transferring information over SSL, credentials will be transferred in +plain text. + +Using the SecurityComponent for HTTP authentication is easy. The code +example below includes the SecurityComponent and adds a few lines of +code inside the controller's beforeFilter method. + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MyRealm' + ); + $this->Security->loginUsers = array( + 'john'=>'johnspassword', + 'jane'=>'janespassword' + ); + $this->Security->requireLogin(); + } + + function index() { + //protected application logic goes here... + } + } + +The loginOptions property of the SecurityComponent is an associative +array specifying how logins should be handled. You only need to specify +the **type** as **basic** to get going. Specify the **realm** if you +want display a nice message to anyone trying to login or if you have +several authenticated sections (= realms) of your application you want +to keep separate. + +The loginUsers property of the SecurityComponent is an associative array +containing users and passwords that should have access to this realm. +The examples here use hard-coded user information, but you'll probably +want to use a model to make your authentication credentials more +manageable. + +Finally, requireLogin() tells SecurityComponent that this Controller +requires login. As with requirePost(), above, providing method names +will protect those methods while keeping others open. diff --git a/es/The-Manual/Core-Components/Sessions.rst b/es/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..2b7f59839412af8be0b2be84c6310977dbb16e27 --- /dev/null +++ b/es/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,156 @@ +Sesiones +######## + +El componente Session de CakePHP provee una forma de persistir datos +entre las peticiones de las páginas. Actúa como una abstracción para +acceder a las variables $\_SESSION, y a su vez, agrega distintos métodos +útiles relacionados con este arreglo. + +Las sesiones pueden persistirse de diferentes maneras. Por defecto se +utilizan los métodos provistos por PHP; sin embargo, existen otras +opciones: + +cake + Guarda los archivos de la sesión en el directorio temporal de tu + aplicación tmp/sessions. +database + Guarda la información dentro de la base de datos. +php + La opción por defecto. Guarda la información como se indica en + php.ini + +Para cambiar el método por defecto de gestión de sesiones, debes +modificar el parámetro de configuración Session.save para reflejar la +opción deseada. Si eliges 'database', también deberías descomentar el +parámetro Session.database y ejecutar el archivo SQL perteneciente a la +sesión localizado en app/config + +*En views/elements la sesión puede ser accedida por medio del helper +Session* + +Métodos +======= + +El componente Session es usado para interactuar con la información de la +sesión. Incluye operaciones básicas de ABM (Alta, Baja y Modificación) +como también opciones para comunicarse con los usuarios. + +Debe notarse que se pueden crear estructuras de arreglos en las sesiones +utilizando la notación de 'punto'. De esta forma, Usuario.email +equivaldrá a: + +:: + + array('Usuario' => + array('email' => 'clarkKent@dailyplanet.com') + ); + +Los puntos son usados para indicar arreglos anidados. + +write +----- + +``write($name, $value)`` + +Guarda en la sesión una variable llamada $name con el valor $value. +$name puede utilizar la notación de 'punto'. Por ejemplo: + +:: + + $this->Session->write('Persona.colorOjos', 'Verde'); + +Escribe el valor 'Verde' en la sesión, dentro de Persona => colorOjos. + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +Se utiliza para guardar una variable de sesión que puede ser mostrada en +la vista. $layout permite controlar qué layout (ubicado en +``/app/views/layouts``) se utilizará para mostrar el mensaje. Si la +variable ``$layout`` queda configurada con su valor por defecto, +'default', el mensaje se mostrará como se ve a continuación: + +:: + +
[message]
+ +**$params** permite pasar variables adicionales para mostrar en el +layout. **$key** es utilizado para modificar el índice del arreglo +$message en el arreglo de mensajes (por defecto es 'flash'). +**$message** es el texto que se quiere cargar en la sesión. + +Los parámetros puede afectar el div que se muestre, por ejemplo pasando +un parámetro 'class' en el arreglo $params, se modificará el ``div`` +resultante de utilizar el método ``$session->flash()`` en la vista. + +:: + + setFlash('Mensaje de ejemplo', 'default', array('class' => 'clase_ejemplo')) + +La salida de ``$session->flash()`` del ejemplo anterior será: + +:: + +
Mensaje de ejemplo
+ +read +---- + +``read($name)`` + +Devuelve el contenido de la variable $name dentro de la sesión. Si $name +es null, se devolverá todo el contenido de la sesión. Ej. + +:: + + $verde = $this->Session->read('Persona.colorOjos'); + +Recupera el valor 'Verde' de la sesión. + +check +----- + +``check($name)`` + +Se utiliza para verificar si una variable ha sido seteada en la sesión. +Devuelve true en caso afirmativo, y false si la variable nunca fue +seteada. + +delete +------ + +``delete($name) /*o*/ del($name)`` + +Borra los datos de la variable $name dentro de la sesión. Ej. + +:: + + $this->Session->del('Persona.colorOjos'); + +Nuestra información de sesión ya no tiene el valor 'Verde', ni siquiera +la clave 'colorOjos'. Sin embardo, Persona todavía existe en la sesión. +Para eliminar la variable completa de la sesión se debe usar: + +:: + + $this->Session->del('Persona'); + +destroy +------- + +El método ``destroy`` eliminará la cookie de sesión y todos los datos +almacenados en los archivos temporales del sistema. Destruirá la sesión +de PHP y creará una sesión nueva. + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +Se utiliza para determinar el último error en una sesión. diff --git a/es/The-Manual/Core-Console-Applications.rst b/es/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..9c1a519f9bdd29bcff793e419fb779507924b287 --- /dev/null +++ b/es/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,23 @@ +Aplicaciones de Consola Principales +################################### + +CakePHP viene con muchas aplicaciones de consola por defecto. Algunas de +ellas se utilizan en combinacion con algunas de las funcionalidades de +Cake, (ejemplo ACL o i18n), y otras son de uso general para ayudarte a +producir más rápido + +Esta sección explica cómo usar las aplicaciones de consola que vienen +empaquetadas con CakePHP + +Antes de que sigas con esta sección, tal vez quisieras ver `La consola +de CakePHP `_. La configuración de la +consola no está cubierta por este capítulo, así que si nunca la has +usado anteriormente, echale un vistazo. + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/es/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/es/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..32a46b96f35970dc9b2036eca7738e321a9aed9c --- /dev/null +++ b/es/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,61 @@ +Generación de Código con Bake +############################# + +Ya has aprendido sobre scaffolding en CakePHP: una forma simple de armar +una aplicación con sólo una base de datos y algunas clases básicas. La +consola Bake es otro esfuerzo para tener CakePHP corriendo rápido. La +consola Bake puede crear cualquiera de los ingredientes básicos de +CakePHP: modelos, vistas y controladores. Y no estamos hablando sólo de +clases estructurales: Bake puede crear una aplicación completamente +funcional en sólo unos minutos. De hecho, Bake es el paso natural que +toman las aplicaciones una vez que han pasado por la etapa de +scaffolding + +Para utilizar Bake necesitar ejecutar el script localizado en el +directorio /cake/console/ + +:: + + $ cd ./cake/console/ + $ cake bake + +Dependiendo de tu configuración, podrías necesitar darle permisos de +ejecución al script bash o llamarlo usando./cake bake. La consola de +Cake corre usando PHP CLI (command line interface). Si tienes algún +problema con el script, asegúrate de que tienes PHP CLI instalado y que +además tienes los módulos necesarios habilitados(Ej: MySQL). + +Cuando utilices Bake por primera vez, te pedirá que crees un archivo de +configuración para la base de datos, si todavía no creaste uno. + +Una vez que hayas configurado la base de datos, cuando ejecutes Bake te +presentará estas opciones: + +:: + + --------------------------------------------------------------- + App : app + Path: /ruta/al/proyecto + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake? (D/M/V/C/P/Q) + > + +Alternativamente, puedes ejecutar cualquiera de estos comandos +directamente de la línea de comandos: + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake project + diff --git a/es/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/es/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..b9c7941ae54d14ec8c2fb810d7d6685d1fdc35cf --- /dev/null +++ b/es/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,12 @@ +Modificando el HTML producido por defecto del script bake +######################################################### + +Si deseas modificar el HTML producido por el comando "bake", sigue los +siguientes pasos: + +#. Dirígete a: cake/console/libs/templates/views +#. Existen cuatro archivos ahí +#. Cópialos en: app/vendors/shells/templates/views +#. Haz los cambios a la salida HTML para controlar la forma en que + "bake" construye las vistas. + diff --git a/es/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/es/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..df5b1db8ad750219a947b85f1b3ff50b9f2fcf30 --- /dev/null +++ b/es/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,89 @@ +Gestión del Esquema de la BBDD y Migraciones +############################################ + +El SchemaShell proporciona la funcionalidad para crear un esquema de +objetos, volcados del esquema sql, así como crear y restaurar +instantáneas de base de datos. + +Generando y Usando Archivos de Esquemas +======================================= + +Un archivo de esquema permite transportar fácilmente el esquema de la +base de datos, sin importar en qué motor se vaya a implementar. Puedes +generar un archivo de esquema de la base de datos usando: + +:: + + $ cake schema generate + +Esto generará un archivo llamado schema.php en tu directorio +``app/config/sql``. + +La aplicación SchemaShell procesará sólo las tablas para las cuales +existe un modelo definido. Para forzar a que cree un esquema de todas +las tablas, debes añadir la opción ``-f`` en la línea de comandos. + +Para reconstruir el esquema de la base de datos a partir de un archivo +schema.php generado anteriormente, debes ejecutar: + +:: + + $ cake schema run create + +Esto borrará y volverá a crear todas las tablas basándose en el +contenido del archivo schema.php. + +Los archivos de esquema pueden ser usado para genera volcados de SQL. +Para generar un archivo SQL que contenga las sencencias ``CREATE TABLE`` +ejecuta: + +:: + + $ cake schema dump volcado.sql + +Donde volcado.sql es el nombre que deseas ponerle al volcado. Si omites +este nombre, el volcado será mostrado por pantalla sin ser escrito en +ningún archivo. + +Migrations with CakePHP schema shell +==================================== + +Migrations allow for versioning of your database schema, so that as you +develop features you have an easy and database agnostic way to +distribute database changes. Migrations are achieved through either SCM +controlled schema files or schema snapshots. Versioning a schema file +with the schema shell is quite easy. If you already have a schema file +created running + +:: + + $ cake schema generate + +Will bring up the following choices: + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +Choosing [s] (snapshot) will create an incremented schema.php. So if you +have schema.php, it will create schema\_2.php and so on. You can then +restore to any of these schema files at any time by running: + +:: + + $ cake schema run update -s 2 + +Where 2 is the snapshot number you wish to run. The schema shell will +prompt you to confirm you wish to perform the ``ALTER`` statements that +represent the difference between the existing database the currently +executing schema file. + +You can perform a dry run by adding a ``-dry`` to your command. + +A schema file generated using the -f option should be updated using the +-f option. diff --git a/es/The-Manual/Core-Helpers.rst b/es/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..d8c3dfafacddbfa41712cc7ae359b98cf90722fb --- /dev/null +++ b/es/The-Manual/Core-Helpers.rst @@ -0,0 +1,29 @@ +Ayudantes del Core +################## + +Los Ayudantes o Helpers son clases a modo de componentes para la capa de +presentación de tu aplicación. Contienen lógica de presentación que +puede ser compartida por muchas vistas, elementos y layouts. + +Esta sección describe cada uno de los Helpers incluidos en CakePHP como +Form, Html, JavaScript y RSS. + +Lee la sección `Helpers `_ para aprender más acerca +de los helpers y cómo puedes crear los tuyos propios. + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/es/The-Manual/Core-Helpers/AJAX.rst b/es/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..f661bd18937db7ced8909048f5e82986af02194c --- /dev/null +++ b/es/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,747 @@ +AJAX +#### + +El AjaxHelper utilizas las populares librerías Prototype y +script.aculo.us para operaciones Ajax y efectos en el lado del cliente. +Para utilizar el AjaxHelper has de tener la versión actual de las +librerías de JavaScript de +`www.prototypejs.org `_ y +`http://script.aculo.us `_ situadas en +``/app/webroot/js``. Además has de incluir las librerías Prototype y +script.aculo.us en los *layouts* o *vistas* que requieran las +funcionalidades de AjaxHelper. + +Para poder cargar las librerías Prototype y script.aculo.us necesitarás +incluir los *helpers* Ajax y Javascript en tu controlador: + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +Una vez que hayas incluido el helper de javascript en tu controlador, +puedes utilizar el método ``link()`` de ``$javascript`` para incluir las +liberías Prototype y script.aculo.us en la vista: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +Ahora ya puedes puedes utilizar el helper Ajax en tu vista: + +:: + + $ajax->loquesea(); + +Si se incluye el `componente +RequestHandler `_ en el controlador, +CakePHP automáticamente aplicará el *layout* Ajax cuando se realize una +petición de una acción mediante AJAX. + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +AjaxHelper Options +================== + +La mayoría de los métodos del AjaxHelper permiten suministrar un arreglo +de opciones ($options). Puedes usar este arreglo para configurar el +comportamiento del AjaxHelper. Antes de cubrir los métodos específicos +del helper, veamos las diferentes opciones disponibles a través de este +arreglo. Esta sección será de utilidad como referencia mientras inicias +el uso de los métodos del AjaxHelper. + +General Options +--------------- + +$options['url'] + La url del controlador/acción que se quiere llamar. +$options['update'] + El id del elemento DOM a ser actualizado con el contenido que + retorne. +$options['frequency'] + El número de segundos entre intervalos de observación. +$options['type'] + Indica si la petición se realiza de forma síncrona ó asíncrona (por + omisión). + +Callback Options +---------------- + +Las opciones de Callback te permiten llamar a funciones de JavaScript en +puntos específicos del proceso de la solicitud. Si estas buscando una +forma de inyectar un poco de lógica antes, después o durante tus +operaciones del AjaxHelper, debes usar esos callbacks (before, after y +during respectivamente) para establecerla. + +$options keys + +Description + +$options['condition'] + +Fragmento de código de JavaScript que necesita ser evaluado antes de que +se ejecute la solicitud. + +$options['before'] + +Se ejecuta antes de que se haga la solicitud. Un uso común de este +callback es habilitar la visibilidad de un indicador de progreso. + +$options['confirm'] + +Texto a mostrar en una alerta de JavaScript antes del procedimiento. + +$options['loading'] + +Código que sera ejecutado mientras la información es obtenida desde el +servidor. + +$options['after'] + +Código JavaScript llamado inmediatamente después de que la solicitud se +haya ejecutado; se lanza antes de que el callback $options['loading'] se +ejecute. + +$options['loaded'] + +Código de callback que se ejecuta cuando el documento remoto ha sido +recibido por el cliente. + +$options['interactive'] + +Llamado cuando el usuario interactúa con el documento remoto, incluso si +este no ha terminado de cargarse. + +$options['complete'] + +Callback de JavaScript que se ejecuta cuando el XMLHttpRequest se ha +completado. + +Métodos +======= + +link +---- + +``link(string $title, string $href, array $options, string $confirm, boolean $escapeTitle)`` + +Devuelve un enlace a una acción remota definida por ``$options['url']`` +o ``$href`` que se llama en background utilizando XMLHttpRequest cuando +se hace clic en el enlace. El resultado de esa petición puede ser +insertada en un objeto DOM cuya identificación se puede especificar con +``$options['update']``. + +Si ``$options['url']`` esta en blanco href es utilizado en su lugar + +Ejemplo: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +Por defecto, estas solicitudes son procesadas asincrónicamente mientras +se utilizan diferentes callbacks + +Ejemplo: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Hello World" )' ) + ); + ?> + +Para usa procesamiento sincrónico especificar +``$options['type'] = 'synchronous'``. + +Para automatizar que el layout utilizado sea ajax incluir el componente +*RequestHandler* en el controlador + +Por defecto el contenido del elemento es reemplazado. Para cambiar este +comportamiento especificar ``$options['position']`` + +Ejemplo: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +``$confirm`` puede ser usado para llamar un JavaScript confirm() message +antes de que la petición se efectúe. Permite al usuario prever la +ejecución. + +Ejemplo: + +:: + +
+
+ link( + 'Delete Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Do you want to delete this post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +Esta funcion crea el codigo JavaScript necesario para hacer una llamada +remota. Es usado principalmente como un helper(ayudante) para los +enlaces(link). Esto no se utiliza muy a menudo a menos que usted +necesite generar algunos codigos personalizados. + +The ``$options`` for this function are the same as for the ``link`` +method + +Example: + +:: + +
+
+ + +It can also be assigned to HTML Event Attributes: + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Mouse Over This +
+ +If ``$options['update']`` is not passed, the browser will ignore the +server response. + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +Periodically calls the action at ``$options['url']``, every +``$options['frequency']`` seconds. Usually used to update a specific div +(specified by ``$options['update']``) with the result of the remote +call. Callbacks can be used. + +``remoteTimer`` is the same as the ``remoteFunction`` except for the +extra ``$options['frequency']`` + +Example: + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post', 'complete' => 'alert( "request completed" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +The default ``$options['frequency']`` is 10 seconds + +form +---- + +``form(string $action, string $type, array $options)`` + +Returns a form tag that submits to $action using XMLHttpRequest instead +of a normal HTTP request via $type ('post' or 'get'). Otherwise, form +submission will behave exactly like normal: data submitted is available +at $this->data inside your controllers. If $options['update'] is +specified, it will be updated with the resulting document. Callbacks can +be used. + +The options array should include the model name e.g. + +:: + + $ajax->form('edit','post',array('model'=>'User','update'=>'UserInfoDiv')); + +Alternatively, if you need to cross post to another controller from your +form: + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'User', + 'update'=>'UserInfoDiv', + 'url' => array( + 'controller' => 'comments', + 'action' => 'edit' + ) + ) + )); + +You should not use the ``$ajax->form()`` and ``$ajax->submit()`` in the +same form. If you want the form validation to work properly use the +``$ajax->submit()`` method as shown below. + +submit +------ + +``submit(string $title, array $options)`` + +Regresa un botón tipo 'submit' que envía los datos del formulario a la +acción especificada por ``$options['url']`` y actualiza el div indicado +en ``$options['update']`` + +:: + +
+ create('Usuario'); + echo $form->input('email'); + echo $form->input('nombre'); + echo $ajax->submit('Submit', array('url'=> array('controller'=>'users', 'action'=>'add'), 'update' => 'testdiv')); + echo $form->end(); + ?> +
+ +Usa el método ``$ajax->submit()`` si quieres que la validación del +formulario funcione correctamente. Por ejemplo, si deseas que los +mensajes que especificaste en las reglas de validación sean mostrados +correctamente. + +observeField +------------ + +``observeField(string $fieldId, array $options)`` + +Observa el campo con el id DOM especificado por $field\_id (cada +$options['frequency'] segundos ) y realiza un XMLHttpRequest si su +contenido ha cambiado. + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'title', array( 'options' => $titles ) ) ?> + + + observeField( 'PostTitle', + array( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +``observeField`` utiliza las mismas opciones que ``link`` + +El campo a enviar puede ser asignado utilizando ``$options['with']``. +Por defecto este contiene ``Form.Element.serialize('$fieldId')``. Los +datos enviados están disponibles en ``$this->data`` de tu controlador. +Los Callbacks pueden ser utilizados con esta función. + +Para enviar un formulario completo cuando el contenido cambie utilice +``$options['with'] = Form.serialize( $('Form ID') )`` + +observeForm +----------- + +``observeForm(string $form, array $options)`` + +Similar to observeField(), but operates on an entire form identified by +the DOM id $form. The supplied $options are the same as observeField(), +except the default value of the $options['with'] option evaluates to the +serialized (request string) value of the form. + +autoComplete +------------ + +``autoComplete(string $field, string $url, array $options)`` + +Renders a text field with $field with autocomplete. The remote action at +$url should return a suitable list of autocomplete terms. Often an +unordered list is used for this. First, you need to set up a controller +action that fetches and organizes the data you'll need for your list, +based on user input: + +:: + + function autoComplete() { + //Partial strings will come from the autocomplete field as + //$this->data['Post']['subject'] + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.subject LIKE' => $this->data['Post']['subject'].'%' + ), + 'fields' => array('subject') + ))); + $this->layout = 'ajax'; + } + +Next, create ``app/views/posts/auto_complete.ctp`` that uses that data +and creates an unordered list in (X)HTML: + +:: + +
    + +
  • + +
+ +Finally, utilize autoComplete() in a view to create your auto-completing +form field: + +:: + + create('User', array('url' => '/users/index')); ?> + autoComplete('Post.subject', '/posts/autoComplete')?> + end('View Post')?> + +Once you've got the autoComplete() call working correctly, use CSS to +style the auto-complete suggestion box. You might end up using something +similar to the following: + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +If you want the user to enter a minimum number of characters before the +autocomplete starts, you can use the minChars-Option as follows: + +:: + + $ajax->autoComplete('Post.subject', '/posts/autoComplete',array('minChars' => 3)); + +isAjax +------ + +``isAjax()`` + +Allows you to check if the current request is a Prototype Ajax request +inside a view. Returns a boolean. Can be used for presentational logic +to show/hide blocks of content. + +drag & drop +----------- + +``drag(string $id, array $options)`` + +Makes a Draggable element out of the DOM element specified by $id. For +more information on the parameters accepted in $options see +`http://github.com/madrobby/scriptaculous/wikis/draggable `_. + +Common options might include: + ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++==========================+=======================================================================================================================================================================================================================================================================================================+ +| $options['handle'] | Sets whether the element should only be draggable by an embedded handle. The value must be an element reference or element id or a string referencing a CSS class value. The first child/grandchild/etc. element found within the element that has this CSS class value will be used as the handle. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | If set to true, the element returns to its original position when the drags ends. Revert can also be an arbitrary function reference, called when the drag ends. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | Constrains the drag to either 'horizontal' or 'vertical', leave blank for no constraints. | ++--------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +Makes the DOM element specified by $id able to accept dropped elements. +Additional parameters can be specified with $options. For more +information see +`http://github.com/madrobby/scriptaculous/wikis/droppables `_. + +Common options might include: + ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options keys | Description | ++===========================+==========================================================================================================================================================================================+ +| $options['accept'] | Set to a string or javascript array of strings describing CSS classes that the droppable element will accept. The drop element will only accept elements of the specified CSS classes. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | The droppable element will only accept the dragged element if it is contained in the given elements (element ids). Can be a string or a javascript array of id references. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | If set to 'horizontal' or 'vertical', the droppable element will only react to a draggable element if it is overlapping the droparea by more than 50% in the given axis. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | A javascript call back that is called when the dragged element is dropped on the droppable element. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +Makes a drop target that creates an XMLHttpRequest when a draggable +element is dropped on it. The $options array for this function are the +same as those specified for drop() and link(). + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +Creates a directional slider control. For more information see +`http://wiki.github.com/madrobby/scriptaculous/slider `_. + +Common options might include: + +$options keys + +Description + +$options['axis'] + +Sets the direction the slider will move in. 'horizontal' or 'vertical'. +Defaults to horizontal + +$options['handleImage'] + +The id of the image that represents the handle. This is used to swap out +the image src with disabled image src when the slider is enabled. Used +in conjunction with handleDisabled. + +$options['increment'] + +Sets the relationship of pixels to values. Setting to 1 will make each +pixel adjust the slider value by one. + +$options['handleDisabled'] + +The id of the image that represents the disabled handle. This is used to +change the image src when the slider is disabled. Used in conjunction +handleImage. + +$options['change'] + $options['onChange'] + +JavaScript callback fired when the slider has finished moving, or has +its value changed. The callback function receives the slider's current +value as a parameter. + +$options['slide'] + $options['onSlide'] + +JavaScript callback that is called whenever the slider is moved by +dragging. It receives the slider's current value as a parameter. + +editor +------ + +``editor(string $id, string $url, array $options)`` + +Creates an in-place editor at DOM id. The supplied ``$url`` should be an +action that is responsible for saving element data. For more information +and demos see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_. + +Common options might include: + +$options keys + +Description + +``$options['collection']`` + +Activate the 'collection' mode of in-place editing. +$options['collection'] takes an array which is turned into options for +the select. To learn more about collection see +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_. + +``$options['callback']`` + +A function to execute before the request is sent to the server. This can +be used to format the information sent to the server. The signature is +``function(form, value)`` + +``$options['okText']`` + +Text of the submit button in edit mode + +``$options['cancelText']`` + +The text of the link that cancels editing + +``$options['savingText']`` + +The text shown while the text is sent to the server + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +The row height of the input field + +``$options['cols']`` + +The number of columns the text area should span + +``$options['size']`` + +Synonym for ‘cols’ when using single-line + +``$options['highlightcolor']`` + +The highlight color + +``$options['highlightendcolor']`` + +The color which the highlight fades to + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +Example + +:: + +
Text To Edit
+ editor( + "in_place_editor_id", + array( + 'controller' => 'Posts', + 'action' => 'update_title', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +Makes a list or group of floated objects contained by $id sortable. The +options array supports a number of parameters. To find out more about +sortable see +`http://wiki.github.com/madrobby/scriptaculous/sortable `_. + +:: + +
+
+ Element 1 +
+
+ Element 2 +
+
+ Element 3 +
+
+ + sortable('sortableContainer',array('tag'=>'div','only'=>'sortableItem','onUpdate'=>'writeupdate')); + ?> + +Make sure that you do not include the parenthesis on the onUpdate +callback, or it will not execute. + +Common options might include: + +$options keys + +Description + +$options['tag'] + +Indicates what kind of child elements of the container will be made +sortable. Defaults to 'li'. + +$options['only'] + +Allows for further filtering of child elements. Accepts a CSS class. + +$options['overlap'] + +Either 'vertical' or 'horizontal'. Defaults to vertical. + +$options['constraint'] + +Restrict the movement of the draggable elements. accepts 'horizontal' or +'vertical'. Defaults to vertical. + +$options['handle'] + +Makes the created Draggables use handles, see the handle option on +Draggables. + +$options['onUpdate'] + +Called when the drag ends and the Sortable's order is changed in any +way. When dragging from one Sortable to another, the callback is called +once on each Sortable. + +$options['hoverclass'] + +Give the created droppable a hoverclass. + +$options['ghosting'] + +If set to true, dragged elements of the sortable will be cloned and +appear as a ghost, instead of directly manipulating the original +element. diff --git a/es/The-Manual/Core-Helpers/Cache.rst b/es/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..605695712fd2ce36fff9c8c29adcb2ce34b27714 --- /dev/null +++ b/es/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,167 @@ +Cache +##### + +El helper Cache permite almacenar temporalmente (caching) layouts y +vistas completas, ahorrando tiempo en descargar y retornar algunos +datos. El caching de vistas en CakePHP almacena temporalmente los +layouts y vistas utilizando el motor de almacenamiento seleccionado. Es +importante notar que el helper de Cache opera de manera diferente a +otros helpers. No tiene metodos que se puedan llamar directamente. En +vez de eso, una vista es marcada con tags, que permiten decirle cuales +bloques no deben ser cacheados. + +Cuando una URL es requerida, CakePHP verifica si aquel requerimiento +esta cacheado.Si lo está, el resto del proceso es saltado. Cualquier +bloque no cacheado se procesa normalmente y la vista es entregada. Esto +crea grandes ahorros en tiempos de proceso para cada requerimiento para +una URL cacheada, asi tambien menos código es ejecutado. Si CakePHP no +encuentra una vista cacheada, o el cache ha expirado para la URL +requerida, se continúa normalmente con el requerimiento. + +Generalidades en Caching +======================== + +El caching tiene como intención ser un medio de almacenamiento temporal +que reduce la carga del servidor. Por ejemplo podrías almacenar el +resultado de una consulta que requiere mucho tiempo de modo de no +realizarla nuevamente en cada actualización de la página. + +El Caching no debe utilizarze nunca para almacenar datos en forma +permanente. Solo debes cachear elementos que puedan ser eventualmente +regenerados si es necesario. + +Motores de Cache en Cake +======================== + +Lo nuevo en la version 1.2 de CakePHP son varios motores de cache. El +helper de cache interactua transparentemente con estos motores, +permitiendo almacenar vistas en multiples formas sin preocuparse de las +caracteristicas especificas del medio de almacenamiento. La elección del +motor de cache es controlada a traves del archivo app/config/core.php +config. Muchas opciones para cada motor de cache son listados en el +archivo de configuracion core.php, mas detalles acerca de cada motor se +puede encontrar en la seccion de Caching. + +File + +El File Engine es el motor por defecto en CakePHP. Escribe archivos +planos en el sistema de archivos y cuenta con numerosos parámetros, pero +funciona bien con los valores por defecto. + +APC + +EL motor APC implementa el método `Alternative PHP +Cache `_. Asi como XCache, este motor almacena +codigo ya compilado de PHP. + +XCache + +El motor XCache opera en forma similar al APC, pero implementanto el +método `XCache `_. Requiere la +autenticacion de usuarios para funcionar apropiadamente. + +Memcache + +El motor Memcache funciona utilizando un servidor de almacenamiento en +memoria, lo que permite crear objetos cache en la memoria del sistema. +Mas informacion acerca de este método puede ser encontrada en +`php.net `_ y +`memcached `_ + +Configuracion del Cache Helper +============================== + +El caching de vistas y el helper de cache tienen varioes elementos de +configuración importantes. Estos se detallan más abajo. + +Para usar el cache helper en cualquier vista o controlador, debes +primero configurar Configure::Cache.check a true en la linea 80 de +``core.php``. Si no se configura a true, entonces el cache no sera +verificado o creado. + +Caching en los Controllers +========================== + +Cualquier controlador que utilice la funcionalidad de caching necesita +incluir el CacheHelper en el arreglo $helpers. + +:: + + var $helpers = array('Cache'); + +Necesitas ademas indicar cuales acciones necesitan caching, y cuanto +tiempo durará cacheada cada acción. Esto se hace a traves de la variable +$cacheAction en tus controladores. $cacheAction debería ser configurada +como un arreglo el cual contiene las acciones a ser cacheadas y la +duracion en segundos que deben permanecer en tal condicion. EL tiempo +puede expresarse en formato strtotime(). (ie. "1 hour", o "3 minutes"). + +Usando como ejemplo ArticlesController, que recibe un gran tráfico que +necesita cachearse. + +Por ejemplo, cachear los articulos visitados frecuentemente por diversos +periodos de tiempo + +:: + + var $cacheAction = array( + 'view/23/' => 21600, + 'view/48/' => 36000, + 'view/52' => 48000 + ); + +Hacer caching de una acción completa en este caso un listado de +articulos + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +Cachear todas las acciones del controlador usando un formato amigable +strtotime() para indicar el tiempo de cacheo. + +:: + + var $cacheAction = "1 hour"; + +Marking Non-Cached Content in Views +=================================== + +There will be times when you don't want an *entire* view cached. For +example, certain parts of the page may look different whether a user is +currently logged in or browsing your site as a guest. + +To indicate blocks of content that are *not* to be cached, wrap them in +`` `` like so: + +:: + + + check('User.name')) : ?> + Welcome, read('User.name')?>. + + link('Login', 'users/login')?> + + + +It should be noted that once an action is cached, the controller method +for the action will not be called - otherwise what would be the point of +caching the page. Therefore, it is not possible to wrap +`` `` around variables which are set from +the controller as they will be *null*. + +Borrando el cache +================= + +Es importante recordar que Cake borrará el cache de una vista si el +modelo usado en esta vista es modificado. Por Ejemplo, si el cache de la +vista usa datos del modelo Post y hubo una consulta INSERT, UPDATE o +DELETE hecha en Post, el cache de esa vista es borrado, y el nuevo +contenido es generado en el siguiente request. + +Si tu necesitas borrar el cache manualmente puedes hacer una llamada a +Cache::clear(). Esto borrará **todos** los datos del cache, excluyendo +el cache de los archivos de las vistas. Si necesitas borrar el cache de +los archivos de las vistas, puedes usar ``clearCache()``. diff --git a/es/The-Manual/Core-Helpers/Form.rst b/es/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..05a53c5bc0abc64d02953ef3bdca076c0c16e5e3 --- /dev/null +++ b/es/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,1358 @@ +Formularios +########### + +El FormHelper es una nueva adición a CakePHP. La mayor parte del trabajo +de creación de formularios recae sobre el uso de esta nueva clase, en +lugar de los (ahora obsoletos) métodos del HtmlHelper. El FormHelper se +centra en la creación de formularios rápidamente, de esta manera agiliza +la validación, el precargado y el diseño de la interfaz. El FormHelper +es bastante flexible - este hará casi todo automáticamente por usted, o +si lo desea puede usar métodos específicos para hacer solo lo que +necesite. + +Creando Formularios +=================== + +El primer método que necesitarás para poder aprovecha el FormHelper es +``create()``. Este método se encarga de escribir la etiqueta de apertura +del formulario. + +``create(string $modelo = null, array $opciones = array())`` + +Todos los parámetros son opcionales. Si ``create()`` es llamado sin +parámetros, asume que estás construyendo un formulario que será enviado +al controlador actual, ya sea vía la acción ``add()`` o ``edit()``. El +método por omisión para el envío es POST. El elemento form es regresado +con un ID DOM. El ID es generado usando el nombre del modelo y el nombre +de la acción del controlador en formato CamelCased. Si fuera a llamar +``create()`` dentro de una vista de UsersController, vería algo como lo +siguiente en la vista + +:: + +
+ +Puedes también pasar ``false`` para el parámetro ``$modelo``. Esto +pondrá los datos de tu formulario en el array: ``$this->data`` (en lugar +de ponerlos en en sub-array: ``$this->data['Model']``). Esto puede ser +muy útil para formularios cortos que quizá no representen nada en tu +base de datos. + +The ``create()`` method allows us to customize much more using the +parameters, however. First, you can specify a model name. By specifying +a model for a form, you are creating that form's *context*. All fields +are assumed to belong to this model (unless otherwise specified), and +all models referenced are assumed to be associated with it. If you do +not specify a model, then it assumes you are using the default model for +the current controller. + +:: + + create('Recipe'); ?> + + //Output: + + +This will POST the form data to the ``add()`` action of +RecipesController. However, you can also use the same logic to create an +edit form. The FormHelper uses the ``$this->data`` property to +automatically detect whether to create an add or edit form. If +``$this->data`` contains an array element named after the form's model, +and that array contains a non-empty value of the model's primary key, +then the FormHelper will create an edit form for that record. For +example, if we browse to http://site.com/recipes/edit/5, we might get +the following: + +:: + + // controllers/recipes_controller.php: + data)) { + $this->data = $this->Recipe->findById($id); + } else { + // Save logic goes here + } + } + ?> + + // views/recipes/edit.ctp: + + // Since $this->data['Recipe']['id'] = 5, we should get an edit form + create('Recipe'); ?> + + //Output: + + + +Since this is an edit form, a hidden input field is generated to +override the default HTTP method. + +The ``$options`` array is where most of the form configuration happens. +This special array can contain a number of different key-value pairs +that affect the way the form tag is generated. + +$options[‘type’] +---------------- + +This key is used to specify the type of form to be created. Valid values +include ‘post’, ‘get’, ‘file’, ‘put’ and ‘delete’. + +Supplying either ‘post’ or ‘get’ changes the form submission method +accordingly. + +:: + + create('User', array('type' => 'get')); ?> + + //Output: + + +Specifying ‘file’ changes the form submission method to ‘post’, and +includes an enctype of “multipart/form-data” on the form tag. This is to +be used if there are any file elements inside the form. The absence of +the proper enctype attribute will cause the file uploads not to +function. + +:: + + create('User', array('type' => 'file')); ?> + + //Output: + + +When using ‘put’ or ‘delete’, your form will be functionally equivalent +to a 'post' form, but when submitted, the HTTP request method will be +overridden with 'PUT' or 'DELETE', respectively. This allows CakePHP to +emulate proper REST support in web browsers. + +$options[‘action’] +------------------ + +The action key allows you to point the form to a specific action in your +current controller. For example, if you’d like to point the form to the +login() action of the current controller, you would supply an $options +array like the following: + +:: + + create('User', array('action' => 'login')); ?> + + //Output: + +
+ +$options[‘url’] +--------------- + +If the desired form action isn’t in the current controller, you can +specify a URL for the form action using the ‘url’ key of the $options +array. The supplied URL can be relative to your CakePHP application, or +can point to an external domain. + +:: + + create(null, array('url' => '/recipes/add')); ?> + // or + create(null, array('url' => array('controller' => 'recipes', 'action' => 'add'))); ?> + + + //Output: +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + //Output: + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +$options[‘default’] +------------------- + +If ‘default’ has been set to boolean false, the form’s submit action is +changed so that pressing the submit button does not submit the form. If +the form is meant to be submitted via AJAX, setting ‘default’ to false +suppresses the form’s default behavior so you can grab the data and +submit it via AJAX instead. + +Closing the Form +================ + +El FormHelper tambien incluye un método end() que completa el código del +formulario. A menudo, el método end() solo escribe la etiqueta de cierre +del formulario, pero el usar end() también hace que el FormHelper +inserte los elementos hidden necesarios en el formulario para los +métodos que dependen de este. + +:: + + create(); ?> + + + + end(); ?> + +Si una cadena es colocada como primer parámetro del end(), el FormHelper +agregará un boton submit llamado de esa manera además de la etiqueta de +cierre del formulario. + +:: + + end('Finish'); ?> + + Output: + +
+ +
+ + +Automagic Form Elements +======================= + +First, let’s look at some of the more automatic form creation methods in +the FormHelper. The main method we’ll look at is input(). This method +will automatically inspect the model field it has been supplied in order +to create an appropriate input for that field. + +input(string $fieldName, array $options = array()) + ++--------------------------------------------------+--------------------------------------------------------+ +| Column Type | Resulting Form Field | ++==================================================+========================================================+ +| string (char, varchar, etc.) | text | ++--------------------------------------------------+--------------------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++--------------------------------------------------+--------------------------------------------------------+ +| text | textarea | ++--------------------------------------------------+--------------------------------------------------------+ +| text, with name of password, passwd, or psword | password | ++--------------------------------------------------+--------------------------------------------------------+ +| date | day, month, and year selects | ++--------------------------------------------------+--------------------------------------------------------+ +| datetime, timestamp | day, month, year, hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ +| time | hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ + +For example, let’s assume that my User model includes fields for a +username (varchar), password (varchar), approved (datetime) and quote +(text). I can use the input() method of the FormHelper to create +appropriate inputs for all of these form fields. + +:: + + create(); ?> + + input('username'); //text + echo $form->input('password'); //password + echo $form->input('approved'); //day, month, year, hour, minute, meridian + echo $form->input('quote'); //textarea + ?> + + end('Add'); ?> + +A more extensive example showing some options for a date field: + +:: + + echo $form->input('birth_dt', array( 'label' => 'Date of birth' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +Besides the specific input options found below you can specify any html +attribute (for instance onfocus). For more information on $options and +$htmlAttributes see `HTML Helper `_. + +And to round off, here's an example for creating a hasAndBelongsToMany +select. Assume that User hasAndBelongsToMany Group. In your controller, +set a camelCase plural variable (group -> groups in this case, or +ExtraFunkyModel -> extraFunkyModels) with the select options. In the +controller action you would put the following: + +:: + + $this->set('groups', $this->User->Group->find('list')); + +And in the view a multiple select can be expected with this simple code: + +:: + + echo $form->input('Group'); + +If you want to create a select field while using a belongsTo- or +hasOne-Relation, you can add the following to your Users-controller +(assuming your User belongsTo Group): + +:: + + $this->set('groups', $this->User->Group->find('list')); + +Afterwards, add the following to your form-view: + +:: + + echo $form->input('group_id'); + +If your model name consists of two or more words, e.g., "UserGroup", +when passing the data using set() you should name your data in a +pluralised and camelCased format as follows: + +:: + + $this->set('userGroups', $this->UserGroup->find('list')); + // or + $this->set('reallyInappropriateModelNames', $this->ReallyInappropriateModelName->find('list')); + +Field naming convention +----------------------- + +The Form helper is pretty smart. Whenever you specify a field name with +the form helper methods, it'll automatically use the current model name +to build an input with a format like the following: + +:: + + + +You can manually specify the model name by passing in +Modelname.fieldname as the first parameter. + +:: + + echo $form->input('Modelname.fieldname'); + +If you need to specify multiple fields using the same field name, thus +creating an array that can be saved in one shot with saveAll(), use the +following convention: + +:: + + input('Modelname.0.fieldname'); + echo $form->input('Modelname.1.fieldname'); + ?> + + + + +$options[‘type’] +---------------- + +You can force the type of an input (and override model introspection) by +specifying a type. In addition to the field types found in the `table +above `_, you can also create +‘file’, and ‘password’ inputs. + +:: + + input('field', array('type' => 'file')); ?> + + Output: + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +Use these keys if you need to inject some markup inside the output of +the input() method. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + ));?> + + Output: + +
+ --before-- + + --between--- + + --after-- +
+ +For radio type input the 'separator' attribute can be used to inject +markup to separate each input/label pair. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ +For ``date`` and ``datetime`` type elements the 'separator' attribute +can be used to change the string between select elements. Defaults to +'-'. + +$options[‘options’] +------------------- + +This key allows you to manually specify options for a select input, or +for a radio group. Unless the ‘type’ is specified as ‘radio’, the +FormHelper will assume that the target output is a select input. + +:: + + input('field', array('options' => array(1,2,3,4,5))); ?> + +Output: + +:: + +
+ + +
+ +Options can also be supplied as key-value pairs. + +:: + + input('field', array('options' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2', + 'Value 3'=>'Label 3' + ))); ?> + +Output: + +:: + +
+ + +
+ +If you would like to generate a select with optgroups, just pass data in +hierarchical format. Works on multiple checkboxes and radio buttons too, +but instead of optgroups wraps elements in fieldsets. + +:: + + input('field', array('options' => array( + 'Label1' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2' + ), + 'Label2' => array( + 'Value 3'=>'Label 3' + ) + ))); ?> + +Output: + +:: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +Si ‘multiple’ es puesto a true para una entrada de tipo select, el +select admitirá multiples selecciones. Alternativamente, poniendo +‘multiple’ igual a ‘checkbox’ la salida será una lista de checkboxes +relacionados. + +:: + + $form->input('Modelo.campo', array( 'type' => 'select', 'multiple' => true )); + $form->input('Modelo.campo', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +Defines the maximum number of characters allowed in a text input. + +$options[‘div’] +--------------- + +Use this option to set attributes of the input's containing div. Using a +string value will set the div's class name. An array will set the div's +attributes to those specified by the array's keys/values. Alternatively, +you can set this key to false to disable the output of the div. + +Setting the class name: + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +Output: + +:: + +
+ + +
+ +Setting multiple attributes: + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +Output: + +:: + +
+ + +
+ +Disabling div output: + +:: + + input('User.name', array('div' => false));?> + +Output: + +:: + + + + +$options[‘label’] +----------------- + +Set this key to the string you would like to be displayed within the +label that usually accompanies the input. + +:: + + input( 'User.name', array( 'label' => 'The User Alias' ) );?> + +Output: + +:: + +
+ + +
+ +Alternatively, set this key to false to disable the output of the label. + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Output: + +:: + +
+ +
+ +Set this to an array to provide additional options for the ``label`` +element. If you do this, you can use a ``text`` key in the array to +customize the label text. + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'The User Alias') ) ); ?> + +Output: + +:: + +
+ + +
+ +$options['legend'] +------------------ + +Some inputs like radio buttons will be automatically wrapped in a +fieldset with a legend title derived from the fields name. The title can +be overridden with this option. Setting this option to false will +completely eliminate the fieldset. + +$options[‘id’] +-------------- + +Set this key to force the value of the DOM id for the input. + +$options['error'] +----------------- + +Using this key allows you to override the default model error messages +and can be used, for example, to set i18n messages. It has a number of +suboptions which control the wrapping element, wrapping element class +name, and whether HTML in the error message will be escaped. + +To disable error message output set the error key to false. + +:: + + $form->input('Model.field', array('error' => false)); + +To modify the wrapping element type and its class, use the following +format: + +:: + + $form->input('Model.field', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +To prevent HTML being automatically escaped in the error message output, +set the escape suboption to false: + +:: + + $form->input('Model.field', array('error' => array('escape' => false))); + +To override the model error messages use an associate array with the +keyname of the validation rule: + +:: + + $form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough', true) ))); + +As seen above you can set the error message for each validation rule you +have in your models. In addition you can provide i18n messages for your +forms. + +$options['default'] +------------------- + +Used to set a default value for the input field. The value is used if +the data passed to the form does not contain a value for the field (or +if no data is passed at all). + +Example usage: + +:: + + input('ingredient', array('default'=>'Sugar')); + ?> + +Example with select field (Size "Medium" will be selected as default): + +:: + + 'Small', 'm'=>'Medium', 'l'=>'Large'); + echo $form->input('size', array('options'=>$sizes, 'default'=>'m')); + ?> + +You cannot use ``default`` to check a checkbox - instead you might set +the value in ``$this->data`` in your controller, ``$form->data`` in your +view, or set the input option ``checked`` to true. + +Date and datetime fields' default values can be set by using the +'selected' key. + +$options[‘selected’] +-------------------- + +Used in combination with a select-type input (i.e. For types select, +date, time, datetime). Set ‘selected’ to the value of the item you wish +to be selected by default when the input is rendered. + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +The selected key for date and datetime inputs may also be a UNIX +timestamp. + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +These two keys specify the number of rows and columns in a textarea +input. + +:: + + echo $form->input('textarea', array('rows' => '5', 'cols' => '5')); + +Output: + +:: + +
+ + +
+ +$options[‘empty’] +----------------- + +If set to true, forces the input to remain empty. + +When passed to a select list, this creates a blank option with an empty +value in your drop down list. If you want to have a empty value with +text displayed instead of just a blank option, pass in a string to +empty. + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(choose one)')); ?> + +Output: + +:: + +
+ + +
+ +If you need to set the default value in a password field to blank, use +'value' => '' instead. + +Options can also supplied as key-value pairs. + +$options[‘timeFormat’] +---------------------- + +Used to specify the format of the select inputs for a time-related set +of inputs. Valid values include ‘12’, ‘24’, and ‘none’. + +$options[‘dateFormat’] +---------------------- + +Used to specify the format of the select inputs for a date-related set +of inputs. Valid values include ‘DMY’, ‘MDY’, ‘YMD’, and ‘NONE’. + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +Used in combination with a date/datetime input. Defines the lower and/or +upper end of values shown in the years select field. + +$options['interval'] +-------------------- + +This option specifies the number of minutes between each option in the +minutes select box. + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +Would create 4 options in the minute select. One for each 15 minutes. + +$options['class'] +----------------- + +You can set the classname for an input field using ``$options['class']`` + +:: + + echo $form->input('title', array('class' => 'custom-class')); + +File Fields +=========== + +To add a file upload field to a form, you must first make sure that the +form enctype is set to "multipart/form-data", so start off with a create +function such as the following. + +:: + + echo $form->create('Document', array('enctype' => 'multipart/form-data') ); + // OR + echo $form->create('Document', array('type' => 'file')); + +Next add either of the two lines to your form view file. + +:: + + echo $form->input('Document.submittedfile', array('between'=>'
','type'=>'file')); + + // or + + echo $form->file('Document.submittedfile'); + +Due to the limitations of HTML itself, it is not possible to put default +values into input fields of type 'file'. Each time the form is +displayed, the value inside will be empty. + +Upon submission, file fields provide an expanded data array to the +script receiving the form data. + +For the example above, the values in the submitted data array would be +organized as follows, if the CakePHP was installed on a Windows server. +'tmp\_name' will have a different path in a Unix environment. + +:: + + + $this->data['Document']['submittedfile'] = array( + 'name' => conference_schedule.pdf + 'type' => application/pdf + 'tmp_name' => C:/WINDOWS/TEMP/php1EE.tmp + 'error' => 0 + 'size' => 41737 + ); + +This array is generated by PHP itself, so for more detail on the way PHP +handles data passed via file fields `read the PHP manual section on file +uploads `_. + +Validating Uploads +------------------ + +Below is an example validation method you could define in your model to +validate whether a file has been successfully uploaded. + +:: + + // Based on comment 8 from: http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty( $val['tmp_name']) && $val['tmp_name'] != 'none')) { + return is_uploaded_file($val['tmp_name']); + } + return false; + } + +Form Element-Specific Methods +============================= + +The rest of the methods available in the FormHelper are for creating +specific form elements. Many of these methods also make use of a special +$options parameter. In this case, however, $options is used primarily to +specify HTML tag attributes (such as the value or DOM id of an element +in the form). + +:: + + text('username', array('class' => 'users')); ?> + +Will output: + +:: + + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +Creates a checkbox form element. This method also generates an +associated hidden form input to force the submission of data for the +specified field. + +:: + + checkbox('done'); ?> + +Will output: + +:: + + + + +button +------ + +``button(string $title, array $options = array())`` + +Crea un botón HTML con el título y el tipo con valor por defecto de +"button". Ajuste el tipo en ``$options['type']`` podiendo seleccionar +entre uno de los tres tipos de boton posibles: + +#. button: Crea un boton estándar (el por defecto). +#. reset: Crea un boton para Restablecer el formulario. (reseteo de + formulario). +#. submit: Similar al método ``$form->submit``. + +:: + + button('Un Boton'); + echo $form->button('Otro Boton', array('type'=>'button')); + echo $form->button('Restablecer el Formulario', array('type'=>'reset')); + echo $form->button('Enviar Formulario', array('type'=>'submit')); + ?> + +Esta es la salida: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, mixed $showEmpty)`` + +Creates a select element populated with the years from ``$minYear`` to +``$maxYear``, with the ``$selected`` year selected by default. +``$selected`` can either be a four-digit year (e.g. 2004) or string +``'now'``. HTML attributes may be supplied in ``$attributes``. + +:: + + year('purchased', 2005, 2009); + ?> + +Will output: + +:: + + + +If ``$showEmpty`` is false, the select will not include an empty option. +If ``$showEmpty`` is a string, it will be used as empty option's name. + +:: + + year('returned', 2008, 2010, null, null, 'Select a year'); + ?> + +Will output: + +:: + + + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with month names. + +:: + + month('mob'); + ?> + +Will output: + +:: + + + +You can pass in your own array of months to be used by setting the +'monthNames' attribute (CakePHP 1.3 only), or have months displayed as +numbers by passing false. (Note: the default months are +internationalized and can be translated using localization.) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a set of select inputs for date and time. Valid values for +$dateformat are ‘DMY’, ‘MDY’, ‘YMD’ or ‘NONE’. Valid values for +$timeFormat are ‘12’, ‘24’, and ‘NONE’. + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the (numerical) days of the +month. + +To create an empty option with prompt text of your choosing (e.g. the +first option is 'Day'), you can supply the text as the final parameter +as follows: + +:: + + day('created'); + ?> + +Will output: + +:: + + + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the hours of the day. + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the minutes of the hour. + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with ‘am’ and ‘pm’. + +error +----- + +``error(string $fieldName, string $text, array $options)`` + +Shows a validation error message, specified by $text, for the given +field, in the event that a validation error has occurred. + +Options: + +- 'escape' bool Whether or not to html escape the contents of the + error. +- 'wrap' mixed Whether or not the error message should be wrapped in a + div. If a string, will be used as the HTML tag to use. +- 'class' string The classname for the error message + +file +---- + +``file(string $fieldName, array $options)`` + +Creates a file input. + +:: + + create('User',array('type'=>'file')); + echo $form->file('avatar'); + ?> + +Will output: + +:: + +
+ + +When using ``$form->file()``, remember to set the form encoding-type, by +setting the type option to 'file' in ``$form->create()`` + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +Creates a hidden form input. Example: + +:: + + hidden('id'); + ?> + +Will output: + +:: + + + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +Returns true if the supplied $fieldName has an active validation error. + +:: + + isFieldError('gender')){ + echo $form->error('gender'); + } + ?> + +When using ``$form->input()``, errors are rendered by default. + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +Creates a label tag, populated with $text. + +:: + + label('status'); + ?> + +Will output: + +:: + + + +password +-------- + +``password(string $fieldName, array $options)`` + +Creates a password field. + +:: + + password('password'); + ?> + +Will output: + +:: + + + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +Creates a radio button input. Use ``$attributes['value']`` to set which +value should be selected default. + +Use ``$attributes['separator']`` to specify HTML in between radio +buttons (e.g.
). + +Radio elements are wrapped with a label and fieldset by default. Set +``$attributes['legend']`` to false to remove them. + +:: + + 'Male','F'=>'Female'); + $attributes=array('legend'=>false); + echo $form->radio('gender',$options,$attributes); + ?> + +Will output: + +:: + + + + + + + +If for some reason you don't want the hidden input, setting +``$attributes['value']`` to a selected value or boolean false will do +just that. + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element, populated with the items in ``$options``, with +the option specified by ``$selected`` shown as selected by default. Set +``$showEmpty`` to false if you do not want an empty select option to be +displayed. + +:: + + 'Male','F'=>'Female'); + echo $form->select('gender',$options) + ?> + +Will output: + +:: + + + +submit +------ + +``submit(string $caption, array $options)`` + +Creates a submit button with caption ``$caption``. If the supplied +``$caption`` is a URL to an image (it contains a ‘.’ character), the +submit button will be rendered as an image. + +It is enclosed between ``div`` tags by default; you can avoid this by +declaring ``$options['div'] = false``. + +:: + + submit(); + ?> + +Will output: + +:: + +
+ +You can also pass a relative or absolute url to an image for the caption +parameter instead of caption text. + +:: + + submit('ok.png'); + ?> + +Will output: + +:: + +
+ +text +---- + +``text(string $fieldName, array $options)`` + +Creates a text input field. + +:: + + text('first_name'); + ?> + +Will output: + +:: + + + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +Creates a textarea input field. + +:: + + textarea('notes'); + ?> + +Will output: + +:: + + + diff --git a/es/The-Manual/Core-Helpers/HTML.rst b/es/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..dce7142ff7e9b1e87e9d44ebc09667e5281e40ed --- /dev/null +++ b/es/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,616 @@ +HTML +#### + +El rol del HtmlHelper de CakePHP es hacer los tags referentes a HTML y +sus opciones simples, rápidas, y más resistentes al cambio. Usando este +ayudante pondremos más luz sobre tu aplicación, y más flexibilidad en +cuanto a donde se encuentre en relación al dominio principal. + +El rol del HtmlHelper ha cambiado significativamente desde CakePHP 1.1. +Los metodos relacionados a Form ya no se usan y han sido movidos al +nuevo FormHelper. Si tu estas buscando ayuda para los formularios HTML, +Revisa lo nuevo de FormHelper. + +Antes de dar un vistaso a los metodos de HtmlHelper, vas a necesitar +conocimientos sobre configuración y usos en las situaciones que te puede +ayudar esta clase. En primer lugar, en un esfuerzo para aliviar a +aquellos que gustan de usar las etiquetas cortas () o muchas +llamadas a echo() en el codigo de sus vistas todos los metodos del +HtmlHelper son pasados por el metodo output(). Si tu deseas activar +automáticamente la salida para generar HTML con el ayudante simplemente +implementa output() en tu AppHelper. + +:: + + function output($cadena) { + echo $cadena; + } + +Haciendo esto no necesitarás agregar llamadas a echo en el codigo de tus +vistas. + +Muchos metodos HtmlHelper incluyen parametros $htmlAttributes, esto te +permite hilvanar cualquier atributo extra en tus tags. Aquí hay algunos +ejemplos de como se usan los parámetros $htmlAttributes + +:: + + Atributos Deseados: + Arreglo de Parametros: array('class'=>'algunaClase') + + Atributos Deseados: + Arreglo de Parametros: array('name' => 'foo', 'value' => 'bar') + +El HtmlHelper está disponible en todas las vistas de forma +predeterminada. Si usted está recibiendo un error que le informa que no +está ahí, es por lo general debido a su nombre que esta faltando y puede +configurarlo manualmente en la variable $helpers del controlador. + +Inserting Well-Formatted elements +================================= + +The most important task the HtmlHelper accomplishes is creating well +formed markup. Don't be afraid to use it often - you can cache views in +CakePHP in order to save some CPU cycles when views are being rendered +and delivered. This section will cover some of the methods of the +HtmlHelper and how to use them. + +charset +------- + +``charset(string $charset=null)`` + +Usada para crear metadatos que especificarán la codificación de los +caracteres del documento. El valor por defecto es UTF-8. + +:: + + + charset(); ?> + +Generará como salida: + +:: + + + +O sino tambien: + +:: + + charset('ISO-8859-1'); ?> + +Generará como salida: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +Crea un enlace a una o más hojas de estilos CSS. Si $inline está en +*false*, los tags de enlace son agregados en la variable +$scripts\_for\_layout, la cual puedes imprimir dentro del tag *head* del +documento. + +Este método de inclusión de CSS asume que el archivo CSS especificado +esté en el directorio **/app/webroot/css**. + +:: + + css('forms'); ?> + +Hará la salida: + +:: + + + +El primer parámetro puede ser un arreglo para incluir varios archivos. + +:: + + css(array('forms','tables','menu')); ?> + +Hará la salida: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +Este método es útil para vincular a los recursos externos como los feeds +RSS / Atom y favicons. Como CSS (), puede especificar si desea o no que +esta etiqueta a aparezca en la línea o en la etiqueta de la cabecera con +el cuarto parámetro. + +Si establece el atributo "type" usando el parámetro $htmlAttributes, +CakePHP contiene algunos atajos: + ++--------+------------------------+ +| type | valor traducido | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> //Salida (saltos de linea añadidos)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + + //Salida (saltos de linea añadidos) + + +Este método también puede utilizarse para agregar las etiquetas "meta" +para las palabras claves y las descripciones. Ejemplo: + +:: + + meta( + 'keywords', + 'ingrese las palabas claves aquí' + );?> + //Salida + // + + meta( + 'description', + 'ingrese alguna descripcion meta aquí' + );?> + + //Salida + +Si deseas añadir una etiqueta meta personalizada en el primer parámetro +se debe establecer una matriz. Para una salida de la etiqueta "robots +noindex" debe utilizar el siguiente código: + +:: + + echo $html->meta(array('name' => 'robots', 'content' => 'noindex')); + +docType +------- + +``docType(string $type = 'xhtml-strict')`` + +Returns a (X)HTML doctype tag. Supply the doctype according to the +following table: + ++----------------+-----------------------+ +| type | translated value | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Builds CSS style definitions based on the keys and values of the array +passed to the method. Especially handy if your CSS file is dynamic. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Will output: + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Crea una etiqueta de imagen, la ruta especificada será relativa a +/app/webroot/img/. + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Mostrará: + +:: + + CakePHP + +Si desea crear un link asociado a la imagen especifique el link de +destino usando la opción ``url option en $htmlAttributes.`` + +:: + + image("recipes/6.jpg", array( + "alt" => "Bizcochos", + 'url' => array('controller' => 'recipes', 'action' => 'view', 6) + )); ?> + +Mostrará: + +:: + + + Bizcochos + + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +Método de propósito general para crear enlaces HTML. Use +``$htmlAttributes`` para especificar los atributos del elemento. + +:: + + link('Enter', '/pages/home', array('class'=>'button','target'=>'_blank')); ?> + +Arrojará como resultado: + +:: + + + Enter + +Especifique ``$confirmMessage`` para desplegar un dialogo javascript +``confirm()``. + +:: + + link( + 'Delete', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "¿Está seguro que desea eliminar esta receta?" + );?> + +Arrojará como resultado: + +:: + + + Delete + +Query strings también pueden ser creados con ``link()``. + +:: + + link('View image', array( + 'controller' => 'images', + 'action' => 'view', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Arrojará como resultado: + +:: + + + View image + +Los caracteres especiales HTML en ``$title`` serán convertidos a +entidades HTML. Para deshabilitar esta conversión, establezca la opción +escape a false (falso) en ``$htmlAttributes``, o establezca +``$escapeTitle`` a false (falso). + +:: + + + link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + null, null, false + ); + ?> + +Ambos arrojarán como resulado lo siguiente: + +:: + + + Brownies + + +Consulte también la documentación del método +`HtmlHelper::url `_ para más +ejemplos de diferentes tipos de urls. + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns text wrapped in a specified tag. If no text is specified then +only the opening is returned. + +:: + + tag('span', 'Hello World.', array('class' => 'welcome'));?> + + //Output + Hello World + + //No text specified. + tag('span', null, array('class' => 'welcome'));?> + + //Output + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Usado para crear contenedores de código HTML tipo div. El primer +parámetro especifica una clase CSS, el segundo es usado para +proporcionar el texto que estará envuelto por las etiquetas div. Si el +último parámetro fue establecido como true, el valor de el parámetro +$text, será impreso escapando el código HTML. + +Si no se especifica un texto por medio del parámetro $text , sólo una +etiqueta div de apertura es regresada. + +:: + + + div('error', 'Por favor, escriba su nombre de usuario.');?> + + // La instrucción anterior imprimiría esta cadena: +
Por favor, escriba su nombre de usuario.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns a text wrapped in a CSS-classed

tag. If no text is supplied, +only a starting

tag is returned. + +:: + + para(null, 'Hello World.');?> + + //Output +

Hello World.

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Creates a row of table header cells to be placed inside of tags. + +:: + + tableHeaders(array('Date','Title','Active'));?> //Output + + + tableHeaders( + array('Date','Title','Active'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + //Output + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Creates table cells, in rows, assigning attributes differently for +odd- and even-numbered rows. Wrap a single table cell within an array() +for specific + + + + tableCells(array( + array('Jul 7th, 2007', array('Best Brownies', array('class'=>'highlight')) , 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', array('No', array('id'=>'special'))), + )); + ?> + + //Output + + + + + tableCells( + array( + array('Red', 'Apple'), + array('Orange', 'Orange'), + array('Yellow', 'Banana'), + ), + array('class' => 'darker') + ); + ?> + + //Output + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Devuelve un URL que apunta a alguna combinación de controlador y acción. +Si $url está vacío devuelve el valor de REQUEST\_URI, en caso contrario +genera el URL para la combinación de controlador y acción. Si $full es +true, se antepondrá el URL base del sitio al resultado. + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "bar"));?> + + // Salida + /posts/view/bar + +Enseguida más ejemplos de uso: + +URL con parámetros nombrados (named parameters) + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "foo" => "bar")); + ?> + + // Salida + /posts/view/foo:bar + +URL con extensión + +:: + + url(array( + "controller" => "posts", + "action" => "list", + "ext" => "rss")); + ?> + + // Salida + /posts/list.rss + +URL (empezando con '/') con el URL completo del sitio agregado al +inicio. + +:: + + url('/posts', true); ?> + + //Salida + http://www.example.com/posts + +URL con parámetros GET y ancla nombrada (named anchor) + +:: + + url(array( + "controller" => "posts", + "action" => "buscar", + "?" => array("foo" => "bar"), + "#" => "primero")); + ?> + + //Salida + /posts/buscar?foo=bar#primero + +Por mas info ver `el +Router::url `_ en +el API. + +Changing the tags output by HtmlHelper +====================================== + +The built in tag sets for ``HtmlHelper`` are XHTML compliant, however if +you need to generate HTML for HTML4 you will need to create and load a +new tags config file containing the tags you'd like to use. To change +the tags used create ``app/config/tags.php`` containing: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +You can then load this tag set by calling ``$html->loadConfig('tags');`` diff --git a/es/The-Manual/Core-Helpers/Javascript.rst b/es/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..f6a9dff9af7e3dba5c5e6456530579c22b0bbb0b --- /dev/null +++ b/es/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,143 @@ +Javascript +########## + +EL ayudante Javascript es usado para en la creación de etiquetas y +bloques de código javascript bien formados. Existen varios métodos +algunos de los cuales están diseñados para trabajar con el framework +Javascript `Prototype `_. + +Methods +======= + +``codeBlock($script, $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), $safe)`` + +- string $script - The JavaScript to be wrapped in SCRIPT tags +- array $options - Set of options: + + - allowCache: boolean, designates whether this block is cacheable + using the current cache settings. + - safe: boolean, whether this block should be wrapped in CDATA tags. + Defaults to helper's object configuration. + - inline: whether the block should be printed inline, or written to + cached for later output (i.e. $scripts\_for\_layout). + +- boolean $safe - DEPRECATED. Use $options['safe'] instead + +codeBlock returns a formatted script element containing $script. But can +also return null if Javascript helper is set to cache events. See +JavascriptHelper::cacheEvents(). And can write in +``$scripts_for_layout`` if you set $options['inline'] to false. + +``blockEnd()`` + +Ends a block of cached Javascript. Can return either a end script tag, +or empties the buffer, adding the contents to the cachedEvents array. +Its return value depends on the cache settings. See +JavascriptHelper::cacheEvents() + +``link($url, $inline = true)`` + +- mixed $url - String URL to JavaScript file, or an array of URLs. +- boolean $inline If true, the '; + echo Sanitize::html($entreeNonSouhaitee); + // sortie : <font size="99" color="#FF0000">SALUT</font><script>...</script> + echo Sanitize::html($entreeNonSouhaitee, true); + // sortie : SALUT... + +escape +====== + +escape(string $string, string $connection) + +Utilisé pour échapper les déclarations SQL par l'ajout de *slashes*, en +tenant compte de la configuration actuelle du système concernant les +magic\_quotes\_gpc. $connection est le nom de la base concernée par +l'échappement, telle que nommée dans votre fichier +app/config/database.php . + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +Cette fonction est un nettoyeur multi-usage de force industrielle, +destinée à être utilisée sur des tableaux entiers (comme $this->data, +par exemple). La fonction prend un tableau (ou une chaîne) et retourne +la version nettoyée. Les opérations de nettoyage suivantes sont +exécutées sur chaque élément du tableau (de façon récursive) : + +- Les espaces bizarres (incluant 0xCA) sont remplacés par des espaces + ordinaires. +- Double vérification des caractères spéciaux et suppression des + retours chariot pour une sécurité SQL accrue. +- Ajout de *slashes* pour SQL (appelle simplement la fonction sql + exposée précédemment). +- Permutation des anti-slashes saisis par l'utilisateur avec des + anti-slashes vérifiés. + +L'argument $options peut être une chaîne ou un tableau. Lorsqu'une +chaîne est fournie, il s'agit du nom de la connexion à la base de +données. Si un tableau est fourni, il sera fusionné avec les options +suivantes : + +- connection +- odd\_spaces +- encode +- dollar +- carriage +- unicode +- escape +- backslash + +L'utilisation de clean() avec des options ressemble à quelque chose +comme : + +:: + + $this->data = Sanitize::clean($this->data, array('encode' => false)); + diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..d21c4741c2c848770521d525f7b58551b93a3c10 --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,1071 @@ +Validation des données +###################### + +La validation des données est une partie importante de toute +application, puisqu'elle permet de s'assurer que les données d'un modèle +respectent les règles métiers de l'application. Par exemple, vous +aimeriez vérifier que les mots de passe sont longs d'au moins huit +caractères ou bien vous assurer que les noms d'utilisateurs sont +uniques. La définition des règles de validation facilite grandement la +gestion des formulaires. + +Il y a de nombreux aspects différents dans le processus de validation. +Ce que nous aborderons dans cette section c'est le côté modèle des +choses. En résumé : ce qui se produit lorsque vous appelez la méthode +save() de votre modèle. Pour obtenir plus d'informations sur la manière +d'afficher les erreurs de validation, regardez la section traitant de +`l'assistant Form `_. + +La première étape pour la validation de données est de créer les règles +dans le Modèle. Pour ce faire, utilisez le tableau Model::validate dans +la définition du modèle, par exemple : + +:: + + + +Dans l'exemple ci-dessus, le tableau $validate est ajouté au modèle +Utilisateur, mais ce tableau ne contient pas de règles de validation. En +supposant que la table "utilisateurs" ait les champs "login", +"mot\_de\_passe", "email" et "date\_de\_naissance", l'exemple ci-dessous +montre quelques règles simples de validation qui s'appliquent à ces +champs : + +:: + + 'alphaNumeric', + 'email' => 'email', + 'date_de_naissance' => 'date' + ); + } + ?> + +Ce dernier exemple montre comment des règles de validation peuvent être +ajoutées aux champs d'un modèle. Pour le champ 'login', seules les +lettres et les chiffres sont autorisés, l'email doit être valide et la +date de naissance doit être une date valide. La définition de règles de +validation active l'affichage "automagique" de messages d'erreurs dans +les formulaires par CakePHP, si les données saisies ne respectent pas +les règles définies. + +CakePHP a de nombreuses règles et leur utilisation peut être très +simple. Certaines de ces règles intégrées vous permettent de vérifier le +format des adresses emails, des URLs, des numéros de carte de crédit, +etc. - mais nous couvrirons cela en détail plus loin. + +Voici un autre exemple de validation plus complexe qui tire avantage de +quelques-unes de ces règles pré-définies : + +:: + + array( + 'alphaNumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Chiffres et lettres uniquement !' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Entre 5 et 15 caractères' + ) + ), + 'mot_de_passe' => array( + 'rule' => array('minLength', '8'), + 'message' => '8 caractères minimum' + ), + 'email' => 'email', + 'date_de_naissance' => array( + 'rule' => 'date', + 'message' => 'Entrez une date valide', + 'allowEmpty' => true + ) + ); + } + ?> + +Deux règles de validation sont définies pour le login : il doit contenir +des lettres et des chiffres uniquement et sa longueur doit être comprise +entre 5 et 15. Le mot de passe doit avoir au minimum 8 caractères. +L'email doit avoir un format correct et la date de naissance être une +date valide. Vous pouvez voir dans cet exemple comment personnaliser les +messages que CakePHP affichera en cas de non respect de ces règles. + +Comme le montre l'exemple ci-dessus, un seul champ peut avoir plusieurs +règles de validation. Si les règles pré-définies ne correspondent pas à +vos critères, vous pouvez toujours ajouter vos propres règles de +validation, selon vos besoins. + +Maintenant que nous avons vu, en gros, comment la validation fonctionne, +voyons comme ces règles sont définies dans le modèle. Il y a trois +manières différentes pour définir les règles de validation : tableaux +simples, une règle par champ et plusieurs règles par champ. + +Note des traducteurs francophones : la règle "alphaNumeric" ne +fonctionne pas pour notre alphabet, les caractères accentués ne sont pas +validés. Pour cela, nous devrons donc passer par une expression +régulière. + +Règles simples +============== + +Comme le suggère le nom, c'est la manière la plus simple de définir une +règle de validation. La syntaxe générale pour définir des règles de +cette manière est : + +:: + + var $validate = array('nomChamp' => 'nomRegle'); + +Où 'nomChamp' est le nom du champ pour lequel la règle est définie, et +'nomRegle' est un nom prédéfini, comme 'alphaNumeric', 'email' ou +'isUnique'. + +Par exemple, pour s'assurer que l'utilisateur fourni une adresse email +correcte, vous pouvez utiliser cette règle : + +:: + + var $validate = array('utilisateur_email' => 'email'); + +Une règle par champ +=================== + +Cette technique de définition permet un meilleur contrôle sur le +fonctionnement des règles de validation. Mais avant d'aborder ce point, +regardons le schéma d'utilisation général pour ajouter une règle à un +seul champ : + +:: + + var $validate = array( + 'champ1' => array( + 'rule' => 'nomRegle', // ou bien : array('nomRegle', 'parametre1', 'parametre2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // ou bien : 'update' + 'message' => 'Votre message d\'erreur' + ) + ); + +La clé 'rule' est obligatoire. Si vous définissez uniquement 'required' +=> true, la validation du formulaire ne fonctionnera pas correctement. +C'est à cause du fait que 'required' n'est pas à proprement parlé une +règle. + +Comme vous pouvez le voir ici, chaque champ (un seul est présenté +ci-dessus) est associé à un tableau contenant cinq clés : 'rule', +'required', 'allowEmpty', 'on' et 'message'. Toutes les clés sont +optionnelles sauf 'rule'. Regardons en détail ces clés. + +La clé 'rule' +------------- + +La clé 'rule' définit la méthode de validation et attend soit une valeur +simple, soit un tableau. La règle spécifiée peut-être le nom d'une +méthode dans votre modèle, une méthode de la classe globale Validation +ou une expression régulière. Pour une liste complète des règles +pré-définies, allez voir `Règles de validation +incluses `_. + +Si la règle ne nécessite pas de paramètre, 'rule' peut-être une simple +valeur, comme : + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +Si la règle nécessite quelques paramètres (tels que un maximum, un +minimum ou une plage de valeurs), 'rule' doit être un tableau : + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +Souvenez-vous, la clé 'rule' est obligatoire pour les définitions de +règles sous forme de tableau. + +required +-------- + +Cette clé doit être définie par une valeur booléenne. Si 'required' est +'true' alors le champ doit être présent dans le tableau de données. Par +exemple, si la règle de validation a été définie comme suit : + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +Les données envoyées à la méthode save() du modèle doivent contenir des +données pour le champ 'login'. Dans le cas contraire, la validation +échouera. La valeur par défaut de cette clé est le booléen 'false'. + +``required => true`` ne signifie pas la même chose que la règle de +validation ``notEmpty()``. ``required => true`` indique que la *clé* du +tableau doit être présente - cela ne veut pas dire qu'elle doit avoir +une valeur. Par conséquent, la validation échouera si le champ n'est pas +présent dans le jeu de données, mais pourra réussir (en fonction de la +règle) si la valeur soumise est vide (''). + +allowEmpty +---------- + +Si définie à 'false', la valeur du champ doit être "non vide", ceci +étant déterminé par ``!empty($valeur) || is_numeric($valeur)``. La +vérification numérique est là pour que CakePHP fasse ce qu'il faut quand +``$valeur`` vaut zéro. + +La différence entre ``required`` et ``allowEmpty`` peut être confuse. +``'required' => true`` signifie que vous ne pouvez pas sauvegarder le +modèle, si la clé pour ce champ n'est pas présente dans ``$this->data`` +(la vérification est réalisé avec ``isset``) ; tandis que +``'allowEmpty' => false`` s'assure que la valeur du champ courant est +"non vide", comme décrit ci-dessus. + +on +-- + +La clé 'on' peut prendre l'une des valeurs suivantes : 'update' ou +'create'. Ceci fournit un mécanisme qui permet à une règle donnée d'être +appliquée pendant la création ou la mise à jour d'un enregistrement. + +Si une règle est définie à 'on' => 'create', elle sera seulement +appliquée lors de la création d'un nouvel enregistrement. Autrement, si +elle est définie à 'on' => 'update', elle s'appliquera uniquement lors +de la mise à jour de l'enregistrement. + +La valeur par défaut pour 'on' est 'null'. Quand 'on' est nul, la règle +s'applique à la fois pendant la création et la mise à jour. + +message +------- + +La clé ‘message’ vous permet de définir un message d'erreur de +validation personnalisé pour la règle : + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'Le mot de passe doit comporter au moins 8 caractères' + ) + ); + +last +---- + +Définir la clé ``'last'`` à ``true`` entraînera l'arrêt du validateur à +la règle courante si elle échoue, plutôt que de continuer avec la règle +suivante. Ceci est pratique si vous voulez arrêter la validation au cas +où le champ est non vide dans un `champ +multi-règles `_. + +:: + + var $validate = array( + 'nom_utilisateur' => array( + 'nom_utilisateurRegle-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Entrez un nom d\'utilisateur svp.', + 'last' => true + ), + 'nom_utilisateurRegle-2' => array( + 'rule' => array('minLength', '8'), + 'message' => '8 caractères au moins.' + ) + ) + ); + +La valeur par défaut de ``'last'`` est ``false``. + +Plusieurs règles par champs +=========================== + +La technique que nous venons de voir nous donne plus de flexibilité que +l'assignation simple de règles, mais il y a une étape supplémentaire que +nous pouvons mettre en œuvre, pour avoir un contrôle encore plus fin sur +la validation des données. La prochaine technique que nous allons voir +nous permet d'affecter plusieurs règles de validation par champ de +modèle. + +Si vous souhaitiez affecter plusieurs règles de validation à un seul +champ, voici basiquement comment il faudrait faire : + +:: + + + var $validate = array( + 'nomChamp' => array( + 'nomRegle' => array( + 'rule' => 'nomRegle', + // clés supplémentaires comme 'on', 'required', etc. à mettre ici + ), + 'nomRegle2' => array( + 'rule' => 'nomRegle2', + // clés supplémentaires comme 'on', 'required', etc. à mettre ici + ) + ) + ); + +Comme vous pouvez le voir, cela ressemble beaucoup à ce que nous avons +vu dans la section précédente. Ici pour chaque champ, nous avons +uniquement un tableau de paramètres de validation. Dans ce cas, chaque +'nomChamp' est un tableau de règles indexé. Chaque 'nomRegle' contient +un tableau indépendant de paramètres de validation. + +Ce sera plus explicite avec un exemple pratique : + +:: + + var $validate = array( + 'login' => array( + 'regle1' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Lettres et chiffres uniquement' + ), + 'regle2' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Taille minimum de 8 caractères' + ), + ) + ); + +L'exemple ci-dessus définit deux règles pour le champ 'login': +'alphanumeric' et 'minlength'. Comme vous pouvez le voir, chaque règle +est identifiée avec un nom arbitraire. + +Par défaut, CakePHP essaye de valider un champ en utilisant toutes les +règles de validation déclarées pour lui et renvoi le message d'erreur +pour la dernière règle qui n'est pas passée. Mais si la clé ``last`` est +définie à ``true`` pour une règle et qu'elle ne passe pas, le message +d'erreur pour cette règle est retourné et les règles suivantes ne sont +pas validées. Donc si vous préférez voir le message d'erreur de la +première règle qui ne passe pas au lieu de la dernière, définissez +``'last' => true`` pour chaque règle. + +Si vous prévoyez d'utiliser des messages d'erreur internationalisé +(traduits), vous pouvez définir les messages d'erreur dans vos vues : + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'regle1' => __('Caractères alphanumériques seulement', true), + 'regle2' => __('Longueur minimum de 8 caractères', true) + ) + )); + +Le champ est maintenant pleinement internationalisé, et vous pouvez +enlever les messages d'erreurs du modèle. Pour plus d'information sur la +function ``__()``, allez voir la section "Localisation et +Internationalisation". + +Règles de validation incluses +============================= + +La classe de validation de CakePHP contient un certain nombre de règles +prédéfinies, qui rendent la validation des données plus simple dans vos +modèles. Cette classe contient de nombreuses règles souvent utilisées +que vous n'aurez pas à ré-écrire vous même. Ci-dessous vous trouverez +une liste complète de toutes les règles, illustrées par des exemples +d'utilisation. + +alphaNumeric +------------ + +Les données pour ce champ ne doivent contenir que chiffres et lettres. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Les données pour ce champ ne doivent contenir que lettres et chiffres.' + ) + ); + +*Note des traducteurs francophones* : la règle "alphaNumeric" ne +fonctionne pas pour notre alphabet, les caractères accentués ne sont pas +validés. Pour cela, nous devrons donc passer par une expression +régulière. + +between +------- + +La longueur des données du champ doit être comprise dans la plage +numérique spécifiée. Les valeurs minimum et maximum doivent être toutes +les deux fournies. Cette méthode utilise <= et non <. + +:: + + var $validate = array( + 'mot_passe' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Le mot de passe doit avoir une longueur comprise entre 5 et 15 caractères.' + ) + ); + +La longueur des données est "le nombre d'octets dans la représentation +des données sous forme de chaîne". Faites attention, car elle peut être +plus grande que le nombre de caractères quand vous manipulez des +caractères non-ASCII. + +blank +----- + +Cette règle est utilisé pour vérifier que le champ est laissé vide ou +que seulement des caractères blancs y sont présent. Les caractères +blancs incluent l'espace, la tabulation, le retour chariot et nouvelle +ligne. + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +Les données pour ce champ doivent être une valeur booléenne. Les valeurs +possibles sont : true ou false, les entiers 0 ou 1, les chaînes '0' ou +'1'. + +:: + + var $validate = array( + 'maCaseACocher' => array( + 'rule' => array('boolean'), + 'message' => 'Valeur incorrecte pour maCaseACocher' + ) + ); + +cc +-- + +Cette règle est utilisée pour vérifier si une donnée est un numéro de +carte de crédit valide. Elle prend trois paramètres : 'type', 'deep' et +'regex'. + +Le paramètre 'type' peut être assigné aux valeurs 'fast', 'all' ou à +l'une des suivantes : + +- amex +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +Si 'type' est défini à 'fast', cela valide les données de la majorité +des formats numériques de cartes de crédits. Définir 'type' à 'all' +vérifiera tous les types de cartes de crédits. Vous pouvez aussi définir +'type' comme un tableau des types que vous voulez détecter. + +Le paramètre 'deep' devrait être défini comme une valeur booléenne. S'il +est défini à *true*, la validation vérifiera l'algorithme Luhn de la +carte de crédit +(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_). +Par défaut, elle est à *false*. + +Le paramètre 'regex' vous permet de passer votre propre expression +régulière, laquelle sera utilisée pour valider le numéro de la carte de +crédit. + +:: + + var $validate = array( + 'numero_cc' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'Le numéro de carte de crédit que vous avez saisi était invalide.' + ) + ); + +comparison +---------- + +Comparison est utilisé pour comparer des valeurs numériques. Il supporte +"est supérieur", "est inférieur", "supérieur ou égal", "inférieur ou +égal", "égal à" et "non égal". Quelques exemples sont indiqués +ci-dessous : + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Vous devez avoir 18 ans au moins pour vous inscrire.' + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Vous devez avoir 18 ans au moins pour vous inscrire.' + ) + ); + +date +---- + +Cette règle s'assure que les données soumises sont des formats de date +valides. Un seul paramètre (qui peut être un tableau) doit être passé et +sera utilisé pour vérifier le format de la date soumise. La valeur de ce +paramètre peut être l'une des suivantes : + +- 'dmy', par exemple : 27-12-2006 ou 27-12-06 (les séparateurs peuvent + être l'espace, le point, le tiret, le slash) +- 'mdy', par exemple : 12-27-2006 ou 12-27-06 (les séparateurs peuvent + être l'espace, le point, le tiret, le slash) +- 'ymd', par exemple : 2006-12-27 ou 06-12-27 (les séparateurs peuvent + être l'espace, le point, le tiret, le slash) +- 'dMy', par exemple : 27 Décembre 2006 ou 27 Déc 2006 +- 'Mdy', par exemple : Décembre 27, 2006 ou Déc 27, 2006 (la virgule + est optionnelle) +- 'My', par exemple : (Décembre 2006 ou Déc 2006) +- 'my', par exemple : 12/2006 ou 12/06 (les séparateurs peuvent être + l'espace, le point, le tiret, le slash) + +Si aucune clé n'est soumise, la clé par défaut 'ymd' sera utilisée. + +:: + + var $validate = array( + 'naissance' => array( + 'rule' => 'date', + 'message' => 'Entrez une date valide au format AA-MM-JJ.', + 'allowEmpty' => true + ) + ); + +Etant donné que de nombreux moteurs de stockage réclament un certain +format de date, vous devriez envisager de faire le plus gros du travail +en acceptant un large choix de formats et en essayant de les convertir, +plutôt que de forcer les gens à les soumettre dans un format donné. Le +plus de travail vous ferez pour les utilisateurs, le mieux ce sera. + +decimal +------- + +Cette règle s'assure que la donnée est un nombre décimal valide. Un +paramètre peut être passé pour spécifier le nombre de décimales requises +après le point. Si aucun paramètre n'est passé, la donnée sera validée +comme un nombre scientifique à virgule flottante, entraînant une erreur +si aucune décimale n'est trouvée après le point. + +:: + + var $validate = array( + 'prix' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +Celle-ci vérifie que la donnée soit une adresse email valide. En passant +un booléen true comme second paramètre de cette règle, elle tentera de +vérifier aussi, que l'hôte de l'adresse soit valide. + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Merci de soumettre une adresse email valide.' + ) + ); + +equalTo +------- + +Cette règle s'assurera que la valeur est égal à la valeur passée et +qu'elle est du même type. + +:: + + var $validate = array( + 'nourriture' => array( + 'rule' => array('equalTo', 'gâteau'), + 'message' => 'Cette valeur devrait être la chaîne gâteau' + ) + ); + +extension +--------- + +Cette règle vérifie les extensions valides de fichier, comme .jpg ou +.png. Permet la vérification d'extensions multiples, en les passant sous +forme de tableau. + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), + 'message' => 'Merci de soumettre une image valide.' + ) + ); + +file +---- + +Cette règle s'assure que la valeur est un nom de fichier valide. Cette +règle de validation n'est pour le moment pas fonctionnelle. + +ip +-- + +Cette règle s'assurera qu'une adresse IPv4 valide ait été soumise. + +:: + + var $validate = array( + 'ip_client' => array( + 'rule' => 'ip', + 'message' => 'Merci de soumettre une adresse IP valide.' + ) + ); + +isUnique +-------- + +La donnée pour le champ doit être unique, elle ne peut être utilisée par +aucune autre ligne. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'Ce nom d\'utilisateur a déjà été choisi.' + ) + ); + +minLength +--------- + +Cette règle s'assure que la donnée satisfait à la longueur minimale +requise. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Les noms d\'utilisateur doivent avoir au moins 8 caractères.' + ) + ); + +La longueur des données est "le nombre d'octets dans la représentation +des données sous forme de chaîne". Faites attention, car elle peut être +plus grande que le nombre de caractères quand vous manipulez des +caractères non-ASCII. + +maxLength +--------- + +Cette règle s'assure que la donnée respecte la longueur maximale +requise. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', '15'), + 'message' => 'Les noms d\'utilisateur ne doivent pas dépasser 15 caractères.' + ) + ); + +La longueur ici est "le nombre d'octets dans la représentation des +données sous forme de chaîne". Faites attention car elle pourrait être +plus grande que le nombre de caractères en manipulant des caractères +non-ASCII. + +money +----- + +Cette règle s'assurera que la valeur est une somme monétaire valide. + +Le second paramètre définit où le symbole est situé (gauche/droite). + +:: + + var $validate = array( + 'salaire' => array( + 'rule' => array('money', 'left'), + 'message' => 'Merci de soumettre une somme monétaire valide.' + ) + ); + +multiple +-------- + +Utilisez cette règle pour valider un champ select multiple. Elle accepte +les paramètres "in", "max" et "min". + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('do', 'ré', 'mi', 'fa', 'sol', 'la', 'si'), 'min' => 1, 'max' => 3)), + 'message' => 'Merci de choisir une, deux ou trois options' + ) + ); + +inList +------ + +Cette règle s'assurera que la valeur est dans un ensemble donné. Elle +nécessite un tableau des valeurs. Le champ est valide si sa valeur +vérifie l'une des valeurs du tableau donné. + +Exemple : + +:: + + var $validate = array( + 'fonction' => array( + 'choixAutorise' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Entrez soit Foo, soit Bar.' + ) + ) + ); + +numeric +------- + +Vérifie si la donnée passée est un nombre valide. + +:: + + var $validate = array( + 'voitures' => array( + 'rule' => 'numeric', + 'message' => 'Merci de soumettre le nombre de voitures.' + ) + ); + +notEmpty +-------- + +La règle de base pour s'assurer qu'un champ n'est pas vide. + +:: + + var $validate = array( + 'titre' => array( + 'rule' => 'notEmpty', + 'message' => 'Ce champ ne peut pas rester vide' + ) + ); + +Ne l'utilisez pas pour un champ select multiple, sinon cela causera une +erreur. A la place, utilisez "multiple". + +phone +----- + +Phone valide les numéros de téléphone US. Si vous voulez valider des +numéros de téléphones non-US, vous pouvez fournir une expression +régulière comme second paramètre pour couvrir des formats de numéros +additionnels. + +:: + + var $validate = array( + 'telephone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +Postal est utilisé pour valider des codes postaux des U.S.A. (us), du +Canada (ca), du Royaume-Uni (uk), de l'Italie (it), d'Allemagne (de) et +de Belgique (be). Pour les autres formats de codes postaux, vous devez +fournir une expression régulière comme second paramètre. + +:: + + var $validate = array( + 'code_postal' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +Cette règle s'assure que la valeur est dans une fourchette donnée. Si +aucune fourchette n'est soumise, la règle s'assurera que la valeur est +un nombre limite valide pour la plateforme courante. + +:: + + var $validate = array( + 'nombre' => array( + 'rule' => array('range', -1, 11), + 'message' => 'Merci d\'entrer un nombre entre 0 et 10' + ) + ); + +L'exemple ci-dessus acceptera toutes les valeurs qui sont plus grandes +que 0 (par ex, 0.01) et plus petite que 10 (par ex, 9.99). Note : Les +deux extrémités données (-1 et 11) ne sont pas incluses !!! + +ssn +--- + +Ssn valide les numéros de sécurité sociale des U.S.A. (us), du Danemark +(dk) et des Pays-Bas (nl). Pour les autres formats de numéros de +sécurité sociale, vous devez fournir une expression régulière. + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +Cette règle vérifie les formats valides d'URL. Elle supporte les +protocoles http(s), ftp(s), file, news et gopher. + +:: + + var $validate = array( + 'siteweb' => array( + 'rule' => 'url' + ) + ); + +Pour s'assurer qu'un protocole est présent dans l'url, le mode strict +mode peut être activé comme ceci. + +:: + + var $validate = array( + 'siteweb' => array( + 'rule' => array('url', true) + ) + ); + +Règles personnalisées de validation des données +=============================================== + +Si ce qui précède ne vous convient pas, vous pouvez toujours créer vos +propres règles de validation. Il y a deux moyens de réaliser cela : en +définissant des expressions régulières ou en créant des méthodes de +validation personnalisées. + +Validation avec Expression Régulière personnalisée +-------------------------------------------------- + +Si la technique de validation dont vous avez besoin peut être complétée +par l'utilisation d'une expression régulière, vous pouvez définir une +expression personnalisée comme une règle de validation de champ. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('custom', '/^[a-z0-9]{3,}$/i'), + 'message' => 'Seulement des lettres et des entiers, minimum 3 caractères' + ) + ); + +L'exemple ci-dessus vérifie que le login contient seulement des lettres +et des entiers et qu'il a au minimum trois caractères. + +L'expression régulière dans ``rule`` doit être délimitée par des +*slashes* (/). Le "i" final optionnel après le dernier *slash* signifie +que l'expression régulière est insensible à la casse. + +Ajouter vos propres méthodes de validation +------------------------------------------ + +Parfois, la vérification des données par un motif d'expression régulière +ne suffit pas. Par exemple, si vous voulez vous assurer qu'un coupon de +réduction (code promo) n'est pas utilisé plus de 25 fois, vous devez +ajouter votre propre méthode de validation, comme indiqué ci-dessous : + +:: + + array( + 'rule' => array('limiteUtilisations', 25), + 'message' => 'Ce code promo a dépassé son nombre maximal d\'utilisation.' + ) + ); + + function limiteUtilisations($check, $limit){ + //$check aura comme valeur : array('code_promo' => 'une valeur') + //$limit aura comme valeur : 25 + $compteur_code_actuel = $this->find( 'count', array('conditions' => $data, 'recursive' => -1) ); + return $compteur_code_actuel < $limit; + } + } + ?> + +Le champ en cours de validation est passé à la fonction comme premier +paramètre, sous la forme d'un tableau associatif avec le nom du champ +comme clé et les données postées comme valeur. + +Si vous voulez passer des paramètres supplémentaires à votre fonction de +validation, ajoutez des éléments dans le tableau 'rule' et manipulez-les +comme des paramètres supplémentaires (après le paramètre principal +``$check``) dans votre fonction. + +Votre fonction de validation peut être dans le modèle (comme dans +l'exemple) ou dans un comportement (*behavior*) que votre modèle +implémente. Ceci inclus les méthodes mappées. + +Les méthodes des modèles/comportements sont vérifiées en premier, avant +de chercher pour une méthode dans la class ``Validation``. Cela veut +dire que vous pouvez écraser les méthodes de validation existantes +(telle que ``alphaNumeric()``) au niveau de l'application (en ajoutant +la méthode dans ``AppModel``) ou au niveau du modèle. + +Quand vous écrivez une règle de validation qui peut être utilisée par +plusieurs champs, prenez soin d'extraire la valeur du champ du tableau +``$check``. Le tableau ``$check`` est passé avec le nom du champ comme +clé et la valeur du champ comme valeur. Le champ complet qui doit être +validé est stocké dans une variable de $this->data. + +:: + + array( + 'rule' => 'alphaNumericDashUnderscore', + 'message' => 'Le pseudo ne peut contenir que des lettres, des nombres, des tirets ou des underscores. + ) + ); + + function alphaNumericDashUnderscore($check) { + // le tableau $check est passé en utilisant le nom du champ de formulaire comme clé + // nous devons extraire la valeur pour rendre la fonction générique + $valeur = array_values($data); + $valeur = $valeur[0]; + + return preg_match('|^[0-9a-zA-Z_-]*$|', $valeur); + } + } + ?> + +Valider des données à partir du contrôleur +========================================== + +Alors que d'habitude, vous utiliser la méthode *save* de votre modèle, +il peut vous arriver parfois de vouloir tester et valider vos données +sans les sauvegarder. Par exemple, vous souhaitez peut-être afficher des +informations complémentaires à l'utilisateur avant de sauvegarder +définitivement le tout dans votre base de données. Dans ce cas, +uniquement tester les données requiert une technique un peu différente +de la sauvegarde habituelle. + +Tout d'abord, envoyez les données à votre modèle : + +:: + + $this->NomDuModele->set( $this->data ); + +Ensuite, pour vérifier que les données sont valides, utilisez la méthode +de validation de votre modèle, qui retournera *true* (vrai) si les +données sont valides, et *false* (faux) sinon : + +:: + + if ($this->NomDuModele->validates()) { + // Si les données sont valides + } else { + // Si elles ne le sont pas + } + +La méthode de validation appelle la méthode *invalidFields* qui remplit +la propriété *validationErrors* du modèle. Elle retourne également ces +erreurs comme résultat de son appel : + +:: + + $erreurs = $this->NomDuModele->invalidFields(); // renvoie le tableau d'erreurs validationErrors + +Il est important de noter que les données doivent être envoyées au +modèle avant de pouvoir être validées. C'est différent de la méthode de +sauvegarde (*save*) qui vous permet de les envoyer en tant que +paramètre. De même, gardez en mémoire qu'il n'est pas nécessaire +d'appeler la méthode de validation avant de sauvegarder vos données, car +la méthode *save* validera automatiquement ces données avant de les +sauvegarder de manière définitive. + +Pour valider vos données dans plusieurs modèles, l'approche suivante +peut être utilisée : + +:: + + if ($this->Model->saveAll($this->data, array('validate' => 'only'))) { + // Les données sont valides + } else { + // Les données ne sont pas valides + } + +Si vous avez validé les données avant sauvegarde, vous pouvez désactiver +la validation pour éviter une seconde vérification. + +:: + + if ($this->Model->saveAll($this->data, array('validate' => false))) { + // sauvegarde sans validation + } + diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..65fb8cab2a97d4c1b2e4152b8327f3a74e910c7b --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,151 @@ +Débogage +######## + +Le débogage est une partie inévitable et nécessaire de tout cycle de +développement. Si CakePHP n'offre aucun outil se connectant avec les IDE +ou les éditeurs, il fournit cependant plusieurs outils d'assistance au +débogage et révélant ce qui se passe sous la capuche de votre +application. + +Débogage basique +================ + +debug($var, $showHTML = false, $showFrom = true) + +La fonction debug() est une fonction disponible globalement qui +fonctionne de façon similaire à la fonction php print\_r(). La fonction +debug() vous permet de visualiser le contenu d'une variable de +différentes façons. Tout d'abord, si vous souhaitez que les données +soient affichées de façon "HTML-friendly", définissez le second +paramètre à vrai (true). La fonction retourne également par défaut la +ligne et le fichier d'où elle provient. + +Le résultat de cette fonction ne sera affiché que si la variable debug +(du core) a été définie à une valeur supérieure à 0. + +Utiliser la classe de débogage +============================== + +Pour utiliser le débogueur, il faut tout d'abord que la variable +*Configure::read('debug')* aie une valeur différente de 0. + +dump($var) + +*Dump* affiche le contenu d'une variable. La fonction affichera toutes +les propriétés et méthodes (s'il en existe) de la variable en question. + +:: + + $toto = array(1,2,3); + + Debugger::dump($toto); + + //Affichera + array( + 1, + 2, + 3 + ) + + //Objet simple + $voiture = new Voiture(); + + Debugger::dump($voiture); + + //sortie + Voiture:: + Voiture::couleur= 'rouge' + Voiture::marque = 'Toyota' + Voiture::modele = 'Camry' + Voiture::kilometrage = '15000' + Voiture::accelerer() + Voiture::decelerer() + Voiture::arreter() + +log($var, $level = 7) + +Crée une description détaillée de la pile au moment de l'exécution. La +méthode *log()* affichera les données de façon similaire à +*Debugger::dump()*, mais dans le fichier *debug.log* plutôt que dans le +*buffer* de sortie. Notez que votre dossier *app/tmp* (et son contenu) +doivent avoir les permissions en écriture pour que *log()* fonctionne +correctement. + +trace($options) + +Retourne l'état courant de la pile. Chaque ligne du retour inclue la +méthode appelée, y compris quel est le fichier et la ligne dont provient +l'appel. + +//Dans MessagesController::index() pr( Debugger::trace() ); //sortie +MessagesController::index() - APP/controllers/downloads\_controller.php, +line 48 Dispatcher::\_invoke() - CORE/cake/dispatcher.php, line 265 +Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 [main] - +APP/webroot/index.php, line 84 + +Ci-dessus, vous avez un exemple du contenu engendré par la méthode +*Debugger::trace()* dans un contrôleur. Lire la pile de bas en haut vous +montre l'ordre dans lequel les fonctions sont appelées. Dans cet +exemple, index.php a appelé *Dispatcher::dispatch()*, qui à son tour a +appelé *Dispatcher::\_invoke()*. La méthode *\_invoke()* à ensuite +appelé *MessagesController::index()*. Cette information est pratique +quand vous travaillez avec des opérations récursives ou des opérations +profondes dans la pile, pour identifier quelles fonctions sont en cours +d'exécution quand la fonction trace() est appelée. + +excerpt($fichier, $ligne, $contexte) + +Extrait un bout du fichier contenu dans $fichier (qui est un chemin +absolu), et met en évidence la ligne $ligne avec les $contexte lignes +l'entourant. + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + // affichera ceci : + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +Bien que cette méthode soit souvent utilisée par la classe en interne, +elle peut être utile si vous voulez créer vos propres messages d'erreurs +ou *logs* pour une situation donnée. + +exportVar($var, $recursion = 0) + +Convertit une variable de n'importe quel type en une chaîne de caractère +pour l'utiliser dans une sortie de débogage. Cette méthode est également +utilisée par une grande partie du débogueur pour des conversions +internes de variables, et peut être aussi bien utilisée dans votre +propre débogueur. + +invoke($debugger) + +Remplace le débogueur CakePHP avec un nouveau message d'erreur. + +Classe de déboguage +=================== + +La classe de déboguage est nouvelle dans CakePHP 1.2 et offre toujours +plus d'options pour obtenir des informations de déboguage. Elle contient +plusieurs fonctions qui sont invoquées par CakePHP, et fournit un +affichage de la mémoire, et des fonctions de gestion d'erreur. + +La classe de déboguage surcharge les messages d'erreurs par défaut de +PHP, en les remplaçant par des messages plus pratiques. Ces nouveaux +messages sont utilisées par défaut dans CakePHP. Comme pour toutes les +fonctions de déboguage, la variable *Configure::debug* doit avoir une +valeur supérieure à 0. + +Quand une erreur survient, le débogueur affiche l'erreur sur l'écran, et +l'enregistre dans le fichier error.log. Le rapport d'erreur est un +affichage à la fois de la pile d'instructions et de l'extrait du code +près de l'erreur. Cliquez sur le lien "Error" pour afficher la pile, et +sur "Code" pour avoir les lignes mises en cause dans l'erreur. diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..a52605fafaae2027cf3f2b20a62559fb1437b39c --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,93 @@ +Gestion des erreurs +################### + +Dans l'éventualité d'une erreur irrécupérable dans votre application, il +est courant d'arrêter le processus et d'afficher une page d'erreur à +l'utilisateur. Pour vous éviter de coder des traitements spécifiques +pour cela dans chacun de vos contrôleurs et composants, vous pouvez +utiliser la méthode fournie : + +:: + + $this->cakeError(, [array parameters]); + +Faire appel à cette méthode affichera une page d'erreur à l'utilisateur +et arrêtera tout processus ultérieur de votre application. + +``parameters`` doit être un tableau de chaînes de caractères. Si le +tableau contient des objets (objets ``Exception`` inclus), ils seront +transtypés en chaînes. + +CakePHP prédéfinit un lot d'erreur-types, mais pour le moment, la +plupart ne sont réellement utiles que par le framework lui-même. Celle +qui est la plus utile pour le développeur d'applications est la bonne +vielle page d'erreur 404. Elle peut être appelée sans paramètres de la +façon suivante : + +:: + + $this->cakeError('error404'); + +Alternativement, vous pouvez forcer la page à signaler que l'erreur +s'est produite à une URL spécifique en passant le paramètre ``url`` : + +:: + + $this->cakeError('error404', array('url' => 'une/autre.url')); + +Cela devient beaucoup plus utile en étendant le gestionnaire d'erreur et +en lui soumettant vos propres types d'erreurs. Les gestionnaires +d'erreur personnalisés fonctionnent pratiquement comme les actions d'un +contrôleur. Vous allez typiquement attribuer ``set()`` chaque paramètre +passé dans la vue, et ensuite afficher un fichier de vue depuis votre +répertoire ``app/views/errors``. + +Créer un fichier ``app/app_error.php`` avec la définition suivante : + +:: + + + +Les gestionnaires pour les nouveaux types d'erreurs peuvent être +implémentés en ajoutant des méthodes à cette classe. Créez simplement +une nouvelle méthode avec le nom que vous voulez utiliser comme type +d'erreur. + +Prenons l'exemple d'une application qui écrit un certain nombre de +fichiers sur le disque et qu'il est approprié d'indiquer les erreurs +d'écriture à l'utilisateur. Nous ne voulons pas ajouter de code pour +cela dans toutes les parties de notre application, c'est donc un bon +exemple pour utiliser un nouveau type d'erreur. + +Ajoutez une nouvelle méthode à votre classe ``AppError``. Nous prenons +un paramètre appelé ``fichier`` qui sera le lien vers le fichier que +nous n'avons pas réussi à écrire. + +:: + + function impossibleEcrireFichier($params) { + $this->controller->set('fichier', $params['fichier']); + $this->_outputMessage('impossible_ecrire_fichier'); + } + +Créez la vue dans ``app/views/errors/impossible_ecrire_fichier.ctp`` + +:: + +

Impossible d'écrire le fichier

+

Nous n'avons pas pu écrire le fichier sur le disque.

+ +et lancez l'erreur dans le contrôleur/composant : + +:: + + $this->cakeError('impossibleEcrireFichier', array('fichier'=>'unnomdefichier')); + +L'implémentation par défaut de +``$this->__outputMessage()`` affichera simplement la vue +dans ``views/errors/.ctp``. Si vous voulez outrepasser ce +comportement, vous pouvez redéfinir ``__outputMessage($template)`` dans +votre classe ``AppError``. diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..4c5f57541c343f9c34e611baf1c78ef9d43c2c67 --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,190 @@ +Internationalisation et Localisation +#################################### + +L'une des meilleures façons pour que vos applications touchent une +audience plus large est de s'adresser à des langues multiples. Cela peut +souvent s'avérer décourageant, mais les outils d'internationalisation et +de localisation de CakePHP rendent cela plus facile. + +Tout d'abord, il est important de comprendre la terminologie. +*Internationalisation* désigne la capacité d'une application d'être +localisée. Le terme *localisation* désigne l'adaptation d'une +application pour qu'elle corresponde aux besoins d'une langue (ou d'une +culture) spécifique (i.e., une "locale"). Internationalisation et +localisation sont souvent abréviées respectivement en i18n et l10n ; 18 +et 10 sont le nombre de caractères entre le premier et le dernier +caractère. + +Localiser votre Application +=========================== + +Localiser votre Application + +Il n'y a que quelques étapes à franchir pour passer d'une application +uni-langue à une application multi-langue, dont la première est +d'utiliser la fonction +```__()`` `_ dans +votre code. Ci-dessous, un exemple de code pour une application +uni-langue : + +:: + +

Posts

+ +Pour localiser votre code, tout ce que vous devez faire est d'inclure +vos chaînes de caractères dans `la fonction de +traduction `_ comme +suit : + +:: + +

+ +Si vous ne faites rien de plus, ces deux bouts de codes donneront un +résultat identique - ils renverront le même contenu au navigateur. La +fonction +```__()`` `_ +traduira la chaîne passée si une traduction est disponible, sinon elle +la renverra non modifiée. Cela fonctionne exactement comme les autres +implémentations `Gettext `_ (comme +les autres fonctions de traductions, comme +```__d()`` `_,\ ```__n()`` `_ +etc) + +Après avoir préparé votre code pour le multi-langue, l'étape suivante +est de créer votre `fichier +pot `_, qui est le template pour +toutes les chaînes traduisibles de votre application. Pour générer votre +(vos) fichier(s) pot, tout ce que vous avez à faire est de lancer `la +tâche i18n de la console +Cake `_, +qui va chercher partout dans votre code où vous avez utilisé une +fonction de traduction, et générer le(s) fichier(s) pot pour vous. Vous +pouvez (et devez) relancer cette tâche console à chaque fois que vous +changez les chaînes traduisibles dans votre code. + +Le(s) fichier(s) pot ne sont pas utilisés par CakePHP, ils sont les +templates utilisés pour créer ou mettre à jour vos `fichiers +po `_, qui contiennent les +traductions. Cake cherchera après vos fichiers po dans les dossiers +suivants : + +:: + + /app/locale//LC_MESSAGES/.po + +Le domaine par défaut est 'default', donc votre dossier "locale" devrait +ressembler à cela : + +:: + + /app/locale/eng/LC_MESSAGES/default.po (Anglais) + /app/locale/fre/LC_MESSAGES/default.po (Français) + /app/locale/por/LC_MESSAGES/default.po (Portugais) + +Pour créer ou éditer vos fichiers po, il est recommandé de *ne pas* +utiliser votre éditeur de texte préféré. Pour créer un fichier po pour +la première fois, il est possible de copier le fichier pot à l'endroit +correcte et de changer l'extension. *Cependant*, à moins que vous ne +soyez familiarisé avec leur format, il est très facile de créer un +fichier po invalide, ou de le sauver dans un mauvais encodage de +caractères (si vous éditez ces fichiers manuellement, utilisez l'UTF-8 +pour éviter les problèmes). Il y a des outils gratuits tel que +`PoEdit `_ qui rendent les tâches d'édition et de +mise à jour de vos fichiers po vraiment simples, spécialement pour la +mise à jour d'un fichier po existant avec un fichier pot nouvellement +mis à jour. + +Les codes des locales en trois caractères suivent la norme `ISO +639-2 `_ mais +si vous créez des locales régionales (en\_US, en\_GB, etc.) Cake les +utilisera dans les cas appropriés. + +Il y a une limite de 1014 caractères pour chaque valeur *msgstr*. + +Souvenez-vous que les fichiers po sont utiles pour des messages courts. +Si vous pensez que vous aurez à traduire de longs paragraphes, ou des +pages entières, vous devriez penser à l'implémentation d'une solution +différente. Par ex : + +:: + + // Code du fichier App Controller. + function beforeFilter() { + $locale = Configure::read('Config.language'); + if ($locale && file_exists(VIEWS . $locale . DS . $this->viewPath)) { + // utilise /app/views/fre/pages/tos.ctp au lieu de /app/views/pages/tos.ctp + $this->viewPath = $locale . DS . $this->viewPath; + } + } + +ou + +:: + + // Code de vue + echo $this->element(Configure::read('Config.language') . '/tos') + +L'internationalisation dans CakePHP +=================================== + +Utiliser des données locales dans votre application est simple. La +première étape consiste à préciser à CakePHP quelle langue l'application +doit utiliser pour délivrer le contenu. Cela peut-être fait, +entre-autres, en détectant les sous-domaines (en.exemple.com ou +fra.exemple.com) ou en récupérant l'agent utilisateur du navigateur. + +C'est mieux de faire le changement de langue dans le contrôleur : + +:: + + $this->L10n = new L10n(); + $this->L10n->get("fra"); + +Vous pourriez avoir envie de placer ce code dans le beforeFilter pour +que chaque action du contrôleur soit affichée dans la langue correcte ou +bien vous pourriez vouloir le placer dans une action qui manipule +l'authentification ou le changement de langue par défaut. + +L'affichage localisé d'un contenu se fait en utilisant la fonction +pratique \_\_(). Cette fonction est disponible globalement, mais elle +sera probablement mieux utilisée dans vos vues. Le premier paramètre de +la fonction est le msgid défini dans les fichiers .po. Le contenu +localisé est par défaut affiché (par un echo), mais un second paramètre +optionnel entraîne à la place, le retour de la valeur (utile pour le +surlignement ou les liens utilisant le TextHelper par exemple). Le court +exemple ci-dessous montre comment afficher du contenu local en utilisant +la fonction \_\_(). Le rendu exact dépend de la locale qui a été +sélectionnée en utilisant la classe L10n et de la définition active de +la configuration. + +:: + + + +Souvenez-vous d'utiliser le paramètre d'échappement des différentes +méthodes du helper si vous envisagez d'utiliser les données localisées +comme une partie d'un appel de méthode du helper. Remarquez l'usage du +second paramètre de \_\_() pour retourner la donnée au lieu de la rendre +par un echo () : + +:: + + error( + 'Carte.numeroCarte', + __("erreurNumeroCarte", true), + array('escape' => false) + ); + ?> + +Si vous aimeriez avoir tous vos messages d'erreurs traduits par défaut, +une solution simple serait d'ajouter le code suivant dans votre +app\_model.php : + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..809f93fdaa41c808f8f192c7340423205fe71116 --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,70 @@ +Journalisation (logging) +######################## + +Bien que les réglages de la Classe Configure du cœur de CakePHP puissent +vraiment vous aider à voir ce qui se passe en arrière plan, vous aurez +besoin certaines fois, de consigner des données sur le disque pour +découvrir ce qui se produit. Dans un monde devenu plus dépendant des +technologies comme SOAP et AJAX, déboguer peut s'avérer difficile. + +La journalisation (*logging*) peut aussi être une façon de découvrir ce +qui s'est passé dans votre application à chaque instant. Quels termes de +recherche ont été utilisés ? Quelles sortes d'erreurs mes utilisateurs +ont-il vues ? A quelle fréquence est exécutée une requête particulière ? + +La journalisation des données dans CakePHP est facile - la fonction +log() est un élément de la classe Object, qui est l'ancêtre commun de la +plupart des classes CakePHP. Si le contexte est une classe CakePHP +(Modèle, Contrôleur, Composant... n'importe quoi d'autre), vous pouvez +journaliser vos données. + +Utiliser la fonction log +======================== + +La fonction log() prend deux paramètres. Le premier est le message que +vous aimeriez écrire dans le fichier de log. Par défaut, ce message +d'erreur est écrit dans le fichier de log "error", qui se trouve dans +app/tmp/logs/error.log. + +:: + + // Exécuter ceci dans une classe CakePHP : + + $this->log("Quelque chose ne marche pas !"); + + // Entraîne un ajout dans app/tmp/logs/error.log + + 2007-11-02 10:22:02 Error: Quelque chose ne marche pas ! + +Le second paramètre est utilisé pour définir le type de fichier de log +dans lequel vous souhaitez écrire le message. S'il n'est pas précisé, le +type par défaut est LOG\_ERROR, qui écrit dans le fichier de log "error" +mentionné précédemment. Vous pouvez définir ce second paramètre à +LOG\_DEBUG pour écrire vos messages dans un fichier de log alternatif +situé dans app/tmp/logs/debug.log : + +:: + + // Exécuter ceci dans une classe CakePHP : + + $this->log('Un message de débug', LOG_DEBUG); + + // Entraîne un ajout dans app/tmp/logs/debug.log (plutôt que dans error.log) + + 2007-11-02 10:22:02 Error: un message de débug. + +Vous pouvez aussi spécifier un nom différent pour le fichier de log, en +définissant le second paramètre avec le nom de ce fichier. + +:: + + // Exécuter ceci dans une classe CakePHP : + + $this->log("Un message spécial pour la journalisation de l'activité", 'activite'); + + // Entraîne un ajout dans app/tmp/logs/activite.log (plutôt que dans error.log) + + 2007-11-02 10:22:02 Activite: Un message spécial pour la journalisation de l’activité + +Votre dossier app/tmp doit être accessible en écriture par le serveur +web pour que la journalisation fonctionne correctement. diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..15230b8f7ebae4bbf407789bd34b9981aa23ec1b --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,369 @@ +Pagination +########## + +L'un des principaux obstacles dans la création d'une application web +flexible et facile à utiliser est le design d'une interface utilisateur +intuitive. Beaucoup d'applications ont rapidement tendance à grandir en +taille et en complexité, et les designers et les programmeurs pensent +qu'ils sont incapables de s'en sortir en montrant des centaines de +milliers d'enregistrements. La refactorisation prend du temps, et les +performances et la satisfaction de l'utilisateur peuvent en souffrir. + +Afficher un nombre raisonnable d'enregistrements par page a toujours été +une partie critique de toute application et a pour habitude de causer +beaucoup de maux de tête aux développeurs. CakePHP diminue +l'encombrement du développeur en fournissant une méthode rapide et +simple pour organiser les informations. Le "PaginatorHelper" offre une +excellente solution car il est simple à utiliser. Mis à part la +pagination, il comprend quelques méthodes de tri également très simple +d'utilisation. Le tri et la pagination avec Ajax est aussi supporté. + +Configuration du contrôleur +=========================== + +Dans le contrôleur, nous commençons par définir la pagination par défaut +dans la variable $paginate. Il est important de noter ici, que la clé +*order* doit être définie dans une structure en tableau. + +:: + + class RecettesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.titre' => 'asc' + ) + ); + } + +Vous pouvez aussi inclure d'autres options de find(), tel que *fields* : + +:: + + class RecettesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.titre' => 'asc' + ) + ); + } + +Les autres clés qui peuvent être incluses dans le tableau *$paginate* +sont similaires aux paramètres de la méthode *Model->find('all')*, +c'est-à-dire :*conditions*, *fields*, *order*, *limit*, *page*, +*contain* et *recursive*. En fait, vous pouvez définir plus d'une +configuration de pagination dans le contrôleur, vous nommez simplement +les parties du tableau, d'après le modèle que vous souhaitez configurer +: + +:: + + class RecettesController extends AppController { + + var $paginate = array( + 'Recette' => array (...), + 'Auteur' => array (...) + ); + } + +Exemple de syntaxe utilisant le Comportement Containable : + +:: + + class RecettesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +Une fois la variable $paginate définie, nous pouvons appeler la méthode +*paginate()* dans les actions du contrôleur. Cette méthode renvoie les +résultats paginés d'un *find()* provenant du modèle et récupère quelques +statistiques additionnelles sur la pagination, qui sont passées en +arrière-plan à la vue. Cette méthode ajoute aussi l'assistant Paginator +à la liste des assistants dans le contrôleur, s'il n'a pas déjà été +ajouté. + +:: + + function liste_recettes() { + // similaire à findAll(), mais récupère les résultats sous forme paginée + $data = $this->paginate('Recette'); + $this->set('data', $data); + } + +Vous pouvez filtrer les enregistrements en passant des conditions via le +second paramètre de la fonction ``paginate()`` : + +:: + + $data = $this->paginate('Recette', array('Recette.titre LIKE' => 'a%')); + +Ou vous pouvez aussi configurer la clé *conditions* dans le tableau +``$paginate`` + +Pagination dans les vues +======================== + +C'est à vous de décider comment afficher les enregistrements dans la +vue, la plupart du temps, ils le seront dans des tables HTML. Les +exemples suivants supposent une mise en page tabulaire, mais l'assistant +de pagination disponible dans les vues n'a pas toujours besoin d'être +construit de cette façon. + +Voyez les détails dans la section +`PaginatorHelper `_ de +l'API. + +Comme mentionné précédemment, l'assistant de pagination offre +différentes options de tri qui peuvent être facilement intégrées dans +les cellules d'en-tête de vos tableaux. + +:: + + // app/views/recettes/liste_recettes.ctp +
DateTitleActive
DateTitleActive
-attributes. + +:: + + tableCells(array( + array('Jul 7th, 2007', 'Best Brownies', 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', 'No'), + )); + ?> + + //Output +
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
RedApple
OrangeOrange
YellowBanana
+ + + + + + + + + + +
sort('ID', 'id'); ?>sort('Titre', 'titre'); ?>
+ +Le lien créé, provenant de la méthode sort() de l'assistant de +pagination, autorise l'utilisateur à cliquer sur les cellules d'en-tête +pour changer le tri des données par le champ cliqué. + +Il est aussi possible de trier une colonne basée sur les associations : + +:: + + + + + + + + + + + + +
sort('Titre', 'titre'); ?>sort('Auteur', 'Auteur.nom'); ?>
+ +La touche finale pour afficher la pagination dans les vues est +l'addition des pages, également fournie par l'assistant de pagination + +:: + + + numbers(); ?> + + prev('« Précédent ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Suivant »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +Le résultat de la méthode counter() peut-être personnalisé grâce à des +marqueurs spécifiques + +:: + + counter(array( + 'format' => 'Page %page% de %pages%, montrant %current% enregistrements sur un total de %count%, en commençant à %start% et se terminant à %end%' + )); + ?> + +Pour passer toute l'URL en paramètre de la fonction de pagination, +faites comme suit : + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +Pour faire passer des éléments en paramètre non-standard, vous devrez +les fusionner manuellement avec ``$this->passedArgs`` : + +:: + + // pour les urls comme http://www.exemple.com/fr/controller/action + // qui sont routées par : Router::connect('/:lang/:controller/:action/*', array(),array('lang'=>'fr|en')); + $paginator->options(array('url'=>array_merge(array('lang'=>$lang),$this->passedArgs))); + +Ou bien vous pouvez spécifier quels paramètres passer manuellement : + +:: + + $paginator->options(array('url' => array("0", "1"))); + +Pagination AJAX +=============== + +Il est très facile d'incorporer les fonctionalités AJAX dans la +pagination.Les seules parties de code à rajouter sont l'inclusion de la +librairie Javascript Prototype, charger les indicateurs (icône de +chargement dans une DIV) et spécifier une DIV pour qu'elle soit mise à +jour(au lieu de recharger complètement la page). + +N'oubliez pas d'ajouter le composant "RequestHandler" dans le contrôleur +pour pouvoir utiliser les fonctionalités AJAX: + +:: + + var $components = array('RequestHandler'); + +Modification de la mise en page +------------------------------- + +Premièrement, il faut inclure la librairie Prototype dans le header, +définir l'icône de chargement (spinner.gif), et définir notre DIV avec +le contenu principal qu'on appellera "content". + +Voici un exemple de mise en page avec ces différents éléments: + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +Modification des vues +--------------------- + +La seule configuration supplémentaire pour la pagination AJAX se fait en +utilisant la méthode options() de l'assistant de pagination, en lui +indiquant les paramètres AJAX requis. Dans ce cas, nous lui indiquons +que les liens de la pagination mettront à jour le contenu de la DIV +"content" avec le résultat de la requête, ainsi que d'afficher l'image +'spinner.gif' comme icône de chargement. + +Si la clé 'update', n'est pas spécifiée, l'assistant de pagination fera +une pagination non-AJAX. + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Previous', null, null, array('class' => 'disabled')); + + echo $paginator->next('Next >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +Requête de pagination personnalisée +=================================== + +Fix me: Please add an example where overriding paginate is justified + +Un bon exemple de cas où vous auriez besoin de ceci, c'est si la base de +données sous-jacente ne supporte pas la syntaxe SQL LIMIT. Ceci est vrai +pour DB2 d'IBM. Vous pouvez quand même utiliser la pagination CakePHP en +ajoutant la requête personnalisée au modèle. + +Si vous devez créer des requêtes personnalisées pour générer les données +que vous souhaitez paginer, vous pouvez surcharger les méthodes du +modèle ``paginate()`` et ``paginateCount()`` utilisées par la logique de +pagination du contrôleur. + +Avant de continuer, vérifiez que vous ne pouvez pas atteindre votre +objectif avec les méthodes du modèle de base. + +La méthode ``paginate()`` utilise les mêmes paramètres que +``Model::find()``. Pour utiliser votre propre méthode/logique, +surchargez-là dans le modèle souhaité pour obtenir les données. + +:: + + /** + * Surcharge de la méthode paginate() - grouper par semaine, equipe_exterieure_id et equipe_locale_id + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $recursive = -1; + $group = $fields = array('semaine', 'equipe_exterieure_id', 'equipe_locale_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')); + } + +Vous devez aussi surcharger le ``paginateCount()`` du cœur, cette +méthode attend les mêmes paramètres que ``Model::find('count')``. +L'exemple suivant utilise quelques fonctions spécifiques à Postgresql, +merci de bien vouloir l'ajuster en fonction de la base de données que +vous utilisez. + +:: + + /** + * Surcharge de la méthode paginateCount() + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(semaine, equipe_locale_id, equipe_exterieure_id) semaine, equipe_locale_id, equipe_exterieure_id FROM parties"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +Le lecteur attentif aura remarqué que la méthode de pagination que nous +avons définie n'était pas vraiment nécessaire. Tout ce que nous avons à +faire est d'ajouter le mot-clé dans la variable de classe ``$paginate`` +du contrôleur. + +:: + + /** + * Ajout de la clause GROUP BY + */ + var $paginate = array( + 'MonModele' => array('limit' => 20, + 'order' => array('semaine' => 'desc'), + 'group' => array('semaine', 'equipe_locale_id', 'equipe_exterieure_id')) + ); + + /** + * Ou bien à la volée, dans l'action du contrôleur + */ + function index() { + $this->paginate = array( + 'MonModele' => array('limit' => 20, + 'order' => array('semaine' => 'desc'), + 'group' => array('semaine', 'equipe_locale_id', 'equipe_exterieure_id')) + ); + +Cependant, il sera tout de même nécessaire de surcharger la méthode +``paginateCount()`` pour obtenir une valeur exacte. diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..6efae299cef4b863796265f64c74b4d83189a60c --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,190 @@ +REST +#### + +De nombreux programmeurs commencent à se rendre compte de la nécessité +de donner accès au coeur de leur application à un public plus large. +Fournir un accès direct et aisé au coeur de votre API peut aider votre +plateforme à être acceptée, permet la création d'applications composites +(*mashups*) et une intégration simple avec d'autres systèmes. + +Bien que d'autres solutions existent, REST est une bonne manière de +fournir un accès facile à la logique que vous avez créée dans votre +application. C'est simple, généralement basé sur du XML (nous parlons de +XML simple, rien de comparable à une enveloppe SOAP), et repose sur les +entêtes HTTP pour la définition des actions à effectuer. Exposer une API +via REST avec CakePHP est simple. + +Mise en place simple +==================== + +Le moyen le plus rapide pour démarrer avec REST est d'ajouter quelques +lignes à votre fichier routes.php, situé dans app/config. L'objet +Routeur (*Router*) comporte une méthode appelée mapResources() utilisée +pour mettre en place un certain nombre de routes par défaut accédant par +REST à vos contrôleurs. Si nous souhaitions permettre l'accès par REST à +une base de données de recettes, nous ferions comme cela : + +:: + + //Dans app/config/routes.php... + + Router::mapResources('recettes'); + Router::parseExtensions(); + +La première ligne met en place un certain nombre de routes par défaut +pour un accès facile par REST. Ces routes correspondent aux méthodes de +requêtes HTTP. + ++----------------+-----------------+-----------------------------------+ +| Méthode HTTP | URL | Action de contrôleur appelée | ++================+=================+===================================+ +| GET | /recettes | RecettesController::index() | ++----------------+-----------------+-----------------------------------+ +| GET | /recettes/123 | RecettesController::view(123) | ++----------------+-----------------+-----------------------------------+ +| POST | /recettes | RecettesController::add() | ++----------------+-----------------+-----------------------------------+ +| PUT | /recettes/123 | RecettesController::edit(123) | ++----------------+-----------------+-----------------------------------+ +| DELETE | /recettes/123 | RecettesController::delete(123) | ++----------------+-----------------+-----------------------------------+ +| POST | /recettes/123 | RecettesController::edit(123) | ++----------------+-----------------+-----------------------------------+ + +La classe Routeur (*Router*) de CakePHP utilise un certain nombre +d'indicateurs différents pour détecter la méthode HTTP utilisée. Les +voici par ordre de préférence : + +#. La variable POST *\_method* +#. Le X\_HTTP\_METHOD\_OVERRIDE +#. L'entête REQUEST\_METHOD + +La méthode POST *\_method* est utile lors de l'utilisation d'un +navigateur en tant que client REST (ou n'importe quoi d'autre capable de +faire facilement du POST). Il suffit d'initialiser la valeur de +*\_method* au nom de la méthode de requête HTTP que vous souhaitez +émuler. + +Une fois que le routeur est paramétré pour faire correspondre les +requêtes REST à certaines actions de contrôleur, nous pouvons nous +mettre à créer la logique dans nos actions de contrôleur. Un contrôleur +basique pourrait ressembler à ceci : + +:: + + // controllers/recettes_controller.php + + class RecettesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recettes = $this->Recette->find('all'); + $this->set(compact('recettes')); + } + + function view($id) { + $recette = $this->Recette->findById($id); + $this->set(compact('recette')); + } + + function edit($id) { + $this->Recette->id = $id; + if ($this->Recette->save($this->data)) { + $message = 'Sauvegardé'; + } else { + $message = 'Erreur'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recette->del($id)) { + $message = 'Supprimé'; + } else { + $message = 'Erreur'; + } + $this->set(compact("message")); + } + } + +Comme nous avons ajouté un appel à Router::parseExtensions(), le routeur +de CakePHP est déjà prêt à servir différentes vues selon différents +types de requêtes. Comme nous avons affaire à des requêtes REST, le type +de la vue est du XML. Nous plaçons les vues REST pour notre +RecettesController dans app/views/xml. Nous pouvons également utiliser +le XMLHelper pour faciliter l'affichage du XML dans ces vues. Voici ce à +quoi pourrait ressembler notre vue d'index : + +:: + + // app/views/recettes/xml/index.ctp + + + serialize($recettes); ?> + + +Les utilisateurs expérimentés de CakePHP auront peut-être remarqué que +nous n'avons pas inclus le XMLHelper dans le tableau $helpers de notre +RecettesController. C'est fait exprès — lorsque nous servons un type de +contenu spécifique grâce à parseExtensions(), CakePHP recherche +automatiquement un Assistant (*Helper*) correspondant au type. Comme +nous utilisons XML comme type de contenu, le XMLHelper est +automatiquement chargé pour être utilisé dans ces vues. + +Le XML généré ressemblera à quelque chose comme ceci : + +:: + + +
+ + +
+
+ + +
+
+ +Créer la logique pour l'action *edit* est un plus complexe, mais de peu. +Comme vous fournissez une API renvoyant du XML, c'est un choix naturel +que de choisir du XML comme format d'entrée. Pas d'inquiétude cependant +: les classes RequestHandler et Router facilitent grandement les choses. +Si une requête POST ou PUT a un type de contenu XML, alors les données +d'entrée sont passées à une instance de l'objet XML de Cake qui est +assigné à la propriété $data du contrôleur. Grâce à cette +fonctionnalité, manipuler des données XML et POST en parallèle est +transparent : aucun changement n'est nécessaire dans le code du +contrôleur ou du modèle. Tout ce dont vous avez besoin devrait être +retrouvé dans $this->data. + +Routage REST personnalisé +========================= + +Si les routes par défaut créées par mapResources() ne vous conviennent +pas, utilisez la méthode Router::connect() pour définir un ensemble +personnalisé de routes REST. La méthode connect() vous permet de définir +un certain nombre d'options pour une URL donnée. Le premier paramètre +est l'URL elle-même, le deuxième vous permet de fournir ces options. Le +troisième paramètre vous permet de spécifier des motifs (*patterns*) +d'expressions régulières pour aider CakePHP à identifier certains +marqueurs dans l'URL spécifiée. + +Nous allons fournir ici un exemple simple et vous permettre d'adpter +cette route pour vos autres actions *RESTful*. Voici ce à quoi +ressemblerait notre route REST *edit*, sans utiliser mapResources() : + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9+]") + ) + +Les techniques avancées de routage sont décrites ailleurs, nous allons +donc nous concentrer sur le point le plus important pour notre but ici : +la clé *method* du tableau options dans le deuxième paramètre. Une fois +cette clé affectée, la route spécifiée fonctionne uniquement pour cette +méthode de requête HTTP (qui pourrait également être GET, DELETE, etc). diff --git a/fr/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/fr/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..7f6fc6299f31ecb27ae2ee764e9b656f3f8fc8a8 --- /dev/null +++ b/fr/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,1114 @@ +Tester +###### + +A partir de la version 1.2, CakePHP inclus le support d'un framework de +test global. Ce framework est une extension du framework SimpleTest pour +PHP. Cette section exposera comment préparer, construire et exécuter vos +tests. + +Préparation aux tests +===================== + +Prêt à commencer les tests ? Bien ! Alors allons-y ! + +Installer SimpleTest +-------------------- + +Le framework de tests fourni avec CakePHP 1.2 est basé sur le framework +de tests SimpleTest. SimpleTest n'est pas livré avec l'installation par +défaut de CakePHP, donc nous avons d'abord besoin de le télécharger. +Vous pouvez le trouver ici : +`http://simpletest.sourceforge.net/ `_. + +Récupérez la dernière version et décompressez le code dans votre dossier +cake/vendors ou votre dossier app/vendors, comme vous préférez. Vous +devriez maintenant avoir un répertoire vendors/simpletest avec tous les +fichiers et dossiers de SimpleTest à l'intérieur. Pensez à mettre le +niveau de DEBUG à 1 au moins dans votre fichier app/config/core.php +avant de lancer vos tests ! + +Si vous n'avez pas défini de connexion à une base de données de test +dans votre fichier app/config/database.php, les tables de test seront +créées avec le préfixe ``test_suite_``. Vous pouvez créer une connexion +à la base de données ``$test`` comme celle présentée ci-dessous, pour +contenir toutes les tables de test : + +:: + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'serveur', + 'login' => 'identifiant', + 'password' => 'mot_passe', + 'database' => 'nomBase' + ); + +Si la base de données test est disponible et que CakePHP peut s'y +connecter, toutes les tables y seront créées. + +Lancer les cas de tests du cœur +------------------------------- + +Les différents packages de CakePHP 1.2 ne sont pas livrés avec les cas +de test du cœur. Pour obtenir ces tests, vous devez les télécharger +depuis le dépôt. Toutes les versions de CakePHP sont actuellement +situées sur le site +`http://code.cakephp.org/ `_. Vous aurez +besoin de vous créer un compte utilisateur avec une clé personnalisée et +utiliser Git pour accéder au dépôt. + +Pour ajouter les tests du cœur à votre application existante, +décompressez le package *nightly* téléchargé dans un répertoire +temporaire. Localisez le répertoire ``/cake/tests`` sur le dépôt et +copiez-le (récursivement) dans votre dossier ``/cake/tests``. + +Ces tests peuvent alors être atteints en naviguant à l'adresse +http://votre.domaine.cake/test.php - qui dépend de vos paramétrages +spécifiques. Essayez d'exécuter l'un des groupes de test du cœur en +cliquant sur le lien correspondant. L'exécution d'un groupe de tests +peut prendre un certain temps, mais vous devriez finir par voir quelque +chose comme : "2/2 test cases complete: 49 passes, 0 fails and 0 +exceptions". + +Félicitations, vous êtes maintenant prêt à commencer l'écriture de tests +! + +Si vous lancez tous les tests du cœur ensemble ou les groupes de tests, +la plupart échoueront. Ceci est connu des développeurs CakePHP et +normal, donc pas de panique. Essayez plutôt de lancer chacun des cas de +test du cœur individuellement. + +Vue d'ensemble du Test - Test unitaire vs Test Web +================================================== + +Le framework de test CakePHP propose 2 façons de tester. L'une est le +Test Unitaire, où vous testez de petites parties de votre code, comme +une méthode dans un composant ou une action dans un contrôleur. L'autre +type de test supporté est le Test Web, où vous automatisez le travail de +test de votre application, à travers la navigation entre pages, le +remplissage de formulaires, le clic sur des liens, etc. + +Préparation des données de test +=============================== + +A propos des fixtures +--------------------- + +Quand on test du code qui dépend des modèles et des données, on peut +utiliser les **fixtures** (garnitures) comme moyen de générer +temporairement des tables, remplies avec des échantillons de données qui +peuvent être utilisés par le test. Le bénéfice apporté par l'utilisation +des fixtures, c'est que votre test n'a aucune chance de corrompre les +données de l'application en production. En plus, vous pouvez commencer à +tester votre code, avant de développer le contenu réel d'une +application. + +CakePHP tente d'utiliser la connexion nommée ``$test`` dans votre +fichier de configuration app/config/database.php. Si cette connexion +n'est pas utilisable, il utilisera la configuration de base de données +``$default`` et créera les tables de test dans la base de données +définie par cette configuration. Dans les deux cas, il ajoutera +"test\_suite\_" à vos propres préfixes de table (s'il y en a) pour +éviter des conflits avec vos tables existantes. + +CakePHP réalise ce qui suit durant l'utilisation d'un cas de test basé +sur une fixture : + +#. Créer les tables nécessaires à chaque fixture +#. Remplir les tables avec des données, si les données sont fournies + dans la fixture +#. Lancer les méthodes de test +#. Vider les tables de fixtures +#. Supprimer les tables de fixture de la base de données + +Créer des fixtures +------------------ + +En créant une *fixture*, vous définirez principalement deux choses : +comment la table est composée (quels champs font partie de la table) et +quels enregistrements seront initialement remplis dans la table de test. +Créons notre première *fixture*, qui sera utilisée pour tester notre +propre modèle Article. Créez un fichier nommé **article\_fixture.php** +dans votre répertoire **app/tests/fixtures**, avec le contenu suivant : + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'titre' => array('type' => 'string', 'length' => 255, 'null' => false), + 'contenu' => 'text', + 'publiable' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'titre' => 'Premier Article', 'contenu' => 'Corps du premier Article', 'publiable' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'titre' => 'Second Article', 'contenu' => 'Corps du second Article', 'publiable' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'titre' => 'Troisième Article', 'contenu' => 'Corps du troisième Article', 'publiable' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'), + ); + } + ?> + +La variable ``$name`` est extrêmement importante. Si vous l'omettez, +cake utilisera un mauvais nom de table lorsqu'il génèrera votre base de +donnée de test, et vous aurez alors d'étranges erreurs qui seront +difficiles à débugger. Si vous utilisez PHP 5.2, vous pouvez utiliser +des classes de modèle sans ``$name``, mais vous devez penser à l'inclure +dans vos fichiers *fixture*. + +Nous utilisons $fields pour spécifier quels champs feront partie de +cette table et comment seront-ils définis. Le format utilisé pour +définir ces champs est le même que celui utilisé dans la fonction +**generateColumnSchema()** définie dans les classes de moteur de base de +données de Cake (par exemple, dans le fichier dbo\_mysql.php.) Voyons +les attributs disponibles qu'un champ peut prendre et leur signification +: + +type + Type interne de donnée CakePHP. Supportés actuellement : string + (correspond à VARCHAR), text (correspond à TEXT), integer + (correspond à INT), float (correspond à FLOAT), datetime (correspond + à DATETIME), timestamp (correspond à TIMESTAMP), time (correspond à + TIME), date (correspond à DATE) et binary (correspond à BLOB) +key + définir à *primary*\ pour rendre le champ AUTO\_INCREMENT et PRIMARY + KEY de la table. +length + définir à la longueur spécifique que le champ devrait prendre. +null + définir soit à *true*\ (pour autoriser les champs NULLs) ou à + *false* (pour les interdire) +default + valeur par défaut que le champ doit prendre. + +Nous pouvons enfin définir un ensemble d'enregistrements qui seront +remplis après que la table de test soit créée. Le format est moyennement +explicite et nécessite un peu plus d'explication. Gardez juste à +l'esprit que chaque enregistrement dans le tableau $records doit avoir +une clé pour **chaque** champ spécifié dans le tableau $fields. Si un +champ pour un enregistrement particulier nécessite d'avoir une valeur +NULL, spécifiez simplement la valeur de cette clé à NULL. + +Importer les informations de la table et des enregistrements +------------------------------------------------------------ + +Votre application peut avoir déjà fait travailler les modèles avec de +vraies données associées et vous pourriez décider de tester votre modèle +avec ces données. Il pourrait être alors redondant, d'avoir à définir la +structure de la table et/ou les enregistrements dans vos fixtures. +Heureusement, il y a une manière pour vous de définir cette structure de +table et/ou les enregistrements pour une fixture particulière provenant +d'un modèle existant ou d'une table existante. + Commençons par un exemple. Considérant que vous avez un modèle nommé +Article disponible dans votre application (qui correspond à la table +nommée articles), changez la fixture donnée en exemple dans la section +précédente (**app/tests/fixtures/article\_fixture.php**) de cette façon +: + +:: + + + + +Cette déclaration indique à la suite de tests d'importer la définition +de votre table liée au modèle nommé Article. Vous pouvez utiliser tout +modèle disponible dans votre application. La déclaration ci-dessus +n'importe pas d'enregistrements, vous pouvez donc le faire en la +modifiant ainsi : + +:: + + class ArticleFixture extends CakeTestFixture { + var $name = 'Article'; + var $import = array('model' => 'Article', 'records' => true); + } + ?> + +Si, au contraire, vous avez une table créée mais pas de modèle +disponible pour elle, vous pouvez préciser que votre import s'effectuera +en lisant cette information de table à la place. Par exemple : + +:: + + 'articles'); + } + ?> + +Importera la définition de table appelée 'articles' en utilisant votre +connexion CakePHP à la base de données nommée 'default'. Si vous voulez +changer la connexion à utiliser, faites simplement : + +:: + + 'articles', 'connection' => 'autre'); + } + ?> + +Puisqu'elle utilise votre connexion CakePHP à la base de données, s'il y +a un quelconque préfixe de table déclaré, il sera automatiquement utlisé +quand vous récupérerez les informations de la table. Les deux fragments +ci-dessus n'importent pas les enregistrements de la table. Pour forcer +la fixture à importer aussi ses enregistrements, changez-la en : + +:: + + 'articles', 'records' => true); + } + ?> + +Vous pouvez naturellement importer votre structure de table depuis une +table/un modèle existant, mais avoir défini vos enregistrements +directement dans la fixture, comme ce fut montré dans la section +précédente. Par exemple : + +:: + + 1, 'titre' => 'Premier Article', 'contenu' => 'Corps du premier Article', 'publiable' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'titre' => 'Second Article', 'contenu' => 'Corps du second Article', 'publiable' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'titre' => 'Troisième Article', 'contenu' => 'Corps du troisième Article', 'publiable' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'), + ); + } + ?> + +Créer des tests +=============== + +D'abord, revoyons un certain nombre de règles ou directives concernant +les tests : + +#. Les fichiers PHP contenant des tests devraient être dans votre + dossier : **app/tests/cases/[un\_dossier]**. +#. Les noms de ces fichiers devraient se terminer en **.test.php** + plutôt que simplement .php. +#. Les classes contenant les tests devraient étendre **CakeTestCase** ou + **CakeWebTestCase**. +#. Le nom de toute méthode contenant un test (par ex. contenant une + assertion) devrait commencer par **test**, comme dans + **testPublished()**. + +Quand vous avez créé un cas de test, vous pouvez l'exécuter en naviguant +à l'adresse **http://votre.domaine.cake/dossier\_cake/test.php** (ceci +dépend de vos réglages spécifiques) et cliquer les cas de test de l'App, +puis cliquer le lien vers votre fichier spéficique. + +CakeTestCase Méthodes Callback +------------------------------ + +Si vous voulez effectuer quelques opérations juste avant ou après un +CakeTestCase individuel, et/ou avant ou après le CakeTestCase entiern +les méthodes "callbacks" suivants sont disponibles : + +**start()** + Première méthode appelée dans un *test case*. + +**end()** + Dernière méthode appelée dans un *test case*. + +**startCase()** + Appelée avant le démarrage d'un *test case*. + +**endCase()** + Appelée après la fin d'un *test case*. + +**before($methode)** + Annonce le démarrage de la méthode "$methode" du *test case* en cours. + +**after($methode)** + Annonce la fin de la méthode "$methode" du *test case* en cours. + +**startTest($methode)** + Appelée juste avant le démarrage de la méthode "$methode" du *test +case* en cours. + +**endTest($methode)** + Appelée juste après la fin de la méthode "$methode" du *test case* en +cours. + +Tester les modèles +================== + +Créer un cas de test +-------------------- + +Disons que nous avons déjà notre modèle Article défini dans +app/models/article.php, lequel ressemble à ceci : + +:: + + name . '.publiable' => 1 + ); + + return $this->find('all',array( + 'conditions'=>$conditions, + 'fields'=>$champs + )); + } + + } + ?> + +Nous voulons maintenant définir un test qui utilisera cette définition +de modèle, mais à travers des *fixtures*, pour tester quelques +fonctionnalités du modèle. La suite de test CakePHP charge un tout petit +ensemble de fichiers (pour garder les tests isolés), ainsi nous +commençons par charger notre modèle parent (dans ce cas le modèle +Article que nous avons déjà défini), puis par informer la suite de test +que nous voulons tester ce modèle, en précisant quelle configuration de +bases de données elle devrait utiliser. La suite de test CakePHP fournit +une configuration de BDD nommée **test\_suite** qui est utilisée pour +tous les modèles reliés aux *fixtures*. Définir $useDbConfig pour cette +configuration indiquera à CakePHP que ce modèle utilise la connexion à +la base de données de la suite de test. + +Les modèles CakePHP utiliseront seulement la configuration de BDD +test\_suite s'ils sont reliés à des *fixtures* dans votre cas de test ! + +Puisque nous voulons également réutiliser tout le code existant de notre +modèle, nous allons créer un test de modèle qui étendra Article et +définir $useDbConfig et $name judicieusement. Créons maintenant un +fichier nommé **article.test.php** dans votre répertoire +**app/tests/cases/models**, avec le contenu suivant : + +:: + + + +Nous avons créé le cas de test ArticleTestCase. Dans la variable +**$fixtures** nous définissons un ensemble de *fixtures* que nous +utiliserons. + +Si votre modèle est associé avec d'autres modèles, vous devrez inclure +TOUTES les *fixtures* pour chaque modèle associé, même si vous ne les +utilisez pas. Par exemple : A hasMany B hasMany C hasMany D. Dans +ATestCase vous devrez inclure les *fixtures* pour a, b, c et d. + +Créer une méthode de test +------------------------- + +Ajoutons maintenant une méthode pour tester la fonction publiable() du +modèle Article. Editer le fichier +**app/tests/cases/models/article.test.php** afin qu'il ressemble +désormais à ceci : + +:: + + Article =& ClassRegistry::init('Article'); + + $resultat = $this->Article->publiable(array('id', 'titre')); + $attendus = array( + array('Article' => array( 'id' => 1, 'titre' => 'Premier Article' )), + array('Article' => array( 'id' => 2, 'titre' => 'Second Article' )), + array('Article' => array( 'id' => 3, 'titre' => 'Troisième Article' )), + ); + + $this->assertEqual($resultat, $attendus); + } + } + ?> + +Vous pouvez voir que nous avons ajouté une méthode appelée +**testPubliable()**. Nous commençons par créer une instance de notre +*fixture* basée sur le modèle **Article**, puis nous lançons notre +méthode **publiable()**. Dans **$attendus**, nous définissons ce que +nous estimons comme devant être le résultat correct (ce que nous savons, +puisque nous avons défini quels enregistrements sont initialement +remplis dans la table article). Nous vérifions que le résultat est égal +à notre prévision, en utilisant la méthode **assertEqual**. Voyez la +section Créer des Tests pour plus d'information sur la manière de lancer +le test. + +Tester les contrôleurs +====================== + +Créer un cas de test +-------------------- + +Imaginons que vous ayez un contrôleur typique articles, avec son modèle +correspondant et qui ressemble à ceci : + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $resultat = $this->Article->findAll(null, array('id', 'titre')); + } else { + $resultat = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $resultat; + } + + $this->set('titre', 'Articles'); + $this->set('articles', $resultat); + } + } + ?> + +Créez un fichier nommé articles\_controller.test.php dans votre +répertoire app/tests/cases/controllers et mettez-y ce qui suit : + +:: + + Démarrage du Cas de Test'; + } + function endCase() { + echo '

Fin du Cas de Test

'; + } + function startTest($methode) { + echo '

Début de la méthode ' . $methode . '

'; + } + function endTest($methode) { + echo '
'; + } + function testIndex() { + $resultat = $this->testAction('/articles/index'); + debug($resultat); + } + function testIndexShort() { + $resultat = $this->testAction('/articles/index/short'); + debug($resultat); + } + function testIndexShortGetRenderedHtml() { + $resultat = $this->testAction('/articles/index/short', array('return' => 'render')); + debug(htmlentities($resultat)); + } + function testIndexShortGetViewVars() { + $resultat = $this->testAction('/articles/index/short', array('return' => 'vars')); + debug($resultat); + } + function testIndexFixturized() { + $resultat = $this->testAction('/articles/index/short', array('fixturize' => true)); + debug($resultat); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'publiable' => 1, 'slug'=>'nouvel-article', 'titre' => 'Nouvel Article', 'contenu' => 'Nouveau Contenu')); + $resultat = $this->testAction('/articles/index', array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($resultat); + } + } + ?> + +La méthode testAction +--------------------- + +La chose nouvelle ici, c'est la méthode **testAction**. Le premier +argument de cette méthode est l'url Cake de l'action du contrôleur à +tester, comme dans '/articles/index/short'. + +Le second argument est un tableau de paramètres, composé de : + +return + Définir à la valeur que vous voulez retourner. + Les valeurs possibles sont : + + - 'vars' - Vous obtenez les variables de la vue disponible après + l'éxécution de l'action + - 'view' - Vous obtenez la vue générée, sans le layout + - 'contents' - Vous obtenez la vue HTML complète, incluant le + layout + - 'result' - Vous obtenez la valeur retournée quand l'action + utilise $this->params['requested']. + + Par défault c'est 'result'. +fixturize + Définir à vrai si vous voulez que vos modèles soient auto-fixturisés + (ainsi les tables de votre application seront copiées, avec leurs + enregistrements, pour les tester sans affecter votre application + réelle en cas de modification des données). Si vous définissez + 'fixturize' comme un tableau de modèles, alors seuls ces modèles + seront auto-fixturisés, tandis que les autres conserveront leurs + vraies tables. Si vous souhaitez utiliser vos fichiers de fixture + avec testAction(), n'utilisez pas fixturize, mais plutôt les + fixtures comme vous l'auriez fait normalement. +method + définir à 'post' ou 'get' si vous voulez passez des données au + contrôleur +data + les données à passer. A définir comme un tableau associatif composé + de champs => valeur. Jetez un oeil à + ``function testIndexPostFixturized()`` dans le cas de test plus + haut, pour voir comment nous émulons le postage des données d'un + formulaire, lors de la soumission d'un nouvel article. + +Pièges +------ + +Si vous utilisez testAction pour tester une méthode de l'un de vos +contrôleurs qui fait une redirection, votre test se terminera +immédiatement, sans retourner aucun résultat. + Voyez +`https://trac.cakephp.org/ticket/4154 `_ +pour une possible correction. + +Tester les Assistants +===================== + +Puisqu'un pourcentage respectable de la logique réside dans les classes +Assistant (*Helper*), il est important de s'assurer que ces classes sont +couvertes par les cas de test. + +Le test d'Assistant est un brin similaire à l'approche utilisée pour les +Composants. Supposez que nous ayons un assistant appelé +InterpreteurDeMonnaieHelper situé dans +``app/views/helpers/interpreteur_de_monnaie_helper.php`` accompagné de +son fichier de cas de test situé dans +``app/tests/cases/helpers/interpreteur_de_monnaie_helper.test.php`` + +Créer un test d'Assistant, 1ère partie +-------------------------------------- + +Pour commencer, nous définirons les responsabilités de notre assistant +DevisesFormateurHelper. En gros, il aura deux méthodes, juste pour les +besoins de la démonstration : + +function dollar($montant) + +Cette fonction recevra le montant à formater. Celui-ci prendra 2 +décimales, avec les espaces manquants remplis par des zéros et le +préfixe 'USD' ajouté. + +function euro($montant) + +Cette fonction fera la même chose que dollar() mais préfixera le montant +retourné avec 'EUR'. Juste pour rendre le tout un peu plus complexe, +nous allons aussi entourer le résultat par des balises span : + +:: + + + +Créons d'abord les tests : + +:: + + devisesFormateur = new DevisesFormateurHelper(); + } + + //test de la fonction dollar(). + public function testDollar() { + $this->assertEqual('USD 5,30', $this->devisesFormateur->dollar(5,30)); + //On devrait toujours avoir deux chiffres après la virgule. + $this->assertEqual('USD 1,00', $this->devisesFormateur->dollar(1)); + $this->assertEqual('USD 2,05', $this->devisesFormateur->dollar(2,05)); + //Test du séparateur de milliers + $this->assertEqual('USD 12 000,70', $this->devisesFormateur->dollar(12 000,70)); + } + } + +Ici, nous appelons ``dollar()`` avec différents paramètres et nous +demandons à la suite de test de vérifier si les valeurs retournées sont +égales à ce qui est attendu. + +Exécuter le test maintenant provoquera des erreurs (parce que +DevisesFormateurHelper n'existe même pas) montrant que nous avons 3 +échecs. + +Une fois que nous savons ce que notre méthode devrait faire, nous +pouvons écrire la méthode elle-même : + +:: + + `_, nous avons besoin d'un contrôleur +pour accéder aux données dans le modèle. + +Si la fonction startup() du composant ressemble à ceci : + +:: + + public function startup(&$controller){ + $this->Transporteur = $controller->Transporteur; + } + +alors nous pouvons simplement définir une fausse classe vraiment toute +simple : + +:: + + class FauxTransporteurController {} + +et lui assigner des valeurs comme çà : + +:: + + $this->TransporteurComponentTest = new TransporteurComponent(); + $controller = new FauxTransporteurController(); + $controller->Transporteur = new TransporteurTest(); + $this->TransporteurComponentTest->startup(&$controller); + +Créer une méthode de test +------------------------- + +Créez simplement une classe qui étende CakeTestCase et commencez à +écrire des tests ! + +:: + + class TransporteurTestCase extends CakeTestCase { + var $fixtures = array('transporteur'); + function testGetTransporteur() { + $this->TransporteurComponentTest = new TransporteurComponent(); + $controller = new FauxTransporteurController(); + $controller->Transporteur = new TransporteurTest(); + $this->TransporteurComponentTest->startup(&$controller); + + $resultat = $this->TransporteurComponentTest->getTransporteur("12345", "Suéde", "54321", "Suède"); + $this->assertEqual($resultat, 1, "SP est meilleur pour 1xxxx-5xxxx"); + + $resultat = $this->TransporteurComponentTest->getTransporteur("41234", "Suéde", "44321", "Suède"); + $this->assertEqual($resultat, 2, "WSTS est meilleur pour 41xxx-44xxx"); + + $resultat = $this->TransporteurComponentTest->getTransporteur("41001", "Suéde", "41870", "Suède"); + $this->assertEqual($resultat, 3, "GL est meilleur pour 410xx-419xx"); + + $resultat = $this->TransporteurComponentTest->getTransporteur("12345", "Suéde", "54321", "Norvège"); + $this->assertEqual($resultat, 0, "Aucun ne peut desservir la Norvège"); + } + } + + +Test Web - Tester les vues +========================== + +La plupart du temps, si ce n'est toujours, les projets CakePHP sont des +applications web. Tandis que les tests unitaires sont un excellent moyen +de tester de petites parties d'une fonctionnalité, vous pourriez aussi +vouloir tester la fonctionnalité à plus large échelle. La classe +**CakeWebTest**\ Case offre une bonne méthode pour réaliser ce test du +point de vue de l'utilisateur. + +A propos de CakeWebTestCase +--------------------------- + +**CakeWebTestCase** est une extension directe du SimpleTest WebTestCase, +sans aucune autre fonctionnalité. Toutes les fonctionnalités trouvées +dans `SimpleTest, documentation pour le test +Web `_ +est également disponible ici. Cela veut dire aussi, qu'aucune autre +fonctionnalité que celles de SimpleTest n'est disponible. Cela veut dire +que vous ne pouvez pas utiliser les fixtures et que **tous les cas de +test web réclamant des mises à jour/sauvegardes dans la base de données, +changeront de manière permanente les valeurs de votre base de données**. +Les résultats de test sont souvent basés sur les valeurs que contient la +base de données, donc s'assurer que la base contient des valeurs que +vous attendez fait partie de la procédure de test. + +Créer un test +------------- + +Pour être en conformité avec les autres conventions de test, vous +devriez créer vos tests de vue dans tests/cases/views. Vous pouvez, bien +sûr, mettre ces tests n'importe où, mais suivre les conventions chaque +fois que c'est possible est toujours une bonne idée. Créons le fichier +tests/cases/views/web\_complet.test.php + +D'abord, lorsque vous voulez écrire des tests web, vous devez penser à +étendre **CakeWebTestCase** plutôt que CakeTestCase : + +:: + + class WebCompletTestCase extends CakeWebTestCase + +Si vous avez besoin de faire quelques préparatifs avant que vous ne +commenciez le test, créez un constructeur : + +:: + + function WebCompletTestCase(){ + //Faire des trucs ici + } + +En écrivant les vrais cas de test, la première chose que vous devez +faire est d'obtenir quelque chose à regarder. Cela peut se faire en +réalisant une requête **get** ou **post**, en utilisant respectivement +**get()**\ ou **post()**. Ces deux méthodes prennent une url absolue +comme premier paramètre. Ceci peut être récupéré dynamiquement, si nous +supposons que le script de test est situé sous +http://votre.domaine/cake/folder/webroot/test.php, en tapant : + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +Vous pouvez alors faire des gets et des posts en utilisant les urls +Cake, comme ceci : + +:: + + $this->get($this->baseurl."/produits/index/"); + $this->post($this->baseurl."/client/login", $data); + +Le second paramètre de la méthode post , **$data**, est un tableau +associatif contenant les données postées au format Cake : + +:: + + $data = array( + "data[Client][mail]" => "utilisateur@utilisateur.com", + "data[Client][mot_passe]" => "passeutilisateur"); + +Quand vous avez requêté la page, vous pouvez faire toutes sortes +d'actions dessus, en utilisant les méthodes standard de test web de +SimpleTest. + +Parcourir une page +------------------ + +CakeWebTest vous donne aussi une option pour naviguer à travers votre +page, en cliquant les liens ou les images, en remplissant des +formulaires et en cliquant les boutons. Merci de vous référer à la +documentation SimpleTest pour plus d'information sur ce sujet. + +Tester les plugins +================== + +Les tests pour plugins sont créés dans leur propre répertoire, à +l'intérieur du dossier plugins. + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +Ils fonctionnent tout simplement comme des tests normaux, mais vous +devez penser à utiliser les conventions de nommage pour les plugins lors +de l'import des classes. Ceci est un exemple de cas de test pour le +modèle CommandePizza vu au chapitre plugins de ce manuel. Une différence +par rapport aux autres tests se trouve à la première ligne où +'Pizza.CommandePizza' est importée. Vous avez aussi besoin de préfixer +les fixtures de votre plugin avec '``plugin.nom_plugin``\ '. + +:: + + CommandePizzaTest =& ClassRegistry::init('CommandePizza'); + + // effectuer quelque test utile ici + $this->assertTrue(is_object($this->CommandePizzaTest)); + } + } + ?> + +Si vous voulez utiliser les fixtures de plugin dans les tests de votre +application, vous pouvez les référencer en utilisant la syntaxe +'plugin.nomPlugin.nomFixture' dans le tableau $fixtures. + +C'est tout ce qu'il y a à dire. + +Divers +====== + +Customiser le reporter de test +------------------------------ + +Le reporter de test standard est **très** minimaliste. Si vous voulez +une sortie plus reluisante pour épater quelqu'un, ne vous inquiétez pas, +il est très simple à étendre en fait. + Le seul danger c'est que vous devez bidouiller le code du cœur de Cake, +plus particulièrement **/cake/tests/libs/cake\_reporter.php**. + +Pour changer la sortie test, vous pouvez surcharger les méthodes +suivantes : + +paintHeader() + S'affiche avant le début du test. +paintPass() + S'affiche chaque fois qu'un cas de test est réussi. Utilisez + $this->getTestList() pour obtenir un tableau d'informations + afférentes au test et $message pour obtenir le résultat du test. + Pensez à appeler parent::paintPass($message). +paintFail() + S'affiche chaque fois qu'un cas de test a échoué. Pensez à appeler + parent::paintFail($message). +paintFooter() + S'affiche quand le test est fini, i.e. quand tous les cas de tests + ont été exécutées. + +Si, quand paintPass et paintFail s'exécutent, vous voulez masquer la +sortie parente, entourez l'appel par des balises de commentaires HTML, +comme çà : + +:: + + echo "\n\n"; + +Voici un exemple de configuration de **cake\_reporter.php** qui crée une +table pour présenter les résultats du test : + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
Rés.Cas de TestMessage
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " cas de test terminés :\n"; + print "" . $this->getPassCount() . " réussites, "; + print "" . $this->getFailCount() . " échecs et "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tRéussi: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\tRaté: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +Grouper les tests +----------------- + +Si vous voulez faire fonctionner plusieurs de vos tests en même temps, +vous pouvez essayer de créer un groupe de test. Créez un fichier dans +**/app/tests/groups/** et nommez-le de cette façon +**nom\_votre\_groupe\_test.group.php**. Dans ce fichier, créez une +classe qui étend **GroupTest** et importez les tests comme suit : + +:: + + + +Le code ci-dessus groupera tout les cas de tests trouvés dans le dossier +**/app/tests/cases/models/**. Pour ajouter un fichier individuel, +utilisez **TestManager::addTestFile**\ ($this, nom\_fichier). + +Lancer les tests depuis la ligne de commande +============================================ + +Si vous avez installé simpletest, vous pouvez lancer vos tests depuis la +ligne de commande de votre application. + +Depuis **app/**, exécutez : + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +Comme le suggère le menu help, vous serez en mesure de lancer tous vos +tests, une partie ou un seul cas de test depuis votre app, votre plugin +ou le cœur, simplement depuis la ligne de commande. + +Si vous avez un modèle de test dans +**test/models/mon\_modele.test.php**, vous lancerez simplement ce cas de +test en exécutant : + +:: + + cake testsuite app models/mon_modele + diff --git a/fr/The-Manual/Core-Behaviors.rst b/fr/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..7bbfb36606cf3850fd2b971fbb9805728cbd2dd1 --- /dev/null +++ b/fr/The-Manual/Core-Behaviors.rst @@ -0,0 +1,15 @@ +Comportements intégrés +###################### + +Les Comportements (*Behaviors*) ajoutent des fonctionnalités +supplémentaires à vos modèles. CakePHP est livré avec un certain nombre +de comportements intégrés tels que : *Tree* et *Containable*. + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/fr/The-Manual/Core-Behaviors/ACL.rst b/fr/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..0a8e9be1782ea1eeb6f84b716843d43c32898ffe --- /dev/null +++ b/fr/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,106 @@ +Listes de Contrôle d'Accès (ACL) +################################ + +Le comportement Acl fournit une solution pour intégrer sans souci un +modèle dans votre système ACL. Il peut créer à la fois les AROs et les +ACOs de manière transparente. + +Pour utiliser le nouveau comportement, vous pouvez l'ajouter à la +propriété $actsAs de votre modèle. Quand vous l'ajoutez au tableau +actsAs, vous choisissez de créer l'entrée Acl correspondante comme un +ARO ou un ACO. Par défaut cela crée des AROs. + +:: + + class Utilisateur extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +Ceci attacherait le comportement Acl en mode ARO. Pour joindre le +comportement ACL dans un mode ACO, utilisez : + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +Vous pouvez aussi attacher le comportement à la volée, comme ceci : + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +Utiliser le Comportement Acl +============================ + +La plupart des tâches du comportement Acl sont réalisées de façon +transparente, dans le callback afterSave() de votre modèle. Cependant, +son utilisation nécessite que votre Modèle ait une méthode parentNode() +définie. Ceci est utilisé par le comportement Acl, pour déterminer les +relations parent->enfant. Une méthode parentNode() de modèle doit +retourner null ou une référence au Modèle parent. + +:: + + function parentNode() { + return null; + } + +Si vous voulez définir un nœud ACO ou ARO comme parent pour votre +Modèle, parentNode() doit retourner l'alias du nœud ACO ou ARO. + +:: + + function parentNode() { + return 'noeud_racine'; + } + +Voici un exemple plus complet. Utilisons un modèle exemple Utilisateur, +avec Utilisateur belongsTo Groupe. + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['Utilisateur']['groupe_id']) { + return null; + } else { + $this->Groupe->id = $data['Utilisateur']['groupe_id']; + $noeudGroupe = $this->Groupe->node(); + return array('Groupe' => array('id' => $noeudGroupe[0]['Aro']['foreign_key'])); + } + } + +Dans l'exemple ci-dessus, le retour est un tableau qui ressemble aux +résultats d'un find de modèle. Il est important d'avoir une valeur d'id +définie ou bien la relation parentNode échouera. Le comportement Acl +utilise ces données pour construire sa structure en arbre. + +node() +====== + +Le Comportement Acl vous permet aussi de récupérer le nœud Acl associé à +un enregistrement de modèle. Après avoir défini $model->id. Vous pouvez +utiliser $model->node() pour récupérer le nœud Acl associé. + +Vous pouvez aussi récupérer le nœud Acl de n'importe quelle ligne, en +passant un tableau de données en paramètre. + +:: + + $this->Utilisateur->id = 1; + $noeud = $this->Utilisateur->node(); + + $utilisateur = array('Utilisateur' => array( + 'id' => 1 + )); + $noeud = $this->Utilisateur->node($utilisateur); + +Ces deux exemples retourneront la même information de nœud Acl. diff --git a/fr/The-Manual/Core-Behaviors/Containable.rst b/fr/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..36d08a71fb3c722ae4900ad30dde80040ab2167c --- /dev/null +++ b/fr/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,683 @@ +Containable +########### + +Une nouvelle intégration au coeur de CakePHP est le Comportement +"Containable". Ce comportement de modèle vous permet de filtrer et +limiter les opérations de récupération de données "find". Utiliser +Containable vous aidera a réduire l'utilisation inutile de votre base de +données et augmentera la vitesse et la plupart des performances de votre +application. La class vous aidera aussi a chercher et filtrer vos +données pour vos utilisateurs d'une façon propre et consistante. + +Pour utiliser le nouveau comportement, vous pouvez l'ajouter à la +propriété $actAs de votre modèle : + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +Vous pouvez aussi attacher le comportement à la volée : + +:: + + $this->Post->Behaviors->attach('Containable'); + +Pour voir comment Containable fonctionne, regardons quelques exemples. +Premièrement, nous commencerons avec un appel find() sur un modèle nommé +Post. Disons que ce Post a plusieurs (hasMany) Comment, et Post a et +appartient à plusieurs (hasAndBelongsToMany) Tag. La quantité de données +récupérées par un appel find() normal est assez étendue : + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => A + ) + [1] => Array + ( + [id] => 2 + [name] => B + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +For some interfaces in your application, you may not need that much +information from the Post model. One thing the ContainableBehavior does +is help you cut down on what find() returns. + +For example, to get only the post-related information, you can do the +following: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +You can also invoke Containable's magic from inside the find() call: + +:: + + $this->Post->find('all', array('contain' => false)); + +Having done that, you end up with something a lot more concise: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +This sort of help isn't new: in fact, you can do that without the +ContainableBehavior doing something like this: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable really shines when you have complex associations, and you +want to pare down things that sit at the same level. The model's +$recursive property is helpful if you want to hack off an entire level +of recursion, but not when you want to pick and choose what to keep at +each level. Let's see how it works by using the contain() method. The +contain method's first argument accepts the name, or an array of names, +of the models to keep in the find operation. If we wanted to fetch all +posts and their related tags (without any comment information), we'd try +something like this: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Again, we can use the contain key inside a find() call: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Without Containable, you'd end up needing to use the unbindModel() +method of the model, multiple times if you're paring off multiple +models. Containable creates a cleaner way to accomplish this same task. + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors—and nothing +else—you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the Containble behavior when you've got deep +and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that 'contain' key is only used once in the main model, you +don't use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must to be attached to all models +used in containment, you may consider attaching it to your AppModel. + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +Utiliser Containable +==================== + +Pour voir comment Containable fonctionne, regardons quelques exemples. +D'abord, nous commencerons par un appel à find() sur un modèle nommé +Post. Disons que Post hasMany Commentaire et Post hasAndBelongsToMany +Tag. La quantité de données récupérées dans un appel à find() normal est +plutôt vaste : + +:: + + debug($this->Post->find('all')); + +:: + + [0] Array + ( + [Post] => Array + ( + [id] => 1 + [titre] => Premier article + [contenu] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Commentaire] => Array + ( + [0] Array + ( + [id] => 1 + [post_id] => 1 + [auteur] => Daniel + [email] => dan@exemple.com + [site_web] => http://exemple.com + [commentaire] => Premier commentaire + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [auteur] => Sam + [email] => sam@exemple.net + [site_web] => http://exemple.net + [commentaire] => Second commentaire + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] Array + ( + [id] => 1 + [nom] => Grandiose + ) + [1] => Array + ( + [id] => 2 + [nom] => Cuisson + ) + ) + [1] => Array + ( + [Post] => Array + (... + +Pour certaines interfaces de votre application, vous n'aurez peut-être +pas besoin d'autant d'informations issues du modèle Post. L'une des +choses que réalise le ``ContainableBehavior``, c'est de vous aider à +réduire ce que retourne un find(). + +Par exemple, pour obtenir uniquement les informations liées à un post, +vous pouvez faire la chose suivante : + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +Vous pouvez aussi invoqué la magie de Containable à l'intérieur de +l'appel à find() : + +:: + + $this->Post->find('all', array('contain' => false)); + +En faisant çà, vous vous retrouvez avec quelque chose de plus concis : + +:: + + [0] Array + ( + [Post] => Array + ( + [id] => 1 + [titre] => Premier article + [contenu] => aaa + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [titre] => Second article + [contenu] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +Ce type d'optimisation n'est pas nouveau : en fait, vous pouvez réaliser +çà sans le comportement ``Containable``, en faisant quelque chose comme +ceci : + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable se distingue réellement, lorsque vous avez des associations +complexes et que vous voulez réduire les choses qui se situent au même +niveau. La propriété de modèle ``$recursive`` est pratique si vous +voulez déconnecter un niveau de récursion entier, mais pas lorsque vous +voulez sélectionner et choisir que garder à chaque niveau. Voyons +comment cela fonctionne en utilisant la méthode ``contain()``. + +Le premier argument de la méthode contain accepte le nom, ou un tableau +de noms, des modèles à conserver dans l'opération find. Si nous avions +voulu récupérer tous les posts et leurs tags liés (sans aucune +information de commentaire, nous aurions essayé quelque chose comme çà : + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Là encore, nous pouvons utiliser la clé contain à l'intérieur de l'appel +à find() : + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Sans Containable, si vous avez plusieurs modèles, vous finiriez par +avoir besoin d'utiliser la méthode ``unbindModel()`` du modèle de +nombreuses fois. Le comportement Containable offre une manière plus +propre d'accomplir cette même tâche. + +Limiter des associations plus profondes +======================================= + +Le comportement Containable fonctionne également à un niveau plus +profond : vous pouvez filtrer les données des modèles *associés*. Si +vous regardez les résultats d'un appel au find() original, vous +remarquez le champ auteur dans le modèle Commentaire. Si vous êtes +intéressé par les posts et les noms des auteurs de commentaire — et rien +d'autre — vous pourriez faire quelque chose comme çà : + +:: + + $this->Post->contain('Commentaire.auteur'); + $this->Post->find('all'); + + // ou... + + $this->Post->find('all', array('contain' => 'Commentaire.auteur')); + +Ici, nous avons dit au Containable de nous transmettre des informations +sur notre post et seulement le champ auteur du modèle associé +Commentaire. La sortie de l'appel à find devrait ressembler à quelque +chose comme çà : + +:: + + [0] Array + ( + [Post] => Array + ( + [id] => 1 + [titre] => Premier article + [contenu] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Commentaire] => Array + ( + [0] Array + ( + [auteur] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [auteur] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +Comme vous pouvez le voir, les tableaux Commentaires contiennent +seulement le champ auteur (plus le post\_id qui est utilisé par CakePHP +pour relier les résultats). + +Vous pouvez aussi filtrer les données du Commentaire associé en +spécifiant une condition : + +:: + + $this->Post->contain('Commentaire.auteur = "Daniel"'); + $this->Post->find('all'); + + // ou... + + $this->Post->find('all', array('contain' => 'Commentaire.auteur = "Daniel"')); + +Ceci nous donne un résultat qui contient les posts avec des commentaires +rédigés par Daniel : + +:: + + [0] Array + ( + [Post] => Array + ( + [id] => 1 + [titre] => Premier article + [contenu] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Commentaire] => Array + ( + [0] Array + ( + [id] => 1 + [post_id] => 1 + [auteur] => Daniel + [email] => dan@exemple.com + [site_web] => http://exemple.com + [commentaire] => Premier commentaire + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Des filtrages aditionnels peuvent être effectués, en passant les options +standards de ``Model->find()`` : + +:: + + $this->Post->find('all', array('contain' => array( + 'Commentaire' => array( + 'conditions' => array('Commentaire.auteur =' => "Daniel"), + 'order' => 'Commentaire.created DESC', + ) + ))); + +Voici un exemple d'utilisation du comportement ``Containable``, lorsque +vous avez des relations profondes et complexes entre modèles. + +Considérons les associations de modèles suivantes : + +:: + + Utilisateur->Profil + Utilisateur->Compte->SyntheseCompte + Utilisateur->Post->PieceJointePost->HistoriquePieceJointePost->NoteHistorique + Utilisateur->Post->Tag + +Voici comment nous récupérons les associations ci-dessus avec +Containable : + +:: + + $this->Utilisateur->find('all', array( + 'contain'=>array( + 'Profil', + 'Compte' => array( + 'SyntheseCompte' + ), + 'Post' => array( + 'PieceJointePost' => array( + 'fields' => array('id', 'nom'), + 'HistoriquePieceJointePost' => array( + 'NoteHistorique' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.nom LIKE' => '%joyeux%') + ) + ) + ) + )); + +Gardez à l'esprit que la clé ``contain`` est utilisée une seule fois +dans le modèle principal, vous n'avez pas à utiliser 'contain' de +nouveau pour les modèles liés. + +Quand vous utilisez les options 'fields' et 'contain', prenez soin +d'inclure toutes les clés étrangères que votre requête nécessite, +directement ou indirectement. Merci de noter également que, puisque le +comportement Containable doit être attaché à tous les modèles utilisés +par la limitation, vous devriez envisager de l'attacher à votre +AppModel. + +Utiliser Containable avec la pagination +======================================= + +Voici un exemple sur la manière de limiter les associations en paginant. + +:: + + $this->paginate['Utilisateur'] = array( + 'contain' => array('Profil', 'Compte'), + 'order' => 'Utilisateur.pseudo' + ); + + $utilisateurs = $this->paginate('Utilisateur'); + +En incluant le paramètre 'contain' dans la propriété ``$paginate``, elle +sera appliquée à la fois au find('count') et au find('all') réalisés +dans le modèle. + +Les options du comportement Containable +======================================= + +Le ``ContainableBehavior`` a plusieurs options qui peuvent être définies +quand le comportement est attaché à un modèle. Ces paramètres vous +permettent d'affiner le comportement de Containable et de travailler +plus facilement avec les autres comportements. + +- **recursive** (boolean, optional), définir à true pour permettre au + comportement Containable, de déterminer automatiquement le niveau de + récursivité nécessaire pour récupérer les modèles spécifiés et de + paramétrer la récursivité du modèle à ce niveau. Le définir à false + désactive cette fonctionnalité. La valeur par défaut est ``true``. +- **notices** (boolean, optional), émet des alertes E\_NOTICES pour les + liaisons référencées dans un appel containable et qui ne sont pas + valides. La valeur par défaut est ``true``. +- **autoFields** (boolean, optional), ajout automatique des champs + nécessaires pour récupérer les liaisons requêtées. La valeur par + défaut est ``true``. + +Vous pouvez changer les paramètres du ContainableBehavior à l'exécution, +en ré-attachant le comportement comme vu au chapitre `Utiliser les +comportements `_ + +Le comportement Containable peut quelque fois causer des problèles avec +d'autres comportements ou des requêtes qui utilisent des fonctions +d'aggrégations et/ou des clauses GROUP BY. Si vous obtenez des erreurs +SQL invalides à cause du mélange de champs aggrégés et non-aggrégés, +essayer de désactiver le paramètre ``autoFields``. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/fr/The-Manual/Core-Behaviors/Translate.rst b/fr/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..77b8cb21f8fb472609c081641bf20bff9637fd8f --- /dev/null +++ b/fr/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,378 @@ +Traduction +########## + +Le comportement de traduction (*TranslateBehavior*) est en fait assez +simple à paramétrer et à faire fonctionner *out of the box*, le tout +avec très peu de configuration. Dans cette section, vous apprendrez +comment ajouter et configurer ce comportement, pour l'utiliser dans +n'importe quel modèle. + +Si vous utilisez le comportement Translate en parallèle de Containable, +assurez-vous de définir la clé 'fields' pour vos requêtes. Sinon, vous +pourriez vous retrouver avec des fragments SQL générés invalides. + +Initialiser les tables i18n +=========================== + +Vous pouvez soit utiliser la console CakePHP, soit les créer +manuellement. Il est recommandé d'utiliser la console pour cela, parce +qu'il pourrait arriver que le gabarit change dans les futures versions +de CakePHP. En restant fidèle à la console, cela garantira que vous ayez +le gabarit correct. + +:: + + ./cake i18n + +Sélectionner ``[I]``, ce qui lancera le script d'initialisation de la +base de données i18n. Il vous sera demandé si vous voulez supprimer +toute base existante et si vous voulez en créer une. Répondez par oui si +vous êtes certain qu'il n'y a pas encore une table i18n et répondez +encore par oui pour créer la table. + +Attacher le Comportement Translate à vos Modèles +================================================ + +Ajoutez-le à votre modèle en utilisant la propriété ``$actsAs`` comme +dans l'exemple suivant. + +:: + + + +Ceci ne produira encore rien, parce qu'il faut un couple d'options avant +que que cela ne commence à fonctionner. Vous devez définir, quels champs +du modèle courant devront être détectés dans la table de traduction que +nous avons créée à la première étape. + +Définir les Champs +================== + +Vous pouvez définir les champs en étendant simplement la valeur +``'Translate'`` avec un autre tableau, comme çà : + +:: + + array( + 'champUn', 'champDeux', 'etc' + ) + ); + } + ?> + +Après avoir fait cela (par exemple, en précisant "nom" comme l'un des +champs), vous avez déjà terminé la configuration de base. Super ! +D'après notre exemple courant, le modèle devrait maintenant ressembler à +quelque chose comme çà : + +:: + + array( + 'nom' + ) + ); + } + ?> + +Conclusion +========== + +A partir de maintenant, chaque mise à jour/création d'un enregistrement +fera que le TranslateBehavior copiera la valeur de "nom" dans la table +de traduction (par défaut : i18n), avec la locale courante. Une "locale" +est un identifiant de langue. + +La *locale courante* est la valeur actuelle de +``Configure::read('Config.language')``. La valeur de *Config.language* +est assignée dans la Classe L10n - à moins qu'elle ne soit déjà définie. +Cependant, le Comportement Translate vous autorise à surcharger ceci à +la volée, ce qui permet à l'utilisateur de votre page de créer de +multiples versions sans avoir besoin de modifier ses préférences. Plus +d'information sur ce point dans la prochaine section. + +Récupérer tous les enregistrements de traduction pour un champ +============================================================== + +Si vous voulez avoir tous les enregistrements de traduction attachés à +l'enregistrement de modèle courant, vous étendez simplement le *tableau +champ* dans votre paramétrage du comportement, comme montré ci-dessous. +Vous êtes complètement libre de choisir le nommage. + +:: + + array( + 'nom' => 'nomTraduction' + ) + ); + } + ?> + +Avec ce paramétrage, le résultat de votre find() devrait ressembler à +quelque chose comme çà : + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [nom] => Exemple d\'entrée + [corps] => lorem ipsum... + [locale] => fr_fr + ) + + [nomTraduction] => Array + ( + [0] Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => nom + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => fr_fr + [model] => Post + [foreign_key] => 1 + [field] => nom + [content] => Exemple d'entrée + ) + + ) + ) + +**Note** : L'enregistrement de modèle contient un champ *virtuel* +appelée "locale". Il indique quelle locale est utilisée dans ce +résultat. + +Utiliser la méthode bindTranslation +----------------------------------- + +Vous pouvez aussi récupérer toutes les traductions seulement quand vous +en avez besoin, en utilisant la méthode bindTranslation + +``bindTranslation($fields, $reset)`` + +``$fields`` est un tableau associatif composé du champ et du nom de +l'association, dans lequel la clé est le champ traduisible et la valeur +est le nom fictif de l'association. + +:: + + $this->Post->bindTranslation(array ('nom' => 'nomTraduction')); + $this->Post->find('all', array ('recursive'=>1)); // il est nécessaire d'avoir au moins un recursive à 1 pour que ceci fonctionne + +Avec ce paramétrage, le résultat de votre find() devrait ressembler à +quelque chose comme çà : + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [nom] => Exemple d'entrée + [corps] => lorem ipsum... + [locale] => fr_fr + ) + + [nomTraduction] => Array + ( + [0] Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => nom + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => fr_fr + [model] => Post + [foreign_key] => 1 + [field] => nom + [content] => Exemple d'entrée + ) + + ) + ) + +Sauvegarder dans une autre langue +================================= + +Vous pouvez forcer le modèle qui utilise le TranslateBehavior à +sauvegarder dans une autre langue que celle détectée. + +Pour dire à un modèle dans quelle langue le contenu devra être sauvé, +changez simplement la valeur de la propriété ``$locale`` du modèle, +avant que vous ne sauvegardiez les données dans la base. Vous pouvez +faire çà dans votre contrôleur ou vous pouvez le définir directement +dans le modèle. + +**Exemple A :** dans votre contrôleur + +:: + + data) { + $this->Post->locale = 'de_de'; // nous allons sauvegarder la version allemande + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**Exemple B :** dans votre modèle + +:: + + array( + 'nom' + ) + ); + + // Option 1) définir simplement la propriété directement + var $locale = 'fr_fr'; + + // Option 2) créer une méthode simple + function setLangue($locale) { + $this->locale = $locale; + } + } + ?> + +Tables de traduction multiple +============================= + +Si vous vous attendez à beaucoup d'entrées, vous vous demandez +probablement comment traiter une table qui grossit rapidement. Il y a +deux propriétés, introduites par le comportement Translate, qui +permettent de spécifier le "Modèle" à associer en tant que modèle +contenant les traductions. + +Ce sont **$translateModel** et **$translateTable**. + +Disons que nous voulons sauvegarder nos traductions pour tous les posts +dans la table "post\_i18ns", au lieu de la table par défaut "i18n". Pour +faire çà, vous avez besoin de configurer votre modèle comme ceci : + +:: + + array( + 'nom' + ) + ); + + // Utiliser un modèle différent (et une table) + var $translateModel = 'PostI18n'; + } + ?> + +**Important** : vous devez "pluraliser" le nom de la table. C'est +maintenant un modèle classique, qui peut être traité comme tel et qui, +par conséquent, dispose des conventions impliquées. Le schéma de table +lui-même doit être identique à celui généré par le script en mode +console de CakePHP. Pour être certain qu'il correspond, vous pouvez +simplement initialiser une table i18n vide en utilisant la console et +renommer la table après. + +Créer le modèle de traduction +----------------------------- + +Pour que cela fonctionne vous devez créer le fichier de l'actuel modèle +dans le dossier des modèles. La raison est qu'il n'y a pas de propriété +pour définir le *displayField* directement dans le modèle utilisant ce +comportement. + +Assurez vous de changer le ``$displayField`` en ``'champ'``. + +:: + + + +C'est tout ce qu'il faut. Vous pouvez aussi ajouter toutes les +propriétés des modèles comme $useTable. Mais pour une meilleure +cohérence nous pouvons faire cela dans le modèle qui utilise ce modèle +de traduction. C'est là que l'option ``$translateTable`` entre en jeu. + +Modification d'une Table +------------------------ + +Si vous voulez changer le nom de la table, il vous suffit simplement de +définir $translateTable dans votre modèle, comme ceci : + +:: + + array( + 'nom' + ) + ); + + // Utilise un Modèle différent + var $translateModel = 'PostI18n'; + + // Utilise une table différente pour translateModel + var $translateTable = 'post_traductions'; + } + ?> + +A noter que **vous ne pouvez pas utiliser $translateTable seul**. Si +vous n'avez pas l'intention d'utiliser un ``$translateModel`` +personnalisé, alors laissez cette propriété inchangée. La raison est +qu'elle casserait votre configuration et vous afficherait un message +"Missing Table" pour le modèle I18n par défaut, lequel est créé à +l'exécution. diff --git a/fr/The-Manual/Core-Behaviors/Tree.rst b/fr/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..e8693104a349fe6741100d9f9a2af288de417ca9 --- /dev/null +++ b/fr/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,643 @@ +Arbre transversal +################# + +C'est assez courant de vouloir stocker ses données sous une forme +hiérarchique dans la table d'une base de données. Des exemples de tels +besoins pourraient être des catégories avec un nombre illimité de +sous-catégories, des données en relation avec un système de menu +multi-niveaux ou une représentation littérale d'une hiérarchie, comme +celle qui est utilisée pour stocker les objets de contrôle d'accès avec +la logique ACL. + +Pour de petits arbres de données et les cas où les données n'ont que +quelques niveaux de profondeurs, c'est simple d'ajouter un champ +parent\_id à votre table et de l'utiliser pour savoir quel objet est le +parent de quel autre. En natif avec CakePHP, il existe cependant un +moyen puissant d'avoir les bénéfices de `la logique +MPTT `_, +sans avoir à connaître les détails de l'implémentation technique - à +moins que ça ne vous intéresse ;). + +Pré-requis +========== + +Pour utiliser le comportement en Arbre (*TreeBehavior*), votre table +nécessite 3 champs tels que listés ci-dessous (tous sont des entiers) : + +- parent - le nom du champ par défaut est parent\_id, pour stocker l'id + de l'objet parent. +- left - le nom du champ par défaut est lft, pour stocker la valeur lft + de la ligne courante. +- right - le nom du champ par défaut est rght, pour stocker la valeur + rght de la ligne courante. + +Si vous êtes familier de la logique MPTT vous pouvez vous demander +pourquoi un champ parent existe - parce qu'il est tout bonnement plus +facile d'effectuer certaines tâches à l'usage si un lien parent direct +est stocké en base, comme rechercher directement les enfants. + +Utilisation basique +=================== + +Le comportement en arbre de données (Tree behavior) possède beaucoup de +fonctionnalités, mais commençons avec un exemple simple. Créons la table +suivante : + +:: + + CREATE TABLE categories ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + name VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(1, 'Mes Catégories', NULL, 1, 30); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(2, 'Fun', 1, 2, 15); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(3, 'Sport', 2, 3, 8); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(4, 'Surf', 3, 4, 5); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(5, 'Tricot extrême', 3, 6, 7); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(6, 'Amis', 2, 9, 14); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(7, 'Gérard', 6, 10, 11); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(8, 'Gwendoline', 6, 12, 13); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(9, 'Travail', 1, 16, 29); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(10, 'Rapports', 9, 17, 22); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(11, 'Annuel', 10, 18, 19); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(12, 'Statut', 10, 20, 21); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(13, 'Voyages', 9, 23, 28); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(14, 'National', 13, 24, 25); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(15, 'International', 13, 26, 27); + +Dans le but de vérifier que tout est défini correctement, nous pouvons +créer une méthode de test et afficher les contenus de notre arbre de +catégories, pour voir à quoi il ressemble. Avec un simple contrôleur : + +:: + + data = $this->Category->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +et une définition de modèle encore plus simple : + +:: + + + +Nous pouvons vérifier à quoi ressemble les données de notre arbre de +catégories, en visitant /categories. Vous devriez voir quelque chose +comme : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Tricto extrême + + - Amis + + - Gérard + - Gwendoline + + - Travail + + - Rapports + + - Annuel + - Statut + + - Voyages + + - National + - International + +Ajouter des données +------------------- + +Dans la section précédente, nous utilisions des données existantes et +nous vérifiions qu'elles semblaient hiérarchiques via la méthode +``generatetreelist``. Toutefois, vous aimeriez normalement ajouter vos +données exactement de la même façon que vous le feriez pour tout modèle. +Par exemple : + +:: + + // pseudo code de contrôleur + $data['Category']['parent_id'] = 3; + $data['Category']['nom'] = 'Skate'; + $this->Category->save($data); + +En utilisant le comportement en arbre, il n'est pas nécessaire de faire +autre chose que de définir le parent\_id, et le comportement en arbre +s'occupera du reste. Si vous ne définissez pas le parent\_id, le +comportement en arbre ajoutera l'enregistrement à la racine, faisant de +votre nouvel ajout une nouvelle entrée de niveau supérieur : + +:: + + // pseudo code de contrôleur + $data = array(); + $data['Category']['nom'] = 'Autres catégories de Personnes'; + $this->Category->save($data); + +Exécuter les deux fragments de code ci-dessus modifiera votre arbre de +cette façon : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Tricot extrême + - Skate **Nouveau** + + - Amis + + - Gérald + - Gwendolyne + + - Travail + + - Rapports + + - Annuel + - Statut + + - Voyages + + - National + - International + +- Catégories des autres Personnes **Nouveau** + +Modifier les données +-------------------- + +Modifier des données est aussi transparent que d'en ajouter des +nouvelles. Si vous modifiez quelque chose, mais que vous ne changez pas +le champ parent\_id, la structure de vos données sera finalement +inchangée. Par exemple : + +:: + + // pseudo code de contrôleur + $this->Category->id = 5; // id de Tricot extrême + $this->Category->save(array('nom' =>'Pêche extrême')); + +Le code ci-dessus n'affecte pas le champ parent\_id, même si le +parent\_id est inclus dans les données qui sont passées au save lorsque +sa valeur ne change pas, il n'affecte pas non plus la structure de +données. Par conséquent, l'arbre de données devrait maintenant +ressembler à : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Pêche extrême **Mis à jour** + - Skate + + - Amis + + - Gérald + - Gwendolyne + + - Travail + + - Rapports + + - Annuel + - Statut + + - Voyages + + - National + - International + +- Catégories des autres Personnes + +Déplacer les données au sein de votre arbre est également une simple +formalité. Disons que Pêche extrême ne va plus sous Sport, mais qu'elle +devrait plutôt être placée sous Autres catégories de Personnes. Avec le +code suivant : + +:: + + // pseudo code de contrôleur + $this->Category->id = 5; // id de Pêche extrême + $nouveauParentId = $this->Category->field('id', array('nom' => 'Autres catégories de Personnes')); + $this->Category->save(array('parent_id' => $nouveauParentId)); + +Comme attendu, la structure est modifiée en : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Skate + + - Amis + + - Gérald + - Gwendolyne + + - Travail + + - Rapports + + - Annuel + - Statut + + - Voyages + + - National + - International + +- Catégories des autres Personnes + + - Pêche extrême **Déplacé** + +Supprimer des données +--------------------- + +Le comportement en arbre fournit de nombreuses manières de gérer la +suppression de données. Pour commencer avec l'exemple le plus simple, +imaginons que la catégorie des rapports n'est plus utilisée. Pour la +supprimer *et tous les enfants qu'elle pourrait avoir* appelez +simplement delete comme vous le feriez pour tout modèle. Par exemple, +avec le code suivant : + +:: + + // pseudo code de contrôleur + $this->Category->id = 10; + $this->Category->delete(); + +L'arbre de catégories sera modifié comme ainsi : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Skate + + - Amis + + - Gérald + - Gwendolyne + + - Travail + + - Voyages + + - National + - International + +- Catégories des autres Personnes + + - Pêche extrême + +Interroger et utiliser vos données +---------------------------------- + +Utiliser et manipuler des données hiérarchiques peut s'avérer une +entreprise compliquée. En plus des méthodes *find* du cœur, avec le +comportement en arbre il y a quelques permutations plus orientées +arborescence à votre disposition. + +La plupart des méthodes du comportement en arbre retournent et dépendent +de données qui ont été triées par le champ ``lft``. Si vous appelez +``find()`` et que vous ne faites pas un ``order by lft`` ou si vous +appelez une méthode du comportement en arbre et que vous lui passez un +ordre de tri, vous pourriez obtenir des résultats indésirables. + +children +~~~~~~~~ + +La méthode ``children`` prend la valeur de la clé primaire (l'id) d'une +ligne et retourne ses enfants, par défaut dans l'ordre où ils +apparaissent dans l'arbre. Le second paramètre, optionnel, définit si +oui ou non les enfants direct uniquement doivent être retournés. +Utilisons les données de l'exemple de la section précédent : + +:: + + $tousEnfants = $this->Category->children(1); // un tableau simple avec 11 items + // -- ou -- + $this->Category->id = 1; + $tousEnfants = $this->Category->children(); // un tableau simple avec 11 items + + // Retourne uniquement les enfants directs + $enfantsDirects = $this->Category->children(1, true); // un tableau simple avec 2 items + +Si vous voulez un tableau récursif, utilisez ``find('threaded')`` + +childCount +~~~~~~~~~~ + +Tout comme la méthode ``children``, ``childCount`` prend la valeur de la +clé primaire (l'id) d'une ligne et retourne combien d'enfants elle a. Le +second paramètre, optionnel, définit si oui ou non les enfants direct +uniquement sont comptés. Utilisons les données de l'exemple de la +section précédente : + +:: + + $nbEnfants = $this->Category->childCount(1); // affichera11 + // -- ou -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // affichera 11 + + // Compte uniquement les descendants directs de cette catégorie + $nbEnfants = $this->Category->childCount(1, true); // affichera 2 + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist (&$model, $conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)`` + +Cette méthode retourne des données similaires à un find('list'), avec un +préfixe d'indentation pour mettre en évidence la structure de l'arbre. +Voici un exemple de rendu de cette méthode. + +:: + + array( + [1] => "Mes Catégories", + [2] => "_Fun", + [3] => "__Sport", + [4] => "___Surf", + [16] => "___Skate", + [6] => "__Amis", + [7] => "___Gérald", + [8] => "___Gwendolyne", + [9] => "_Travail", + [13] => "__Voyages", + [14] => "___National", + [15] => "___International", + [17] => "Catégories des autres personnes", + [5] => "_Pêche extrême" + ) + +getparentnode +~~~~~~~~~~~~~ + +Cette fonction pratique retournera, comme son nom l'indique, le nœud +parent d'un nœud ou *false* si le nœud n'a pas de parent (c'est le nœud +racine). Par exemple : + +:: + + $parent = $this->Category->getparentnode(2); //<- id pour fun + // $parent contient Mes Catégories + +getpath +~~~~~~~ + +Le 'path' quand on se réfère à des données hiérarchiques, c'est comment +vous faites pour aller d'où vous êtes jusqu'en haut. Ainsi par exemple, +le chemin depuis la catégorie "International" est : + +- Mes Catégories + + - ... + - Travail + + - Voyages + + - ... + - International + +Utiliser l'id de "International" pour getpath retournera chacun de ses +parents à tour de rôle (en commençant depuis le sommet). + +:: + + $parents = $this->Category->getpath(15); + +:: + + // contenus de $parents + array( + [0] => array('Category' => array('id' => 1, 'name' => 'Mes Catégories', ..)), + [1] => array('Category' => array('id' => 9, 'name' => 'Travail', ..)), + [2] => array('Category' => array('id' => 13, 'name' => 'Voyages', ..)), + [3] => array('Category' => array('id' => 15, 'name' => 'International', ..)), + ) + +Autres méthodes +=============== + +Le comportement en arbre de données (Tree behavior) ne travaille pas +seulement en arrière plan, il y a une certains nombres de méthodes +spécifiques définies dans ce comportemant (bahavior) qui peuvent être +appélées directement : Ci-dessous une description brève et un exemple +pour chacune d'entre elles: + +moveDown +-------- + +Utilisé pour descendre un noeud dans l'arbre hiérarchique. Vous devez +spécifier l'id de l'élément à descendre et un entier positif spécifiant +de combien de positions le noeud devrait être descendu. Tous les +sous-noeuds seront également déplacés dans l'arbre. + +Ci-dessus un exemple d'une action d'un contrôleur (dans un contrôleur +nommé Categories) qui déplace un noeud spécifique dans l'arbre +hiérarchique: + +:: + + function movedown($title = null, $delta = null) { + $cat = $this->Category->findByTitle($title); + if (empty($cat)) { + $this->Session->setFlash('Aucune catégorie ne porte le nom ' . $title); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Merci de préciser de combien de crans le noeud doit être déplacé'); + } + + $this->redirect(array('action' => 'show'), null, true); + } + +Par exemple, si vous vouliez déplacer la catégories "Cookies" d'un cran +vers la bas, votre requête serait : /categories/movedown/Cookies/1. + +moveUp +------ + +Utilisé pour déplacer vers le haut un seul nœud dans l'arbre +hiérarchique. Vous devez spécifier l'id de l'élément à déplacer et un +entier positif spécifiant de combien de positions le nœud devra être +déplacé. Tous les nœuds enfants seront également déplacés. + +Ci-dessous un exemple d'une action de contrôleur (dans un contrôleur +nommé Categories) qui déplace un nœud spécifique dans l'arbre : + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('Il n\'y a pas de catégorie nommée ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Merci de préciser de combien de positions la catégorie doit être montée.'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +Par exemple, si vous voulez déplacer la catégorie "Gwendolyn" d'un cran +vers le haut, votre requête sera : /categories/moveup/Gwendolyn/1. +Maintenant l'ordre de Friends sera Gwendolyn, Gerald. + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Utiliser cette méthode supprimera ou déplacera un nœud, mais conservera +son sous-arbre, lequel sera ré-apparenté un niveau plus haut. Cela offre +plus de contrôle que ```delete()`` `_ qui, pour un +modèle utilisant le comportement en arbre, effacera le nœud spécifié et +tous ses enfants. + +Prenons l'arbre suivant comme point de départ : + +- Mes Catégories + + - Fun + + - Sport + + - Surf + - Tricot extrême + - Skate + +Lançons le code suivant avec l'id de 'Sport' + +:: + + $this->Category->removeFromTree($id); + +Le nœud Sport va devenir un nœud de niveau principal : + +- Mes Catégories + + - Fun + + - Surf + - Tricot extrême + - Skate + +- Sport **Déplacé** + +Ceci démontre le comportement par défaut de ``removeFromTree`` : +déplacer le nœud pour qu'il n'ait pas de parent, puis réapparenter tous +les enfants. + +Si par contre, le fragment de code suivant était utilisé avec l'id de +'Sport' + +:: + + $this->Category->removeFromTree($id,true); + +L'arbre deviendrait + +- Mes Catégories + + - Fun + + - Surf + - Tricot extrême + - Skate + +Ceci démontre l'usage alternatif de ``removeFromTree`` : les enfants ont +été ré-apparenté et 'Sport' a été supprimé. + +reorder +------- + +Cette méthode peut être utilisée pour trier hiérarchiquement les +données. + +Intégrité des données +===================== + +A cause de la nature des structures de données auto-référencées +complexes comme les arbres et les listes liées, elles peuvent devenir +occasionnellement corrompues par un appel imprudent. Courage, tout n'est +pas perdu ! Le comportement Tree contient plusieurs fonctionnalités +non-documentées auparavant, élaborées pour se sortir de telles +situations. + +Ces fonctions qui peuvent vous épargner du temps sont : + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +Le paramètre mode est utilisé pour spécifier la source de l'info qui est +valide/correcte. La source de données opposée sera remplie en fonction +de cette source d'info. Par ex : si les champs MPTT sont corrompus ou +vides, avec $mode 'parent' les valeurs du champ parent\_id seront +utilisées pour remplir les champs left et right. Le paramètre +missingParentAction s'applique uniquement au mode "parent" et détermine +que faire si le champ parent contient un id qui n'est pas présent. + +reorder(&$model, $options = array()) + +Ré-ordonne les nœuds (et les nœuds enfants) de l'arbre en accord avec le +champ et la direction spécifiés dans les paramètres. Cette méthode ne +change le parent d'aucun nœud. + +Le tableau options contient, par défaut, les valeurs 'id' => null, +'field' => $model->displayField, 'order' => 'ASC' et 'verify' => true. + +verify(&$model) + +Retourne vrai si l'arbre est valide, sinon un tableau composé de : +"type", "incorrect left/right index", "message". diff --git a/fr/The-Manual/Core-Components.rst b/fr/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..5d42f32d723d8ec19a6f484c052668bf42a1d77a --- /dev/null +++ b/fr/The-Manual/Core-Components.rst @@ -0,0 +1,62 @@ +Composants intégrés +################### + +CakePHP contient un certain nombre de composants intégrés. Ils +fournissent des fonctionnalités toutes prêtes pour de nombreuses tâches +couramment utilisées. + +Acl + +Le composant Acl fournit une interface facile à utiliser pour les listes +de contrôles d'accès basées sur une base de données ou un fichier ini. + +Auth + +Le composant Auth fournit un système d'authentification facile à +utiliser, à travers une grande variété de processus d'authentification, +comme les *callbacks* de contrôleur, l'Acl ou les *callbacks* du modèle +Object. + +Session + +Le composant Session fournit un gestionnaire de stockage indépendant +pour les sessions PHP. + +RequestHandler + +Le RequestHandler vous permet d'analyser plus finement les requêtes de +vos visiteurs et de renseigner votre application sur les types de +contenus et les informations demandés. + +Security + +Le composant Security vous permer de définir une sécurité renforcé, +d'utiliser et de managee l'authentification HTTP. + +Email + +Une interface qui peut être utilisée pour envoyer des emails grâce à +l'un des nombreux agents de transfert de mail existant, y compris la +fonction mail() de php et le smtp. + +Cookie + +Le composant Cookie se comporte d'une façon similaire au composant +Session, dans le sens où il fournit une encapsulation pour le support +natif des cookies en PHP. + +Pour en savoir plus à propos de chaque composant, voyez le menu sur la +gauche ou apprenez comment `créer vos propres +composants `_. + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/fr/The-Manual/Core-Components/Access-Control-Lists.rst b/fr/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..85d72e55616debb1d6c1d965fd662a6541dc90f2 --- /dev/null +++ b/fr/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,919 @@ +Listes de Contrôle d'Accès (ACL) +################################ + +La fonctionnalité de listes de contrôle d'accès (*Access Control List, +ACL*) de CakePHP est l'une des plus souvent discutée, probablement parce +qu'elle est la plus recherchée, mais aussi parce qu'elle peut-être la +plus déroutante. Si vous recherchez une bonne façon de débuter avec les +ACLs en général, lisez ce qui suit. + +Soyez courageux et persévérant avec ce sujet, même si au départ cela +paraît difficile. Une fois que vous aurez pris le coup, ce sera un outil +extrêmement puissant, à garder sous la main quand vous développez votre +application. + +Comprendre comment les ACL fonctionnent +======================================= + +Les choses puissantes requièrent un contrôle d'accès. Les listes de +contrôles d'accès sont une façon de gérer les permissions d'une +application d'une manière très précise et pourtant facilement +maintenable et manipulable. + +Les listes de contrôles d'accès, ou ACL (*Access Control Lists*), +manipulent deux choses principales : les choses qui veulent accéder à +des trucs et celles qui sont recherchées. Dans le jargon ACL, les choses +qui veulent accéder à des trucs (le plus souvent les utilisateurs) sont +appelées *access request objects* (objets requête d'accès) ou AROs. Les +choses du système qui sont recherchées (le plus souvent les actions ou +les données) sont appelées *access control objects* (objets contrôle +d'accès) ou ACOs. Les entités sont appelées "objets", parce que parfois, +l'objet demandé n'est pas une personne - des fois, vous pourriez vouloir +limiter l'accès à certains contrôleurs de Cake qui doivent initier leur +logique dans d'autres parties de votre application. Les ACOs pourraient +être n'importe quoi que vous voudriez contrôler, d'une action de +contrôleur à un service Web, en passant par une case de l'agenda en +ligne de votre Mamy. + +Rappel : + +- ACO - Objet Contrôle d'Accès - Quelque chose qui est recherchée +- ARO - Objet Requête d'Accès - Quelque chose qui veut quelque chose + +Généralement, les ACL sont utilisées pour décider quand un ARO peut +obtenir l'accès à un ACO. + +Afin de vous aider à comprendre comment toutes les choses travaillent +ensemble, utilisons un exemple semi-fonctionnel. Imaginons un moment, un +ordinateur utilisé par un célèbre groupe d'aventuriers tirés du roman +fantastique le *Seigneur des Anneaux*. Le chef du groupe, Gandalf, veut +gérer les biens du groupe, tout en maintenant un bon niveau de +confidentialité et de sécurité entre les autres membres de l'équipe. La +première chose dont il a besoin est de créer une liste d'AROs qui +comprend : + +- Gandalf +- Aragorn +- Bilbo +- Frodo +- Gollum +- Legolas +- Gimli +- Pippin +- Merry + +Comprenez que l'ACL n'est *pas* la même chose que l'authentification. +L'ACL est ce qui vient *après* qu'un utilisateur ait été authentifié. +Par contre, les deux sont habituellement utilisés de paire, il est +important de faire la distinction entre savoir qui est quelqu'un +(authentification) et savoir ce qu'il peut faire (ACL). + +La chose suivante que Gandalf doit faire, c'est de créer une liste +initiale des choses, ou ACOs, que le système va contrôler. Sa liste +devrait ressembler à quelque chose comme ça : + +- Les armes +- L'Anneau +- Le porc salé +- La diplomatie +- La bière + +Traditionnellement, les systèmes étaient gérés en utilisant une sorte de +matrice, qui présentait un ensemble basique d'utilisateurs et de +permissions en relation avec les objets. Si ces informations étaient +stockées dans un tableau, il ressemblerait à ça : + +Les armes + +L'Anneau + +Le porc salé + +La diplomatie + +La bière + +Gandalf + +Autorisé + +Autorisé + +Autorisé + +Aragorn + +Autorisé + +Autorisé + +Autorisé + +Autorisé + +Bilbo + +Autorisé + +Frodo + +Autorisé + +Autorisé + +Gollum + +Autorisé + +Legolas + +Autorisé + +Autorisé + +Autorisé + +Autorisé + +Gimli + +Autorisé + +Autorisé + +Pippin + +Autorisé + +Autorisé + +Merry + +Autorisé + +A première vue, il semble que ce système pourrait très bien fonctionner. +Les affectations peuvent être mises en place à des fin de sécurité (seul +Frodo peut accéder à l'Anneau) et pour éviter les accidents (en gardant +les hobbits à distance du porc salé et des armes). Cela paraît +suffisamment complet et assez facile à lire, n'est-ce pas ? + +Pour un petit système comme celui-ci, peut-être qu'une configuration en +matrice pourrait fonctionner. Mais pour un système évolutif ou un +système avec un fort pourcentage de ressources (ACOs) et d'utilisateurs +(AROs), un tableau peut devenir plus lourd que rapide. Imaginez une +tentative de contrôler l'accès à des centaines de camps militaires et de +gérer cela par unité. Un autre inconvénient des matrices est que vous ne +pouvez par vraiment regrouper logiquement des sections d'utilisateurs ou +faire des changements de permissions en cascade, pour des groupes +d'utilisateurs basés sur ces regroupements logiques. Par exemple, il +serait certainement plus chouette d'autoriser automatiquement les +hobbits à accéder à la bière et au porc une fois que le combat est fini +: faire ça sur une base d'utilisateurs gérés individuellement pourrait +être fastidieux et source d'erreur. Faire des changements de permissions +en cascade pour tous les "hobbits" serait plus facile. + +Les ACL sont très souvent implémentés dans une structure en arbre. Il y +a généralement un arbre d'AROs et un arbre d'ACOs. En organisant vos +objets en arbres, les permissions peuvent toujours être distribuées +d'une façon granulaire, tout en maintenant encore une bonne cohérence de +l'ensemble. En chef raisonnable qu'il est, Gandalf choisit d'utiliser +l'ACL dans son nouveau système et d'organiser ses objets de la manière +suivante : + +- La Communauté de l'Anneau™ + + - Les Guerriers + + - Aragorn + - Legolas + - Gimli + + - Les Magiciens + + - Gandalf + + - Les Hobbits + + - Frodo + - Bilbo + - Merry + - Pippin + + - Les Visiteurs + + - Gollum + +L'utilisation d'une structure en arbre pour les AROs permet à Gandalf, +de définir en une fois des autorisations qui s'appliquent à un groupe +entier d'utilisateurs. Ainsi, en utilisant notre arbre ARO, Gandalf peut +ajouter, après coup, quelques permissions de groupe : + +- La Communauté de l'Anneau + (**Refuser** : tout) + + - Guerriers + (**Autoriser** : Armes, Bière, Rations pour les Elfes, Porc salé) + + - Aragorn + - Legolas + - Gimli + + - Magiciens + (**Autoriser** : Porc salé, Diplomatie, Bière) + + - Gandalf + + - Hobbits + (**Autoriser** : Bière) + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visiteurs + (**Autoriser** : Porc salé) + + - Gollum + +Si nous voulions utiliser les ACL pour voir si Pippin était autorisé à +accéder à la bière, nous devrions d'abord récupérer son chemin dans +l'arbre, lequel est Communauté->Hobbits->Pippin. Ensuite nous verrions +les différentes permissions qui résident à chacun de ces points et nous +utiliserions la plus spécifique des permissions reliant Pippin et la +bière. + ++-----------------------------+---------------------------------+----------------------------------+ +| Nœud de l'ARO | Information sur la permission | Résultat | ++=============================+=================================+==================================+ +| La Communauté de l'Anneau | Refuse tout | Refuser l'accès à la bière. | ++-----------------------------+---------------------------------+----------------------------------+ +| Les Hobbits | Autorise la bière | Autoriser l'accès à la bière ! | ++-----------------------------+---------------------------------+----------------------------------+ +| Pippin | -- | Toujours autoriser la bière ! | ++-----------------------------+---------------------------------+----------------------------------+ + +Puisque le nœud "Pippin" dans l'arbre d'ACL ne refuse pas spécifiquement +l'accès à l'ACO bière, le résultat final est que nous donnons l'accès à +cet ACO. + +L'arbre nous permet aussi de faire des ajustements plus fins pour un +meilleur contrôle granulaire, tout en conservant encore la capacité de +faire de grands changements pour les groupes d'AROs : + +- Communauté de l'Anneau + (**Refuser** : tout) + + - Guerriers + (**Autoriser** : Armes, Bière, Rations pour les Elfes, Porc salé) + + - Aragorn + (Autoriser : Diplomatie) + - Legolas + - Gimli + + - Magiciens + (**Autoriser** : Porc salé, Diplomatie, Bière) + + - Gandalf + + - Hobbits + (**Autoriser** : Bière) + + - Frodo + (Autoriser : Anneau) + - Bilbo + - Merry + (Refuser : Bière) + - Pippin + (Autoriser : Diplomatie) + + - Visiteurs + (**Autoriser** : Porc salé) + + - Gollum + +Cette approche nous donne plus de possibilités pour faire des +changements de permissions de grande ampleur, mais aussi des ajustements +plus précis. Cela nous permet de dire que tous les hobbits peuvent +accéder à la bière, avec une exception — Merry. Pour voir si Merry peut +accéder à la bière, nous aurions trouvé son chemin dans l'arbre : +Communauté->Hobbits->Merry et appliqué notre principe, en gardant une +trace des permissions liées à la bière : + ++--------------------------+---------------------------------+----------------------------------+ +| Nœud de l'ARO | Information sur la permission | Résultat | ++==========================+=================================+==================================+ +| Communauté de l'Anneau | Refuse tout | Refuser l'accès à la bière. | ++--------------------------+---------------------------------+----------------------------------+ +| Hobbits | Autorise la bière | Autoriser l'accès à la bière ! | ++--------------------------+---------------------------------+----------------------------------+ +| Merry | Refuse la bière | Refuser la bière | ++--------------------------+---------------------------------+----------------------------------+ + +Définir les permissions : ACL de Cake basées sur des fichiers INI +================================================================= + +La première implémentation d'ACL sur Cake était basée sur des fichiers +INI stockés dans l'installation de Cake. Bien qu'elle soit stable et +pratique, nous recommandons d'utiliser plutôt les solutions d'ACL basées +sur les bases de données, surtout pour leur capacité à créer de nouveaux +ACOs et AROs à la volée. Nous recommandons son utilisation dans de +simples applications - et spécialement pour ceux qui ont une raison plus +ou moins particulière de ne pas vouloir utiliser une base de données. + +Par défaut, les ACL de CakePHP sont gérés par les bases de données. Pour +activer les ACL basés sur les fichiers INI, vous devez dire à CakePHP +quel système vous utilisé en mettant à jour les lignes suivantes dans +app/config/core.php + +:: + + // Changer ces lignes : + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + // Pour qu'elles ressemblent à çà : + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +Les permissions des ARO/ACO sont spécifiées dans +**/app/config/acl.ini.php**. L'idée de base est que les AROs qui sont +spécifiés dans une section INI qui a trois propriétés : *groups*, +*allow* et *deny*. + +- *groups* : nom du groupe dont l'ARO est membre. +- *allow* : nom des ACOs auxquels l'ARO a accès. +- *deny* : nom des ACOs auxquels l'ARO ne devrait pas avoir accès. + +Les ACOs sont spécifiés dans des sections INI qui incluent seulement les +propriétés *allow* et *deny*. + +Par exemple, voyons à quoi la structure ARO de la Communauté que nous +avions façonnée pourrait ressembler dans une syntaxe INI : + +:: + + ;------------------------------------- + ; Les AROs + ;------------------------------------- + [aragorn] + groups = guerriers + allow = diplomatie + + [legolas] + groups = guerriers + + [gimli] + groups = guerriers + + [gandalf] + groups = magiciens + + [frodo] + groups = hobbits + allow = anneau + + [bilbo] + groups = hobbits + + [merry] + groups = hobbits + deny = biere + + [pippin] + groups = hobbits + + [gollum] + groups = visiteurs + + ;------------------------------------- + ; Groupe de l'ARO + ;------------------------------------- + [guerriers] + allow = armes, biere, porc_sale + + [magiciens] + allow = porc_sale, diplomatie, biere + + [hobbits] + allow = biere + + [visiteurs] + allow = porc_sale + +Maintenant que vous avez défini vos permissions, vous pouvez passer à +`la section sur la vérification des +permissions `_ utilisant le +composant ACL. + +Définir les permissions : ACL de Cake via une base de données +============================================================= + +Maintenant que nous avons vu les permissions ACL basées sur les fichiers +INI, voyons les ACL via une base de données (les plus communément +utilisées). + +Pour commencer : +---------------- + +L'implémentation par défaut des permissions ACL profite des +fonctionnalités offertes par les bases de données. La base de données +Cake pour les ACL est composé d'un ensemble de modèles du coeur et d'une +console d'application qui sont créés lors de votre installation de Cake. +Les Modèles sont utilisés par Cake pour interagir avec la base de +données afin de stocker et de retrouver les noeuds de notre +implémentation en format d'arbre. La console d'application est quand à +elle utilisée pour initialiser votre base de données et pour interagir +avec vos arbres d'ACO et d'ARO. + +Pour commencer, vous devrez d'abord être sur que votre +``/app/config/database.php`` soit présent et correctement configuré. +Voir la section 4.1 pour plus d'information sur la configuration d'une +base de données. + +Une fois que vous l'avez fait, utilisez la console de CakePHP pour créer +vos tables dans votre base de données d'ACL : + +:: + + $ cake schema create DbAcl + +Cette commande va supprimer et recréer les tables nécessaires au +stockage des informations des ACO et des ARO dans notre implémentation +en arbre. La sortie console devrait ressembler à ça : + +:: + + --------------------------------------------------------------- + Shell du schéma de Cake + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +Ceci remplace une commande désuète et dépréciée, "initdb". + +Vous pouvez aussi vous servir du fichier SQL que vous trouverez dans +``app/config/sql/db_acl.sql``, mais ça sera moins sympa. + +Quand ce sera fini, vous devriez avoir trois nouvelles tables dans votre +système de base de données : acos, aros, et aros\_acos (la table de +jointure pour créer les permissions entre les deux arbres). + +Si vous êtes intéressé par la façon de stocker de Cake dans ces tables, +parcourez les tables. Le composant ACL utilise `le comportement en +arbre `_ pour gérer les héritages. Le +fichiers de modèle de classe pour ACL sont compilés dans un seul fichier +`db\_acl.php `_. + +Maintenant que nous avons tout configuré, attelons nous à la création +d'arbres ARO et ACO. + +Créer des Objet Contrôle d'Accès (ACOs) et des Objet Requête d'Accès (AROs) +--------------------------------------------------------------------------- + +Pour la création de nouveaux objets (ACOs et AROs), il y a deux +principales façons de nommer et d'accéder aux noeuds. La *première* +méthode est de lier un objet ACL directement à un enregistrement dans +votre base de données en spécifiant le nom du modèle et la clé +étrangère. La *seconde* méthode peut être utilisée quand un objet n'est +pas en relation directe avec un enregistrement de votre base de données +- vous pouvez fournir un alias textuel pour l'objet. + +Généralement, quand vous créez un groupe ou un objet de niveau +supérieur, nous recommandons d'utiliser un alias. Si vous gérez l'accès +à un enregistrement ou à un article particulier de la base de données, +nous recommandons d'utiliser la méthode du modèle/clé étrangère. + +Vous voulez créer de nouveaux objets ACL en utilisant le modèle ACL du +coeur de CalePHP. Pour ce faire, il y a un nombre de champs que vous +aurez à utiliser pour enregistrer les données : ``model``, +``foreign_key``, ``alias``, et ``parent_id``. + +Les champs ``model`` et ``foreign_key`` pour un objet ACL vous +permettent de créer un lien entre les objets qui correspondent à +l'enregistrement du modèle (s'il en est). Par exemple, un certain nombre +d'AROs correspondraient aux enregistrement User de la base de données. +Il faut configurer la ``foreign_key`` pour que l'ID du User vous +permette de lier les informations de l'ARO et de User avec un seul appel +find() au modèle User avec la bonne association. Réciproquement, si vous +voulez gérer les opérations d'édition sur un article spécifique d'un +blog ou d'une liste de recette, vous devez choisir de lier un ACO à cet +enregistrement spécifique du modèle. + +L'``alias`` d'un objet ACL est un simple label lisible pour un humain +que vous pouvez utiliser pour identifier un objet ACL qui n'est pas en +relation directe avec un enregistrement d'un modèle. Les alias sont +couramment utilisés pour nommer les groupes d'utilisateurs ou les +collections d'ACOs. + +Le ``parent_id`` d'un objet ACL vous permet de remplir la structure de +l'arbre. Il fournit l'ID du noeud parent dans l'arbre pour créer un +nouvel enfant. + +Avant que vous ne puissiez créer de nouveaux objets ACL, nous devront +charger leurs classes respectives. La façon la plus facile de le faire +et d'inclure les composants ACL de Cake dans votre tableau $composents +du contrôleur : + +:: + + var $components = array('Acl'); + +Quand ce sera fait, nous verrons quelques exemples de création de ces +objets. Le code suivant pourrait être placé quelque part dans l'action +d'un contrôleur : + +Tant que les exemples que nous voyons ici nous montrent la création +d'ARO, les mêmes techniques pourront être utilisées pour la création +d'un arbre d'ACO. + +Pour rester dans notre configuration de Communauté, nous allons d'abord +créer nos groups d'ARO. De fait que nos groupes n'ont pas réellement +d'enregistrements spécifiques qui leurs soient reliés, nous allons +utiliser les alias pour créer ces objets ACL. Ce que nous faisons ici +est en perspective d'une action du contrôleur mais pourrait être fait +ailleurs. Ce que nous allons aborder ici est un peu une approche +artificielle, mais vous devriez trouver ces techniques plus confortables +à utiliser pour créer des ARIs et des ACOs à la volée. + +Ce ne devrait rien avoir de radicalement nouveau - nous sommes justes +entrain d'utiliser les modèles pour enregistrer les données comme nous +le faisons toujours : + +:: + + function touteslesActions() + { + $aro =& $this->Acl->Aro; + + //Ici ce sont toutes les informations sur le tableau de notre groupe que nous + //pouvons itérer comme ceci + + $groups = array( + 0 => array( + 'alias' => 'guerriers' + ), + 1 => array( + 'alias' => 'magiciens' + ), + 2 => array( + 'alias' => 'hobbits' + ), + 3 => array( + 'alias' => 'visiteurs' + ), + ); + + //Faisons une itération et créons les groupes d'ARO + foreach($groups as $data) + { + //Pensez à faire un appel à create() au moment d'enregistrer dans + //la boucle... + + $aro->create(); + + //Enregistrement des données + $aro->save($data); + } + + //Les autres actions logiques seront à placer ici... + } + +Une fois que nous avons cela, nous pouvons utiliser la consile +d'application ACL pour vérifier la structure de l'arbre. + +:: + + $ cake acl view aro + + Arbre d'Aro : + --------------------------------------------------------------- + [1]guerriers + + [2]magiciens + + [3]hobbits + + [4]visiteurs + + --------------------------------------------------------------- + +Je suppose qu'il n'y en a pas beaucoup dans l'arbre à ce niveau, mais au +minimum quelques vérifications que nous avons faites aux quatres noeuds +de niveaux supérieurs. Ajoutons quelques enfants à ces noeuds ARO en +ajoutant nos AROs utilisateurs dans ces groupes. Tous les bons citoyens +de la Terre du Milieu ont un accompte dans notre nouveau système, nous +allons alors lier les enregistrements d'ARO aux enregistrements +spécifiques du modèle de notre base de données. + +Quand nous ajouterons un noeud enfant à un arbre, nous devrons nous +assurer d'utiliser les ID des noeuds ACL, plutôt que d'utiliser la +valeur de la foreign\_key (clé étrangère). + +:: + + function anyAction() + { + $aro = new Aro(); + + //Ici nous avons les enregistrement de nos utilisateurs prêts à être liés aux + //nouveaux enregistrements d'ARO. Ces données peuvent venir d'un modèle et + //modifiées, mais nous utiliserons des tableaux statiques pour les besoins de la + //démonstration. + + + $users = array( + 0 => array( + 'alias' => 'Aragorn', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'Legolas', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'Gimli', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'Gandalf', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'Frodo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'Bilbo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'Merry', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'Pippin', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'Gollum', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + //Faisons une itération et créons les AROs (comme des enfants) + foreach($users as $data) + { + //Pensez à faire un appel à create() au moment d'enregistrer dans + //la boucle... + $aro->create(); + + //Enregistrement des données + $aro->save($data); + } + + //Les autres actions logiques se trouveront ici ... + } + +Typiquement vous n'aurez pas à fournir et un alias, et un +modèle/clé\_étrangère, mais nous les utiliserons ici pour faire une +structure d'arbre plus facile à lire pour les besoins de la +démonstrations. + +La sortie console de cette commande peut maintenant nous intéresser un +peu plus. Nous allons faire un essai : + +:: + + $ cake acl view aro + + Arbre d'Aro: + --------------------------------------------------------------- + [1]guerriers + + [5]Aragorn + + [6]Legolas + + [7]Gimli + + [2]magiciens + + [8]Gandalf + + [3]hobbits + + [9]Frodo + + [10]Bilbo + + [11]Merry + + [12]Pippin + + [4]visiteurs + + [13]Gollum + + --------------------------------------------------------------- + +Maintenant que nous avons notre arbre d'ARO configuré proprement, +revenons sur une possible approche de structure d'arbre d'ACO. Tant que +nous pouvons structurer plus que par une représentation abstraite que +celle de nos ACO, il est parfois plus pratique de modéliser un arbre ACO +après que la configuration faite par le Contrôleur/Action de Cake. Nous +avons cinq principaux objets à manipuler dans le scénario de la +Communauté, pour la configuration naturelle de ce dernier dans une +application Cake est un groupe de modèles, et enfin pour les contrôleurs +qui le manipulent. A côté des contrôleurs eux-mêmes, nous allon vouloir +contrôler l'accès à des actions spécifiques de ces contrôleurs. + +Basés sur cette idée, nous allons configurer un arbre d'ACO qui va +imiter une configuration d'application Cake. Depuis nos cinq ACOs, nous +allons créer un arbre d'ACO qui devra ressembler à ça : + +- Armes +- Anneaux +- MorceauxPorc +- EffortsDiplomatiques +- Bières + +Une bonne chose concernant la configuration des ACL et que chaque ACO va +automatiquement contenir quatre propriétés relatives aux actions CRUD +(créer, lire, mettre à jour et supprimer). Vous pouvez créer des noeuds +fils sous chacun de ces cinq principaux ACOs, mais l'utilisation des +actions de management intégrées à Cake permet d'aborder les opérations +basiques de CRUD sur un objet donné. Gardez à l'esprit qu'il faudra +faire vos arbres d'ACO plus petits et plus faciles à maintenir. Nous +allons voir comment ils sont utilisés plus tard quand nous parlerons de +comment assigner les permissions. + +Nous sommes maintenant des pro de l'ajout d'AROs et de l'utilisation des +techniques de création d'arbres d'ACO. La création de groupes d'un +niveau supérieur utilise le modèle Aco du coeur. + +Assigner les Permissions +------------------------ + +Après la création de nos ACOs et AROs, nous pouvons finalement assigner +des permissions entre les deux groupes. Ceci est réalisé en utilisant le +composant Acl du cœur de CakePHP. Continuons avec notre exemple. + +Ici nous travaillerons dans un contexte d'une action de contrôleur. Nous +faisons cela parce que les permissions sont managées par le composant +Acl. + +:: + + class ChosesController extends AppController + { + // Vous pourriez placer çà dans AppController + , + // mais cela fonctionne bien ici aussi. + + var $components = array('Acl'); + + } + +Configurons quelques permissions de base, en utilisant le Composant Acl +dans une action à l'intérieur de ce contrôleur. + +:: + + function index() + { + //Autorise un accès complet aux armes pour les guerriers + //Ces exemples utilisent tous deux la syntaxe avec un alias + $this->Acl->allow('guerriers', 'Armes'); + + //Encore que le Roi pourrait ne pas vouloir laisser n'importe qui + //disposer d'un accès sans limites + $this->Acl->deny('guerriers/Legolas', 'Armes', 'delete'); + $this->Acl->deny('guerriers/Gimli', 'Armes', 'delete'); + + die(print_r('done', 1)); + } + +Le premier appel que nous faisons au composant Acl donne, à tout +utilisateur appartenant au groupe ARO 'guerriers', un accès total à tout +ce qui appartient au groupe ACO 'Armes'. Ici nous adressons simplement +les ACOs et AROs d'après leurs alias. + +Avez-vous noté l'usage du troisième paramètre ? C'est là où nous +utilisons ces actions bien pratiques qui sont intégrées à tous les ACOs +de Cake. Les options par défaut pour ce paramètre sont ``create``, +``read``, ``update`` et ``delete``, mais vous pouvez ajouter une colonne +dans la table ``aros_acos`` de la base de données (préfixée avec \_ - +par exemple ``_admin``) et l'utiliser en parallèle de celles par défaut. + +Le second ensemble d'appels est une tentative de prendre une décision un +peu plus précise sur les permissions. Nous voulons qu'Aragorn conserve +ses privilèges de plein accès, mais nous refusons aux autres guerriers +du groupe, la capacité de supprimer les enregistrements de la table +Armes. Nous utilisons la syntaxe avec un alias pour adresser les AROs +ci-dessus, mais vous pourriez utiliser votre propre syntaxe modèle/clé +étrangère. Ce que nous avons ci-dessus est équivalent à ceci : + +:: + + // 6342 = Legolas + // 1564 = Gimli + + $this->Acl->deny(array('model' => 'Utilisateur', 'foreign_key' => 6342), 'Armes', 'delete'); + $this->Acl->deny(array('model' => 'Utilisateur', 'foreign_key' => 1564), 'Armes', 'delete'); + +L'adressage d'un nœud en utilisant la syntaxe avec un alias, nécessite +une chaîne délimitée par des slashs +('/utilisateurs/salaries/developpeurs'). L'adressage d'un nœud en +utilisant la syntaxe modèle/clé étrangère nécessite un tableau avec deux +paramètres : ``array('model' => 'Utilisateur', 'foreign_key' => 8282)``. + +La prochaine section nous aidera à valider notre configuration, en +utilisant le composant Acl pour contrôler les permissions que nous +venons de définir. + +Vérification des Permissions : le Composant ACL +----------------------------------------------- + +Utilisons le Composant Acl pour s'assurer que les nains et les elfes ne +peuvent déplacer des choses depuis l'armurerie. Maintenant, nous +devrions être en mesure d'utiliser le Composant Acl, pour faire une +vérification entre les ACOs et les AROs que nous avons créés. La syntaxe +de base pour faire une vérification des permissions est : + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +Faisons un essai dans une action de contrôleur : + +:: + + function index() + { + // Tout cela renvoie "true" + $this->Acl->check('guerriers/Aragorn', 'Armes'); + $this->Acl->check('guerriers/Aragorn', 'Armes', 'create'); + $this->Acl->check('guerriers/Aragorn', 'Armes', 'read'); + $this->Acl->check('guerriers/Aragorn', 'Armes', 'update'); + $this->Acl->check('guerriers/Aragorn', 'Armes', 'delete'); + + // Souvenez-vous, nous pouvons utiliser la syntaxe modèle/clé étrangère + // pour nos AROs utilisateur + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), 'Armes'); + + // Ceci retourne "true" également : + $result = $this->Acl->check('guerriers/Legolas', 'Armes', 'create'); + $result = $this->Acl->check('guerriers/Gimli', 'Armes', 'read'); + + // Mais ceci retourne "false" : + $result = $this->Acl->check('guerriers/Legolas', 'Armes', 'delete'); + $result = $this->Acl->check('guerriers/Gimli', 'Armes', 'delete'); + } + +L'usage fait ici est démonstratif, mais vous pouvez sans doute voir +comment une telle vérification peut être utilisée, pour décider à quel +moment autoriser, ou pas, quelque chose à se produire, pour afficher un +message d'erreur ou rediriger l'utilisateur vers un login. diff --git a/fr/The-Manual/Core-Components/Authentication.rst b/fr/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..7676e3969027aa53d91a81fb47f824527721e0ef --- /dev/null +++ b/fr/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,817 @@ +Authentification +################ + +Les systèmes d'authentification constituent une partie courante de +nombreuses applications Web. Avec CakePHP, il y a plusieurs systèmes +pour authentifier un utilisateur, chacun fournissant des options +différentes. Au coeur du composant d'authentification, on vérifie si +l'utilisateur possède un compte sur le site. Si c'est la cas, le +composant donne l'accès à tout le site à l'utilisateur. Ce composant +peut être combiné avec le composant ACL (access control lists) pour +créer des niveaux d'accès à l'intérieur du site. Cela permettra à un +utilisateur de s'authentifier comme utilisateur générique tandis qu'un +autre sera authentifié comme administrateur qui possède des droits pour +accéder aux parties protégées du site. + +Le composant AuthComponent peut servir à créer un tel système rapidement +et facilement. Regardons comment construire un système +d'authentification simple. + +Comme n'importe quel composants, vous devrez l'ajouter à la liste des +composants en ajoutant 'Auth' dans votre contrôleur : + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +Ou ajoutez-le à votre AppController pour le rendre disponible à +l'ensemble des contrôleurs : + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +Maintenant, il y a quelques conventions auxquelles penser quand on +utilise le composant AuthComponent. Par défaut, AuthComponent s'attend à +ce que la table des utilisateurs s'appelle 'users' et qu'elle comporte +les champs nommées 'username' et 'password'. + +Le script SQL suivant met en place une telle table : + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(50), + PRIMARY KEY (id) + ); + +Pour une sécurité accrue, le composant AuthComponent hashe +automatiquement les données soumises de n'importe quel formulaire dont +le nom correspond au nom du champ que vous avez rattaché au champ +password de AuthComponent. Par exemple, si vous avez créé un nouvel +utilisateur, la valeur stockée dans $data['User']['password'] sera une +valeur hashée plutôt que la chaîne de caractère saisie par l'utilisateur +à l'origine. + +Ce comportement peut créer quelques difficultés si vous chercher à vous +assurer que que le nouvel utilisateur a choisi un mot de passe +suffisamment complexe. Par exemple, vous pourriez vous assurer que tous +les gens ont des mots de passe de 8 caractères au moins. + +Vous pouvez contourner la difficulté en créant un champ dans votre +formulaire, dont le nom ne correspond pas au nom du champ mot de passe +dans le modèle. Vous pouvez alors valider l'entrée de l'utilisateur, +hasher ce champ manuellement et le sauvegarder en base en utilisant le +modèle User. + +Ce procédé de hashage automatique de mot de passe doit être gardé à +l'esprit quand on crée une table pour stocker l'authentification de +l'utilisateur. Assurez-vous que le champ mot de passe est suffisamment +long pour stocker le hash (40 caractères pour SHA1 par exemple). + +Pour le paramétrage le plus basique, il vous faudra créer deux actions +dans votre contrôleur : + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +Vous n'avez réellement besoin que de créer une vue, la page de login +(située dans app/views/users/login.ctp). L'exemple ci-dessous présuppose +que vous utilisez le Helper Form + +:: + + check('Message.auth')) $session->flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +Cette vue crée un formulaire d'authentification où on peut entrer son +nom d'utilisateur et un mot de passe. Quand on soumet le formulaire, le +composant AuthComponent prend en charge le reste pour vous. Le message +de session flash affichera les messages généré par AuthComponent. + +Si vous n'utilisez pas $form->create, assurez-vous que vous éléments +input adressent bien les champs en tant que User.username et +User.password. + +Croyez le ou no, on en a terminé ! Voici comment écrire un système +d'authentification fondé sur la base de données avec le composant +AuthComponent. Cependant, il y a encore plus de choses qu'on peut faire. +Intéressons-nous maintenant à quelques usages avancés du composant. + +A chaque fois que vous voulez modifier une option par défaut du +composant, vous pouvez le faire en créant une méthode beforeFilter() +pour votre contrôleur et en appelant les diverses méthodes natives et +les variables de paramétrage du composant. + +:: + + class UsersController extends AppController { + var $component = array('Auth'); + + function beforeFilter() { + .... + } + } + +La touche finale est de faire en sorte que votre layout (gabarit) +récupère les messages d'erreur en provenance du composant. Voici ce que +ça donne si on les regroupe avec les messages flash ordinaires : + +:: + + check('Message.flash')) { + $session->flash(); + } + if ($session->check('Message.auth')) { + $session->flash('auth'); + } + ?> + +Attardons nous maintenant sur les méthodes du composant AuthComponent à +notre disposition. + +Configurer les variables du composant Auth +========================================== + +Chaque fois que vous voulez modifier une option par défaut du composant +Auth, vous devez le faire en créant une méthode beforeFilter() dans +votre contrôleur, puis en appelant les différentes méthodes +pré-existantes ou en configurant les variables du composant. + +Par exemple, pour changer le nom du champ utilisé pour le mot de passe +de 'password' à 'mot\_secret', vous devez faire ceci : + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'mot_secret' + ); + } + } + +Dans cette situation particulière, vous devrez aussi penser à changer le +nom du champ dans la vue correspondante ! + +Une autre utilisation commune des variables du composant Auth est +d'autoriser l'accès à certaines méthodes sans que l'utilisateur ne soit +identifié (par défaut, Auth interdit l'accès à toutes les actions sauf +aux méthodes login et logout). + +Par exemple, si nous voulions autoriser tous les utilisateurs à accéder +aux méthodes index et voir (mais à aucune autre), nous ferions comme çà +: + +:: + + function beforeFilter() { + $this->Auth->allow('index','voir'); + } + +Afficher les messages d'erreur du composant Auth +================================================ + +Pour afficher les messages d'erreur que Auth renvoie, vous devez ajouter +le code suivant à votre vue. Dans ce cas, le message apparaitra à la +suite des messages flash normaux : + +:: + + flash(); + $session->flash('auth'); + ?> + +Diagnostic des problèmes avec Auth +================================== + +Il peut être parfois un peu difficile de diagnostiquer les problèmes +quand ça ne marche pas comme prévu, voici donc quelques points à se +rappeler. + +*Hâchage du mot de passe* + +Quand vous postez des informations à une action via un formulaire, le +composant Auth hâche (crypte) automatiquement le contenu de votre champ +mot de passe, si vous avez également une donnée dans le champ +'username'. Donc, si vous essayez de créer une page d'inscription +quelconque, assurez-vous que l'utilisateur ait rempli le champ +'confirmation du mot de passe' pour comparer les deux. Voici un exemple +de code : + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +Changer la fonction de hâchage +============================== + +Le composant Auth utilise la classe Security pour hacher un mot de +passe. La classe Security utilise le procédé SHA1 par défaut. Pour +changer la fonction de hash utilisée par le composant Auth, servez-vous +de la méthode ``setHash`` en lui passant ``md5``, ``sha1`` ou ``sha256`` +comme premier et unique paramètre. + +:: + + Security::setHash('md5'); // ou sha1 ou sha256. + +La classe Security utilise une valeur *salt* (définie dans +/app/config/core.php) pour hacher le mot de passe. + +Si vous voulez utiliser une logique de hachage du mot de passe +différente, autre que md5/sha1 ajouté au *salt* de l'application, vous +devrez surcharger le mécanisme standard de hashPassword. Vous aurez +besoin de faire cela si vous avez, par exemple, une base de données +existante, qui utilisait précédemment un procédé de hachage sans *salt*. +Pour faire cela, créez la méthode ``hashPasswords`` dans la classe à +laquelle vous souhaitez confier le hachage de vos mots de passe +(habituellement le modèle User) et définissez ``authenticate`` par +l'objet sur lequel vous réalisez l'authentification (habituellement, +c'est User), comme ceci : + +:: + + function beforeFilter() { + $this->Auth->authenticate = ClassRegistry::init('User'); + ... + parent::beforeFilter(); + } + +Avec le code ci-dessus, la méthode hashPasswords() du modèle User sera +appelée chaque fois que Cake appelle AuthComponent::hashPasswords(). + +Les Méthodes du composant Auth +============================== + +action +------ + +``action (string $action = ':controller/:action')`` + +Si vous utilisez les ACOs dans le cadre de votre structure ACL, vous +pouvez obtenir le chemin jusqu'au nœud ACO relié à un couple +contrôleur/action particulier : + +:: + + $acoNode = $this->Auth->action('users/delete'); + +Si vous ne passez pas de valeur, le couple contrôleur/action courant est +utilisé. + +allow +----- + +Si vous avez des actions dans votre contrôleur que vous n'avez pas +besoin d'authentifier (comme une action d'enregistrement d'un +utilisateur), vous pouvez ajouter des méthodes que le composant Auth +devrait ignorer. L'exemple suivant montre comment autoriser une action +intitulée 'enregistrer'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('enregistrer'); + } + +Si vous souhaitez autoriser plusieurs actions qui échapperont à +l'authentification, passez-les en paramètres à la méthode allow() : + +:: + + function beforeFilter() { + ... + $this->Auth->allow('foo', 'bar', 'baz'); + } + +Raccourci : vous pouvez aussi autoriser toutes les actions d'un +contrôleur en utilisant '\*'. + +:: + + function beforeFilter() { + ... + $this->Auth->allow('*'); + } + +Si vous utilisez requestAction dans votre layout ou vos éléments, vous +devriez autoriser ces actions de façon à être capable d'ouvrir la page +de login proprement. + +Le composant Auth suppose que les noms de vos actions respectent `les +conventions `_ +et qu'elles sont "underscorées". + +deny +---- + +Il peut arriver que vous vouliez retirer des actions de la liste des +actions autorisées (déclarée en utilisant $this->Auth->allow()). Voici +un exemple : + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +Cette méthode vérifie si ``$data`` contient les champs *username* et +*password*, comme spécifié par la variable ``$fields``, elle même +indexée par le nom du modèle, comme spécifié dans ``$userModel``. Si le +tableau ``$data`` contient à la fois *username* et *password*, la +méthode encode le champ *password* du tableau et retourne le tableau +``$data`` dans le même format. Cette fonction devrait être utilisée en +priorité pour les requêtes d'insertion ou de mise à jour de +l'utilisateur, quand le champ *password* est affecté. + +:: + + $data['User']['username'] = 'moi@moi.com'; + $data['User']['password'] = 'changemoi'; + $hashedPasswords = $this->Auth->hashPasswords($data); + print_r($hashedPasswords); + /* retourne : + Array + ( + [User] => Array + ( + [username] => moi@moi.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +Le champ *$hashedPasswords['User']['password']* sera maintenant encodé +en utilisant la fonction ``password`` du composant. + +Si votre contrôleur utilise le composant Auth et que les données postées +contiennent les champs mentionnés ci-dessus, il encodera automatiquement +le mot de passe en utilisant cette fonction. + +mapActions +---------- + +Si vous utilisez les Acl en mode CRUD, vous aimeriez peut-être assigner +certaines actions non-standards à chaque partie du CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('uneAction'), + 'read' => array('uneAction', 'uneAction2'), + 'update' => array('uneAction'), + 'delete' => array('uneAction') + ) + ); + +login +----- + +``login($data = null)`` + +Si vous souhaitez une authentification depuis un composant Ajax, vous +pouvez utiliser cette méthode pour authentifier manuellement un +utilisateur dans le système. Si vous ne passez aucune valeur pour +``$data``, les données reçues en POST seront alors automatiquement +passées au controlleur. + +logout +------ + +Cette méthode fournit une manière rapide de désauthentifier quelqu'un et +de le rediriger là où il a besoin d'aller. + +Cette méthode est également pratique si vous voulez proposer un lien +'Déconnexion' dans la partie membres de votre application. + +Exemple : + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +Passez une chaîne à cette méthode et vous pourrez voir à quoi +ressemblera le mot de passe crypté. C'est une fonctionnalité essentielle +si vous créez un écran d'inscription où les utilisateurs doivent entrer +deux fois leur mot de passe pour le confirmer. + +:: + + if ($this->data['User']['password'] == + $this->Auth->password($this->data['User']['password2'])) { + + // Les mots de passe correspondent, on continue + ... + } else { + $this->flash('Les mots de passe saisis ne correspondent pas', 'users/register'); + } + +Le composant Auth encryptera automatiquement le champ ``password`` si le +champ ``username`` est aussi présent dans les données envoyées. + +Cake ajoute un "grain de sécurité" (``Security.salt``) à votre chaîne de +mot de passe et crypte le tout ensuite. La fonction de cryptage utilisée +dépend de celle définie dans la classe utilitaire ``Security`` de +CakePHP (sha1 par défaut). Vous pouvez utiliser la fonction +``Security::setHash`` pour changer la méthode de cryptage. Le "grain de +sécurité" est configuré dans le fichier ``core.php`` de votre +application. + +user +---- + +``user(string $key = null)`` + +Cette méthode fournit des informations sur l'utilisateur connecté. Ces +informations sont issues de la session. Par exemple : + +:: + + if ($this->Auth->user('role') == 'admin') { + $this->flash('Vous avez un accès administrateur'); + } + +Elle peut aussi être utilisée pour obtenir des informations complètes +sur la session de l'utilisateur, de cette façon : + +:: + + $data['User'] = $this->Auth->user(); + +Si cette méthode renvoie null, l'utilisateur n'est pas connecté. + +Dans les vues, vous pouvez utiliser l'assistant Session, pour retrouver +les informations sur l'utilisateur actuellement connecté : + +:: + + $session->read('Auth.User'); // renvoie l'ensemble des informations sur l'utilisateur + $session->read('Auth.User.first_name') // renvoie la valeur d'un champ en particulier + +La clé de session peut être différente en fonction du modèle configuré +pour utiliser Auth. Par exemple, si vous utilisez le modèle ``Compte`` +au lieu de ``User``, alors la clé de session sera ``Auth.Compte``. + +Variables du composant Auth +=========================== + +Désormais, il y a plusieurs variables liées à Auth que vous pouvez +utiliser. Habituellement, vous ajoutez ces configurations dans la +méthode beforeFilter() de votre contrôleur. Ou bien, si vous devez +appliquer ces règles dans tout le site, vous aurez envie de les ajouter +au beforeFilter() du contrôleur App. + +userModel +--------- + +Vous ne voulez pas utiliser un modèle Utilisateur pour vous authentifier +? Pas de problème, modifiez ce comportement en configurant cette +variable avec le nom du modèle que vous voulez utiliser. + +:: + + Auth->userModel = 'Membre'; + ?> + +fields +------ + +Pour outrepasser les champs utilisateur et mot de passe utilisés par +défaut pour l'authentification. + +:: + + Auth->fields = array('username' => 'email', 'password' => 'motdepasse'); + ?> + +userScope +--------- + +Utilisez cette propriété pour ajouter des contraintes supplémentaires +afin que l'authentification réussisse. + +:: + + Auth->userScope = array('Utilisateur.actif' => true); + ?> + +loginAction +----------- + +Vous pouvez changer l'adresse de connexion par défaut */users/login* par +toute action de votre choix. + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'membres', 'action' => 'login'); + ?> + +loginRedirect +------------- + +Le Composant Auth mémorise quelle paire contrôleur/action vous essayiez +d'obtenir avant que l'on vous demande de vous authentifier, en stockant +cette valeur dans la Session, sous la clé Auth.redirect. Cependant, si +cette valeur de session n'est pas définie (par exemple, si vous arrivez +à la page d'identification depuis un lien externe), alors l'utilisateur +sera redirigé à l'URL spécifiée dans loginRedirect. + +Exemple : + +:: + + Auth->loginRedirect = array('controller' => 'membres', 'action' => 'accueil'); + ?> + +logoutRedirect +-------------- + +Vous pouvez également spécifier où vous voulez que l'utilisateur soit +redirigé après sa déconnexion, ayant pour action par défaut l'action de +login. + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'membres', 'action' => 'logout'); + ?> + +loginError +---------- + +Change le message d'erreur par défaut affiché lorsque quelqu'un ne +s'authentifie pas correctement. + +:: + + Auth->loginError = "Non, vous vous êtes trompé! Ce n'est pas le bon mot de passe!"; + ?> + +authError +--------- + +Change le message d'erreur par défaut affiché lorsque quelqu'un essaye +d'accéder à une ressource ou une action qu'il n'est pas autorisé à +accéder. + +:: + + Auth->authError = "Désolé, vous n'avez pas les droits suffisants."; + ?> + +autoRedirect +------------ + +Normalement, le Composant Auth vous redirige automatiquement dès lors +qu'il vous authentifie. Parfois, vous souhaitez faire d'autres +vérifications avant de rediriger les utilisateurs : + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- le code de cette fonction ne s'exécute que lorsque autoRedirect est défini à false (i.e. dans un beforeFilter). + if ($this->Auth->user()) { + if (!empty($this->data['Utilisateur']['se_souvenir_de_moi'])) { + $cookie = array(); + $cookie['nom'] = $this->data['Utilisateur']['nom']; + $cookie['motdepasse'] = $this->data['Utilisateur']['motdepasse']; + $this->Cookie->write('Auth.Utilisateur', $cookie, true, '+2 weeks'); + unset($this->data['Utilisateur']['se_souvenir_de_moi']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.Utilisateur'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // Efface le message auth, seulement si nous l'utilisons + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +Le code de la fonction login ne s'exécutera pas *sauf si* vous +définissez $autoRedirect à *false* dans un beforeFilter. Le code présent +dans la fonction de login ne s'exécutera *qu'après* un essai +d'authentification. C'est le meilleur endroit pour déterminer si oui ou +non une connexion réussie a été effectuée par le Composant Auth (vous +aurez peut-être envie d'enregistrer la dernière date d'authentification +réussie, etc.). + +authorize +--------- + +Normalement, le Composant Auth essaiera de vérifier que les critères de +login que vous avez saisis sont exacts, en les comparant à ce qui a été +stocké dans votre modèle utilisateur. Cependant, vous voudrez peut-être +certaines fois effectuer du traitement additionnel, en déterminant vos +propres critères. En assignant à cette variable l'une des nombreuses +valeurs possibles, vous pouvez faire différentes choses. En voici +quelques-unes, parmi les plus communes, que vous souhaiterez peut-être +utiliser. + +:: + + Auth->authorize = 'controller'; + ?> + +Lorsque authorize est défini à 'controller', vous aurez besoin d'ajouter +une méthode appelée isAuthorized() à votre contrôleur. Cette méthode +vous permet de faire plus de vérifications d'authentification et de +retourner ensuite soit true, soit false. + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } else { + return false; + } + } + + return true; + } + ?> + +Souvenez-vous que cette méthode sera inspectée, après que vous ayez déjà +passé la vérification d'authentification simple du modèle utilisateur. + +:: + + Auth->authorize = 'model'; + ?> + +Vous ne souhaitez rien ajouter à votre contrôleur et peut-être utiliser +les ACO's ? Vous pouvez demander au Composant Auth d'appeler une +méthode, nommée isAuthorized(), dans votre modèle utilisateur, pour +faire le même genre de choses : + +:: + + + +Enfin, vous pouvez utiliser authorize avec les actions, comme montré +ci-dessous : + +:: + + Auth->authorize = 'actions'; + ?> + +En utilisant actions, Auth utilisera l'ACL et vérifiera avec +AclComponent::check(). Une fonction isAuthorized n'est pas nésessaire. + +:: + + Auth->authorize = 'crud'; + ?> + +En utilisant crud, Auth utilisera l'ACL et vérifiera avec +AclComponent::check(). Les actions devraient correspondre aux CRUD (voir +`mapActions `_). + +sessionKey +---------- + +Nom de la clé du tableau de session où l'enregistrement de l'utilisateur +actuellement authentifié est stocké. + +Par défaut, vaut "Auth", donc si non spécifié, l'enregistrement est +stocké dans "Auth.{nom $modeleUtilisateur}". + +:: + + Auth->sessionKey = 'Autorise'; + ?> + +ajaxLogin +--------- + +Si vous faites des requêtes Ajax ou Javascript qui nécessitent des +sessions authentifiées, donnez à cette variable le nom d'un élément de +vue que vous souhaiteriez rendre et retourner quand vous avez une +session invalide ou expirée. + +Comme dans toute partie de CakePHP, soyez certains d'avoir jeté une œil +à `la classe +AuthComponent `_ dans +l'API, pour avoir une vision plus approfondie du composant Auth. + +authenticate +------------ + +Cette variable contient une référence à l'objet responsable du hashage +des mots de passe, s'il est nécessaire de changer/surcharger le +mécanisme de hashage des mots de passe par défaut. Voyez `Changer le +type de cryptage `_ pour plus +d'info. + +actionPath +---------- + +Si vous utilisez le contrôle d'accès basé sur les actions, ceci définit +la façon dont sont déterminés les chemins vers les nœuds ACO de +l'action. Si, par exemple, tous les nœuds de contrôleur sont imbriqués +sous un nœud ACO nommé 'Controllers', $actionPath devrait être défini à +'Controllers/'. diff --git a/fr/The-Manual/Core-Components/Cookies.rst b/fr/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..3e137c58690249264231b812aa525012c8dcdc24 --- /dev/null +++ b/fr/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,182 @@ +Cookies +####### + +Le Composant Cookie est un *wrapper* de la méthode native PHP setcookie. +Il inclue aussi une foule de nappages délicieux, pour rendre le codage +des cookies dans les contrôleurs très pratique. Avant de se lancer dans +l'utilisation du Composant Cookie, vous devez être sûr que 'Cookie' est +listé dans le tableau $components de vos contrôleurs. + +Paramètrage du contrôleur +========================= + +Il y a plusieurs variables de contrôleur qui vous permettent de +configurer la façon dont les cookies sont crées et gérés. Définir ces +variables spéciales dans la méthode beforeFilter() de votre contrôleur, +vous permet de définir la façon dont fonctionne le Composant Cookie. + +Variable du Cookie + +Valeur par défaut + +Description + +string $name + +'CakeCookie' + +Le nom du cookie. + +string $key + +null + +Cette chaine est utilisée pour encoder la valeur écrite dans le cookie. +Elle doit être aléatoire et difficile à deviner. + +string $domain + +'' + +Le nom de domaine autorisé à accéder au cookie. Par ex : utilisez +'.votredomaine.com' pour autoriser l'accès à tous vos sous-domaines. + +int ou string $time + +'5 Days' + +Le temps d'expiration de votre cookie. Les nombres entiers sont +interprétés comme des secondes et la valeur 0 est équivalente à 'cookie +de session' : c'est à dire que le cookie expire quand le navigateur est +fermé. Si une chaîne est définie, elle sera interprétée avec la fonction +PHP strtotime(). Vous pouvez paramétrer cela directement dans la méthode +write(). + +string $path + +'/' + +Le chemin sur le serveur où le cookie sera utilisé. Si $cookiePath est +'/foo/', le cookie sera seulement utilisable dans le dossier /foo/ et +dans tous ses sous-répertoires comme /foo/bar/ de votre domaine. La +valeur par défaut est le domaine entier. Vous pouvez le paramétrer +directement dans la méthode write(). + +boolean $secure + +false + +Indique que le cookie doit être transmis seulement par une connexion +sécurisée HTTPS. Quand il vaut true, le cookie sera créé seulement si +une connexion sécurisée existe. Vous pouvez le paramétrer directement +dans la méthode write(). + +Le fragment de contrôleur suivant montre comment inclure le Composant +Cookie et paramétrer les variables du contrôleur nécessaires pour écrire +un cookie nommé 'boulanger\_id', sur le domaine 'exemple.com' qui +nécessite une connexion sécurisée, accessible via le chemin +'/boulangers/preferences/' et qui expire dans une heure. + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'boulanger_id'; + $this->Cookie->time = 3600; // ou '1 hour' + $this->Cookie->path = '/boulangers/preferences/'; + $this->Cookie->domain = 'exemple.com'; + $this->Cookie->secure = true; // envoyé seulement si connexion HTTPS utilisée + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +Maintenant, voyons comment utiliser les différentes méthodes du +Composant Cookie. + +Utiliser le Composant +===================== + +Cette section expose brièvement les méthodes du Composant Cookie. + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +La méthode write() est le cœur du composant cookie, $key est le nom de +la variable de cookie que vous souhaitez et $value est l'information à +stocker. + +:: + + $this->Cookie->write('nom','Larry'); + +Vous pouvez aussi regrouper vos variables en utilisant la notation avec +point pour le paramètre key. + +:: + + $this->Cookie->write('Utilisateur.nom', 'Larry'); + $this->Cookie->write('Utilisateur.role','Chef'); + +Si vous voulez écrire plus d'une valeur pour le cookie en une seule +fois, vous pouvez passer un tableau : + +:: + + $this->Cookie->write( + array('nom'=>'Larry','role'=>'Chef') + ); + +Toutes les valeurs du cookie sont cryptées par défaut. Si vous voulez +stocker les valeurs en texte brut, définissez à false le troisième +paramètre de la méthode write(). + +:: + + $this->Cookie->write('nom','Larry',false); + +Le dernier paramètre pour write est $expires – nombre de secondes avant +que votre cookie n'expire. Par commodité, ce paramètre peut aussi être +passé sous forme d'une chaîne compréhensible par la fonction php +strtotime() : + +:: + + // Les deux cookies expirent dans une heure. + $this->Cookie->write('prenom','Larry',false, 3600); + $this->Cookie->write('nom','Masters',false, '1 hour'); + +**read(mixed $key)** + +Cette méthode est utilisée pour lire la valeur d'une variable de cookie, +dont le nom est spécifié par $key. + +:: + + // Affiche "Larry" + echo $this->Cookie->read('nom'); + + // Vous pouvez aussi utiliser la notation avec point pour read + echo $this->Cookie->read('Utilisateur.nom'); + + // Pour récupérer sous forme de tableau, les variables que vous avez groupées + // en utilisant la notation avec point, faites quelque chose comme : + $this->Cookie->read('Utilisateur'); + + // ceci affiche quelque chose comme : + array('nom' => 'Larry', 'role'=>'Chef') + +**del(mixed $key)** + +Supprime une variable de cookie, dont le nom est $key. Fonctionne pour +la notation avec point. + +:: + + // Supprime une variable + $this->Cookie->del('bar') + + // Supprime la variable de cookie bar, mais pas tout ce qui se trouve sous foo + $this->Cookie->del('foo.bar') + + +**destroy()** + +Détruit le cookie courant. diff --git a/fr/The-Manual/Core-Components/Email.rst b/fr/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..f802fa2e5cf86fdb4f3d46b571c27ea4edef447b --- /dev/null +++ b/fr/The-Manual/Core-Components/Email.rst @@ -0,0 +1,235 @@ +Email +##### + +Le composant Email est une manière simple d'ajouter une fonctionnalité +d'envoi de mail à votre application CakePHP. Utilisant les mêmes +concepts de fichiers dispositions (layout) et de vues pour envoyer des +emails au format html, texte ou les deux. Il offre le support de l'envoi +via la fonction incluse dans PHP, via un serveur smtp ou un mode debug +qui écrit dans un message flash de session. Il supporte l'envoi de +fichiers joints et opère quelques vérifications/filtrages basics sur le +header. Il y a quand même pas mal de choses que ce composant ne peut pas +faire pour vous, mais il offre des fonctionnalités de démarrage. + +Attributs de la classe et variables +=================================== + +Ci-dessous les valeurs qu'on peut positionner avant d'appeler +``EmailComponent::send()`` + +to + adresse de destination (string) +cc + tableau des adresses en copie du message +bcc + tableau des adresses en copie cachée *bcc (blind carbon copy)* +replyTo + adresse de réponse (string) +return + adresse email de retour utilisée en cas d'erreur (string) (pour les + erreurs de démon de mail : *mail-daemon/errors*) +from + adresse de provenance (string) +subject + sujet du message (string) +template + l'élément email à utiliser pour le message (situé dans + ``app/views/elements/email/html/`` et + ``app/views/elements/email/text/``) +layout + le layout utilisé pour l'email (situé dans + ``app/views/layouts/email/html/`` et + ``app/views/layouts/email/text/``) +lineLength + longueur à laquelle les lignes doivent être coupées. Défaut à 70. + (integer) +sendAs + format auquel vous souhaitez envoyer le message (string, valeurs + possibles : ``text``, ``html`` ou ``both``) +attachments + tableau des fichiers à joindre (chemin relatif ou absolu) +delivery + comment envoyer le message (``mail``, ``smtp`` [requiert le + positionnement des smtpOptions ci-dessous] et ``debug``) +smtpOptions + tableau associatif d'options pour smtp mailer (``port``, ``host``, + ``timeout``, ``username``, ``password``, ``client``) + +Il y a quelques autres choses qui peuvent être paramétrées, mais vous +devriez vous référez à l'API pour plus d'informations + +Envoyer des messages multiples dans une boucle +---------------------------------------------- + +Si vous désirez envoyer des emails multiples dans une boucle, vous aurez +besoin de re-initialiser les propriétés du composant avec la méthode +reset. Cette initialisation doit précéder le positionnement des +propriétés du nouvel email. + +:: + + $this->Email->reset() + +Envoyer un message simple +========================= + +Pour envoyer un message sans utiliser de template, passez simplement le +corps du message comme une chaîne (ou un tableau de lignes) à la méthode +send(). Par exemple : + +:: + + $this->Email->from = 'Quelqu\'un '; + $this->Email->to = 'Quelqu\'un d\'autre '; + $this->Email->subject = 'Test'; + $this->Email->send('Corps du message !'); + +Mettre en place les gabarits +---------------------------- + +Pour obtenir à la fois des emails au format html et texte, vous aurez +besoin de créer deux gabarits (*layouts*), comme lorsque vous mettez en +place vos gabarits pour l'affichage de vos vues dans le navigateur. Vous +devrez mettre en place des gabarits par défaut pour les messages email. +Dans le répertoire ``app/views/layouts/``, il vous faudra placer la +structure minimum suivante : + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +Ce sont les fichiers qui contiennent les modèles de gabarits par défaut +pour vos messages. Des exemples ci-dessous : + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +Mettre en place un élément email pour le corps du message +--------------------------------------------------------- + +Dans le répertoire de code ``app/views/elements/email/`` vous devrez +créer deux répertoires pour ``text`` et ``html`` à moins que vous ne +prévoyiez d'envoyer les messages que dans un des deux formats. Dans +chacun de ces répertoires, vous devrez créer les patrons (templates) +pour chaque type de message en se référant au contenu que vous envoyiez +à la vue en utilisant soit $this->set() soit le paramètre $contents de +la méthode send(). Quelques exemples simples sont montrés ci-dessous. +Pour ces exemples on appelle le patron (template) simple\_message.ctp + +``text`` + +:: + + Cher , + Merci de votre intérêt. + +``html`` + +:: + +

Cher ,
+    Merci de votre intérêt.

+ +Contrôleur +---------- + +Dans votre contrôleur, vous devrez ajouter le composant au tableau de +composants ``$components`` ou ajouter ce tableau $components à votre +contrôleur de cette façon : + +:: + + + +Dans cet exemple, nous allons écrire une méthode privée pour prendre en +charge les messages email vers un utilisateur identifié par son $id. +Dans votre contrôleur (le contrôleur User dans cet exemple) : + +:: + + + Utilisateur ->read(null,$id); + $this->Email->to = $Utilisateur ['Utilisateur']['email']; + $this->Email->bcc = array('secret@exemple.com'); + $this->Email->subject = 'Bienvenue à ce truc très cool'; + $this->Email->replyTo = 'support@exemple.com'; + $this->Email->from = 'Appli Web Extra Cool '; + $this->Email->template = 'simple_message'; // notez l'absence de '.ctp' + // Envoi en 'html', 'text' ou 'both' (par défaut c'est 'text') + $this->Email->sendAs = 'both'; // parce que nous aimons envoyer de jolis emails + // Positionner les variables comme d'habitude + $this->set('Utilisateur', $Utilisateur); + // Ne passer aucun argument à send() + $this->Email->send(); + } + ?> + +Voilà pour l'envoi du message. Vous pourriez appeler cette méthode +depuis une autre méthode de cette façon : + +:: + + + $this->_envoiMailNouvelUtilisateur( $this->Utilisateur->id ); + +Envoyer un Message par SMTP +=========================== + +Pour envoyer un email en utilisant un serveur SMTP, les étapes sont +similaires à l'envoi d'un message basique. Définissez la méthode de +distribution à ``smtp`` et assignez toutes les options à la propriété +``smtpOptions`` de l'objet Email. Vous pouvez aussi récupérer les +erreurs SMTP générées durant la session, en lisant la propriété +``smtpError`` du composant. + +:: + + /* Options SMTP */ + $this->Email->smtpOptions = array( + 'port'=>'25', + 'timeout'=>'30', + 'host' => 'votre.serveur.smtp', + 'username'=>'votre_login_smtp', + 'password'=>'votre_mot_de_passe_smtp', + 'client' => 'nom_machine_smtp_helo' + ); + + /* Définir la méthode de distribution */ + $this->Email->delivery = 'smtp'; + + /* Ne passer aucun argument à send() */ + $this->Email->send(); + + /* Vérification des erreurs SMTP. */ + $this->set('smtp-errors', $this->Email->smtpError); + +Si votre serveur SMTP nécessite une authentification, assurez-vous de +définir les parmètres nom d'utilisateur et mot de passe dans +``smtpOptions``, comme indiqué dans l'exemple. + +Si vous ne savez pas ce qu'est un HELO SMTP, alors vous ne devriez pas +avoir besoin de définir le paramètre ``client`` dans ``smtpOptions``. +Celui-ci est seulement nécessaire pour les serveurs SMTP qui ne +respectent pas pleinement la RFC 821 (SMTP HELO). diff --git a/fr/The-Manual/Core-Components/Request-Handling.rst b/fr/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..f3285e1ed8066e402206979806d6f478f9924825 --- /dev/null +++ b/fr/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,279 @@ +Gestion de requêtes +################### + +Le composant Request Handler est utilisé dans CakePHP pour obtenir des +informations additionnelles au sujet des requêtes HTTP qui sont faites à +votre application. Vous pouvez l'utiliser pour informer vos contrôleurs +des process Ajax, tout autant que pour obtenir des informations +complémentaires sur les types de contenus que le client accepte et +modifier automatiquement le layout approprié, quand les extensions de +fichier sont disponibles. + +Par défaut, RequestHandler détectera automatiquement les requêtes Ajax +basée sur le header HTTP-X-Requested-With, qui est utilisé par de +nombreuses librairies javascript. Quand il est utilisé conjointement +avec Router::parseExtensions(), RequestHandler changera automatiquement +le layout et les fichiers de vue par ceux qui correspondent au type +demandé. En outre, s'il existe un assistant avec le même nom que +l'extension demandée, il sera ajouté au tableau d'assistant des +Contrôleurs. Enfin, si une donnée XML est POST'ée vers vos Contrôleurs, +elle sera décomposée en un objet XML, lequel sera assigné à +Controller::data et pourra alors être sauvegardé comme une donnée de +modèle. Afin d'utiliser le Request Handler il doit être inclus dans +votre tableau $components. + +:: + + + +Obtenir des informations sur une requête +======================================== + +Request Handler contient plusieurs méthodes qui nous donne des +informations à propos du client et de ses requêtes. + +accepts ( $type = null) + +$type peut être un string, un tableau , ou 'null'. Si c'est un string, +la méthode accepts() renverra true si le client accepte ce type de +contenu.Si c'est un tableau, accepts() renverra true si un des types du +contenu est accepté par le client. Si c'est 'null', elle renverra un +tableau des types de contenu que le client accepte. Par exemple: + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // Execute le code seulement si le client accepte les réponse HTML (text/html) + } elseif ($this->RequestHandler->accepts('xml')) { + // Execute seulement le code XML + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // Execute seulement si le client accepte un des suivants: XML, RSS ou Atom + } + } + } + +D'autres méthodes de détections du contenu des requêtes: + +isAjax() + +Renvoie true si la requête contient une réponse XMLHttpRequest. + +isSSL() + +Renvoie true si la requête a été faite à travers une connection SSL. + +isXml() + +Renvoie true si la requête actuelle accepte les réponses XML. + +isRss() + +Renvoie true si la requête actuelle accepte les réponses RSS. + +isAtom() + +Renvoie true si l'appel accepte les réponse Atom, false dans le cas +contraire. + +isMobile() + +Renvoie true si le navigateur du client correspond à un téléphone +portable, ou si sle client accepte le contenu WAP. Les navigateurs +mobiles supportés sont les suivants: + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +Returns true if the client accepts WAP content. Renvoie true si le +client accepte le contenu WAP. + +Toutes les méthodes de détection des requêtes précédentes peuvent être +utilisée dans un contexte similaire pour filtrer les fonctionnalités +destiné à du contenu spécifique.Par exemple, au moment de répondre aux +requêtes AJAX, si vous voulez vider le cache du navigateur, et changer +le niveau de débogage. Cependant, si vous voulez ne pas vider le cache +pour les requêtes non-AJAX. , le code suivant vous permettra de le +faire: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + //Continue Controller action + +Vous pouvez aussi vider le cache grâce à cette fonction analogue +``Controller::disableCache()``. + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + //Action du contrôleur + +Détection du type de requête +============================ + +RequestHandler fournit aussi des informations quant au type des requêtes +HTTP qui ont été faites et vous autorise à répondre à chaque type de +requêtes. + +isPost() + +Renvoie true si la requête est de type POST. + +isPut() + +Renvoie true si la requête est de type PUT. + +isGet() + +Renvoie true si la requête est de type GET. + +isDelete() + +Renvoie true si la requête est de type DELETE. + +Obtenir des informations supplémentaires sur le client +====================================================== + +getClientIP() + +Renvoie l'adresse IP du client. + +getReferrer() + +Renvoie le nom de domaine à partir duquel la requête a été faite. + +getAjaxVersion() + +Renvoie la version de la librairie 'Prototype' si la requête est de type +AJAX ou une chaîne de caractères vide dans le cas contraire. La +librairie 'Prototype' contient un header HTTP spécifique qui permet de +déterminer sa version. + +Répondre aux Requêtes +===================== + +En plus de la détection de requêtes, RequestHandler fournit également +une solution simple pour modifier la sortie de façon à ce que le type de +contenu corresponde à votre application. + +setContent($name, $type = null) + +- $name string - Le nom du type de contenu (*Content-type*), par ex : + html, css, json, xml. +- $type mixed - Le(s) type(s) mime(s) auquel se réfère Content-type. + +setContent ajoute/définit les Content-types pour le nom précisé. Permet +aux content-types d'être associés à des alias simplifiés et/ou à des +extensions. Ceci permet à RequestHandler de répondre automatiquement aux +requêtes de chaque type dans sa méthode startup. De plus, ces types de +contenu sont utilisées par prefers() et accepts(). + +setContent est bien mieux utilisé dans le beforeFilter() de vos +contrôleurs, parce qu'il tirera un meilleur profit de *l'automagie* des +alias de content-type. + +Les correspondances par défaut sont : + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +Détermine quels content-types préfère le client. Si aucun paramètre +n'est donné, le type de contenu le plus approchant est retourné. Si +$type est un tableau, le premier type que le client accepte sera +retourné. La préférence est déterminée, premièrement par l'extension de +fichier analysée par Router, si il y en avait une de fournie et +secondairement, par la liste des content-types définis dans +HTTP\_ACCEPT. + +renderAs($controller, $type) + +- $controller - Référence du contrôleur +- $type - nom simplifié du type de contenu à rendre, par exemple : xml, + rss. + +Change le mode de rendu d'un contrôleur pour le type spécifié. Ajoutera +aussi l'assistant (*helper*) approprié au tableau des assistants du +contrôleur, s'il est disponible et qu'il n'est pas déjà dans le tableau. + +respondAs($type, $options) + +- $type - nom simplifié du type de contenu à rendre, par exemple : xml, + rss ou un content-type complet, tel application/x-shockwave +- $options - Si $type est un nom simplifié de type, qui a plus d'une + association avec des contenus, $index est utilisé pour sélectionner + le type de contenu. + +Définit l'en-tête de réponse basé sur la correspondance +content-type/noms. Si DEBUG est plus grand que 2, l'en-tête n'est pas +défini. + +responseType() + +Retourne l'en-tête Content-type du type de réponse courant ou null s'il +y en a déjà un de défini. + +mapType($ctype) + +Rétro-associe un content-type à un alias diff --git a/fr/The-Manual/Core-Components/Security-Component.rst b/fr/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..5ef275791255727ccd0cd3bc9e1e1a7f54aace80 --- /dev/null +++ b/fr/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,246 @@ +Composant Security +################## + +Le composant Security offre une manière simple d'inclure une sécurité +renforcée à votre application. Une interface pour gérer les requêtes +HTTP-authentifiées peut être créée avec le composant Security. Il est +défini dans le beforeFilter() de vos contrôleurs. Il possède de nombreux +paramètres configurables. Toutes ces propriétés peuvent être définies +directement ou par l'intermédiaire de méthodes *setters* du même nom. + +Si une action est restreinte par le composant Security, elle devient un +trou noir, comme une requête invalide qui aboutira à une erreur 404 par +défaut. Vous pouvez configurer ce comportement, en définissant la +propriété $this->Security->blackHoleCallback par une fonction de rappel +(*callback*) dans le contrôleur. Gardez à l'esprit que ces trous noirs, +issus de toutes les méthodes du composant Security, seraient exécutés à +travers cette méthode de rappel. + +Quand vous utilisez le composant Security, vous **devez** utiliser +l'assistant Form pour créer vos formulaires. Le composant Security +recherche certains indicateurs qui sont créés et gérés par l'assistant +Form (spécialement ceux créés dans create() et end()). + +Configuration +============= + +$blackHoleCallback + Un contrôleur de callback qui gèrera les requêtes envoyées dans le + "trou noir" +$requirePost + Liste d'actions de contrôleurs qui exigent que se produise une + requête POST. Un tableau d'actions de contrôleurs ou '\*' pour + forcer toutes les actions à exiger un POST. +$requireSecure + Liste d'actions qui exigent que se produise une connexion SSL. Un + tableau d'actions de contrôleurs ou '\*' pour forcer toutes les + actions à exiger une connexion SSL. +$requireAuth + Liste d'actions qui exigent une clé d'authentification valide. Cette + clé de validation est définie par le Composant Security. +$requireLogin + Liste d'actions qui exigent des connexions HTTP Authentifiées (basic + ou digest). Acceptent aussi '\*' pour indiquer que toutes les + actions de ce contrôleur exigent une authentification HTTP. +$loginOptions + Les options pour les requêtes de connexion HTTP authentifiée. Vous + permet de définir le type d'authentification et le contrôleur de + callback pour le processus d'authentification. +$loginUsers + Un tableau associatif de noms\_utilisateurs => mots\_de\_passe qui + est utilisé pour les connexions HTTP authentifiées. Si vous utilisez + l'authentification digest, votre mot de passe devra être de hashage + MD5 +$allowedControllers + Une liste de contrôleurs à partir desquelles les actions du + contrôleur courant sont autorisées à recevoir des requêtes. Ceci + peut être utilisé pour contrôler les demandes croisées de + contrôleur. +$allowedActions + Les actions parmi celles du contrôleur courant qui sont autorisées à + recevoir des requêtes. Ceci peut être utilisé pour contrôler les + demandes croisées de contrôleur. +$disabledFields + Liste des champs de formulaire qui devraient être ignorés lors de la + validation du POST. La valeur, présence ou absence de ces champs de + formulaire, ne sera pas prise en compte lors de la vérification de + la validité de soumission du formulaire. Spécifiez les champs comme + vous le faites pour l'assistant Form (Model.nomduchamp). + +Méthodes +======== + +requirePost() +------------- + +Définit les actions qui nécessitent une requête POST. Prend un nombre +indéfini de paramètres. Peut être appelé sans arguments, pour forcer +toutes les actions à requérir un POST. + +requireSecure() +--------------- + +Définit les actions qui nécessitent une requête sécurisée avec SSL. +Prend un nombre indéfini de paramètres. Peut être appelé sans arguments +pour forcer toutes les actions à requérir une connexion sécurisée par +SSL. + +requireAuth() +------------- + +Définit les actions qui nécessitent un jeton valide généré par le +Composant Security. Prend un nombre indéfini de paramètres. Peut être +appelé sans arguments pour forcer toutes les actions à requérir une +authentification valide. + +requireLogin() +-------------- + +Définit les actions qui nécessitent une requête HTTP-Authentifiées +valide. Prend un nombre indéfini de paramètres. Peut être appelé sans +arguments pour forcer toutes les actions à requérir une authentification +HTTP. + +loginCredentials(string $type) +------------------------------ + +Tente de valider les caractéristiques du login pour une requête +HTTP-authentifiée. $type est le type d'authentification HTTP que vous +voulez vérifier. Soit 'basic', soit 'digest'. Si laissé null/vide, les +deux seront essayés. Retourne un tableau avec le nom et le mot de passe +en cas de succès. + +loginRequest(array $options) +---------------------------- + +Génère le texte pour l'en-tête d'une requête HTTP-Authentifiée, d'après +un tableau $options. + +$options contient généralement un 'type', un 'realm' (domaine). Le type +indique quelle méthode HTTP-Authentifiée utiliser. Le domaine par défaut +est lié à l'environnement actuel du serveur HTTP. + +parseDigestAuthData(string $digest) +----------------------------------- + +Analyse une requête d'authentification HTTP digest. Retourne un tableau +associatif de données digest en cas de succès et null en cas d'échec. + +generateDigestResponseHash(array $data) +--------------------------------------- + +Crée un hash à comparer avec une réponse HTTP-authentifiée digest. $data +devrait être un tableau créé par +SecurityComponent::parseDigestAuthData(). + +blackHole(object $controller, string $error) +-------------------------------------------- + +Met en "trou noir" (*black-hole*) une requête invalide, avec une erreur +404 ou un callback personnalisé. Sans callback, la requête sera +abandonnée. Si un callback de contrôleur est défini pour +SecurityComponent::blackHoleCallback, il sera appelé et passera toute +information sur l'erreur. + +Utilisation +=========== + +Le composant Security est généralement utilisé dans la méthode +beforeFilter() de votre contrôleur. Vous pouvez spécifier les +restrictions de sécurité que vous voulez et le composant Security les +forcera au démarrage + +:: + + Security->requirePost('delete'); + } + } + ?> + +Dans cette exemple, l'action delete peut être effectuée avec succès si +celui ci reçoit une requête POST + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +Cette exemple forcera toutes les actions qui proviennent de la "route" +Admin à être effectuées via des requêtes sécurisées SSL. + +Authentification HTTP Basic +=========================== + +Le composant Security a quelques fonctions d'authentifications très +puissantes. Parfois, vous pouvez avoir besoin de protéger quelques +fonctionnalités de votre application, en utilisant une `authentification +HTTP Basic `_. L'un +des usages courant de l'authentification HTTP est la protection d'une +API REST ou SOAP. + +Ce type d'authentification est appelée basic pour une raison simple. A +moins que vous ne transfériez les informations par SSL, les identifiants +seront transférés en texte brut. + +Utiliser le composant Security pour les authentifications HTTP est +facile. L'exemple suivant inclue le composant Security et quelques +lignes de code dans la méthode beforeFilter() du contrôleur. + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MonDomaine' + ); + $this->Security->loginUsers = array( + 'john'=>'mot_passe_john', + 'jane'=>'mot_passe_jane' + ); + $this->Security->requireLogin(); + } + + function index() { + // Logique protégée de l'application ici... + } + } + +La propriété loginOptions du composant Security est un tableau +associatif qui spécifie comment le login devrait être manipulé. Vous +avez uniquement besoin de spécifier le **type** comme **basic**, pour +que cela fonctionne. Spécifier le **realm** (domaine) si vous voulez +afficher un joli message à quiconque essaiera de s'identifier ou si vous +avez plusieurs sections avec authentification dans votre application (= +*realms*) que vous voulez garder séparées. + +La propriété loginUsers du composant Security est un tableau associatif +contenant les utilisateurs qui peuvent accéder à ce domaine et leurs +mots de passe. Les exemples montrés ici utilisent des informations +client codées en dur, mais vous voudrez certainement utiliser un modèle +pour rendre vos identifiants d'authentification plus manageables. + +Enfin, requireLogin() indique au composant Security que ce contrôleur +nécessite une identification. Comme avec requirePost(), ci-dessus, +fournir des noms aux méthodes protègera celles-ci tout en laissant les +autres accessibles. diff --git a/fr/The-Manual/Core-Components/Sessions.rst b/fr/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..8b6487cf8f0e4c0ecf7b1d0248c0b7a5ef9ba54e --- /dev/null +++ b/fr/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,162 @@ +Sessions +######## + +Le composant session de CakePHP fournit le moyen de faire persister les +données client entre les pages requêtées. Il agit comme une interface +pour $\_SESSION et offre aussi des méthodes pratiques pour de nombreuses +fonctions relatives à $\_SESSION. + +Les sessions peuvent persister de différentes façons. Par défaut, elles +utilisent les paramètres fournis par PHP, cependant, d'autres options +existent. + +cake + Sauvegarde les fichiers de session dans votre dossier tmp/sessions + de app. +database + Utilise les sessions en base de données de CakePHP +cache + Utilise le moteur de cache configuré par Cache::config(). Très utile + quand utilisé conjointement avec Memcache (dans les configurations + avec des multiples serveurs d'applications) pour stocker à la fois + les données mises en cache et les sessions. +php + C'est le paramètre par défaut. Sauvegarde les fichiers de session + comme indiqué dans php.ini + +Pour changer la méthode de manipulation des session par défaut, changez +la configuration de Session.save selon vos objectifs. Si vous choisissez +'database', vous devriez aussi décommenter les paramètres de +Session.datababase et exécuter le fichier SQL de base de données de +session qui se trouve dans app/config. + +Méthodes +======== + +Le composant Session est utilisé pour interagir avec les informations de +session. Il inclut les fonctions CRUD basiques, mais aussi des +fonctionnalités pour créer des messages de *feedback* aux utilisateurs. + +Il est important de noter que ces structures en tableaux peuvent être +créées dans la session en utilisant la notation avec un point. Par +exemple, Utilisateur.identifiant se réfèrera au tableau suivant : + +:: + + array('Utilisateur' => + array('identifiant' => 'ClarkKent@dailyplanet.com') + ); + +Les points sont utilisés pour indiquer les tableaux imbriqués. Cette +notation est utilisée pour toutes les méthodes du composant Session dans +lesquelles une variable $name est utilisée. + +write +----- + +``write($name, $value)`` + +Ecrit dans la Session, en mettant $value dans $name. $name peut-être un +tableau séparé par un point. Par exemple : + +:: + + $this->Session->write('Personne.couleurYeux', 'Vert'); + +Cela écrit la valeur 'Vert' dans la session sous Personne => +couleurYeux. + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +Utilisé pour définir une variable de session qui peut être utilisée pour +un rendu dans la Vue. $layout vous permet de contrôler quel gabarit +(situé dans ``/app/views/layouts``) doit être utilisé pour le rendu du +message. Si vous laissez ``$layout`` sur 'default', le message sera +encadré par ce qui suit : + +:: + +
[message]
+ +$params vous permet de passer des variables additionnelles pour le +gabarit rendu. $key place l'index $messages dans le tableau Message. Par +défaut, c'est 'flash'. + +Des paramètres peuvent être passés pour modifier le ``div`` rendu, par +exemple, ajouter "class" dans le tableau $params, affectera une classe +CSS au ``div`` de sortie, en utilisant ``$session->flash()`` dans votre +gabarit ou votre vue. + +:: + + $this->Session->setFlash('Message textuel d\'exemple', 'default', array('class' => 'classe_exemple')) + +Le résultat après utilisation de ``$session->flash()`` avec l'exemple +ci-dessus devrait être : + +:: + +
Message textuel d'exemple
+ +read +---- + +``read($name)`` + +Retourne la valeur de $name dans la session. Si $name vaut null, la +session entière sera retournée. Par ex : + +:: + + $vert = $this->Session->read('Personne.couleurYeux'); + +Récupère la valeur "vert" dans la session. + +check +----- + +``check($name)`` + +Utilisé pour vérifier qu'une variable de Session a été créée. Retourne +vrai si la variable existe et faux dans le cas contraire. + +delete +------ + +``delete($name) /*ou*/ del($name)`` + +Supprime les données de Session de $name. Par ex : + +:: + + $this->Session->del('Personne.couleurYeux'); + +Notre donnée de session n'a plus la valeur 'Vert' ni même l'index +couleurYeux attribué. Cependant, le modèle Personne est toujours dans la +Session. Pour supprimer de la session toutes les informations de +Personne, utilisez : + +:: + + $this->Session->del('Personne'); + +destroy +------- + +La méthode ``destroy`` supprimera le cookie de session et toutes les +données de session stockées dans le fichier temporaire du système. Cela +va détruire la session PHP et ensuite en créer une nouvelle. + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +Utilisé pour déterminer la dernière erreur dans une session. diff --git a/fr/The-Manual/Core-Console-Applications.rst b/fr/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..e74699204a75131b1ef3d425df63ca51471cd9e2 --- /dev/null +++ b/fr/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,25 @@ +Applications en mode console intégrées +###################################### + +CakePHP offre un certain nombre d'applications en mode console prêtes à +l'emploi. Certaines des ces applications sont utilisées conjointement +avec d'autres fonctionnalités de CakePHP (comme ACL ou i18n) et d'autres +sont faites pour un usage général en vous permettant de les lancer +rapidement. + +Cette rubrique explique comment utiliser les applications en mode +console livrées avec CakePHP. + +Avant que vous ne vous y plongiez, vous pourriez revoir la section +`Console CakePHP `_ couverte +précédemment. La configuration de la Console n'est pas expliquée ici, +donc si vous ne l'avez jamais utilisée avant, relisez la section +correspondante. + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/fr/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/fr/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..a28422bbd96603011cc05f32837bc7b25d635c52 --- /dev/null +++ b/fr/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,64 @@ +Génération de code avec Bake +############################ + +Vous avez déjà découvert le prototypage *(scaffolding)* avec CakePHP : +une façon simple de visualiser l'application finale avec seulement une +base de données et quelques classes minimales. La console *Bake* de +CakePHP est un autre outil permettant de réaliser son application +rapidement. La console *Bake* peut créer chacun des ingrédients basiques +de CakePHP : modèles, vues et controlleurs. Et nous ne parlons pas +seulement des squelettes de classes : *Bake* peut créer une application +complète fonctionnelle en seulement quelques minutes. En réalité, *Bake* +est l'étape naturelle faisant suite au prototypage d'une application. + +Pour utiliser *Bake*, vous devrez exécuter le script *cake* situé dans +le dossier /cake/console/. + +:: + + $ cd ./cake/console/ + $ cake bake + +Suivant la configuration de votre installation, vous devrez peut être +donner les droits d'éxécution au script bash *cake* ou l'appeler avec la +commande ./cake bake. La console *cake* est exécutée en utilisant le CLI +PHP (Interface de Ligne de Commande). Si vous avez des problèmes en +exécutant ce script, vérifiez que le CLI PHP est installé et qu'il a les +modules adéquats autorisés (ex: MySQL). + +En exécutant *Bake* la première fois, vous serez invité à créer un +fichier de configuration de la base de données, si vous n'en avez pas +créé auparavant. + +Une fois la configuration à la base de donnée réalisée, exécuter *Bake* +vous présentera les options suivantes : + +:: + + --------------------------------------------------------------- + App : app + Path: /path-to/project/app + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake? (D/M/V/C/P/Q) + > + +De façon alternative, vous pouvez exécuter chacune de ces commandes +directement depuis la ligne de commande : + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake project + $ cake bake test + diff --git a/fr/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/fr/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..62a29b1291da977968a6e78ca2761b11f6d42cd4 --- /dev/null +++ b/fr/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,39 @@ +Modifier le rendu HTML produit par les templates de "bake" +########################################################## + +Si vous voulez modifier le rendu HTML produit par défaut par la commande +"bake", suivez ces simples étapes : + +**Pour "cuire" des vues personnalisées :** + +#. Allez dans : cake/console/libs/templates/views +#. Notez la présence de 4 fichiers +#. Copiez les vers : app/vendors/shells/templates/views +#. Changez le rendu HTML pour contrôler la façon dont "bake" construit + vos vues. + +**Pour "cuire" des projets personnalisés :** + +#. Allez dans: cake/console/libs/templates/skel +#. Notez la présence des fichiers de base de l'application +#. Copiez les dans : app/vendors/shells/templates/skel +#. Changez le rendu HTML pour contrôler la façon dont "bake" construit + vos vues. +#. Passez le paramètre 'chemin du squelette' à la tâche project : + + :: + + cake bake project -skel vendors/shells/templates/skel + +Notes + +- Vous devez lancer la commande ``cake bake project`` pour que le + paramètre path puisse être passé. +- Le chemin du template est relatif au dossier courant de votre + interface de ligne de commande. +- Vu que le chemin absolu vers le squelette nécessite d'être entré + manuellement, vous pouvez spécifier n'importe quel répertoire + contenant le template que vous voulez construire, y compris en + utilisant plusieurs templates. (A moins que Cake commence à gérer la + surcharge du répertoire skel comme il le fait pour les vues) + diff --git a/fr/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/fr/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..6fa9ad1d57afd5dc214f6a50d6f574d023d6db6a --- /dev/null +++ b/fr/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,89 @@ +Gestion du schéma et migrations +############################### + +Le shell *Schema* fournit une fonctionnalité pour créer des objets, des +dumps sql et pour créer et restaurer des vues instantanées de votre base +de données. + +Générer et utiliser les fichiers Schema +======================================= + +Un fichier de schéma généré vous permet de transporter facilement un +schéma agnostique de votre base de données. Vous pouvez générer un +fichier de schéma de votre base en utilisant la commande : + +:: + + $ cake schema generate + +Cela va créer un fichier schema.php dans votre dossier +``app/config/sql`` . + +Le shell *schema* n'utilise que les tables pour lesquelles des modèles +sont définis. Pour forcer le shell à considérer toutes les tables, vous +devez ajouter l'option ``-f`` à votre ligne de commande. + +Pour reconstruire plus tard votre schéma de base de données à partir +d'un fichier schema.php précédemment réalisé, lancez : + +:: + + $ cake schema run create + +Cela va supprimer et créer les tables en se basant sur le contenu de +schema.php. + +Les fichiers de schéma peuvent aussi être utilisés pour générer des +dumps sql. Pour générer un fichier sql comprennant les définitions +``CREATE TABLE``, lancez: + +:: + + $ cake schema dump nomdufichier.sql + +*nomdufichier.sql* est le nom souhaité pour le fichier contenant le dump +sql. Si vous omettez *nomdufichier.sql*, le dump sql sera affiché sur la +console, mais ne sera pas écrit dans un fichier. + +Migrations avec le shell schema de CakePHP +========================================== + +Les migrations permettent de "versionner" votre schéma de base de +données, de telle façon que lorsque vous développez des fonctionnalités, +vous avez une méthode facile et élégante pour relever les modifications +apportées à votre base. Les migrations sont réalisées soit grâce aux +fichiers de schémas, soit grâce aux vues instantanées. Versionner un +fichier de schéma avec le shell *schema* est assez facile. Si vous avez +déjà un fichier *schema* créé en utilisant : + +:: + + $ cake schema generate + +Vous aurez alors les choix suivants : + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +Choisir [s] (snapshot - vue instantanée) va créer un fichier schema.php +incrémenté. Ainsi, si vous avez schema.php, cela va créer schema\_2.php +et ainsi de suite. Vous pouvez ensuite restaurer chacun de ces schémas +en utilisant : + +:: + + $ cake schema run update -s 2 + +Où 2 est le numéro de la vue instantanée que vous voulez exécuter. Le +shell vous demandera de confirmer votre intention d'exécuter les +définitions ``ALTER`` qui représentent les différences entre la base +existante et le fichier de schéma exécuté à ce moment. + +Vous pouvez effectuer un lancement d'essai ("dry run") en ajoutant +``-dry`` à votre commande. diff --git a/fr/The-Manual/Core-Helpers.rst b/fr/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..31dfc0ba061afc6fc175eb57d8565ef4ef4dfa2e --- /dev/null +++ b/fr/The-Manual/Core-Helpers.rst @@ -0,0 +1,27 @@ +Assistants intégrés +################### + +Les Assistants (*Helpers*) sont des classes comme les composants pour la +couche de présentation de votre application. Ils contiennent de la +logique de présentation qui est partagée entre plusieurs vues, éléments +ou *layouts*. Cette section décrit chacun de ces assistants intégrés à +CakePHP tels que Form, Html, JavaScript et RSS. Lisez la rubrique +Assistants pour en apprendre davantage sur eux et sur la manière dont +vous pouvez créer vos propres assistants. + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/fr/The-Manual/Core-Helpers/AJAX.rst b/fr/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..137830b3ac585c387e28b5b25910495cf19c97ba --- /dev/null +++ b/fr/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,801 @@ +AJAX +#### + +L'assistant AJAX utilise les librairies populaires que sont Prototype et +script.aculo.us pour les requêtes AJAX et les effets de slide côté +client. Pour utiliser l'assistant AJAX, vous devez avoir la version +actuelle de la librairie Javascript +`www.prototypejs.org `_ et +`http://script.aculo.us `_ placé dans +/app/webroot/js/.De plus, vous devrez inclure les librairies Javascript +Prototype et script.aculo.us dans chaque vues utilisant les +fonctionnalités de l'assistant AJAX. + +Vous devez inclure les assistants AJAX et Javascript dans votre +contrôleur. + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +Une fois l'assistant Javascript inclus dans votre contrôleur, vous +pouvez utiliser la méthode link() de l'assistant Javascript pour inclure +Prototype et Scriptaculous: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +Vous pouvez désormais utiliser l'assistant AJAX dans votre vue: + +:: + + $ajax->whatever(); + +Si le `Composant RequestHandler `_ est +inclus dans le contrôleur, cakePHP appliquera automatiquement la mise en +page AJAX quand une requête sera demandée. + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +Options de l'assistant AJAX +=========================== + +La plupart des méthodes de l'assistant AJAX vous autorisent à fournir un +tableau $options. Vous pouvez utiliser ce tableau pour configurer le +comportement de l'assistant AJAX. Avant que nous abordions les méthodes +spécifiques à l'assistant, jetons un œil aux différentes options +disponibles via ce tableau spécial. Vous vous référerez à cette section +quand vous commencerez à utiliser les méthodes de l'assistant AJAX. + +Options générales +----------------- + +``$option`` keys + +Description + +``$options['evalScripts']`` + +Détermine si les balises script dans le contenu retourné sont évaluées. +Défini à *true* par défaut. + +``$options['frequency']`` + +Le nombre de seconde entre les vérifications à intervalle régulier. + +``$options['indicator']`` + +L'id DOM d'un élément à montrer lorsqu'une requête est en chargement et +à cacher quand la requête est finie. + +``$options['position']`` + +Pour insérer plutôt que remplacer, utilisez cette option pour préciser +la position d'insertion entre *top*, *bottom*, *after* ou *before*. + +``$options['update']`` + +L'id de l'élement du DOM qui sera mis à jour avec le contenu retourné. + +``$options['url']`` + +L'url au format contrôleur/action que vous souhaitez appeler. + +``$options['type']`` + +Indique si la requête doit être 'synchronous' (synchrone) ou +'asynchronous' (asynchrone, valeur par défaut). + +``$options['with']`` + +Une chaîne "URL-encodée" qui sera ajoutée à l'URL pour les méthodes get +ou dans le corps du post pour tout autre méthode. Exemple : +``x=1&toto=tata&y=2``. Les paramètres seront disponibles dans +``$this->params['form']`` ou dans ``$this->data``, en fonction du +format. Pour plus d'information, voyez la méthode `Serialize de +Prototype `_. + +Options Callback +---------------- + +Les options *Callbacks* vous permettent d'appeler des fonctions +JavaScript à des endroits spécifiques. Si vous cherchez un moyen +d'injecter un peu de logique, avant, après ou lors de vos opérations +utilisant l'assistant Ajax, utilisez ces *callbacks* pour mettre les +choses en place. + +$options + +Description + +$options['condition'] + +Extrait de code JavaScript qui doit évaluer *true* avant que la demande +soit initialisée. + +$options['before'] + +Exécuté avant que la demande soit faite. Une utilisation courante de ce +*callBack* est de permettre l'affichage d'un indicateur de progression. + +$options['confirm'] + +Texte à afficher dans une alerte de confirmation JavaScript avant de +continuer. + +$options['loading'] + +*Callback* exécuté lorsque des données sont récupérées à partir du +serveur. + +$options['after'] + +JavaScript appelé après que la requête soit lancée et avant que le +*callback* $options['loading'] se lance. + +$options['loaded'] + +*Callback* exécuté lorsque le document distant a été reçu par le client. + +$options['interactive'] + +Appelée lorsque l'utilisateur peut interagir avec le document distant, +même si il n'a pas fini de se charger. + +$options['complete'] + +*Callback* JavaScript à exécuter lorsque XMLHttpRequest est terminé. + +Méthodes +======== + +link +---- + +``link(string $title, mixed $href, array $options, string $confirm, boolean $escapeTitle)`` + +Retourne un lien vers une action distante définie par +``$options['url']`` ou ``$href`` qui est appelée via la méthode +XMLHttpRequest quand le lien est cliqué. Le résultat de cette requête +peut être inséré dans un objet DOM pour qui l'id sera spécifié par +``$options['update']``. + +Si ``$options['url']`` est vide le href est utilisé comme substitution + +Exemple: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +Par défaut, ces requêtes distantes sont asynchrones et pendant +lesquelles une série de callbacks peuvent être déclenchés. + +Exemple: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Hello World" )' ) + ); + ?> + +Pour utiliser une méthode synchrone, spécifiez +``$options['type'] = 'synchronous'``. + +Pour définir automatiquement le layout ajax, insérez le composant +*RequestHandler* dans votre Controller. + +Par défaut le contenu de l'élément cible (DOM) sera remplacé. Pour +changer cela définissez ``$options['position']`` + +Exemple: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +``$confirm`` peut être utilisé pour appeler un message de confirmation +Javascript avant que la requête ne soit démarrée ceci afin de prévenir +l'utilisateur de l'exécution de la tâche. + +Exemple: + +:: + +
+
+ link( + 'Delete Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Do you want to delete this post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +Cette fonction crée le code JavaScript nécessaire pour effectuer un +appel distant. Elle est principalement utilisée en tant qu'assistant +pour link(). Ceci n'est pas utilisé très souvent, à moins que vous +n'ayez besoin de générer des scripts personnalisés. + +Les ``$options`` pour cette fonction sont les mêmes que pour la méthode +``link`` + +Exemple : + +:: + +
+
+ + +Il peut aussi être assigné à un attribut d'évènement HTML : + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'voir', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Bougez la souris ici +
+ +Si ``$options['update']`` n'est pas transmis, le navigateur ignorera la +réponse du serveur. + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +Appelle périodiquement l'action ``$options['url']``, toutes les +``$options['frequency']`` secondes. Généralement utilisé pour mettre à +jour un div spécifique (défini dans ``$options['update']``) avec le +résultat de l'appel distant. Les *Callbacks* peuvent être utilisés. + +``remoteTimer`` est identique à ``remoteFunction`` à l'exception du +paramètre supplémentaire ``$options['frequency']`` + +Exemple : + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'voir', 1 ), + 'update' => 'post', 'complete' => 'alert( "requête terminée" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +La valeur par défaut de ``$options['frequency']`` est 10 secondes + +form +---- + +``form(string $action, string $type, array $options)`` + +Retourne une balise form qui soumet à $action, en utilisant +XMLHttpRequest à la place d'une requête HTTP normale via $type ('post' +ou 'get'). Autrement, la soumission du formulaire se comportera +exactement comme d'habitude : les données soumises sont disponibles par +$this->data à l'intérieur de vos contrôleurs. Si $options['update'] est +spécifié, elles seront mises à jour avec le document résultant. Les +callbacks peuvent être utilisés. + +Le tableau options devrait inclure le nom du modèle, par exemple : + +:: + + $ajax->form('edit','post',array('model'=>'Utilisateur','update'=>'DivInfoUtilisateur')); + +Alternativement, si vous avez besoin de croiser des données post avec un +autre contrôleur depuis votre formulaire : + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'Utilisateur', + 'update'=>'DivInfoUtilisateur', + 'url' => array( + 'controller' => 'commentaires', + 'action' => 'edit' + ) + ) + )); + +Vous ne devriez pas utiliser ``$ajax->form()`` et ``$ajax->submit()`` +dans le même formulaire. Si vous voulez que la validation du formulaire +fonctionne proprement, utilisez la méthode ``$ajax->submit()`` comme +indiqué dans la section suivante. + +submit +------ + +``submit(string $titre, array $options)`` + +Retourne un bouton *submit* qui soumet le formulaire à +``$options['url']`` et met à jour le div spécifié dans +``$options['update']`` + +:: + +
+ create('Utilisateur'); + echo $form->input('email'); + echo $form->input('nom'); + echo $ajax->submit('Soumettre', array('url'=> array('controller'=>'utilisateurs', 'action'=>'ajouter'), 'update' => 'testdiv')); + echo $form->end(); + ?> +
+ +Utilisez la méthode ``$ajax->submit()`` si vous voulez que la validation +de formulaire fonctionne proprement c'est-à-dire, si vous voulez que vos +messages que vous spécifiez dans vos règles de validation s'affichent +correctement. + +observeField +------------ + +``observeField(string $field, array $options)`` + +Surveille le champ dont l'id DOM est spécifié par $field (toutes les +$options['frequency'] secondes) et réalise un XMLHttpRequest quand son +contenu a changé. + +Lorsqu'aucune fréquence ou un petit intervalle de fréquence (entre 0 et +1) est spécifié, un prototype ``Form.Element.EventObserver`` sera +utilisé à la place d'un ``Form.Element.Observer``. Le +``Form.Element.EventObserver`` n'est pas minuté et sera exécuté en même +temps que la valeur de l'élément aura changé. + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'titre', array( 'options' => $titres ) ) ?> + + + observeField( 'PostTitre', + Array + ( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +``observeField`` utilise les mêmes options que ``link`` + +Le champ à transmettre peut être défini en utilisant +``$options['with']``. Celui-ci constitue la valeur par défaut de +``Form.Element.serialize('$field')``. Les données soumises sont +disponibles par ``$this->data`` dans vos contrôleurs. Les callbacks +peuvent être utilisés avec cette fonction. + +Pour envoyer le formulaire entier quand le champ change, utilisez +``$options['with'] = Form.serialize( $('Form ID') )`` + +observeForm +----------- + +``observeForm(string $form, array $options)`` + +Similaire à observeField(), mais fonctionne sur un formulaire complet, +identifié par son id DOM $form. Les $options fournies sont les mêmes que +observeField(), à l'exception de la valeur par défaut de l'option +$options['with'], qui est évaluée à la valeur sérialisée (chaine de +requête) du formulaire. + +autoComplete +------------ + +``autoComplete(string $field, string $url, array $options)`` + +Affiche un champ texte avec auto-complétion pour $field. L'action +distante située à $url devrait retourner une liste appropriée de termes +auto-completés. Une liste non-ordonnée est souvent utilisée pour celà. +Premièrement, vous avez besoin de paramétrer une action de contrôleur, +qui récupère et organise les données dont vous avez besoin pour votre +liste, en fonction de la saisie utilisateur : + +:: + + function autoComplete() { + // Chaînes partielles qui arriveront du champ auto-complété comme + // $this->data['Post']['sujet'] + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.sujet LIKE' => $this->data['Post']['sujet'].'%' + ), + 'fields' => array('sujet') + ))); + $this->layout = 'ajax'; + } + +Ensuite, créez ``app/views/posts/auto_complete.ctp`` qui utilise ces +données et crée une liste non-ordonnée en (X)HTML : + +:: + +
    + +
  • + +
+ +Enfin, utilisez autoComplete() dans une vue pour créer votre champ de +formulaire auto-complété : + +:: + + create('Utilisateur', array('url' => '/utilisateurs/index')); ?> + autoComplete('Post.sujet', '/posts/autoComplete')?> + end('Voir le Post')?> + +Une fois que vous obtenez un appel autoComplete() qui fonctionne +correctement, utilisez les CSS pour styler la boîte de suggestion de +l'auto-complétion. Vous pourriez terminer en utilisant quelque chose de +similaire à ce qui suit : + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +Si vous voulez que l'utilisateur saisisse un nombre minimum de +caractères avant que l'auto-complétion ne démarre, vous pouvez utiliser +l'option minChars comme ceci : + +:: + + $ajax->autoComplete('Post.sujet', '/posts/autoComplete',array('minChars' => 3)); + +isAjax +------ + +``isAjax()`` + +Vous permet de vérifier si la requête actuelle est une requête Ajax de +Prototype à l'intérieur d'une vue. Renvoie un booléen. Peut être utilisé +pour une logique de présentation, pour afficher/cacher des blocs de +contenu. + +drag & drop +----------- + +``drag(string $id, array $options)`` + +Rend un élément déplaçable à l'extérieur de l'élément DOM spécifié par +$id. Pour plus d'informations sur les paramètres acceptés dans $options, +voir +`http://github.com/madrobby/scriptaculous/wikis/draggable `_. + +Des options courantes peuvent être incluses : + ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Clés $options | Description | ++==========================+========================================================================================================================================================================================================================================================================================================================================+ +| $options['handle'] | Détermine si l'élément devrait être déplaçable seulement par une poignée intégrée. La valeur doit être une référence d'élément ou un id d'élément ou une chaîne référençant une valeur de classe CSS. Le premier élément enfant/petit-enfant/etc. trouvé dans l'élément qui a cette valeur de classe CSS sera utilisé comme poignée. | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | Si défini à true, l'élément retourne à sa position de départ quand le déplacement prend fin. Revert peut aussi être une référence de fonction arbitraire, appelée quand le déplacement se termine. | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | Contraint le déplacement à 'horizontal' ou 'vertical', laisser blanc pour ne pas contraindre. | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +Rend l'élément DOM spécifié par $id, capable de recevoir des éléments +déplacés. Des paramètres additionnels peuvent être spécifiés avec +$options. Pour plus d'informations, voir +`http://github.com/madrobby/scriptaculous/wikis/droppables `_. + +Des options courantes peuvent être incluses : + ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Clés $options | Description | ++===========================+==============================================================================================================================================================================================================================+ +| $options['accept'] | Définir par une chaîne, ou un tableau javascript de chaînes, décrivant les classes CSS que l'élément capable de recevoir acceptera. L'élément récepteur acceptera seulement des éléments ayant les classes CSS spécifiées. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | L'élément capable de recevoir acceptera seulement l'élément déplacé s'il fait partie des éléments précisés (ids d'éléments). Peut être une chaîne ou un tableau javascript de références à des id. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | Si défini à 'horizontal' ou 'vertical', l'élément capable de recevoir réagira seulement à un élément déplaçable, s'il chevauche la zone de dépôt à plus de 50% de l'axe donné. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | Un rappel javascript appelé quand l'élément déplacé est déposé sur l'élément capable de recevoir. | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +Produit une cible dépôt qui crée un XMLHttpRequest quand un élément +déplaçable est déposée sur elle. Le tableau $options pour cette fonction +est le même que ceux spécifiés pour drop() et link(). + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +Crée un contrôle de défilement directionnel. Pour plus d'informations, +voir +`http://wiki.github.com/madrobby/scriptaculous/slider `_. + +Des options courantes peuvent être incluses : + +Clés $options + +Description + +$options['axis'] + +Définit la direction dans laquelle le défilement se produira. +'horizontal' ou 'vertical'. Horizontal par défaut + +$options['handleImage'] + +L'id de l'image qui représente la poignée. Ceci est utilisé pour +échanger le src de l'image avec le src de l'image désactivée, quand la +barre de défilement est activé. Utilisé en conjonction avec +handleDisabled. + +$options['increment'] + +Définit le rapport des pixels aux valeurs. Définir à 1, fera que chaque +pixel ajustera de un la valeur de la barre de défilement. + +$options['handleDisabled'] + +L'id de l'image qui représente la poignée désactivée. Ceci est utilisé +pour changer le src de l'image quand la barre de défilement est +désactivée. Utilisé en conjonction avec handleImage. + +$options['change'] + $options['onChange'] + +Rappel JavaScript exécuté quand la barre de défilement a fini de bouger +ou que sa valeur a changé. La fonction de rappel reçoit la valeur +courante de la barre de défilement comme paramètre. + +$options['slide'] + $options['onSlide'] + +Rappel JavaScript qui est appelé chaque fois que la barre de défilement +est déplacée par étirement. Il reçoit la valeur courante de la barre de +défilement comme paramètre. + +editor +------ + +``editor(string $id, string $url, array $options)`` + +Crée un éditeur local au niveau de l'id DOM $id. L'``$url`` fournie +devrait être une action en charge de sauvegarder des données. Pour plus +d'informations et des démos, voir +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_. + +Des options courantes peuvent être incluses : + +Clés $options + +Description + +``$options['collection']`` + +Active le mode 'collection' de l'édition locale. $options['collection'] +prend un tableau qui se transforme en options pour le select. Pour en +apprendre plus sur 'collection', voir +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_. + +``$options['callback']`` + +Une fonction à exécuter avant que la requête ne soit envoyée au serveur. +Ceci peut être utilisé pour formater l'information envoyée au serveur. +La signature est ``fonction(formulaire, valeur)`` + +``$options['okText']`` + +Texte du bouton de soumission en mode édition + +``$options['cancelText']`` + +Le texte du lien qui efface l'édition + +``$options['savingText']`` + +Le texte affiché pendant que le texte est envoyé au serveur + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +La hauteur de la ligne du champ input + +``$options['cols']`` + +Le nombre de colonnes que la zone de texte devrait couvrir + +``$options['size']`` + +Synonyme de 'cols' quand utilisé sur une seule ligne + +``$options['highlightcolor']`` + +La couleur de surlignement + +``$options['highlightendcolor']`` + +La couleur avec laquelle le surlignement se confond + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +Exemple + +:: + +
Texte à Editer
+ editor( + "id_editeur_local", + array + ( + 'controller' => 'Posts', + 'action' => 'update_titre', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +Crée une liste ou un groupe d'objets flottants contenus dans le +conteneur triable $id. Le tableau options supporte un certain nombre de +paramètres. Pour vous informez davantage au sujet de sortable, voyez +`http://wiki.github.com/madrobby/scriptaculous/sortable `_. + +:: + +
+
+ Elément 1 +
+
+ Elément 2 +
+
+ Elément 3 +
+
+ + sortable('conteneurTriable',array('tag'=>'div','only'=>'itemTriable','onUpdate'=>'ecriremiseajour')); + ?> + +Assurez-vous que vous n'incluez pas les parenthèses dans le callback +onUpdate ou bien il ne sera pas exécuté. + +Des options courantes peuvent être incluses : + +Clés $options + +Description + +$options['tag'] + +Indique quelle sorte d'éléments fils du conteneur seront rendus +triables. Par défaut 'li'. + +$options['only'] + +Permet d'augmenter le filtrage des éléments fils. Accepte une classe +CSS. + +$options['overlap'] + +Soit 'vertical', soit 'horizontal'. Vertical par défaut. + +$options['constraint'] + +Restreint le mouvement des éléments déplaçables. Accepte 'horizontal' ou +'vertical'. Vertical par défaut. + +$options['handle'] + +Permet aux éléments *Draggables* créés d'utiliser des poignées, voir +l'option *handle* des éléments *Draggables*. + +$options['onUpdate'] + +Appelé quand le glissé se termine et que l'ordre de tri est modifié +d'une façon ou d'une autre. Quand on glisse depuis un élément triable +vers un autre, le *callback* est appelé une fois sur chaque élément +triable. + +$options['hoverclass'] + +Donne une classe css de survol (*hoverclass*) à l'élément déplaçable +créé. + +$options['ghosting'] + +Si défini à true, les éléments glissés du conteneur triable seront +clonés et apparaîtront comme des fantômes, au lieu de manipuler +directement l'élément original. diff --git a/fr/The-Manual/Core-Helpers/Cache.rst b/fr/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..616bafc4b62bd60fb29373daa73b4384add17f88 --- /dev/null +++ b/fr/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,173 @@ +Cache +##### + +L'assistant Cache permet de mettre en cache des vues et layouts, ce qui +permet de sauver le temps de récupération des données répétitives. Le +système de cache des vues de Cake sauvegarde les vues et layouts avec le +moteur de stockage de votre choix. Il faut noter que l'assistant Cache +fonctionne de façon assez différente des autres assistants. Il ne +possède pas de méthodes appelées directement. A la place, une vue est +marquée de tags, indiquant quels blocs de contenus ne doivent pas être +mis en cache. + +Quand une URL est appelée, Cake vérifie si cette requête a déjà été mise +en cache. Si c'est le cas, le processus de distribution de l'URL est +abandonné. Chacun des blocs non mis en cache sont rendus selon le +processus normal, et la vue est servie. Cela sauve véritablement du +temps pour chaque requête d'une URL mise en cache, puisqu'un minimum de +code est exécuté. Si Cake ne trouve pas une vue mise en cache, ou si le +cache a expiré pour l'URL appelée, le processus de requête normal se +poursuit. + +Généralités sur la mise en cache +================================ + +Mettre en cache permet un stockage temporaire pour aider à réduire les +chargements sur le serveur. Par exemple, il est possible de stocker les +résultats des requêtes coûteuses en temps sur la base de données, de +manière à ne pas les demander pour chaque page. + +Gardons ça à l'esprit, le cache n'est pas un stockage permanent et ne +devrait jamais être utilisé à cette fin. Il faut mettre en cache des +choses qu'on est capable de générer de nouveau au besoin. + +Les moteurs de cache de Cake +============================ + +Nouveauté de la 1.2 : les moteurs de cache. Ce sont des interfaces +transparentes avec l'asssistant (Helper) cache qui vous autorisent à +stocker des vues de différentes façons sans vous préoccuper du comment. +Le choix du moteur de cache est positionné dans le fichier de +configuration app/config/core.php. La plupart des options sont listées +dans la version du fichier qui vient avec la distribution et des +informations plus détaillées sur chaque moteur se trouve dans la section +Cache. + +Fichiers + +Le moteur de cache de type fichier est celui que Cake utilise par +défaut. Il écrit des fichiers plats dans l'arborescence de fichier, il +possède des options mais fonctionne bien avec les options par défaut. + +APC + +Le moteur de cache APC implémente l'alternative APC : `Alternative PHP +Cache `_, le cache d'opcode libre et ouvert pour +PHP. Comme XCache, ce moteur met en cache le code intermédiaire PHP. + +XCache + +Ce moteur de cache est fonctionnellement similaire à APC met implémente +`XCache `_, le cache d'opcode. Il nécessite +de rentrer une authentification utilisateur pour fonctionner +correctement. + +Memcache + +Le moteur de cache Memcache fonctionne avec serveur Memcache: un démon +vous autorisant à créer un objet cache dans la mémoire vive du système. +Plus d'information sur le module Memcache qui sert d'interface au démon +sur `php.net `_ et sur le `démon memcached +ici. `_ + +La configuration de l'assistant Cache +===================================== + +La mise en cache de vue et l'Assistant Cache possèdent plusieurs +éléments de configuration détaillés ci-dessous. + +Pour utiliser l'Assistant Cache dans n'importe quel vue ou contrôleur, +vous devez d'abord dé-commenter et définir Configure::Cache.check à true +à la ligne 80 de ``core.php`` dans votre /app/config. Si ce n'est pas le +cas, alors le cache ne sera pas vérifié, ni créé. + +Mettre en cache depuis le contrôleur +==================================== + +Tout contrôleur qui utilise des fonctionnalités de cache doit inclure +l'assistant cache (CacheHelper) dans son tableau d'assistants +($helpers). + +:: + + var $helpers = array('Cache'); + +Vous devez aussi indiquer quels actions doivent être mises en cache et +pour combien de temps. On fait cela avec la variable $cacheAction du +contrôleur. $cacheAction devrait être un tableau qui contient les +actions concernées et la durée de mise en cache exprimée en seconde. La +durée peut aussi être exprimé au format de la fonction strtotime() +format. (ie. "1 hour", ou "3 minutes"). + +En utilisant l'exemple d'un contrôleur ArticlesController, qui reçoit +beaucoup de trafic qu'on a besoin de mettre en cache. + +Mise en cache des articles fréquemment lus pour des durées de temps +variables : + +:: + + var $cacheAction = array( + 'view/23/' => 21600, + 'view/48/' => 36000, + 'view/52' => 48000 + ); + +Mise en cache d'une action complète, dans notre cas un listing +d'articles : + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +Mise en cache de toutes les actions du contrôleur en utilsant un format +strtotime() pour indiquer un temps long. + +:: + + var $cacheAction = "1 hour"; + +Contenus non mis en cache dans les Vues +======================================= + +Il y aura des fois où vous ne voudrez par mettre en cache une vue +*intégrale*. Par exemple, certaines parties d'une page peuvent être +différentes, selon que l'utilisateur est actuellement identifié ou qu'il +visite votre site en tant qu'invité. + +Pour indiquer que des blocs de contenu *ne doivent pas* être mis en +cache, entourez-les par `` `` comme +ci-dessous : + +:: + + + check('Utilisateur.nom')) : ?> + Bienvenue, read('Utilisateur.nom')?>. + + link('Login', 'users/login')?> + + + +Il est à noter, qu'une fois une action mise en cache, la méthode du +contrôleur correspondante ne sera plus appelée - sinon il n'y aurait pas +d'intérêt à mettre la page en cache. Par conséquent, ce n'est pas +possible d'entourer par `` ``, des +variables qui ont été définies dans le contrôleur, puisqu'elles auront +la valeur *null*. + +Nettoyer le cache +================= + +Il est important de se rappeler que Cake va nettoyer le cache si un +modèle utilisé dans la vue mise en cache a été modifié. Par exemple, si +une vue mise en cache utilise des données du modèle Post et qu'il y a eu +une requête INSERT, UPDATE, ou DELETE sur Post, le cache est nettoyé, et +un nouveau contenu est généré à la prochaine requête. + +Si vous avez besoin de nettoyer le cache manuellement, vous pouvez le +faire en appelant Cache::clear(). Cela nettoiera **toutes** les données +mises en cache, à l'exception des fichiers de vues mis en cache. Si vous +avez besoin de nettoyer les fichiers de vues, utilisez ``clearCache`` diff --git a/fr/The-Manual/Core-Helpers/Form.rst b/fr/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..c61637df6456eae3c4c75a22d5924d90446cb97c --- /dev/null +++ b/fr/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,1397 @@ +Formulaires +########### + +Le Helper Form est un nouvel ajout à CakePHP. La plupart du gros oeuvre, +dans la création de formulaire s'effectue maintenant en utilisant cette +nouvelle classe, plutôt que par les méthodes (maintenant obsolètes) du +Helper HTML. Le Helper Form se concentre sur la création rapide de +formulaires, d'une manière qui permet de rationaliser la validation, le +repeuplement et la mise en page. Le Helper Form est également flexible : +il fera presque tout à votre place, "automagiquement", sinon vous pouvez +utiliser ses méthodes spécifiques, pour réaliser seulement ce dont vous +avez besoin. + +Créer des formulaires +===================== + +La première méthode dont vous aurez besoin d'utiliser pour prendre +pleinement avantage du *FormHelper* est ``create()``. Cette méthode +affichera un tag d'ouverture de formulaire. + +``create(string $model = null, array $options = array())`` + +Tous les paramètres sont optionnels. Si ``create()`` est appelée sans +paramètres, CakePHP supposera que vous voulez créer un formulaire en +rapport avec le contrôleur courant, avec selon les cas l'action +``add()`` ou l'action ``edit()``. La méthode par défaut pour les +formulaires est *POST*. L'élément du formulaire est également renvoyée +avec un DOM ID. Cet identifiant est créé à partir du nom du modèle, et +du nom du contrôleur. Si j'apelle ``create()`` dans une vue de +MembresController, j'obtiendrai ce genre de rendu dans ma vue : + +:: + +
+ +La méthode ``create()`` nous permet également de personnaliser plusieurs +paramètres. Premièrement, vous pouvez spécifier un nom de modèle. Ce +faisant, vous modifiez le contexte de ce formulaire. Tous les champs +seront supposés dépendre de ce modèle (sauf si spécifié), et tous les +modèles devront être liés à lui. Si vous ne spécifiez pas de modèle, +CakePHP supposera que vous utilisez le modèle par défaut pour le +contrôleur courant. + +:: + + create('Recette'); ?> + + // affichera : + + +Ce formulaire enverra les données à votre action ``add()`` de +*RecettesController*. Cependant, vous pouvez utiliser la même logique +pour créer et modifier des formulaires. Le helper *FormHelper* utiliser +la propriété ``$this->data`` pour détecter automatiquement s'il faut +créer un formulaire d'ajout ou de modification. Si ``$this->data`` +contient un élément tabulaire après le nom du modèle, et que ce tableau +contient une valeur non nulle pour la clé primaire du modèle, alors le +*FormHelper* créera un formulaire de modification pour cet +enregistrement précis. Par exemple, si on va à l'adresse +http://site.com/recettes/edit/5, nous devrions obtenir ceci : + +:: + + // controllers/recettes_controller.php: + data)) { + $this->data = $this->Recette->findById($id); + } else { + // Le code de sauvegarde vient ici + } + } + ?> + + // views/recettes/edit.ctp: + + // Comme $this->data['Recipe']['id'] = 5, on doit obtenir un formulaire de modification + create('Recipe'); ?> + + //affichera : + + + +Comme c'est un formulaire de modification, un champ caché (*hidden*) est +créé pour surcharger la méthode HTTP par défaut + +Le tableau ``$options`` est l'endroit où la plupart des paramètres de +configurations est stockée. Ce tableau peut contenir un certain nombre +de paires clé-valeur qui peuvent affecter la manière dont le formulaire +sera créé. + +$options['type'] +---------------- + +Cette clé est utilisée pour spécifier le type de formulaire à créer. Les +valeurs que peuvent prendre cette variable sont 'post', 'get', 'file', +'put' et 'delete'. + +Choisir 'post' ou 'get' changera la méthode de soumission du formulaire +en fonction de votre choix. + +:: + + create('Membre', array('type' => 'get')); ?> + + //Affichera : + + +Choisir 'file' changera la méthode de soumission à 'post', et ajoutera +la mention "multipart/form-data" dans le tag du formulaire. Vous devez +l'utiliser si vous avez des demandes de fichiers dans votre formulaire. +L'absence de cet attribut d'enctype empêchera l'envoi de fichiers à +partir de ce formulaire. + +:: + + create('Membre', array('type' => 'file')); ?> + + //Affichera : + + +Quand vous utilisez 'put' ou 'delete', votre formulaire sera équivalent +à un formulaire de type 'post', mais quand il sera envoyé, la méthode de +requête HTTP sera respectivement surchargée avec 'PUT' ou 'DELETE'. Ca +permettra à CakePHP de créer son propre support REST dans les +navigateurs web. + +$options['action'] +------------------ + +La variable action vous permet de définir à quelle action dans votre +contrôleur pointera le formulaire. Par exemple, si vous voulez que le +formulaire appelle l'action login() de votre contrôleur courant, vous +créeriez le tableau $options comme ceci : + +:: + + create('Membre', array('action' => 'login')); ?> + + //Affichera : + +
+ +$options['url'] +--------------- + +Si l'action que vous désirez appeler avec le formulaire n'est pas dans +le contrôleur courant, vous pouvez spécifier une URL précise dans le +formulaire en utilisant la clé 'url' de votre tableau $options. L'URL +ainsi donnée peut être relative à votre application CakePHP ou peut +pointer vers un domaine extérieur. + +:: + + create(null, array('url' => '/recettes/ajouter')); ?> + // ou + create(null, array('url' => array('controller' => 'recettes', 'action' => 'ajouter'))); ?> + + + // Affichera : +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + // Affichera : + + +Regardez aussi la méthode `HtmlHelper::url `_ pour +plus d'exemples sur les différent types d'urls. + +$options['default'] +------------------- + +Si la variable *'default'* (NdT : attention, il y a bien un L +contrairement au mot français !) a été affectée du booléen *false*, +l'action de soumission du formulaire a été changée de telle manière que +le bouton de soumission ne valide plus le formulaire. Si le formulaire a +été créé pour être validé par AJAX, mettre la variable 'default' à FALSE +supprime le comportement par défaut du formulaire, ainsi vous pouvez +collecter les données et les soumettre par AJAX à la place. + +Fermeture du Formulaire +======================= + +Le FormHelper inclus également une méthode end() qui complète le +marquage du formulaire. Souvent, end() affiche juste la base fermante du +formulaire, mais le FormHelper permet aussi d'ajouter des champs cachées +en utilisant la méthode end() other methods may be depending on. + +:: + + create(); ?> + + + + end(); ?> + +Si une chaine est fournie comme premier argument à end(), le FormHelper +affichera un bouton submit nommé en conséquence en même temps que la +balise de fermeture du formulaire. + +:: + + end('Finish'); ?> + + Sortie : + +
+ +
+
+ +Éléments de formulaire automagiques +=================================== + +Tout d'abord, intéressons-nous à quelques-unes des méthodes de création +automatique de formulaire de l'assistant Form. La principale méthode que +nous allons étudier est input(). Cette méthode inspecte automatiquement +le champ du modèle qui lui est fourni afin de créer une entrée +appropriée pour ce champ. + +input(string $fieldName, array $options = array()) + ++---------------------------------------------------+------------------------------------------------------------+ +| Type de colonne | Champ de formulaire résultant | ++===================================================+============================================================+ +| string (char, varchar, etc.) | text | ++---------------------------------------------------+------------------------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++---------------------------------------------------+------------------------------------------------------------+ +| text | textarea | ++---------------------------------------------------+------------------------------------------------------------+ +| text, avec password, passwd ou psword comme nom | password | ++---------------------------------------------------+------------------------------------------------------------+ +| date | selects jours, mois et années | ++---------------------------------------------------+------------------------------------------------------------+ +| datetime, timestamp | selects jours, mois, années, heures, minutes et méridien | ++---------------------------------------------------+------------------------------------------------------------+ +| time | selects heures, minutes et méridien | ++---------------------------------------------------+------------------------------------------------------------+ + +Par exemple, supposons que mon modèle Utilisateur contient les champs +nom\_utilisateur (varchar), password (varchar), accepte (datetime) et +citation (text). Je peux utiliser la méthode input() de l'assistant +Forms pour créer une entrée appropriée pour tous ces champs du +formulaire. + +:: + + create(); ?> + + input('nom_utilisateur'); //text + echo $form->input('password'); //password + echo $form->input('accepte'); //day, month, year, hour, minute, meridian + echo $form->input('citation'); //textarea + ?> + + end('Ajouter'); ?> + +Un exemple plus complet montrant quelques options pour un champ de date +: + +:: + + echo $form->input('date_naissance', array( 'label' => 'Date de naissance' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +En plus des options d'entrée spécifiques trouvées ci-dessous, vous +pouvez spécifiez n'importe quel attribut html (par exemple onfocus). +Pour plus d'information sur $options et $htmlAttributes voir `HTML +Helper `_. + +Et pour finir, voici un exemple pour la création d'une sélection +hasAndBelongsToMany. Supposons que Utilisateur hasAndBelongsToMany +Groupe. Dans votre contrôleur, définissez une variable camelCased au +pluriel (groupe -> groupes dans cette exemple ou ExtraFunkyModele -> +extraFunkyModeles) avec les options de sélection. Dans le contrôleur +vous pouvez définir : + +:: + + $this->set('groupes', $this->Utilisateur->Groupe->find('list')); + +Et dans la vue une sélection multiple sera créée avec cette simple ligne +de code : + +:: + + echo $form->input('Groupe'); + +Si vous voulez créer un champ de sélection utilisant une relation +belongsTo ou hasOne, vous pouvez ajouter ceci dans votre contrôleur +Utilisateurs (supposant que Utilisateur belongsTo Groupe) : + +:: + + $this->set('groupes', $this->Utilisateur->Groupe->find('list')); + +Ensuite, ajoutez ceci à la vue du formulaire : + +:: + + echo $form->input('groupe_id'); + +Si le nom de votre modèle consiste en deux mots ou plus, par ex +"GroupeUtilisateur", quand vous passez les données en utilisant set(), +vous devriez nommer vos données dans un format pluralisé et camelCased, +comme ceci : + +:: + + $this->set('groupeUtilisateurs', $this->GroupeUtilisateur->find('list')); + // ou + $this->set('nomDeModeleVraimentInappropries', $this->NomDeModeleVraimentInapproprie->find('list')); + +Convention de nommage des champs +-------------------------------- + +Le Helper Form est assez évolué. Lorsque vous définissez un nom de champ +avec les méthodes du Helper Form, celui-ci génère automatiquement une +balise input basée sur le nom de modèle courant, selon le format suivant +: + +:: + + + +Vous pouvez également préciser le nom du modèle manuellement, en passant +un premier paramètre de la forme Nommodele.nomchamp. + +:: + + echo $form->input('Nommodele.nomchamp'); + +Si vous avez besoin de définir plusieurs champs ayant le même nom, donc +de créer un tableau qui peut être enregistré en une seule fois avec +``saveAll()``, utilisez la convention suivante : + +:: + + input('Nommodele.0.nomchamp'); + echo $form->input('Nommodele.1.nomchamp'); + ?> + + + + +$options[‘type’] +---------------- + +Vous pouvez forcer le type d'un input (et donc remplacer la logique +d'analyse du modèle) en définissant un type. En plus des types de champs +décrits dans le tableau ci-dessus, vous pouvez également créer des +inputs 'file' et 'password'. + +:: + + input('champ', array('type' => 'file')); ?> + + Affiche : + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +Utilisez ces clés si vous avez besoin d'injecter quelques balises à la +sortie de la méthode input(). + +:: + + input('field', array( + 'before' => '--avant--', + 'after' => '--après--', + 'between' => '--au milieu---' + ));?> + + Output: + +
+ --avant-- + + --au milieu--- + + --après-- +
+ +Pour un *input* de type radio l'attribut *'separator'* peut être utilisé +pour injecter des balise pour séparer input/label. + +:: + + input('field', array( + 'before' => '--avant--', + 'after' => '--après--', + 'between' => '--au milieu--', + 'separator' => '--séparateur--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --avant-- + + + --séparateur-- + + + --au milieu--- + --après-- +
+ +Pour un élément de type ``date`` et ``datetime`` l'attribut +*'separator'* peut être utilisé pour modifier la chaine entre les +*select*. Par défaut '-'. + +$options[‘options’] +------------------- + +Cette clé vous permet de spécifier manuellement les options pour un +select ou pour un groupe de boutons radio. A moins que 'type' ne soit +spécifié comme 'radio', l'Assistant Form assumera que la cible rendu est +un champ select. + +:: + + input('champ', array('options' => array(1,2,3,4,5))); ?> + +Affiche : + +:: + +
+ + +
+ +Options peut aussi être passé comme des paires clés/valeurs. + +:: + + input('champ', array('options' => array( + 'Valeur 1'=>'Label 1', + 'Valeur 2'=>'Label 2', + 'Valeur 3'=>'Label 3' + ))); ?> + +Affiche : + +:: + +
+ + +
+ +Si vous aimeriez générer un select avec des optgroups, passez simplement +les données dans un format hiérarchique. Fontionne aussi sur les cases à +cocher multiple et les boutons radio, mais à la place des optgroups, +entoure les éléments par des fieldsets. + +:: + + input('champ', array('options' => array( + 'Label1' => array( + 'Valeur 1'=>'Label 1', + 'Valeur 2'=>'Label 2' + ), + 'Label2' => array( + 'Valeur 3'=>'Label 3' + ) + ))); ?> + +Affiche : + +:: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +Si ‘multiple’ a été définit à vrai pour un champ qui génère un select, +le select autorisera les sélections multiples. Il est également possible +de définir la valeur de l'option ‘multiple’ à ‘checkbox’ pour générer +une liste de case à cocher. + +:: + + $form->input('Model.field', array( 'type' => 'select', 'multiple' => true )); + $form->input('Model.field', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +Cette option permet de définir le nombre maximum de caractères autorisés +dans un champ de texte. + +$options[‘div’] +--------------- + +utiliser cette option pour mettre a jour les attributs contenus dans la +balise div. L'introduction d'une chaine de caractère mettra a jour + l'attribut class . L'introduction d'un tableau mettra a jour les +attributs correspondants au champs clé/valeur du tableau . +Alternativement , + vous pouvez mettre cette option a faux pour pour annuler l'affichage du +div . + +Modification du nom de la class : + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +Code produit : + +:: + +
+ + +
+ +Modification de plusieurs attributs : + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +Code produit : + +:: + +
+ + +
+ +Annulation de l'affichage du div : + +:: + + input('User.name', array('div' => false));?> + +Code produit : + +:: + + + + +$options[‘label’] +----------------- + +Mettez a jour cette option pour modifier la chaine de caractere qui va +etre affichée dans le libellé qui va accompagner le input + +:: + + input( 'User.name', array( 'label' => 'The User Alias' ) );?> + +Code produit : + +:: + +
+ + +
+ +Alternativement , mettez cette option a faux pour annuler l'affichage du +libellé . + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Code produit : + +:: + +
+ +
+ +mettez cette option sous forme de tableau pour apporter des options +supplémentaires a l'élément ``label`` . Si vous faites cela , vous +pourrai utiliser la clé ``text`` dans le tableau pour modifier le texte +du libellé . + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'The User Alias') ) ); ?> + +Code produit : + +:: + +
+ + +
+ +$options['legend'] +------------------ + +Certains inputs comme les boutons radio seront automatiquement entourés +par un *fieldset*, avec un titre pour la légende dérivé du nom du champ. +Ce titre peut être remplacé avec cette option. Définir cette option à +false éliminera complètement le *fieldset*. + +$options[‘id’] +-------------- + +Définissez cette clé pour forcer la valeur de l'id DOM de l'*input*. + +$options['error'] +----------------- + +Utiliser cette clé vous permet de surcharger les messages d'erreur par +défaut du modèle et elle peut être utilisée, par exemple, pour définir +des messages i18n. Elle a un certain nombre de sous-options qui +contrôlent l'élément englobant, le nom de la classe de l'élément +englobant et si le HTML dans le message d'erreur sera échappé ou non. + +Pour désactiver l'affichage du message d'erreur, définissez la clé error +à false. + +:: + + $form->input('Model.champ', array('error' => false)); + +Pour modifier le type de l'élément et sa classe, utilisez le format +suivant : + +:: + + $form->input('Model.champ', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +Pour éviter que le HTML soit automatiquement échappé à l'affichage du +message d'erreur, définissez la sous-option escape à false : + +:: + + $form->input('Model.champ', array('error' => array('escape' => false))); + +Pour surcharger les messages d'erreur du modèle, utilisez un tableau +associatif avec le nom de la règle de validation : + +:: + + $form->input('Model.champ', array('error' => array('tropCourt' => __('Ceci n\'est pas assez long', true) ))); + +Comme vu ci-dessus, vous pouvez définir le message d'erreur pour chaque +règle de validation que vous avez dans vos modèles. En plus, vous pouvez +fournir des messages i18n pour vos formulaires. + +$options['default'] +------------------- + +Utilisé pour définir une valeur par défaut pour le champ input. La +valeur est utiliisée si les données transmises au formulaire ne +contiennent pas de valeur pour le champ (ou si aucune donnée n'est +passée du tout). + +Exemple d'utilisation : + +:: + + input('ingredient', array('default'=>'Sucre')); + ?> + +Exemple avec un champ select (la taille "Medium" sera sélectionnée par +défaut) : + +:: + + 'Small', 'm'=>'Medium', 'l'=>'Large'); + echo $form->input('taille', array('options'=>$tailles, 'default'=>'m')); + ?> + +Vous ne pouvez pas utiliser ``default`` pour cocher une checkbox - à la +place vous devez définir cette valeur dans le ``$this->data`` de votre +contrôleur, dans le ``$form->data`` de votre vue ou définir l'option +``checked`` à true. + +Les valeurs par défaut des champs date et datetime peuvent être définies +en utilisant la clé 'selected'. + +$options[‘selected’] +-------------------- + +Utilisé en combinaison avec un *input* de type *select* (A savoir: pour +les types *select*, *date*, *time*, *datetime*). Défini la valeur +*'selected'* de l'élément que vous voulez sélectionner par défaut lors +de l'affichage de l'*input*. + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +La clé sélectionnée pour un *input* de type *date* et *datetime* peut +être un *timestamp* UNIX. + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +Ces deux clés définissent le nombre de lignes et de colonnes dans un +*input* de type *textarea*. + +:: + + echo $form->input('textarea', array('rows' => '5', 'cols' => '5')); + +Affichera: + +:: + +
+ + +
+ +$options[‘empty’] +----------------- + +Si vrai, force l'*input* à rester vide. + +Lorsqu'il est passé à une liste de sélection, il créé une option vide +avec une valeur vide dans votre liste déroulante. Si vous voulez avoir +une valeur vide avec un texte affiché au lieu d'une option vide, passer +lui une chaine. + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(choisissez un texte)')); ?> + +Affichera: + +:: + +
+ + +
+ +Si vous avez besoin de définir une valeur par défaut dans un champ +*password* à vide, utiliser à la place 'value' => ''. + +Les options peuvent être fournies sous forme de paire clés-valeurs. + +$options[‘timeFormat’] +---------------------- + +Utilisé pour spécifier le format d'un *input* de type *select* pour un +champs lié au temps. Les valeurs valides sont '12 ', '24', et 'none'. + +$options[‘dateFormat’] +---------------------- + +Utilisé pour spécifier le format d'un *input* de type *select* lié à une +date. Les valeurs valides sont 'DMY', 'MDY', 'YMD', et 'NONE'. + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +Utilisé en combinaison avec un *input* de type *date/datetime*. Défini +la valeur minimal et maximal d'un champs de type *select* pour les +années. + +$options['interval'] +-------------------- + +Cette option spécifie le nombre de minutes entre chaque option dans la +boîte de sélection des minutes. + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +Créera 4 options dans la boite de sélection des minutes. Une toute les +15 minutes. + +$options['class'] +----------------- + +Vous pouvez définir le nom de la classe CSS pour un champ input en +utilisant ``$options['class']`` + +:: + + echo $form->input('titre', array('class' => 'classe-custom')); + +Champs de fichiers +================== + +Pour ajouter un champ d'*upload* de fichier dans un formulaire, vous +devez d'abord vous assurer que l'attribut enctype du formulaire est fixé +à "multipart/form-data", vous devez donc commencer par une fonction de +création définie comme ci-dessous. + +:: + + echo $form->create('Document', array('enctype' => 'multipart/form-data') ); + + // ou + + echo $form->create('Document', array('type' => 'file')); + +Ensuite, ajoutez une des deux lignes suivantes à votre fichier de vue +formulaire. + +:: + + echo $form->input('Document.fichiersoumis', array('between'=>'
','type'=>'file')); + + // ou + + echo $form->file('Document.fichiersoumis'); + +A cause des limitations liées à HTML, il n'est pas possible de définir +une valeur par défaut dans les champs inputs de type 'file'. Chaque fois +que le formulaire est affiché, le champ sera vide. + +Dès la soumission, les champs de fichier fournissent un tableau étendu +de données au script qui reçoit les données du formulaire. + +Dans l'exemple ci-dessus, les valeurs du tableau de données soumis +seraient organisées de la manière suivante, si CakePHP était installé +sur un serveur Windows. 'tmp\_name' aurait un chemin différent dans un +environnement Unix. + +:: + + + $this->data['Document']['fichiersoumis'] = array( + 'name' => planning_conference.pdf + 'type' => application/pdf + 'tmp_name' => C:/WINDOWS/TEMP/php1EE.tmp + 'error' => 0 + 'size' => 41737 + ); + +Ce tableau est généré par PHP lui-même, donc pour plus de détail sur la +façon dont PHP gère les données passées dans les champ de fichier, lisez +la `section sur l'upload de fichier du manuel +PHP `_. + +Valider un upload de fichier +---------------------------- + +Voici un exemple de méthode de validation que vous pourriez définir dans +votre modèle, afin de vérifier qu'un fichier a été uploadé avec succès. + +:: + + // Basé sur le commentaire 8 de : http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty($val['tmp_name']) && $val['tmp_name'] != 'none')) + { + return is_uploaded_file($val['tmp_name']); + } else { + return false; + } + } + +Eléments du Formulaire - Méthodes Spécifiques +============================================= + +Les autres méthodes disponibles dans l'Assistant Form permettent la +création d'éléments spécifiques de formulaire. La plupart de ces +méthodes utilisent également un paramètre spécial $options. Toutefois, +dans ce cas, $options est utilisé avant tout pour spécifier les +attributs des balises HTML (comme la valeur ou l'id DOM d'un élément du +formulaire). + +:: + + text('pseudo', array('class' => 'utilisateurs')); ?> + + Affichera : + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +Cette méthode créer une checkbox. Elle génère également un champ input +de type hidden afin de forcer la soumission des données pour le champ +spécifié. + +:: + + checkbox('fait'); ?> + +Donnera: + +:: + + + + +button +------ + +``button(string $title, array $options = array())`` + +Crée un bouton HTML avec le titre spécifié et un type de *"button"* par +défaut. La configuration de ``$options['type']`` affichera l'un des 3 +types de bouton possible: + +#. button: Créer un bouton standard (celui par défaut). +#. reset: Créer un bouton de réinitialisation de formulaire. +#. submit: Similaire à la methode ``$form->submit``. + +:: + + button('Un bouton'); + echo $form->button('Un autre bouton', array('type'=>'button')); + echo $form->button('Réinitialiser le formulaire', array('type'=>'reset')); + echo $form->button('Soumettre le formulaire', array('type'=>'submit')); + ?> + +Devrai afficher: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, mixed $showEmpty)`` + +Crée un menu de sélection composé des années allant de ``$minYear`` à +``$maxYear``, avec l'année $selected sélectionnée par défaut. +``$selected`` peut être soit une année sur quatre chiffres (ex. 2004), +soit la chaîne de caractères 'now'. Des attributs HTML peuvent être +fournis dans $attributes. + +:: + + year('acquis',2000,date('Y')); + ?> + +Affichera : + +:: + + + +Si ``$showEmpty`` est faux, le menu de sélection n'incluera pas d'option +vide. Si ``$showEmpty`` est une chaîne de caractères alors celle-ci sera +utilisée comme nom de l'option vide. + +:: + + year('retourne', 2008, 2010, null, null, 'Sélectionnez une année'); + ?> + +Affichera : + +:: + + + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection composé des noms des mois. + +:: + + month('mob'); + ?> + +Affichera: + +:: + + + +Vous pouvez insérer votre propre tableau de mois à utiliser, en +définissant l'attribut 'monthNames' (CakePHP 1.3 uniquement) ou avoir +les mois affichés sous forme numérique en passant false. (Note : les +mois affichés par défaut sont internationalisés et peuvent être traduits +en utilisant la localisation.) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection pour la date et le temps. Les valeurs valides +de $dateformat sont ‘DMY’, ‘MDY’, ‘YMD’ ou ‘NONE’. Les valeurs valides +pour $timeFormat sont ‘12’, ‘24’, et ‘NONE’. + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection composé des jours (numériques) du mois. + +Pour créer une option vide avec un texte de votre choix (par exemple, la +première option est "Jour"), vous pouvez définir le texte comme +paramètre final: + +:: + + day('created'); + ?> + +Devrai afficher: + +:: + + + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection composé de l'heure du jour. + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection composé des minutes de l'heure. + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection composé de ‘am’ et ‘pm’. + +error +----- + +``error(string $fieldName, string $text, array $options)`` + +Affiche un message d'erreur de validation, spécifiée par $texte, pour le +champ donné, dans le cas où une erreur de validation a eu lieu. + +Options: + +- 'escape' bool Échapper ou non le contenu de l'erreur. +- 'wrap' mixed Enveloppe ou non le message d'erreur d'une div. Si c'est + une chaine, elle sera utilisé comme tag HTML. +- 'class' string Le nom de la *class* du message d'erreur + +file +---- + +``file(string $fieldName, array $options)`` + +Crée un *input* de type *file*. + +:: + + create('User',array('type'=>'file')); + echo $form->file('avatar'); + ?> + +Devrai afficher: + +:: + +
+ + +Lors de l'utilisation de ``$form->file()``, rappelez vous de bien +utilisé l'*encoding-type file*, en définissant le type en option à +'file' dans ``$form->create()`` + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +Crée un champs invisible. Exemple: + +:: + + hidden('id'); + ?> + +Devrai afficher: + +:: + + + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +Renvoie vrai si le champ $fieldName a une erreur de validation. + +:: + + isFieldError('genre')){ + echo $form->error('genre'); + } + ?> + +Lors de l'utilisation de ``$form->input()``, les erreurs sont affichées +par défaut. + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +Crée une étiquette (*tag label*), contenant $text. + +:: + + label('status'); + ?> + +Devrai afficher: + +:: + + + +password +-------- + +``password(string $fieldName, array $options)`` + +Crée un champs de mot de passe. + +:: + + password('password'); + ?> + +Devrai afficher: + +:: + + + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +Crée un bouton de type *radio*. Utilisez ``$attributes['value']`` pour +définir quel valeur devra être sélectionnée par défaut. + +Utilisé ``$attributes['separator']`` pour spécifier le HTML entre les +boutons *radio* (e.g.
). + +Les boutons sont enveloppé par défaut d'un *label* et d'un *fieldset*. +Définissez ``$attributes['legend']`` à *false* pour les supprimer. + +:: + + 'Male','F'=>'Female'); + $attributes=array('legend'=>false); + echo $form->radio('gender',$options,$attributes); + ?> + +Devrai afficher: + +:: + + + + + + + +Si pour n'importe quel raison vous ne voulez pas de l'*input* caché, +définissez ``$attributes['value']`` par une valeur à sélectionnée ou une +boolean à *false*. + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +Crée un menu de sélection, composé des éléments de ``$options``, avec +l'option spécifiée par ``$selected`` qui sera le champ sélectionné par +défaut. Définissez ``$showEmpty`` à *false* si vous ne voulez pas +afficher le champ vide. + +:: + + 'Homme','F'=>'Femme'); + echo $form->select('sexe',$options) + ?> + +Devrai afficher: + +:: + + + +submit +------ + +``submit(string $caption, array $options)`` + +Crée un bouton de soumission de formulaire avec une legende +``$caption``. Si ``$caption`` est l'URL d'image (qui contient un ‘.’), +le bouton sera afficher en temps qu'image. + +Il est enveloppé d'une ``div`` par défaut; vous pouvez annuler cette +déclaration ``$options['div'] = false``. + +:: + + submit(); + ?> + +Devrai afficher: + +:: + +
+ +Vous pouvez définir une url d'image relative ou absolue pour la légende +à la place d'un texte comme légende. + +:: + + submit('ok.png'); + ?> + +Devrai afficher: + +:: + +
+ +text +---- + +``text(string $fieldName, array $options)`` + +Crée un champ de texte. + +:: + + text('prenom'); + ?> + +Devrai afficher: + +:: + + + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +Crée un champ de zone de texte. + +:: + + textarea('notes'); + ?> + +Devrai afficher: + +:: + + + diff --git a/fr/The-Manual/Core-Helpers/HTML.rst b/fr/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..f65c736cce111f0485da81ef10edca8b924b527b --- /dev/null +++ b/fr/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,652 @@ +HTML +#### + +Le rôle de l'assistant Html dans CakePHP est de créer des options +HTML-apparentées plus facilement, plus rapidement et plus résistantes au +changement. L'utilisation de cet assistant permettra à votre application +d'être plus à l'aise dans ses baskets et plus flexible selon où elle est +placée par rapport à la racine d'un domaine. + +Le rôle de l'assistant Html a changé significativement depuis CakePHP +1.1. Les méthodes relatives aux formulaires ont été dépréciées et +déplacées dans le nouvel assistant Form. Si vous cherchez de l'aide pour +les formulaires HTML, jeter un oeil au nouvel assistant Form. + +Avant que nous regardions les méthodes de l'assistant Html, vous aurez +besoin de connaître quelques situations de configuration et d'usage qui +vous aideront à utiliser cette classe. Premièrement, dans un souci +d'apaiser ceux qui n'aiment pas les balises courtes () ou les +nombreux appels à echo() dans le code de leur vue toutes les méthodes de +l'assistant Html sont passées à la méthode output(). Si vous souhaitez +activer l'affichage automatique du HTML généré par l'assistant, vous +pouvez simplement implémenter output() dans votre classe AppHelper. + +:: + + function output($str) { + echo $str; + } + +Faire cela supprimera la nécessité d'ajouter des déclarations echo au +code de votre vue. + +Beaucoup de méthodes du HtmlHelper incluent aussi un paramètre +$htmlAttributes, qui vous permet d'ajouter tout attribut supplémentaire +à vos balises. Voici quelques exemples d'utilisation du paramètre +$htmlAttributes : + +:: + + Attributs désirés : + Tableau paramètre : array('class'=>'uneClasse') + + Attributs désirés : + Tableau paramètre : array('name' => 'foo', 'value' => 'bar') + +L'assistant Html est disponible par défaut dans toutes les vues. Si vous +obtenez une erreur vous informant qu'il n'y est pas, c'est +habituellement du au fait que son nom a été oublié lors d'une +configuration manuelle de la variable de contrôleur $helpers. + +Insérer des éléments bien formatés +================================== + +La tâche la plus importante accomplie par l'assistant HTML est de créer +des balises correctement formatées. N'hésitez pas à l'utiliser - vous +pouvez mettre en cache les vues dans CakePHP, afin d'économiser des +ressources CPU quand celles-ci sont affichées. Cette partie couvrira +quelques-unes des méthodes fournies par l'assistant HTML et comment les +utiliser. + +charset +------- + +``charset(string $charset=null)`` + +Utilisé pour créer une balise META précisant le type d'encodage des +caractères du document. Par défaut UTF-8. + +:: + + + charset(); ?> + +Va afficher: + +:: + + + +Ainsi que, + +:: + + charset('ISO-8859-1'); ?> + +Va afficher: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +Crée un lien vers une feuille de style CSS. Si $inline est défini à +false, les balises seront ajoutées à la variable $scripts\_for\_layout +que vous pouvez intégrer à l'intérieur de la balise "head" du document. + +Cette méthode d'inclusion suppose que le fichier spécifié réside à +l'intérieur du dossier /app/webroot/css ... + +:: + + css('forms'); ?> + +Affichera: + +:: + + + +Le premier paramètre peut être un tableau pour inclure plusieurs +fichiers. + +:: + + css(array('forms','tables','menu')); ?> + +Affichera: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +Cette méthode est pratique pour lier des ressources externes comme les +flux RSS/Atom et les favicons. Comme pour css(), vous pouvez spécifier +si, oui ou non, vous aimeriez que cette balise apparaissent en ligne ou +dans la balise head en utilisant le quatrième paramètre. + +Si vous définissez l'attribut "type" en utilisant le paramètre +$htmlAttributes, CakePHP contient quelques raccourcis : + ++--------+-------------------------+ +| type | valeur correspondante | ++========+=========================+ +| html | text/html | ++--------+-------------------------+ +| rss | application/rss+xml | ++--------+-------------------------+ +| atom | application/atom+xml | ++--------+-------------------------+ +| icon | image/x-icon | ++--------+-------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> // Affiche (retours à la ligne ajoutés) + + + meta( + 'Commentaires', + '/commentaires/index.rss', + array('type' => 'rss')); + ?> + + // Affiche (retours à la ligne ajoutés) + + +Cette méthode peut aussi être utilisée pour ajouter les balises meta +*keywords* et *description*. Exemple : + +:: + + meta( + 'keywords', + 'entrez n\'importe quelle meta keyword ici' + );?> + // Affiche + // + + meta( + 'description', + 'entrez n\'importe quelle meta description ici' + );?> + + // Affiche + +Si vous voulez ajouter une balise meta personnalisée, alors le premier +paramètre devrait être défini par un tableau. Pour afficher une balise +*robots noindex*, utilisez le code suivant : + +:: + + echo $html->meta(array('name' => 'robots', 'content' => 'noindex')); + +docType +------- + +``docType(string $type = 'xhtml-strict')`` + +Retourne une balise doctype (X)HTML. Les doctypes fournis sont ceux du +tableau suivant: + ++----------------+-----------------------+ +| type | valeur traduite | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Construit des définitions de styles CSS, basées sur les clés et valeurs +du tableau passé à la méthode. Particulièrement pratique si votre +fichier CSS est dynamique. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Affichera : + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Crée une balise image formatée. Le chemin fourni doit être relatif à +/app/webroot/img/. + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Affichera : + +:: + + CakePHP + +Pour créer une image avec un lien, spécifiez l'adresse en utilisant +l'option ``url dans $htmlAttributes``. + +:: + + image("recettes/6.jpg", array( + "alt" => "Brownies", + 'url' => array('controller' => 'recettes', 'action' => 'voir', 6) + )); ?> + +Affichera : + +:: + + + Brownies + + +Vous pouvez aussi utiliser cette méthode alternative pour créer une +image lien, en assignant l'image à une variable (par ex $image) et en la +passant à ``$html->link()`` comme premier argument : + +:: + + image('recettes/6.jpg', array( + 'alt' => 'Brownies', + )); + + //$image est passée comme premier argument au lieu d'un lien texte + echo $html->link($image, array( + 'controller' => 'recettes', + 'action' => 'voir', + 6 + ), + array( + 'escape' => false // important pour que htmlHelper n'échappe par votre lien image + ) + ); + ?> + +Ceci est pratique si vous voulez garder votre lien et votre image un peu +plus séparés ou si vous voulez inclure un peu de balisage dans votre +lien. Assurez-vous de passer ``'escape' => false`` dans le tableau +d'options de `` $html->link($string, $url, $options)`` pour éviter au +htmlHelper d'échapper le code. + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +Méthode universelle pour créer des liens HTML. Utilisez +``$htmlAttributes`` pour spécifier les attributs de l'élément. + +:: + + link('Entrer', '/pages/home', array('class'=>'bouton','target'=>'_blank')); ?> + +Affichera : + +:: + + + Entrer + +Spécifiez ``$confirmMessage`` pour afficher un dialogue javascript +``confirm()``. + +:: + + link( + 'Supprimer', + array('controller'=>'recettes', 'action'=>'supprimer', 6), + array(). + "Etes-vous certain de vouloir supprimer cette recette ?" + );?> + +Affichera : + +:: + + + Supprimer + +Des chaînes de requête peuvent aussi être créées avec ``link()``. + +:: + + link('Voir image', array( + 'controller' => 'images', + 'action' => 'voir', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Affichera : + +:: + + + Voir image + +Les caractères spéciaux HTML dans ``$title`` seront convertis en entités +HTML. Pour désactiver cette conversion, définissez l'option escape à +false dans ``$htmlAttributes`` ou définissez ``$escapeTitle`` à false. + +:: + + link( + $html->image("recettes/6.jpg", array("alt" => "Brownies")), + "recettes/voir/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recettes/6.jpg", array("alt" => "Brownies")), + "recettes/voir/6", + null, null, false + ); + ?> + +Afficheront tous deux : + +:: + + + Brownies + + +Voyez également la méthode +`HtmlHelper::url `_ pour +davantage d'exemples des différents types d'urls. + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Retourne le texte entouré du tag spécifié. Si aucun texte n'est +spécifié, seul le ouvrant sera retourné. + +:: + + tag('span', 'Bonjour le Monde.', array('class' => 'bienvenue'));?> + + // Affichera + Bonjour le Monde. + + // Aucun texte spécifié + tag('span', null, array('class' => 'bienvenue'));?> + + // Affichera + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Utilisé pour créer des sections de balisage entourées de div. Le premier +paramètre spécifie une classe CSS et le second est utilisé pour fournir +le texte à entourer par les balises div. Si le dernier paramètre a été +défini à true, $text sera affiché en HTML échappé. + +Si aucun texte n'est spécifié, seul une balise div ouvrante est +retournée. + +:: + + + div('erreur', 'SVP, entrez votre numéro de carte de crédit.');?> + + // Affiche +
SVP, entrez votre numéro de carte de crédit.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Retourne un texte entouré par une balise

avec une classe CSS. Si +aucun texte n'est soumis, seule une balise

ouvrante est retournée. + +:: + + para(null, 'Bonjour le Monde.');?> + + //Output +

Bonjour le Monde.

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Crée une rangée de cellules d'en tête de tableau, à placer à l'intérieur +des tags . + +:: + + tableHeaders(array('Date','Titre','Actif'));?> //Output + + + tableHeaders( + array('Date','Titre','Actif'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + // Affichera + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Crée des cellules de tableau, en lignes, en assignant des attributs +différents aux + +pour les lignes paires et impaires. Entoure une cellule simple par un +tableau d'attributs spécifiques aux + +. + +:: + + tableCells(array( + array('7 juillet 2007', 'Meilleurs Brownies', 'Oui'), + array('21 juin 2007', 'Gâteaux chics', 'Oui'), + array('1er août 2006', 'Cake Anti-Java', 'Non'), + )); + ?> + + // Affichera + + + + + tableCells(array( + array('7 juillet 2007', array('Meilleurs Brownies', array('class'=>'surligne')) , 'Oui'), + array('21 juin 2007', 'Gâteaux chics', 'Oui'), + array('1er août 2006', 'Cake Anti-Java', array('Non', array('id'=>'special'))), + )); + ?> + + // Affichera + + + + + tableCells( + array( + array('Rouge', 'Pomme'), + array('Orange', 'Orange'), + array('Jaune', 'Banane'), + ), + array('class' => 'sombre') + ); + ?> + + // Affichera + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Retourne une URL pointant sur une combinaison de contrôleur et action. +Si $url est vide, retourne le REQUEST\_URI, sinon génère l'url pour le +groupe contrôleur/action. Si full est à true, l'URL de base complète +sera préfixée au résultat. + +:: + + url(array( + "controller" => "posts", + 'action' => 'voir', + "bar"));?> + + // Affiche + /posts/voir/bar + +Voici quelques exemples supplémentaires d'utilisation : + +URL avec paramètres nommés + +:: + + url(array( + "controller" => "posts", + 'action' => 'voir', + "foo" => "bar")); + ?> + + // Affiche + /posts/voir/foo:bar + +URL avec extension + +:: + + url(array( + "controller" => "posts", + "action" => "liste", + "ext" => "rss")); + ?> + + // Affiche + /posts/liste.rss + +URL (débutant par '/') avec l'URL de base complète préfixée. + +:: + + url('/posts', true); ?> + + // Affiche + http://undomaine.com/posts + +URL avec des paramètrges GET et une ancre nommée + +:: + + url(array( + "controller" => "posts", + "action" => "cherche", + "?" => array("foo" => "bar"), + "#" => "premier")); + ?> + + // Affiche + /posts/cherche?foo=bar#premier + +Pour davantage d'information voyez +`Router::url `_ +dans l'API. + +Modifier les balises avec HtmlHelper +==================================== + +La construction des balises avec ``HtmlHelper`` sont compatible XHTML, +si vous avez besoin de générer des balises HTML compatibles HTML4 vous +devrez créer et charger un nouveau fichier de configuration de balises, +avec les nouvelles balises dont vous aurez besoin. Pour se faire créez +le fichier ``app/config/tags.php`` qui contiendra vos balises: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +Vous pouvez désormais charger ces balises en appelant +``$html->loadConfig('tags');`` diff --git a/fr/The-Manual/Core-Helpers/Javascript.rst b/fr/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..126e51ebb26d096c6df6a8c3a5d00dc97cabffd1 --- /dev/null +++ b/fr/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,154 @@ +Javascript +########## + +L'assistant Javascript est utilisé dans l'aide à la création de tags et +blocs de code Javascript bien formatés. Il y a plusieurs méthodes dont +certaines sont conçues pour fonctionner avec la librairie Javascript +`Prototype `_. + +Méthodes +======== + +``codeBlock($script, $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), $safe)`` + +- string $script - Le JavaScript à encadrer par les balises SCRIPT +- array $options - Ensemble d'options : + + - allowCache : booléen, détermine si ce bloc peut-être mis en cache + en utilisant les paramètres courants de cache. + - safe : booléen, indique si ce bloc doit être encadré par des + balises CDATA. Par défaut, équivaut à la configuration de l'objet + du helper. + - inline : détermine si le bloc doit être écrit en ligne ou écrit + dans le cache pour un affichage ultérieur (c'est-à-dire dans + $scripts\_for\_layout). + +- boolean $safe - DEPRECIE. Utilisez $options['safe'] à la place + +codeBlock retourne un élément script formaté contenant $script. Mais +peut aussi retourner null si l'assistant Javascript est paramétré pour +mettre en cache les événements. Voir JavascriptHelper::cacheEvents(). Et +peut écrire dans ``$scripts_for_layout`` si vous définissez +$options['inline'] à false. + +``blockEnd()`` + +Termine un bloc Javascript mis en cache. Peut retourner soit une balise +script fermante, soit vider le buffer, en ajoutant le contenu au tableau +cachedEvents. Sa valeur de retour dépend des paramètres du cache. Voir +JavascriptHelper::cacheEvents() + +``link($url, $inline = true)`` + +- mixed $url - Chaîne URL vers un fichier JavaScript ou un tableau + d'URLs. +- boolean $inline Si true, la balise '; + echo Sanitize::html($badString); + // 次の文字列が出力されます: <font size="99" color="#FF0000">HEY</font><script>...</script> + echo Sanitize::html($badString, true); + // 次の文字列が出力されます: HEY... + +escape +====== + +escape(string $string, string $connection) + +SQLステートメントにスラッシュを加え、エスケープします。この動作は、プログラムが実行されるシステムにおける「magic\_quotes\_gpc」の設定によって変化します。引数「$connection」に、 +app/config/database.php +で設定した接続の名前を指定すると、接続先のデータベースに応じたエスケープが行われます。 + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +この関数は、配列全体に処理を行うための、実用的で多目的なクリーナーです。たとえば、$this->data +全体にサニタイズを行うといった利用法があります。この関数は与えられた配列(または文字列)をクリーンにし、それを返します。このクリーンにする処理は、配列の全ての要素に再帰的に行われます。 + +- 「0xCA」を含むおかしなスペースを、標準的な半角スペースに置換する。 +- SQL文のセキュリティ向上のため、特殊な文字や復帰文字の削除をダブルチェックする。 +- 前述した機能を用い、SQL文で用いるため、データにスラッシュを追加する。 +- ユーザが入力したバックスラッシュを、信頼できるバックスラッシュに置き換える。 + +$options +には文字列と配列のいずれも使用可能です。文字列を渡す場合は、データベースの接続名を指定してください。配列を渡す場合は、次のオプションを併せて使用します。 + +- connection +- odd\_spaces +- encode +- dollar +- carriage +- unicode +- escape +- backslash + diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..4a35178a5679afdeb17a9b7dcc82ef2406f23e2a --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,831 @@ +データのバリデーション(Data Validation) +####################################### + +あらゆるアプリケーションにおいて、データのバリデーションは重要です。これは、モデルのデータがアプリケーションのビジネスルールに必ず従うようにすることに役立ちます。例えば、パスワードは8文字以上であるとか、ユーザ名は必ずユニークにするといったことが挙げられます。バリデーションを定義することは、フォームの取り扱いをとても簡単にします。 + +バリデーションの仕組みは多くの異なる場面で使います。 +この章ではモデルでの利用、基本的には save() +メソッドを呼び出した時の振る舞いについてを説明します。バリデーションエラーをどのように取り扱うかについての詳細な情報は、FormHelperについての項目を参照してください。 + +データのバリデーションを行うには、まずモデルにバリデーションのルールを作成します。これは、モデルの定義の中の +Model::validate 配列で行います。次の例を見てください。 + +:: + + + +この例では、User モデルに $validate 配列 +を追加していますが、バリデーションのルールは何も存在しません。users +テーブルに login、password、email、born +というフィールドがあるとして、次の例では簡単なバリデーションのルールをこれらのフィールドに加えます。 + +:: + + 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + ?> + +この例は、モデルのフィールドに対してどのようにバリデーションのルールを追加できるかを表しています。「login」フィールドがアルファベットか数字のみ、「email」は電子メールアドレスとして有効な文字列、「born」は日付として有効な文字列が許可されます。バリデーションルールを定義すると、もしそのルールに従わないデータが送信された時に +CakePHP +のオートマジック(automagic)はフォームにエラーメッセージを表示します。 + +CakePHPはバリデーションのルールを多く持ち、それらを使うことは大変簡単です。あらかじめ組み込まれたルールには、電子メールのアドレス、URL、クレジットカードの番号を表すものもありますが、これらは後で説明します。 + +次の例は、組み込みのバリデーションルールを便利に使うための、より複雑な例です。 + +:: + + array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Alphabets and numbers only' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Between 5 to 15 characters' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Mimimum 8 characters long' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date', + 'allowEmpty' => true + ) + ); + } + ?> + +フィールド「login」に対して、「文字の種類がアルファベットまたは数字」かつ「長さが5文字以上で15文字以下」という2つのバリデーションルールが定義されています。同様に、フィールド「password」は長さが8文字以上であり、「email」は電子メールアドレスとして有効な文字列、「born」は日付として有効な文字列であると定義されています。 +また、データがバリデーションのルールに従わない時に、CakePHPは任意のメッセージ表示します。それををどのように追加するかに注目してください。 + +これまでの例の通り、1個のフィールドは複数のバリデーションのルールを持つことが出来ます。そして、もし使いたいルールが組み込まれていなければ、必要に応じて独自のバリデーションのルールを追加することができます。 + +さて、ここまででバリデーションがどのように動くかについての概要を説明しました。 +次に、これらのルールをモデル中でどのように定義するのかを見ていきましょう。これには、「単純な配列で定義する」、「1個のフィールドに1個のルールを定義する」、「1個のフィールドに複数のルールを定義する」という3つの異なった方法があります。 + +単純ルール +========== + +読んで字のごとく、これがバリデーションのルールを定義する最も単純な方法です。単純ルールの書き方は次の通りです。 + +:: + + var $validate = array('fieldName' => 'ruleName'); + +ここで、「fieldName」にはルールの対象となるフィールドの名前を、「ruleName」には +'alphaNumeric'、'email'、'isUnique' +などの定義済みのルールの名前を書きます。 + +たとえば、ユーザーがちゃんとした形式のメールアドレスを入力したことを確認するためには、次のようなルールを使えばいいでしょう。 + +:: + + var $validate = array('user_email' => 'email'); + +1個のフィールドに1個のルールを定義する +====================================== + +この定義の方法は、バリデーションのルールの働きをうまく制御できるよう考慮したものです。しかしそれを論じる前に、1個のフィールドに1個のルールを定義する一般的な利用パターンをまず見てみましょう。 + +:: + + var $validate = array( + 'fieldName1' => array( + 'rule' => 'ruleName', // または: array('ruleName', 'param1', 'param2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // または: 'update' + 'message' => 'バリデーションエラーの時に表示するメッセージ' + ) + ); + +'rule' キーは必須です。'required' => true +をセットしただけでは、フォームバリデーションは正しく動作しません。なぜなら、 +'required' は正確にはルールではないからです。 + +各フィールドには「rule」「required」「allowEmpty」「on」「message」という5個のキーからなる配列を結び付けます(この例ではフィールドが1個しかありませんが)。「rule」は必須であり、他はオプションです。これらのキーについて詳しく見てみましょう。 + +rule +---- + +「rule」キーはバリデーションメソッドを定義します。単一の値と配列、いずれも使用できます。「rule」の指定は、モデル中に作成したメソッド名か、コアのバリデーションクラスのメソッド名、もしくは正規表現である必要があります。全ての組み込みルールの一覧は、「組み込みのバリデーションルール」の節を参照してください。 + +もしパラメータを必要としないルールなら、「rule」キーには単一の値を持たせることができます。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +もしパラメータを必要とするルールなら(たとえば「max」「min」「range」)、「rule」キーの値は配列にします。 + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +「rule」キーが配列をベースにした定義を必要とすることを忘れないでください。 + +required +-------- + +このキーにはブール値(boolean)を割り当てます。もし「required」がtrueであれば、このフィールドはデータの配列中に存在しなければなりません。例えば、このバリデーションルールの定義は次のように行います。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +モデルの save() +メソッドへ送られたデータには、かならずloginフィールドが存在しなければなりません。もし無ければ、バリデーションは失敗します。このキーのデフォルトはfalseです。 + +送られたデータに「login」というキーは存在し、値が空の場合、バリデーションは成功します。「required」を +true にすると、キーの存在だけを検証します。 + +allowEmpty +---------- + +``allowEmpty`` キーにはブール値(boolean)を割り当てます。もし +``allowEmpty`` に false を割り当てたら、データがモデルの ``save()`` +メソッドを通過するには、フィールドが存在しその値が空で無いことが必要です。 +true +にセットした場合、空のフィールドは他に設定された全てのバリデーションを無視します。 + +``allowEmpty`` のデフォルトは null です。 +この設定は、そのフィールドに常にバリデーションルールが適用されることを意味します。このルールには、独自のバリデーション関数を実行することも含みます。 + +on +-- + +「on」キーには「update」「create」のいずれかの値をセットできます。これは、レコードを更新する時だけ、あるいは追加する時だけバリデーションのルールを適用する機能を提供します。 + +もし「on」の値が「create」に設定していた場合、バリデーションのルールは新規レコード追加の時だけ適用されます。「update」の場合は、既存レコードの更新の時だけ適用されます。 + +「on」キーのデフォルトの値は null です。null +のとき、バリデーションルールはレコードの追加および更新の両方で適用されます。 + +message +------- + +「message」キーではルールに対するエラーメッセージを定義します。 + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'パスワードは8文字以上の長さにしてください。' + ) + ); + +last +---- + +Setting the ``'last'`` key to ``true`` will cause the validator to stop +on the rule if it fails instead of continuing with the next rule. This +is handy if you want validation to stop if the field is notEmpty in a +`multi-rule field `_. + +:: + + var $validate = array( + 'username' => array( + 'usernameRule-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Please enter a username.', + 'last' => true + ), + 'usernameRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters.' + ) + ) + ); + +The default value for ``'last'`` is ``false``. + +1個のフィールドに複数のルールを定義する +======================================= + +先に概要を説明したテクニックは、単純な定義よりずいぶん柔軟です。しかし、さらにきめ細かくバリデーションルールを制御するための付加的な方法があります。次に概説するテクニックでは、フィールドに複数のバリデーションルールを割り当てることが出来ます。 + +1個のフィールドに複数のバリデーションを割り当てる基本的な方法は、次のようになります。 + +:: + + + var $validate = array( + 'fieldName' => array( + 'ruleName' => array( + 'rule' => 'ruleName', + // like on, required, 等、他のキーをここに書く... + ), + 'ruleName2' => array( + 'rule' => 'ruleName2', + // like on, required, 等、他のキーをここに書く... + ) + ) + ); + +前の章で説明した方法にとても似ていますね。さて、各フィールドにバリデーションパラメータの配列を1つ定義しました。このケースにおいて、それぞれの「fieldName」はルールのインデックスを配列として保持しています。それぞれの「ruleName」はバリデーションパラメータの配列を別々に持っています。 + +わかりやすく説明するために、実用的な例を見てみましょう。 + +:: + + var $validate = array( + 'login' => array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Only alphabets and numbers allowed', + 'last' => true + ), + 'minlength' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Minimum length of 8 characters' + ), + ) + ); + +上記の例では、「login」フィールドに「使用できる文字の種類」と「長さの最小値」という2つのルールを定義しています。ご覧の通り、それぞれのルールをインデックスの名前で区別しています。インデックスの名前は、何でも好きなものを使えます。この例では利用するルールに近いものを採用しています。 + +デフォルトでは、CakePHP +は宣言された全てのバリデーションルールを使用して、フィールドをバリデートしようとし、最後に失敗したルールのエラーメッセージを返します。しかし、キー「\ ``last``\ 」の値が「\ ``true``\ 」といるものが失敗した場合、このルールのエラーメッセージが返され、後ろのルールはバリデートされません。ですので、最初に失敗したルールのエラーメッセージを表示したい場合は、それぞれのルールに +``'last' => true`` をセットしてください。 + +もし国際化したエラーメッセージを使うことを考えているなら、モデル(model)に定義する代わりにビュー(view)へエラーメッセージを指定した方が良いでしょう。 + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'alphanumeric' => __('Only alphabets and numbers allowed', true), + 'minlength' => __('Minimum length of 8 characters', true) + ) + ) + ); + +これでフィールドは完全に国際化され、モデルからエラーメッセージの指定を外すことができます。「\_\_()」関数について詳しい情報は、「地域化と国際化」の章を参照してください。 + +組み込みのバリデーションルール(Validation Rules) +================================================ + +CakePHP +のバリデーション(Validation)クラスには、あらかじめ組み込まれたルールがたくさんあり、これらを用いるとバリデーションがとても簡単になります。このクラスには、あらたに定義を書き起こさなくていいように、よく使われるバリデーションのテクニックがふんだんに盛り込まれています。全てのルールの説明と使用例の一覧は、下記を参照してください。 + +alphaNumeric +------------ + +半角のアルファベットか数字のみ許可されます。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'ユーザ名は半角英数字のみ使用できます。' + ) + ); + +between +------- + +データの長さが整数で指定された範囲におさまっていることを確認します。最小値と最大値は必須です。「<」ではなく「<=」が使用されます。 + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Passwords must be between 5 and 15 characters long.' + ) + ); + +blank +----- + +このルールは、データが空かホワイトスペースのみで構成されているかどうかを確認するために使われます。ホワイトスペースは、半角スペースとタブ、復帰文字(carriage +return)および改行文字(newline)を含みます。 + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +The data for the field must be a boolean value. Valid values are true or +false, integers 0 or 1 or strings '0' or '1'. + +:: + + var $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'Incorrect value for myCheckbox' + ) + ); + +cc +-- + +このルールはデータがクレジットカードの番号として適切かどうかをチェックする時に使います。パラメータは「type」「deep」「regex」の3つです。 + +「type」キーには「fast」「all」あるいは次のいずれかを値として割り当てることができます。 + +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +「type」を「fast」にセットすると、主要なクレジットカード番号の型でチェックします。「all」にセットすると、全てのクレジットカード番号のタイプでチェックします。マッチさせたいクレジットカードのタイプを配列にして、それを「type」にセットすることもできます。 + +「deep」キーにはブール値(boolean)をセットします。 true +にセットした場合、バリデーションはクレジットカードのルーン・アルゴリズム(Luhn +algorithm, +`http://en.wikipedia.org/wiki/Luhn\_algorithm `_)を用いてチェックします。この項目のデフォルトは +false です。 + +「regex」キーにはクレジットカード番号であるかを検証するための、独自の正規表現を設定します。 + +:: + + var $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'あなたが入力したデータは、クレジットカードの番号ではありません。' + ) + ); + +comparison +---------- + +「comparison」は数字を比較する時に使います。「is greater +(~より大きい)」「is less (~より小さい)」「greater or equal +(~以上)」「less or equal (~以下)」「equal to (~と等しい)」「not equal +(~と等しくない)」というものをサポートしています。 +いくつか例を次に示します。 + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => '18歳以上の方のみ対象です。' + ) + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => '18歳以上の方のみ対象です。' + ) + ); + +date +---- + +このルールは、送信されたデータが日付として有効なフォーマットであるかどうかを確認します。フォーマットの確認に使うためのパラメータを一つ持たせることができます。(パラメタは配列にすることもできます。) +パラメータの値として、次のものが指定できます。 + +- ‘dmy’ + 例:「27-12-2006」または「27-12-06」(セパレータには、スペース、ピリオド、ダッシュ、スラッシュを使用できます) +- ‘mdy’ + 例:「12-27-2006」または「12-27-06」(セパレータには、スペース、ピリオド、ダッシュ、スラッシュを使用できます) +- ‘ymd’ + 例:「2006-12-27」または「06-12-27」(セパレータには、スペース、ピリオド、ダッシュ、スラッシュを使用できます) +- ‘dMy’ 例:「27 December 2006」または「27 Dec 2006」 +- ‘Mdy’ 例:「December 27, 2006」または「Dec 27, + 2006」(カンマはオプションです) +- ‘My’ 例:「December 2006」または「Dec 2006」 +- ‘my’ + 例:「12/2006」または「12/06」(セパレータには、スペース、ピリオド、ダッシュ、スラッシュを使用できます) + +デフォルトのパラメータは「ymd」です。 + +:: + + var $validate = array( + 'born' => array( + 'rule' => 'date', + 'message' => '正しいデータを「YY-MM-DD」のフォーマットで入力してください。', + 'allowEmpty' => true + ) + ); + +大抵のデータ・ストアは、特定の日付の書式を必要とします。しかし、指定の書式に従った入力をユーザに強要するのではなく、労を惜しまずにさまざまな形式のデータを受け付けて変換するというやりかたを考えるかもしれません。ユーザのためにできることは、やったほうが好ましいでしょう。 + +decimal +------- + +このルールは、データが小数かどうかを確認します。パラメータは、小数点以下の桁数(位)のみ与えられます。もしパラメータを何も与えなかったら、データが浮動小数点であってもバリデーションは成功します。ただしこの時、小数点以下に数字が無いとバリデーションは失敗します。 + +:: + + var $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +このルールは、データが電子メールのアドレスとして適切な文字列かどうかを判定します。第一引数にブール値で +true +を設定すると、このルールはメールサーバーのホストが存在するかどうかを確認しようとします。 + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'メールアドレスを正しく入力してください。' + ) + ); + +equalTo +------- + +このルールは、データと第一引数が、値と型の両方で同じかどうかを確認します。 + +:: + + var $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'この項目は文字列で「cake」としなければなりません。' + ) + ); + +extension +--------- + +このルールはデータとして与えられたファイル名の拡張子(「.jpg」や「.png」など)が、指定したものにマッチするか確認します。複数の拡張子をマッチさせる場合は、配列で指定します。 + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg'), + 'message' => '適切な画像ファイル名を入力してください。' + ) + ); + +file +---- + +This rule ensures that the value is a valid file name. This validation +rule is currently non-functional. + +ip +-- + +このルールはデータがIPv4の形式であるかどうかを確認します。 + +:: + + var $validate = array( + 'clientip' => array( + 'rule' => 'ip', + 'message' => 'IPアドレスを正しく入力してください。' + ) + ); + +isUnique +-------- + +与えられた値が他の行で出現せず、ユニークであるかどうかを確認します。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'このユーザ名はすでに使用されています。' + ) + ); + +minLength +--------- + +このルールはデータの長さが指定したものより小さくならないようにします。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', '8'), + 'message' => 'ユーザ名は8文字以上にしてください。' + ) + ); + +maxLength +--------- + +このルールはデータの長さが指定したものより大きくならないようにします。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', '15'), + 'message' => 'ユーザ名は15文字以下にしてください。' + ) + ); + +money +----- + +このルールは、値が金額として有効なものであるかを確認します。 + +二番目のパラメータは通貨記号の位置(右か左)を定義します。 + +:: + + var $validate = array( + 'salary' => array( + 'rule' => array('money', 'left'), + 'message' => '金額として有効なものを入力してください。' + ) + ); + +Multiple +-------- + +これは、複数選択する入力のバリデーションに使用します。パラメータとして「in」「max」そして「min」をサポートします。 + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('foo', 'bar'), 'min' => 1, 'max' => 3)), + 'message' => '1~3個の項目を選択してください' + ) + ); + +inList +------ + +このルールは、送信されたデータが、あらかじめ指定したリストの中に含まれているかを確認します。リストは必ず配列で指定してください。その配列の値のどれかが一致した場合、バリデーションは成功します。 + +例: + +:: + + var $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => '「Foo」か「Bar」を入力してください。' + ) + ) + ); + +numeric +------- + +データが数字もしくは数値形式であるかどうかをチェックします。 + +:: + + var $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => '車の番号を入力してください。' + ) + ); + +notEmpty +-------- + +フィールドが空で無いかどうかを確認する基本的なルールです。 + +:: + + var $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'このフィールドは必ず入力してください。' + ) + ); + +このルールを複数選択のinputで使うとエラーになります。代わりに、「multiple」ルールを使ってください。 + +phone +----- + +アメリカの電話番号の形式であるかを確認します。もしアメリカ以外の電話番号形式を検証したいのなら、それに適する正規表現を第二パラメータに指定してください。 + +:: + + var $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +郵便番号かどうかを確認します。対応している国は、アメリカ(us)、カナダ(ca)、イギリス(uk)、イタリア(it)、ドイツ(de)、ベルギー(be)です。その他の国の郵便番号を確認したい場合、それに適する正規表現を第二引数で定義してください。 + +:: + + var $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +このルールは、指定した範囲にデータがおさまるかどうかを確認します。もし範囲を指定しなかった場合は、プログラムが実行されているプラットフォーム上で有限な数値かどうかを判定します。 + +:: + + var $validate = array( + 'number' => array( + 'rule' => array('range', 0, 10), + 'message' => '0より大きく10より小さい数を入力してください。' + ) + ); + +この例では、0より大きく (たとえば 0.01) 10より小さい (たとえば9.99) +値が許されます。 + +ssn +--- + +データが社会保障番号であるかを確認します。対応している国は、アメリカ(us)、デンマーク(dk)、オランダ(dk)です。その他の国の社会保障番号を確認したい場合、それに適する正規表現を第二引数で定義してください。 + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +正しいURLの形式であるかどうかを確認します。http(s)、ftp(s)、file、news、gopher +のプロトコルに対応しています。 + +:: + + var $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + +URLがプロトコル名から始まっていることを確認するためには、次のように厳密モードを有効にしてください。 + +:: + + var $validate = array( + 'website' => array( + 'rule' => array('url', true) + ) + ); + +独自のバリデーションルール +========================== + +もし必要とするものが見つからないなら、バリデーションルールを新たに作成してください。これには正規表現による定義と、独自のバリデーションメソッドの作成という2つの方法があります + +独自の正規表現による定義 +------------------------ + +もし必要とするバリデーションのテクニックが正規表現で完全に表せるなら、その正規表現をバリデーションルールのフィールドに定義してください。 + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('custom', '/[a-z0-9]{3,}$/i'), + 'message' => '半角英数字が3文字以上必要です。' + ) + ); + +この例では、「login」が半角アルファベットか数字で、長さは3文字以上であることを確認します。 + +独自のバリデーションメソッド +---------------------------- + +正規表現だけではデータのチェックが十分に行えない時があります。たとえば、販促コードは25回までしか使えないといった場合です。こうした時には独自のバリデーション関数を追加する必要があります。次に例を示します: + +:: + + array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'この販促コードは使い切りました。' + ) + ); + + function limitDuplicates($data, $limit){ + $existing_promo_count = $this->find( 'count', array('conditions' => $data, 'recursive' => -1) ); + return $existing_promo_count < $limit; + } + } + ?> + +独自に定義するバリデーション関数へパラメータを渡すには、まず「rule」キーに2つ以上の要素を持つ配列を割り当てます。そしてその2番目以降の要素を、バリデーション関数で必要な +``$data`` パラメータの後に続けて渡すようにしてください。 + +独自のバリデーション関数は、この例のように、モデルの中や、このモデルが実行するビヘイビアの中で定義できます。これにはマップされたメソッドも含みます。 + +モデルとビヘイビアのメソッドはバリデーションクラスのメソッドよりも前に実行されることに注意してください。これは、組み込みのバリデーションメソッド(たとえば +``alphaNumeric()``) を、\ ``AppModel`` +クラスやモデルなどのアプリケーションレベルで上書きできることを意味します。 + +コントローラ(Controller)からデータのバリデーションを実行する +============================================================ + +データを保存する前に、データのバリデーションを実行したい時があるでしょう。例えば、データをデータベースへ保存してしまう前に、ユーザに対して追加の情報を表示したい時です。こういった場合のバリデーションは、データをただ保存する時とは少し異なる方法で行います。 + +まずは、モデルにデータをセットします。 + +:: + + $this->ModelName->set( $this->data ); + +次に、データがバリデーションのルールに適しているかを確認するために、モデルの +validates +メソッドを実行します。このメソッドは、バリデーションが成功すれば true +を、失敗したら false を返します。 + +:: + + if ($this->ModelName->validates()) { + // バリデーションが成功した場合のロジックをここに書く + } else { + // バリデーションが失敗した場合のロジックをここに書く + } + +validates メソッドは、invalidFields メソッドを内部で実行し、それにより +validationErrors +プロパティがセットされます。結果のデータは、invalidFields +メソッドで取得します。 + +:: + + $errors = $this->ModelName->invalidFields(); // validationErrors 配列を含むデータを取得する + +save メソッドではデータをパラメータとして渡せます。validates +メソッドでは、実行前に必ず、モデルへデータをセットしなければなりません。この点に注意してください。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..93b3dcc587e895d1dcebd1463bc8adc0a3d0d042 --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,135 @@ +デバッグ(Debugging) +################### + +デバッグは、あらゆる開発サイクルにおいて、不可避かつ必須です。CakePHP は +IDE +やエディタから直接扱えるツールは何も提供しません。しかし、アプリケーション内部で動作し、デバッグや問題の摘出を容易にするツールをいくつか提供します。 + +基本的なデバッグ +================ + +debug($var, $showHTML = false, $showFrom = true) + +debug() 関数は、アプリケーションのどこからでも呼び出せる関数で、PHP の +print\_r() 関数に似た動作をします。debug() +関数はいくつかの異なった方法で変数の中身を表示します。変数を HTML +中でうまく表示したい場合、2番目のパラメータ $showHTML を true +にセットしてください。また、この関数は、実行されたファイル名と行番号をデフォルトで表示します。 + +この関数の出力は、コア(core)で定義したデバッグ変数(debug variable)が 0 +より大きい場合にのみ表示されます。 + +デバッガクラス(Debugger Class)の利用 +==================================== + +デバッガを使うには、まずはじめに Configure::read('debug') が 0 +より大きい値に設定されていることを確認してください。 + +dump($var) + +これは変数の中身を出力します。もしメソッドやプロパティが存在した場合、代入されている値もあわせて出力します。 + +:: + + $foo = array(1,2,3); + + Debugger::dump($foo); + + //出力 + array( + 1, + 2, + 3 + ) + + //単純なオブジェクト + $car = new Car(); + + Debugger::dump($car); + + //出力 + Car:: + Car::colour = 'red' + Car::make = 'Toyota' + Car::model = 'Camry' + Car::mileage = '15000' + Car::acclerate() + Car::decelerate() + Car::stop() + +log($var, $level = 7) + +実行した時点で、詳細なスタックトレースログを作成します。log() +メソッドが出力するデータは Debugger::dump() +と同様のものですが、出力先はバッファではなく debug.log です。log() +メソッドを正しく動作させるためには、app/tmp +とそこに含まれるディレクトリやファイルに対して、ウェブサーバを実行するユーザが書き込み権限を持つ必要があります。 + +trace($options) + +現在のスタックトレースを返します。トレースのそれぞれの行には、呼び出したメソッドと、その呼び出しが行われたのはどのファイルの何行目かの情報が含まれます。 + +:: + + //PostsController::index() 中に次のコード記入したとします + pr( Debugger::trace() ); + + //すると出力は次のようになります + PostsController::index() - APP/controllers/downloads_controller.php, line 48 + Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 265 + Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 + [main] - APP/webroot/index.php, line 84 + +これは、コントローラのアクションの中に記述された Debugger::trace() +によって生成されたスタックトレースです。スタックトレースを下から上へ読むと、現在実行されている関数の順番がわかります。これはスタッフフレームと呼びます。上述した例では、まず +index.php が Dispatcher::dispatch() を呼び出しています。次に dispatch() +メソッドは Dispatcher::\_invoke() を呼び出し、最後に \_invoke() +メソッドが PostsController::index() を呼び出します。trace() +を実行した時に呼び出されている関数が特定でき、この情報は複雑な処理や深いスタックを用いて作業する時に役立ちます。 + +excerpt($file, $line, $context) + +ファイルの一部を抜粋してハイライトします。$file +にはファイルの絶対パス、$line には何行目を抜粋するか、$context には +$line で指定した行の前後何行を抜粋するかを指定します。 + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + // これの出力は次のようになります。 + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +このメソッドは内部的に使われていますが、特定の状況でログやメッセージを表示したい場合にも便利です。 + +exportVar($var, $recursion = 0) + +デバッグで出力するために、あらゆる種類の変数を文字列に変換します。このメソッドは +CakePHP +のデバッガ内部で使われていますし、独自のデバッガで利用しても良いでしょう。 + +invoke($debugger) + +CakePHP のデバッガを新たなエラーハンドラに置き換えます。 + +デバッガクラス(Debugger Class) +============================== + +デバッガクラスは CakePHP 1.2 +の新機能で、デバッグ情報を得るための手段を提供します。このクラスは、ダンプやログの記録、そしてエラーハンドリングといった機能を提供する静的な関数を持っています。 + +デバッガクラスは PHP +のデフォルトのエラーハンドリングを上書きし、エラーレポートをより便利なものに置き換えます。CakePHP +において、デバッガクラスのエラーハンドリングは、デフォルトで使用されます。全ての関数は、Configure::debug +が 0 より大きく設定すると動作します。 + +エラーが発生したら、デバッガはページに情報を表示し、error.log +ファイルにエントリーを作成します。生成されたエラーレポートには、スタックトレースと、エラーが発生したコードの抜粋が含まれています。スタックトレースを確認するには「Error」リンクをクリックし、エラーが発生した行の抜粋を見るには「Code」リンクをクリックします。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..400a480385738a5f62cbc873d50cd4c0d904f3de --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,73 @@ +エラーハンドリング(Error Handling) +################################## + +アプリケーションにおいて、修復不可能なエラーが発生した場合、処理を停止し、ユーザへエラーメッセージを表示することが一般的です。それぞれのコントローラとコンポーネントにおいて、独自のハンドリングを作成する手間を省くため、あらかじめ提供されているメソッドを使用することができます。 + +``$this->cakeError(string $errorType [, array $parameters]);`` + +このメソッドを呼び出すと、ユーザへエラーページが表示され、アプリケーションのこれより後のあらゆる処理が停止されます。 + +``parameters`` +は、文字列型のみを含む配列でなければなりません。もし配列の中にオブジェクト(例外クラスも含みます)があった場合、文字列型へ変換されます。 + +CakePHP +はエラータイプのセットをあらかじめ定義していますが、コードを書くにあたって、ほとんどのエラータイプはフレームワークそのものにおいてのみ便利です。アプリケーションの開発者にとって、最も使い勝手の良いものは、古き良き +404 エラーでしょう。これは、次のように引数無しで呼び出すことができます: + +:: + + $this->cakeError('error404'); + +また他に、 ``url`` パラメータを渡すことで、ある特別な URL +で発生したエラーであることを報告するためのページを表示することができます: + +:: + + $this->cakeError('error404', array('url' => 'some/other.url')); + +独自のエラータイプを使うためにエラーハンドラを拡張すると、より便利になってきます。独自のエラーハンドラは、コントローラのアクションによく似ています。例によって、ビューで変数が利用できるようにするため +set() で渡し、\ ``app/views/errors`` +ディレクトリのビューファイルを出力します。 + +``app/app_error.php`` +に次のように定義をおこなったファイルを作成してください。 + +:: + + + +新しいエラータイプのハンドラは、このクラスにメソッドを追加することで実行可能になります。エラータイプとして使いたい名前のメソッドを、ただ単純に作成します。 + +いくつかのファイルをディスクに書き込むアプリケーションがあるとしましょう。書き込みエラーが発生したら、ユーザにそれを通知することが適切です。これらのコードを、アプリケーションのあちこちに追加することは望ましくありませんので、このケースは新しいエラータイプを使用することが望ましいケースです。 + +``AppError`` +クラスに新しいメソッドを追加してください。書き込みに失敗したファイルのパスを表す +``file`` というパラメータを使ってみましょう。 + +:: + + function cannotWriteFile($params) { + $this->controller->set('file', $params['file']); + $this->_outputMessage('cannot_write_file'); + } + +``app/views/errors/cannot_write_file.ctp`` にビューを作成します。 + +:: + +

ファイルを読み込めません。

+

ディスクから が読み込めませんでした。

+ +次に controller/component へエラーを投げます。 + +:: + + $this->cakeError('cannotWriteFile', array('file'=>'somefilename')); + +``$this->_outputMessage()`` のデフォルトの動作は、 +``views/errors/.ctp`` +を、ただ表示するだけです。この振る舞いを上書きするには、 AppError +クラスで ``_outputMessage($template)`` を再定義します。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..b778e01ee670625682da87362f2fb64cec9d0588 --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,116 @@ +地域化と国際化 +############## + +より多くのユーザにリーチする最も良い方法のひとつは、たくさんの異なる言語でアプリケーションを作成することです。これはしばしば、気が遠くなるような作業になります。しかし、 +CakePHP +の地域化(\ *localization*)と国際化(\ *internationalization*)の機能は、これを簡単にします。 + +まずは、いくつかの専門用語について理解しましょう。 +*国際化(\ *Internationalization*)*\ とは、あるアプリケーションをローカライズ出来るようにすることです。\ *地域化(\ *localization*)*\ とは、あるアプリケーションを特定の言語や文化での表現(すなわちロケール(\ *locale*))に適応させることです。 +国際化と地域化は、それぞれ「i18n」と「l10n」というように省略されます。 +「internationalization(国際化)」の最初と最後の文字の間に18文字あるから「i18n」となり、「localization(地域化)」も同様の理由で「l10n」となります。 + +アプリケーションを地域化する +============================ + +地域化されたコンテンツを扱う全てのコントローラで、 CakePHP +のユーティリティクラスである「L10n」を読み込んでおきましょう。 + +:: + + //Include the L10n class: + App::import('Core', 'l10n'); + class RecipesController extends AppController { //... } + +次に必要なことは、地域化された文字列を管理するための言語ファイルを作成することです。各言語は、 +ID +と文字列のセットを含みます。このファイルを作成することで、系統立てと翻訳のためのコンテンツが準備できます。各言語は、その言語の一部を含む名前がついたフォルダの中に、必ず +default.po ファイルを持たなければなりません。 + +:: + + /app/locale/eng/LC_MESSAGES/default.po (英語) + /app/locale/fre/LC_MESSAGES/default.po (フランス語) + /app/locale/por/LC_MESSAGES/default.po (ポルトガル語) + +「locale」フォルダは、 CakePHP がインストールされた場所の app +フォルダ内にあります。3文字のロケールコードは ISO 639-2 +標準に準拠します。例はアメリカ議会図書館のウェブサイトで確認できます。 +http://www.loc.gov/standards/iso639-2/php/code\_list.php +を参照してください。 + +ファイルを作成したら、ローカライズの過程で使用する、文字列のキーと値を入力してください。文字列の各キーは必ず、ユニークで対応する値をもたなければなりません。英語の +.po ファイルの基本的な記入例は、次の通りです。 + +:: + + msgid "purchase" + msgstr "Please purchase a pastry by selecting its name and pressing BUY NOW." + msgid "search" + msgstr "Click here to search our recipe database." + +.po ファイルは必ず UTF-8 で入力し、それぞれの msgstr の値の文字数上限は +1014 であることに注意してください。 Macintosh を使う場合、必ず Unix +の改行文字(LF)を使うようにしてください。そうしないと、言語ファイルが正確に解析されないことがあります。 +po ファイルを編集するにあたり、フリーのツールである +`Poedit `_ +を利用することで、このあたりの面倒を回避することができます。 + +.po ファイルを正しく作成すれば、アプリケーションは地域化されます。 + +CakePHP における国際化 +====================== + +ロケールデータはアプリケーションの中で簡潔に扱えます。まず、アプリケーションがコンテンツを出力する言語を +CakePHP +に伝えます。これはとりわけ、サブドメインで判定したり(en.example.com +は英語、 fra.example.com +はフランス語といったように)、ブラウザのユーザエージェントから情報を拾うことで行います。 + +言語の切り替えを行う最も良い場所はコントローラ内です。 + +:: + + $this->L10n = new L10n(); + $this->L10n->get("eng"); + +コントローラの各メソッドが正しい言語で出力されるよう、このコードを +beforeFilter +に設置するという使い方があります。あるいは、認証のハンドルとなるアクションか、ロケールをデフォルトから切り替えるアクションに設置するのも良いでしょう。 + +地域化されたコンテンツを表示するには、「\_\_()」という便利な関数を利用します。この関数はグローバルに使用できますが、ビューの中で使うことが多いでしょう。この関数に渡す第一引数は、 +.po ファイル中で定義されている msgid +です。デフォルトでは、地域化されたコンテンツが echo() +により出力されます。しかしオプションの第二引数によって、 echo() +による出力の代わりに値が返されます。これは例えば、テキストヘルパーを使って文字列をハイライトしたりリンクを作成したりする時に便利です。「\_\_()」 +関数を使ってどのように地域化されたコンテンツを出力するのかを、簡単な例を見ながら確認していきましょう。これらの例での実際の出力は、「L10n」クラスでの選択や、有効な構成の設定に依存します。 + +:: + + + +地域化されたデータを引数として渡しヘルパーメソッドを実行する場合、異なるヘルパーメソッドの +escape +パラメータを使用することを忘れないでください。また、「\_\_()」関数の第二引数を利用することで、 +echo() +によって出力せず、データを戻り値として返すことに注意してください。 + +:: + + error( + 'Card.cardNumber', + __("errorCardNumber", true), + array('escape' => false) + ); + ?> + +もし全てのバリデーションエラーのメッセージをデフォルトから翻訳したい時、次の記述を +app\_model.php に書き加えるという簡単な方法があります。 + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..936fc15d8e1e187210ac47f736653960c97afd8f --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,51 @@ +ログの記録(Logging) +################### + +CakePHP +コア「Configure」クラスの設定は、内部で何が起きているのかを知るための有益な手段です。しかしながら、起きていることの情報をディスクにログデータとして保存したい場合が出てくるでしょう。SOAP +や AJAX +といった技術に依存することが多くなるにつれ、デバッグはより困難になります。 + +ログの記録(\ *Logging*)は、アプリケーションが実行されていくにつれ何が起きているのかを知るための手段です。たとえば、「実行された検索の条件は何か?」「ユーザに表示されたエラーの種類は何なのか?」「ある特定のクエリが出現する頻度はどれくらいか?」といったことです。 + +CakePHP +データをログに記録するのは簡単で、ほとんどのクラスの親となっている「Object」クラスで定義されている「log()」関数を呼び出すだけです。Model、Controller、Component +他、ほとんど全ての CakePHP +のクラス中で、この機能を使ってデータをログに記録できます。 + +log 関数を使う +============== + +「log()」 +関数にはふたつの引数をあたえます。ひとつめは、ログファイルに記録したいメッセージです。デフォルトでは、ログは +app/tmp/logs/error.log に記録されます。 + +:: + + // CakePHP のクラス中で log 関数を実行する + + $this->log("Something didn't work!"); + + // app/tmp/logs/error.log に、次のように追加される + + 2007-11-02 10:22:02 Error: Something didn't work! + +第二引数では、記録するログのタイプを定義します。 +指定しなかった場合、前述したエラーログのパスに書き出す LOG\_ERROR +がデフォルトで定義されます。もし LOG\_DEBUG +に設定した場合は、デバッグログとして app/tmp/logs/debug.log +にログが出力されます。 + +:: + + // CakePHP のクラス中で log 関数を実行する + + $this->log('A debugging message.', LOG_DEBUG); + + // error.log ではなく app/tmp/logs/debug.log に、次のように追加される + //Results in this being appended to app/tmp/logs/debug.log (rather than error.log) + + 2007-11-02 10:22:02 Error: A debugging message. + +ログ機能を正しく動作させるためには、ウェブサーバが実行されるユーザが +app/tmp ディレクトリに書き込み権限を持つ必要があります。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..fd67acf80917bd500cc4ce18a2f38bc002489ad9 --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,320 @@ +ページ付け(Pagination) +###################### + +柔軟でユーザーフレンドリーなウェブアプリケーションを作成する上での主要な課題のひとつに、直感的なユーザインターフェースをデザインするということがあります。多くのアプリケーションは、その規模と複雑さが急激に増える傾向にあります。そしてデザイナーもプログラマーも、ひとつの画面に100行1000行というレコードを表示することがかなわないことに気づくのですが、それを解決するリファクタリングには時間がかかります。その間パフォーマンスは失われ、ユーザの不満はつのります。 + +ひとつのページあたりに表示するレコード数を適切にすることは、あらゆるアプリケーションにおいてとても重要なことなのですが、これは開発者にとって頭痛の種となります。 +CakePHP +はデータのページ付けを簡単にすばやく行う機能を提供することで、開発者の悩みを和らげます。 + +「PaginatorHelper」はとても簡単に使えるため、重要な解決策となります。これにはページ付けのほかにも、簡単に使える並び替えのための機能があります。最後になりましたが、 +CakePHP は Ajax +を用いた並び替えやページ付けもサポートしていることを付け加えておきます。 + +コントローラのセットアップ +========================== + +コントローラにおいてまずすべき事柄は、コントローラ変数 *$paginate* +でページ付けの初期設定値を定義することです。そのとき、必ず並び替えを定義しなければならない点に注意してください。これは「order」をキーにして値を配列で渡すことで行います。 + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +*fields* のような他の find() のオプションも含めることができます。 + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +*$paginate* 配列に含まれる他のキーは +*conditions*\ 、\ *fields*\ 、\ *order*\ 、\ *limit*\ 、\ *page*\ 、\ *contain* +そして *recursive* +となり、「\ *Model->find('all')*\ 」メソッドのパラメータに似ています。 +似ているというより、実は各モデル名をキーにすることで、複数のモデルに対してページ付けの初期設定値を定義できるのです。 + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'Recipe' => array (...), + 'Author' => array (...) + ); + } + +「Containable」ビヘイビアを使用した構文の例です: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +*$paginate* 変数を定義すれば、コントローラのアクションの中で +*paginate()* +メソッドを呼び出すことができます。このメソッドはモデルから1ページ分の +*find()* +の結果を返し、ページ分けの統計情報を取得し、この統計情報を自動的にビューへ渡します。また、このメソッドは、ヘルパーのリストが +PaginatorHelper に追加されていない場合、追加を実行します。 + +:: + + function list_recipes() { + // findAll() に類似したデータを1ページ分取得する + $data = $this->paginate('Recipe'); + $this->set('data', $data); + } + +取得するレコードを絞るには、\ ``paginate()`` +関数の第2引数に検索条件を渡します。 + +:: + + $data = $this->paginate('Recipe', array('Recipe.title LIKE' => 'a%')); + +あるいは、 ``$paginate`` 配列の *conditions* +キーに検索条件を指定します。 + +ビューにおけるページ付け +======================== + +レコードをユーザへどのように見せるかは自由ですが、 HTML +のテーブルタグを用いた表示がよく使われるでしょう。下の例は表組みを用いた例になります。ただし、ビューの中で利用できる +PaginatorHelper は、この例のように制限されているわけではありません。 + +また、PaginatorHelper +は、テーブルのカラムヘッダに簡単に入れられる並び替えの機能を提供します。 + +:: + + // app/views/recipes/list_recipes.ctp +
DateTitreActif
DateTitreActif
7 juillet 2007Meilleurs BrowniesOui
21 juin 2007Gâteaux chicsOui
1er août 2006Cake Anti-JavaNon
7 juillet 2007Meilleurs BrowniesOui
21 juin 2007Gâteaux chicsOui
1er août 2006Cake Anti-JavaNon
RougePomme
OrangeOrange
JauneBanane
+ + + + + + + + + + +
sort('ID', 'id'); ?>sort('Title', 'title'); ?>
+ +PaginatorHelper の sort() +メソッドがテーブルのカラムヘッダに出力するリンクをクリックすることで、ユーザはデータの並び順を変更することができます。 + +アソシエーションによって関連づいたテーブルのカラムを並び替えに使用することもできます。 + +:: + + + + + + + + + + + + +
sort('Title', 'title'); ?>sort('Author', 'Author.name'); ?>
+ +最後に、ビューで表示するページ付けの要素であるページナビゲーションについて説明します。これも +PaginationHelper によって提供される機能です。。 + +:: + + + numbers(); ?> + + prev('« Previous ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Next »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +counter() +メソッドが出力する文言は、特別なマーカーを使うことで変更できます。 + +:: + + counter(array( + 'format' => '合計 %pages% ページ中の %page% ページ目です。 + 総レコード %count% のうち、 %start% 行目から %end% 行目までの %current% 行を表示しています。' + )); + ?> + +すべての URL +の引数をページ付けの関数に送るには、次のコードをビューに書いてください。 + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +あるいは、特定のパラメータのみを手動で渡すこともできます。 + +:: + + $paginator->options(array('url' => array("0", "1"))); + +AJAX によるページ付け +===================== + +ページ付けに Ajax +を取り入れることはとても簡単です。必要となる特別なコードは、 JavaScript +ライブラリの prototype.js +を読み込むことと、読み込み中のアイコンが含まれるインジケーターをセットすること、そしてページをリロードする代わりに上書きされる +DIV 要素を定義することだけです。 + +Ajax を利用する場合、コントローラで必ず RequestHandler +コンポーネントを読み込んでください。 + +:: + + var $components = array('RequestHandler'); + +レイアウトの変更 +---------------- + +まず、 HTML 文書の head タグの中で prototype.js を読み込みます。 +次にステータスを表示するインジケーター用の画像(spinner.gif)のセットアップを行い、 +そして最後にメインコンテンツをラップする id を "content" とする DIV +要素を作成します。 + +これらを行ったレイアウトを一部抜粋すると、次のようになります。 + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +ビューの変更 +------------ + +ビューにおいて Ajax のページ付けを行うための特別なことは、 +PaginationHelper::options() メソッドを使って必要な Ajax +のパラメータを定義することだけです。このケースでは、「content」という ID +の要素を取得した結果で上書きし、読み込み中に表示するインジケーターとして +spinner を使うと定義しています。 + +もし「update」キーが定義されていなかったら、 PaginationHelper は Ajax +を使わないページ付けと並び替え、ページ遷移のリンクを出力します。 + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Previous', null, null, array('class' => 'disabled')); + + echo $paginator->next('Next >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +カスタムしたクエリによるページ付け +================================== + +ページ付けしたいデータを作成するためにクエリをカスタムする必要がある場合、 +PaginationHelper で使われている paginate() メソッドと paginateCount() +メソッドを上書きしてください。 paginate() メソッドは Model::find() +と同じパラメータを持ちます。 独自の paginate() +を使うには、そのデータを取得するモデルの中に paginate() +関数を作成してください。 + +:: + + /** + * カスタムした paginate メソッド + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $conditions[] ="1 = 1 GROUP BY week, away_team_id, home_team_id"; + $recursive = -1; + $fields = array('week', 'away_team_id', 'home_team_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } + +この時、 paginateCount() +も同じモデルの中で上書きする必要があるでしょう。このメソッドは、 +Model::findCount() と同じ引数を受け付けます。次の例は PostgreSQL +だけで使える例です。実際に使う場合は、利用するデータベース管理システムに適した記述にしてください。 + +:: + + /** + * カスタムした paginateCount メソッド + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +RC2 といったごく最近の CakePHP では、Model::find() メソッドに **group** +というキーワードが追加され、これを使うと paginate() +を上書きせずにすみます。 コントローラの $paginate +クラス変数を次のように指定するだけです。 + +:: + + /** + * GROUP BY を追加する + */ + public $paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + +paginateCount() +メソッドは、まだ上書きする必要があります。例は上述したものと同じです。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..12bc6291b73007db57913d23cfe21238d4001a76 --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,187 @@ +REST +#### + +最近のアプリケーションプログラマの多くは、たくさんのユーザに対してコアとなる機能を開放することの重要性を実感しています。コア +API +へ自由なアクセスが簡単に行えるなら、そのアプリケーションは多くの人に受け入れられるでしょうし、マッシュアップや他のシステムとの連携もしやすくなるでしょう。 + +REST +はアプリケーション中のロジックへの容易なアクセスを提供する便利な方法です。他にも方法はありますが、 +REST はとてもシンプルです。 SOAP エンベロープに比べると非常に簡潔な XML +を用いてデータのやりとりを行い、制御は HTTP ヘッダで行います。 CakePHP +において REST を用いた API の公開は簡単に行えます。 + +単純なセットアップ +================== + +REST を起動して実行するための最も早い方法は、 app/config にある +routes.php に、必要な行を加えることです。「Router」オブジェクトの +mapResources() メソッドを呼び出すと、 REST +でコントローラにアクセスするルートの初期状態を変更できます。たとえば +recipe データベースに REST +で接続できるようにするには、次のような記述をします。 + +:: + + // app/config/routes.php の中で次のコードを書く + + Router::mapResources('recipes'); + Router::parseExtensions(); + +最初の行で、 REST +によるアクセスのルートの状態を設定しています。設定したルートは HTTP +リクエストメソッドに応じたものになっています。 + ++-----------------+----------------+----------------------------------+ +| HTTP メソッド | URL | 起動するコントローラアクション | ++=================+================+==================================+ +| GET | /recipes | RecipesController::index() | ++-----------------+----------------+----------------------------------+ +| GET | /recipes/123 | RecipesController::view(123) | ++-----------------+----------------+----------------------------------+ +| POST | /recipes | RecipesController::add() | ++-----------------+----------------+----------------------------------+ +| PUT | /recipes/123 | RecipesController::edit(123) | ++-----------------+----------------+----------------------------------+ +| DELETE | /recipes/123 | RecipesController::delete(123) | ++-----------------+----------------+----------------------------------+ +| POST | /recipes/123 | RecipesController::edit(123) | ++-----------------+----------------+----------------------------------+ + +CakePHP の Router クラスは、何の HTTP +メソッドが使われているのかを判断するために、いくつかの情報を使います。優先度が高い順に次のようになります。 + +#. POST 変数の *\_method* 。 +#. X\_HTTP\_METHOD\_OVERRIDE +#. REQUEST\_METHOD ヘッダ + +POST 変数の *\_method* は、 REST +のクライアントとしてブラウザ(もしくは簡単に POST +が実行できるクライアント)を利用する時に便利です。 \_method の値に HTTP +リクエストメソッドの種類を入れれば、その HTTP +メソッドをエミュレートすることができます。 + +router で REST +リクエストをコントローラアクションにマップしたら、そのコントローラアクションを実装していきましょう。基本的なコントローラの構造は次のようになるでしょう。 + +:: + + // controllers/recipes_controller.php + + class RecipesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recipes = $this->Recipe->find('all'); + $this->set(compact('recipes')); + } + + function view($id) { + $recipe = $this->Recipe->findById($id); + $this->set(compact('recipe')); + } + + function edit($id) { + $this->Recipe->id = $id; + if ($this->Recipe->save($this->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recipe->del($id)) { + $message = 'Deleted'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + } + +Router::parseExtensions() の呼び出しを追加しているので、 CakePHP の +router +はすでに、異なる種類のリクエストに基づき通常とは異なるビューを出力するようになっています。 +REST でのりクエストを実行すると、ビューのタイプは XML になります。 +RecipesController のためのビューは app/views/xml +の中に設置しましょう。これらのビューの中で XmlHelper +を用いると、簡単にすばやく XML +を出力することができます。ビューのコードは次のようになります。 + +:: + + // app/views/recipes/xml/index.ctp + + + serialize($recipes); ?> + + +賢明な CakePHP ユーザー諸氏の中には、 RecipesController の $helpers +配列に XmlHelper +が含まれていないことにお気づきの方もいるかもしれません。これは意図的にそうしています。 +parseExtension() を使って特殊なコンテンツタイプのデータを出力するとき、 +CakePHP は自動的に最適なビューヘルパーを探し出すのです。 XML +型のコンテンツを出力するときは、ビューに XmlHelper +が自動的に読み込まれます。 + +構築される XML は次のようなものになるでしょう。 + +:: + + + + + + + + + + + + +edit +アクションの作成では注意する点があります。大したことはありませんが、少しややこしいのです。 +REST を使うと XML を出力するわけですから、入力も XML +で行うことが自然です。しかし心配しないでください。 RequestHandler と +Router クラスによって XML での入力を簡単に扱えます。 POST あるいは PUT +リクエストが XML 型のコンテンツを持つ場合、その入力値は CakePHP の Xml +オブジェクトのインスタンスに渡されます、インスタンスはコントローラにおける +$data +変数のプロパティに登録されます。この機能により、コントローラやモデルのコードを変更することなく、 +XML と POST +のデータを違和感なく同時に扱うことができます。必要なデータは、 +$this->data の中にあるのです。 + +独自の REST ルーティング +======================== + +もし mapResources() +によって作られたルートの初期設定値が用途にそぐわなかったら、 +Router::connect() を使って独自の REST ルートを定義してください。 +connect() メソッドには、与えられた URL +に対していくつかの異なるオプションを定義します。一つ目のパラメータは、 +URL +自身で、二つ目のパラメータはそれらのオプションを提供します。三つ目のパラメータは、指定された +URL の中で CakePHP +がある特定のマーカーを見つけるために指定する正規表現です。 + +その他の REST +の目的に応じたルートを仕立てる方法を、例で示します。ここでは、 +mapResources() を使わずに REST のルートを編集しています。 + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9+]") + ) + +より進んだルーティングのテクニックは他のところで説明されているので、ここでは特に重要な点である第二引数の配列の +[method] +キーについて説明します。このキーの値がセットされると、その値で指定した +HTTP リクエストメソッドの時だけ定義されたルートが機能します。 HTTP +リクエストメソッドには GET 、 DELETE やその他のものも指定できます。 diff --git a/ja/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/ja/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..f580865c42cb383ae017afc4bd06137fb38c00e5 --- /dev/null +++ b/ja/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,955 @@ +テスト(Testing) +############### + +CakePHP 1.2 +には、テストのための包括的なフレームワークが組み込まれています。このフレームワークは、PHP +向けの SimpleTest +フレームワークを拡張したものです。この章では、テスト、それの構築および実行についての準備をどのようにするかを説明します。 + +テストの準備 +============ + +心の準備はよろしいですか?では早速はじめてみましょう! + +SimpleTest のインストール +------------------------- + +SimpleTest のインストール + +CakePHP 1.2 のテストフレームワークは、SimpleTest +で構築されています。SimpleTest はデフォルトで CakePHP +に含まれていないため、まずそれをダウンロードしてこなければなりません。ダウンロードは\ `http://simpletest.sourceforge.net/ `_\ から行えます。 + +最新版を取得し、解凍後に vendors もしくは app/vendors +フォルダに展開してください。どちらでも好きな方に設置してかまいません。これで +vendors/simpletest ディレクトリの中に、SimpleTest +のファイルが全て設置された状態になります。テストを走らせる前に、app/config/core.php +でデバッグレベル(DEBUG level)を 1 +以上に設定することを忘れないでください! + +もし、app/config/database.php +においてテストのためのデータベース接続が定義されていない場合は、\ ``test_suite_`` +という前置詞がついたテスト用のテーブルが生成されます。 +テスト用のテーブルを格納するための ``$test`` +データベース接続を作成するには、次のように行います。 + +:: + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'dbhost', + 'login' => 'dblogin', + 'password' => 'dbpassword', + 'database' => 'databaseName' + ); + +もしテスト用のデータベースが利用可能で、CakePHP +がそれに接続することができるなら、全てのテスト用のテーブルはそのデータベースに作成されます。 + +組み込みのテストケースを実行する +-------------------------------- + +CakePHP +は、コアの機能としてテストケースをすでに含んでいます。このテストに接続するには、http://ドメイン名/CakePHPのフォルダ/test.php +をブラウズしてください(ただし、この URL +は設定に依ります)。リンクをクリックして、コアのテストグループをどれか一つ実行してみてください。実行には少し時間がかかります。しばらくして、「2/2 +test casese complete: 49 passes, 0 fails and 0 +exceptions.」といったメッセージが表示されると、テストの実行は完了です。 +これで、独自のテストを書く準備が出来ました。 + +テストについて - 単体テストとウェブテスト +========================================= + +CakePHP +のテストのフレームワークは、2種類のテストをサポートしています。ひとつは単体テスト(ユニットテスト、Unit +Testing)です。これはコンポーネントのメソッドや、コントローラのアクションなど、小さな部分に対するテストです。もうひとつはウェブテスト(Web +Testing)です。これは、アプリケーションのテストの時に行うページのナビゲート、フォームの入力、リンクのクリック等の作業を自動化します。 + +テストデータの準備 +================== + +フィクスチャ(Fixtures)について +------------------------------ + +テストコードがモデルやデータに依存する場合、テストで利用する一時的なサンプルデータをデータテーブルに読み込むために、\ **フィクスチャ(\ *fixtures*)**\ を利用できます。フィクスチャを使用することの利点は、テストのために実際のデータをいじらずに済むこと、そして実際のデータを作成する前にコードをテストできるということです。 + +CakePHP は app/config/database.php で ``$test`` +と名前が付けられた設定で接続を試み、もしこの接続が使用できない場合、\ ``$default`` +の設定で接続します。接続したら、テスト用のテーブルを作成します。いずれの接続においても、テーブル名の衝突を避けるため、プレフィックスに「test\_suite」を追加した名前でテーブルを作成します。 + +CakePHP +はフィクスチャに基づいたテストケースを実行するにあたり、次の動作をします。 + +#. 各フィクスチャで必要なテーブルを作成する。 +#. フィクスチャにデータが存在すれば、それをテーブルに投入する。 +#. テストのメソッドを実行する。 +#. フィクスチャのテーブルを空にする。 +#. データベースからフィクスチャが作成したテーブルを削除する。 + +フィクスチャの作成 +------------------ + +フィクスチャを作成するには、主にふたつの定義を行います。ひとつめは、テーブル構造、すなわち存在するフィールドの定義です。もうひとつは、そのテスト用のテーブルが初期状態で持つデータの定義です。例として、Article +というモデルに対するフィクスチャを作成してみましょう。\ **app/tests/fixtures** +ディレクトリに、\ **article\_fixture.php** +というファイルを作成し、次のように記述してください。 + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'title' => array('type' => 'string', 'length' => 255, 'null' => false), + 'body' => 'text', + 'published' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'title' => '1番目の記事', 'body' => '1番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => '2番目の記事', 'body' => '2番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => '3番目の記事', 'body' => '3番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +$fields +には、テーブルのフィールドがどのように定義されているかを記述します。書式は、dbo\_mysql.php +など CakePHP のデータベースエンジンクラスにある +**generateColumnSchema()** +関数と同じものです。使用可能な属性とその意味を見ていきましょう。 + +type + CakePHP 内部の型。現在は、string (VARCHAR にマップ)、text (TEXT + にマップ)、integer (INT にマップ)、float (FLOAT にマップ)、datetime + (DATETIME にマップ)、timestamp (TIMESTAMP にマップ)、time (TIME + にマップ)、date (DATE にマップ)、そして binary (BLOB にマップ) + という型がサポートされています。 +key + 値を「primary」にセットすると、そのフィールドは AUTO\_INCREMENT + 属性になり、テーブルの主キーになります。 +length + フィールドの長さを設定します。 +null + true をセットすると NULL を許可、false だと NULL を許可しません。 +default + フィールドの初期値を定義します。 + +最後に、テスト用のテーブルを作成した後に投入するデータをセットします。この書式はとても簡単なので説明は省略しますが、一点だけ注意が必要です。それは、 +$records の配列は $fields +で定義した\ **全て**\ のフィールドをキーとして持つ必要があるということです。もし、あるフィールドに +NULL を入れる場合は、そのフィールド名をキーにして値を NULL +をセットしてください。 + +テーブルの情報とレコードの読み込み +---------------------------------- + +アプリケーションに動作するモデルがあり、モデルが扱うテーブルに実際のデータが存在する場合、そのデータとモデルをテストに使うことができます。そうした場合、テーブル定義とレコードをフィクスチャに移す必要があります。幸い、存在するテーブルとデータを利用して、特定のフィクスチャのテーブルおよびレコードの定義を行う方法があります。 + +例を見てみましょう。アプリケーション中に「Article」という名前のモデルがあるとして、それは「articles」テーブルにマップされているとします。前節で作成した例(\ **app/tests/fixtures/article\_fixture.php**)を次のように書き換えてください。 + +:: + + + + +この構文は、「Article」モデルにリンクしたテーブルから、テーブル定義を読み込むよう統合テストツール(\ *test +suite*)に伝えます。モデルは、アプリケーションに存在する全てのものを扱えます。上記の構文ではレコードを読み込みません。読み込むためにはコードを次のように変更してください。 + +:: + + 'Article', 'records' => true); + } + ?> + +一方、モデルが存在しないテーブルの場合はどうするのでしょうか。その場合、代わりにテーブルの情報を読み込みよう定義することができます。例は次の通りです。 + +:: + + 'articles'); + } + ?> + +この例では「articles」という名前のテーブルから、テーブル定義を読み込んでいます。その時に +CakePHP +は「default」という名前の設定を使ってデータベースへ接続します。これを変更したい場合は次のようにしてください。 + +:: + + 'articles', 'connection' => 'other'); + } + ?> + +CakePHP +のデータベース接続においてテーブル名のプレフィックスが指定されていたら、テーブル情報を取得するときにそのプレフィックスは自動的に使用されます。また、前述したふたつの例において、レコードは読み込まれません。読み込むには、次のようにします。 + +:: + + 'articles', 'records' => true); + } + ?> + +以上の方法で、テーブルまたはモデルから、テーブル情報を読み込むことができます。前の章で説明したように、フィクスチャに対して読み込むレコードを直接定義することもできます。例は次の通りです。 + +:: + + 1, 'title' => '1番目の記事', 'body' => '1番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => '2番目の記事', 'body' => '2番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => '3番目の記事', 'body' => '3番目の記事の内容', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +テストの作成 +============ + +まずはテストに関するルールとガイドラインを順番に見ていきましょう。 + +#. テストのための PHP ファイルは、\ **app/tests/cases/フォルダ** + に設置します。 +#. ファイル名は「.php」ではなく「\ **.test.php**\ 」で終わらなければいけません。 +#. テストのためのクラスは、\ **CakeTestCase** または **CakeWebTestCase** + を拡張したものでなければなりません。 +#. テストのためのメソッド、つまりアサーション(assertion)の名前は、「\ **test**\ 」で始まらなければなりません。たとえば、「\ **testPublished()**\ 」といったようにです。 + +テストケースを作成したら、\ **http://ドメイン名/CakePHPのフォルダ/test.php**\ (設定に依る)をブラウズし、「App」の「Test +Cases」をクリックし、作成したファイルへのリンクをクリックすることで実行できます。 + +CakeTestCase Callback Methods +----------------------------- + +If you want to sneak in some logic just before or after an individual +CakeTestCase method, and/or before or after your entire CakeTestCase, +the following callbacks are available: + +**start()** + First method called in a *test case*. + +**end()** + Last method called in a *test case*. + +**startCase()** + called before a *test case* is started. + +**endCase()** + called after a *test case* has run. + +**before($method)** + Announces the start of a *test method*. + +**after($method)** + Announces the end of a *test method*. + +**startTest($method)** + Called just before a *test method* is executed. + +**endTest($method)** + Called just after a *test method* has completed. + +モデルのテスト +============== + +テストケースの作成 +------------------ + +すでに app/models/article.php +で「Article」モデルが次のように定義されているとします。 + +:: + + name . '.published' => 1 + ); + + return $this->findAll($conditions, $fields); + } + + } + ?> + +モデルの機能をテストするために、上述のモデル定義にフィクスチャを用いてテストするように準備を行うとします。CakePHP +の統合テストツールは、最小限のファイルだけを読み込みます。これは問題の切り分けを容易にするため、他のモデルの干渉を避けるためです。そのためまず、親のモデル(今回の場合は、定義済みの「Article」モデル)を読み込みます。次にどのデータベースの設定を使用するかを指定します。CakePHP +の統合テストツールは、フィクスチャに頼るすべてのモデルで使われる、 +test\_suite という名前の DB 設定を有効にします。$useDbConfig +を設定すると、 CakePHP +の統合テストツールが、どのデータベース接続を利用するのかを指定できます。 + +定義済みのモデルのコードを再利用するために、「Article」クラスを拡張したテストのモデルを作成し、 +$useDbConfig と $name を適切に設定します。そのファイルは +**app/tests/cases/models** に **article.test.php** +という名前で保存してください。内容は次のようになります。 + +:: + + + +「ArticleTestCase」を作成しました。\ **$fixtures** +変数では、どのフィクスチャを使うかを定義します。 + +モデルが別のモデルと関連しているなら、関連しているモデルを使用しない場合でも、全てのモデルのフィクスチャを用意しておく必要があります。例えば、 +A hasMany B hasMany C hasMany D といった場合、 ATestCase には a, b, c, d +のフィクスチャを含めます。 + +テストのためのメソッドを作成する +-------------------------------- + +「Article」モデルの「published()」関数をテストするためのメソッドを作成してみましょう。\ **app/tests/cases/models/article.test.php** +を次のように編集してください。 + +:: + + Article =& ClassRegistry::init('Article'); + + $result = $this->Article->published(array('id', 'title')); + $expected = array( + array('Article' => array( 'id' => 1, 'title' => '1番目の記事' )), + array('Article' => array( 'id' => 2, 'title' => '2番目の記事' )), + array('Article' => array( 'id' => 3, 'title' => '3番目の記事' )) + ); + + $this->assertEqual($result, $expected); + } + } + ?> + + +「\ **testPublished()**\ 」という名前のメソッドを追加しました。まずフィクスチャを用いた「\ **Article**\ 」モデルのインスタンスを作成し、次にそのインスタンスの「published()」メソッドを実行します。「article」が初期状態で持つレコードをフィクスチャで定義したので、「published()」メソッドが返すべき値はわかります。この値を、 +**$expected** +に設定してください。「\ **assertEqual**\ 」メソッドを用いて、実際の値と期待した値が一致するかをテストします。テストの実行方法は、「テストの作成」の章を参照してください。 + +コントローラのテスト +==================== + +テストケースの作成 +------------------ + +下記のように、「Article」モデルに対応した典型的なコントローラーがあるとします。 + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $result = $this->Article->findAll(null, array('id', + 'title')); + } else { + $result = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $result; + } + + $this->set('title', 'Articles'); + $this->set('articles', $result); + } + } + ?> + +ファイル名でディレクトリ app/tests/cases/controllers に +articles\_controller.test.php +というファイルを作成し、次のように記述してください。 + +:: + + テストケースを開始します'; + } + function endCase() { + echo '

テストケースを終了します

'; + } + function startTest($method) { + echo '

メソッド「' . $method . '」を開始します

'; + } + function endTest($method) { + echo '
'; + } + function testIndex() { + $result = $this->testAction('/articles/index'); + debug($result); + } + function testIndexShort() { + $result = $this->testAction('/articles/index/short'); + debug($result); + } + function testIndexShortGetRenderedHtml() { + $result = $this->testAction('/articles/index/short', + array('return' => 'render')); + debug(htmlentities($result)); + } + function testIndexShortGetViewVars() { + $result = $this->testAction('/articles/index/short', + array('return' => 'vars')); + debug($result); + } + function testIndexFixturized() { + $result = $this->testAction('/articles/index/short', + array('fixturize' => true)); + debug($result); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'published' + => 1, 'slug'=>'new-article', 'title' => '新しい記事', 'body' => '新しい記事の本文')); + $result = $this->testAction('/articles/index', + array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($result); + } + } + ?> + +testAction メソッド +------------------- + +「\ **testAction**\ 」メソッドが、新しく登場しました。第一引数はテストするコントローラアクションの +Cake URL (例えば「/articles/index/short」といったもの) です。 + +第二引数にはパラメータを配列で渡し、パラメータは次のキーから成ります。 + +return + 戻り値として得られるべきものを定義します。 + 有効な値は次の通りです。 + 'vars' - アクションを実行した後に view 変数を取得する。 + 'view' - レンダリングされた view + のうち、レイアウトを除いたものを取得する。 + 'contents' - レンダリングされた view + のうち、レイアウトを含めた完全な HTML を取得する。 + 'result' - アクションが $this->params['requested'] + を使用した時に返す値を取得する。 + なお、デフォルトは 'result' です。 +fixturize + true にセットすると、モデルに対し自動的にフィクスチャを適用します。 + それにより、実際のテーブルはレコードと一緒にテスト用のテーブルへとコピーされ、実際のアプリケーションに影響を与えません。 + + もしモデル名を配列で与えたら、それらに対して自動的にフィクスチャを適用し、その他のモデルは実際のテーブルを使用します。 +method + コントローラにデータを渡す方法を「post」または「get」から選べます。 +data + 渡すデータを、「フィールド => 値」から成る連想配列でセットします。 + + フォームから送るデータをどのようにエミュレートするかについては、先のテストケース例における + testIndexPostFixturized() 関数を参照してください。 + +落とし穴 +-------- + +リダイレクトを行うコントローラのメソッドに対して testAction +を使用した場合、テストは何も結果を返さず、ただちに中断します。 + +`https://trac.cakephp.org/ticket/4154 `_ +を参照して、修正を確認してください。 + +ヘルパーのテスト +================ + +ヘルパークラスにたくさんのロジックがあるなら、それらがテストケースでカバーされていることの確認は重要です。 + +ヘルパーのテストは、コンポーネントのテストのアプローチに少し似ています。 +CurrencyRendererHelper というヘルパーが +``app/views/helpers/currency_renderer.php`` +に存在する場合、それに対応したテストケースのファイルは +``app/tests/cases/helpers/currency_renderer.test.php`` に設置します。 + +Creating Helper test, part I +---------------------------- + +まずはじめに、CurrencyRendererHelper +が何を行うかを決めます。デモンストレーション用なので、簡単に2つのメソッドだけを持つことにしまします。 + +function usd($amount) + +この関数は表示する金額の数字を受け取ります。この関数は、小数点以下第二位までの数字を返し、もし桁が足りなければゼロで埋め、前置詞に +'USD' を付けます。 + +function euro($amount) + +この関数は usd() と同じように働きますが、前置詞は 'EUR' +を付けます。少し複雑にするために、span タグで囲うようにしましょう。 + +:: + + + +まずはじめに、テストを作成します。 + +:: + + currencyRenderer = new CurrencyRendererHelper(); + } + + //usd() 関数のテスト + public function testUsd() { + $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); + //We should always have 2 decimal digits. + $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); + $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); + //3桁ごとに入れるセパレータのテスト + $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70)); + } + +これで、\ ``usd()`` +が異なるパラメータで呼び出され、統合テストツールは戻り値が期待したものと一致するかどうかをチェックするようになりました。 + +まだ currencyRendererHelper +を作成していないので、テストを実行すると3つのテスト失敗という結果が表示されます。 + +メソッドがどのように動作するべきかはわかっているので、実装していきましょう。 + +:: + + `_)、モデルのデータにアクセスするためのコントローラが必要です。 + コンポーネントの startup() 関数を次のようにします。 + +:: + + public function startup(&$controller){ + $this->Transporter = $controller->Transporter; + } + +次に、ごく簡単な仮のクラスを作成します。 + +:: + + class FakeTransporterController {} + +そしてその中に次のように値を設定していきます。 + +:: + + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + +テスト用のメソッドの作成 +------------------------ + +CakeTestCase を拡張したクラスを作成し、テストを書いていきましょう。 + +:: + + class TransporterTestCase extends CakeTestCase { + var $fixtures = array('transporter'); + function testGetTransporter() { + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); + $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); + + $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); + $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); + + $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); + $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); + $this->assertEqual($result, 0, "Noone can service Norway"); + } + } + + +ウェブテスト - ビューのテスト +============================= + +CakePHP +でのプロジェクトは、ほとんど全てがウェブアプリケーションです。単体テストにおいて、小分けにした機能のテストがうまくいったら、それより大きなスケールでの機能をテストしたくなるでしょう。「\ **CakeWebTest**\ Case」クラスは、ユーザの視点からこのテストを行う優れた方法を提供します。 + +CakeWebTestCase について +------------------------ + +**CakeWebTestCase** は、SimpleTest の WebTestCase +をただ拡張したもので、特に機能追加はありません。\ `SimpleTest の Web +testing +に関する文書 `_\ 中に記載がある全ての機能は、 +CakeWebTestCase で利用できます。これはまた、 SimpleTest +が持つ機能以外のものは使えないことを意味します。すなわち、 +CakeWebTestCase +においてフィクスチャは利用できず、\ **テストケースにデータベースに対する更新や保存が含まれていた場合、恒久的にデータベースの値が変更されることを意味します。**\ テストの結果は、しばしばデータベースが持つ値に基づくので、テスト手順の一部としてデータベースが期待した値を持つことを確認してください。 + +テストの作成 +------------ + +ウェブテストを実行したい場合、まず CakeTestCase の代わりに +**CakeWebTestCase** を拡張したクラスを必ず用意してください。 + +:: + + class CompleteWebTestCase extends CakeWebTestCase + +テストを開始する前に何か準備を行う必要があれば、コンストラクタを作成してください。 + +:: + + function CompleteWebTestCase(){ + //テスト開始前の準備をここで行う + } + +実際にテストケースを書く場合、まずは出力結果を取得することから始めます。出力結果を得るためには +get もしくは post のリクエストを行うのですが、これには **get()** と +**post()** +関数をそれぞれ使用します。これらの関数は両方とも、第一引数として完全な +URL を必要とします。URL は、もしテストのスクリプトが +http://ドメイン名/cake/folder/webroot/test.php +であるとすると、次のようにすることで動的に取得できます。 + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +Cake 流の URL を用いて get や post を行うには、次のようにします。 + +:: + + $this->get($this->baseurl."/products/index/"); + $this->post($this->baseurl."/customers/login", $data); + +post メソッドの第二引数 $data は、Cake フォーマットのデータを含む、 post +するデータを連想配列にしたものです。 + +:: + + $data = array( + "data[Customer][mail]" => "user@user.com", + "data[Customer][password]" => "userpass"); + +ページをリクエストしたら、標準的な SimpleTest +ウェブテストのメソッドを用いて、全ての種類のアサーションを行うことが可能です。 + +ページ全体を確認する +-------------------- + +CakeWebTest +は、ハイパーリンクや画像をクリックすることでページ全体をナビゲートする手段も提供します。詳しくは、 +SimpleTest の文書を参照してください。 + +プラグインのテスト +================== + +プラグインのテストは、プラグインのフォルダの中にディレクトリを作成してそこに設置してください。 + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +このように設置したテストは、通常のテストと同様に動作します。ただし、別のクラスを読み込む場合、プラグインの命名に留意してください。これは、このマニュアルのプラグインの章で登場する「PizzaOrder(ピザの注文)」モデルに対するテストケースの例です。他のテストと異なるところは、最初に「Pizza.PizzaOrder」をインポートする部分だけです。また、「\ ``plugin.plugin_name.``\ 」というプレフィックスを付けたプラグインのフィクスチャも必要です。 + +:: + + PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); + + // 通常のテストを行います + $this->assertTrue(is_object($this->PizzaOrderTest)); + } + } + ?> + +アプリケーションのテストにおいてプラグインのフィクスチャを使用したい場合は、$fixtures +配列において「plugin.pluginName.fixtureName」シンタックスを利用することで、それが可能になります。 + +プラグインのテストに関しては以上です。 + +その他 +====== + +テストでのレポート出力機能をカスタマイズする +-------------------------------------------- + +テストにおける標準のレポート出力機能は\ **極めて**\ 単純です。見栄えをよくしたり、堅苦しくないようにするため拡張するのは、とても簡単です。 + 唯一の危険は、Cake のコアとなるコード、特に +**/cake/tests/libs/cake\_reporter.php** をいじらなくてはならない点です。 + +テストにおける出力を変更するには、次のメソッドを上書きしてください。 + +paintHeader() + テストが開始される前に出力されます。 +paintPass() + テストケースがパスした時にいつも出力されます。テストに含まれる情報を配列で取得するには、 + $this->getTestList() を使用してください。 + parent::paintPass($message) を呼び出すことを忘れないでください。 +paintFail() + テストケースが失敗した時に出力されます parent::paintFail($message) + を呼び出すことを忘れないでください。 +paintFooter() + テストが終了した時に主強くされます。例えば、テストケースが全て実行された時などです。 + +もし、 paintPass や paintFail で親クラスの出力を隠したい場合は、 HTML +のコメントタグでそれを覆い隠します。 + +:: + + echo "\n\n"; + +テスト結果を表組みで出力するサンプル **cake\_reporter.php** +は、次のようになります。 + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
結果テストケースメッセージ
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tパス: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\t失敗: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +テストをグループ化する +---------------------- + +いくつかのテストを同時に実行したい場合、テストグループを作成します。 +**/app/tests/groups/** +ディレクトリに「\ **your\_test\_group\_name.group.php**\ 」といった名前のファイルを作成してください。このファイルの中で「\ **GroupTest**\ 」を拡張し、テストを読み込んでください。次のようになります。 + +:: + + + +上記のコードでは、 **/app/tests/cases/models/** +フォルダ内で見つけた全てのテストケースをグループ化します。独立したファイルを加えるには +**TestManager::addTestFile**\ ($this, ファイル名) としてください。 + +Running tests in the Command Line +================================= + +If you have simpletest installed you can run your tests from the command +line of your application. + +from **app/** + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +As the help menu suggests, you'll be able to run all, part, or just a +single test case from your app, plugin, or core, right from the command +line. + +If you have a model test of **test/models/my\_model.test.php** you'd run +just that test case by running: + +:: + + cake testsuite app case models/my_model + diff --git a/ja/The-Manual/Core-Behaviors.rst b/ja/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..36be14930fc8979dda7d2ad2290714e66096fb3c --- /dev/null +++ b/ja/The-Manual/Core-Behaviors.rst @@ -0,0 +1,14 @@ +主要なビヘイビア +################ + +ビヘイビアは、付加的な機能をモデルに追加します。 CakePHP には、 Tree や +Containable のような多くの組み込みのビヘイビアがあります。 + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/ja/The-Manual/Core-Behaviors/ACL.rst b/ja/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..fbbf90c60e10ac369671e1f45dac39c61e29b004 --- /dev/null +++ b/ja/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,106 @@ +ACL +### + +ACL +ビヘイビアはモデルとアクセス制御システムを速やかに統合する方法を提供します。 +これを使うことで、 ARO と ACO の両方を透過的に作成することができます。 + +新しいビヘイビアを使おうとする場合には、まずモデルの $actsAs +プロパティにそのビヘイビアを追加します。 $actsAs 配列に ACL +ビヘイビアを追加したら、 ARO と ACO +が関連付いたアクセス制御リストのエントリーを作成することができます。 +デフォルトでは ARO が作成されます。 + +:: + + class User extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +この例では ACL ビヘイビアを ARO モードにしています。 ACO +モードにするには、次のようにします。 + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +次のようにすることで自動的にビヘイビアを付けることもできます。 + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +AclBehavior を使う +================== + +AclBehavior は、 Model の afterSave() で動作させることが多いでしょう。 +しかしこれを使うにあたり、モデル中で parentNode() +メソッドを定義しておく必要があります。 このメソッドは AclBehavior +が親子関係を確定するために使用されます。 parentNode() メソッドは null +または親モデルの情報を返すようにしなければなりません。 + +:: + + function parentNode() { + return null; + } + +ACO または ARO ノードをモデルの親としてセットしたい場合は、 parentNode() +は ACO または ARO の別名を返す必要があります。 + +:: + + function parentNode() { + return 'root_node'; + } + +より複雑な User モデルを例にしてみましょう。 この例では User +モデルがどの Group モデルに対して belongsTo +の関係を持つかをあらわします。 + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['User']['group_id']) { + return null; + } else { + $this->Group->id = $data['User']['group_id']; + $groupNode = $this->Group->node(); + return array('Group' => array('id' => $groupNode[0]['Aro']['foreign_key'])); + } + } + +上記の例では Model::find() の結果に似た体裁の配列を返します。 id +の値をセットすることは重要です。さもないと親ノード(parentNode)との関連付けは失敗します。 +AclBehavior はその木構造において、このデータの構成を使用します。 + +node() +====== + +AclBehavior は、モデルのレコードから ACL +ノードの結びつきを見つけ出すことも行います。 これを行うには、 $model->id +をセットした後に $model->node() を実行します。 + +データの配列を渡すことで、任意の列から ACL +ノードを検索することもできます。 + +:: + + $this->User->id = 1; + $node = $this->User->node(); + + $user = array('User' => array( + 'id' => 1 + )); + $node = $this->User->node($user); + +この例は両方とも同じ ACL ノードの情報を返します。 diff --git a/ja/The-Manual/Core-Behaviors/Containable.rst b/ja/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..462683dc5c24ae7632abaefca2c779b7b8c7d390 --- /dev/null +++ b/ja/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,660 @@ +コンテイナブル +############## + +ContainableBehavior は CakePHP のコアの新機能です。このビヘイビアは find +を実行するときに関連したモデルを選別したり限定したりするために使用します。コンテイナブル(\ *Containable*)は、データベース中の不要なものを削減し、アプリケーションの速度やパフォーマンスを改善します。このクラスを使うと、ユーザに対するデータの検索とフィルタを、美しく一貫した方法で行うこともできます。 + +新しいビヘイビアを使用する場合は、モデルの $actsAs +にビヘイビアの指定を追加します。 + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +動的にビヘイビアを追加することもできます。 + +:: + + $this->Post->Behaviors->attach('Containable'); + +コンテイナブルがどのように動作するか、いくつかの例を見てみましょう。まずは +Post というモデルで find() を実行した場合の例からはじめます。なお、 Post +は Comment と hasMany 、 Tag と hasAndBelongsToMany +のアソシエーションを持つものとします。普通に find() +を実行した場合に取得できるデータ全体は、かなり大きな規模になります。 + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => 最初の記事 + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => ダニエル + [email] => dan@example.com + [website] => http://example.com + [comment] => 最初のコメント + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => サム + [email] => sam@example.net + [website] => http://example.net + [comment] => 二番目のコメント + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => A + ) + [1] => Array + ( + [id] => 2 + [name] => B + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +アプリケーションのインターフェースによっては、この Post +モデルから得られた情報が多すぎるかもしれません。ContainableBehavior +が行うことの一つは、 find() によって返されるデータを削減することです。 + +例えば、 Post に関連した情報だけを取得するには、次のようにします。 + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +find() の呼び出し中にコンテイナブルの機能の起動を含めることも出来ます。 + +:: + + $this->Post->find('all', array('contain' => false)); + +この実行結果として、より簡潔なデータを取得できます。 + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => 最初の記事 + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => 二番目の記事 + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +この類の呼び出しの補助機能は特に目新しいものではありません。実際のところ、これは +ContainableBehavior を使わずとも次のようにすることで行うことができます。 + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +コンテイナブルが真価を発揮するのは、複雑なアソシエーションを持ち、同じレベルに存在する情報を切り詰める場合です。モデルの +$recursive プロパティはある recursive +レベル全体を取得する場合に便利ですが、各レベルで特定のモデルのデータを選び出す時には使えません。contain() +メソッドを使用した場合にどのように動作するのかを見てみましょう。contain() +メソッドの最初の引数には、 find() +を行うにあたりデータを取得するモデルの名前を渡します。複数のモデルを指定する場合は、配列で渡します。全ての +Post とそれに関連する Tag だけを取得し、 Comment +の情報は取得しない場合、次のように行います。 + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +find() の呼び出しの中に contain キーを含める場合の記述を見てみましょう。 + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +コンテイナブルを使わないならモデルの unbindModel() +を使用することになります。複数のモデルを切り離すなら、 unbindModel() +を何度も実行しなければなりません。コンテイナブルによって同じことをより簡潔に行えます。 + +さらに進んだ使い方があります。コンテイナブルには、\ *アソシエーション*\ で関連付いたモデルのデータをフィルタリングするというさらに進んだ使い方もあります。最初の例で +find() を呼び出した結果のうち、 Comment モデルの author +フィールドに注目してください。投稿(\ *post*)のうち、コメントをした人の名前(\ *author*)を取得し、他は取得したくない場合、次のようにします。 + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +ここまでで、コンテイナブルで投稿(post)の情報を取得し、アソシエーションで関連付いた +Comment モデルのうち author +フィールドだけを取得する方法を説明しました。find() +による出力は、次のようになるでしょう。 + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => 最初の記事 + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => ダニエル + [post_id] => 1 + ) + [1] => Array + ( + [author] => サム + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +Comment 配列に author +フィールドだけが含まれていることが確認できると思います。ただし、 CakePHP +が結果をマップするために必要な post\_id は含まれます。 + +条件(\ *condition*)を定義して、アソシエーションで関連付いた Comment +のデータにフィルタをかけることもできます。 + +:: + + $this->Post->contain('Comment.author = "ダニエル"'); + $this->Post->find('all'); + + // または + + $this->Post->find('all', array('contain' => 'Comment.author = "ダニエル"')); + +これにより、投稿(\ *post*)とダニエルによるコメントを取得できます。 + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => 最初の記事 + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => ダニエル + [email] => dan@example.com + [website] => http://example.com + [comment] => 最初のコメント + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +フィルタを追加するには、標準的な ``Model->find()`` +のオプションを与えます。 + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "ダニエル"), + 'order' => 'Comment.created DESC' + ) + ))); + +より深い recursive +と複雑なモデルの関連の時にコンテイナブルビヘイビアを使う方法の例は次のようになります。 + +モデル間のアソシエーションは次のようになっているとします。 + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +上述のアソシエーションにおいてコンテイナブルを使った検索は次のように行います。 + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +メインのモデルで「contain」キーは一度しか使わないことに留意してください。関連したモデルにでもう一度「contain」は使いません。 + +'fields' と 'contain' +オプションを使う場合は、クエリが直接的あるいは間接的に使う外部キーを含めるよう注意してください。また、Containable +ビヘイビアは、この機能を使って出力を抑制するモデルの全てに付与しなければなりません。そのため、AppModel +で Containable +ビヘイビアを付与することを検討すべきかもしれないということも留意してください。 + +次のものは、ページ付けを行う際のアソシエーションの抑制を行うという例です。 + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +Using Containable +================= + +To see how Containable works, let's look at a few examples. First, we'll +start off with a find() call on a model named Post. Let's say that Post +hasMany Comment, and Post hasAndBelongsToMany Tag. The amount of data +fetched in a normal find() call is rather extensive: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Second comment + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => Awesome + ) + [1] => Array + ( + [id] => 2 + [name] => Baking + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +For some interfaces in your application, you may not need that much +information from the Post model. One thing the ``ContainableBehavior`` +does is help you cut down on what find() returns. + +For example, to get only the post-related information, you can do the +following: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +You can also invoke Containable's magic from inside the find() call: + +:: + + $this->Post->find('all', array('contain' => false)); + +Having done that, you end up with something a lot more concise: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Second article + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +This sort of help isn't new: in fact, you can do that without the +``ContainableBehavior`` doing something like this: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +Containable really shines when you have complex associations, and you +want to pare down things that sit at the same level. The model's +``$recursive`` property is helpful if you want to hack off an entire +level of recursion, but not when you want to pick and choose what to +keep at each level. Let's see how it works by using the ``contain()`` +method. + +The contain method's first argument accepts the name, or an array of +names, of the models to keep in the find operation. If we wanted to +fetch all posts and their related tags (without any comment +information), we'd try something like this: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Again, we can use the contain key inside a find() call: + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Without Containable, you'd end up needing to use the ``unbindModel()`` +method of the model, multiple times if you're paring off multiple +models. Containable creates a cleaner way to accomplish this same task. + +Containing deeper associations +============================== + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors — and +nothing else — you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the ``ContainableBehavior`` when you've got +deep and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that ``contain`` key is only used once in the main model, +you don't need to use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must be attached to all models used +in containment, you may consider attaching it to your AppModel. + +Using Containable with pagination +================================= + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +By including the 'contain' parameter in the ``$paginate`` property it +will apply to both the find('count') and the find('all') done on the +model + +ContainableBehavior options +=========================== + +The ``ContainableBehavior`` has a number of options that can be set when +the Behavior is attached to a model. The settings allow you to fine tune +the behavior of Containable and work with other behaviors more easily. + +- **recursive** (boolean, optional) set to true to allow containable to + automatically determine the recursiveness level needed to fetch + specified models, and set the model recursiveness to this level. + setting it to false disables this feature. The default value is + ``true``. +- **notices** (boolean, optional) issues E\_NOTICES for bindings + referenced in a containable call that are not valid. The default + value is ``true``. +- **autoFields**: (boolean, optional) auto-add needed fields to fetch + requested bindings. The default value is ``true``. + +You can change ContainableBehavior settings at run time by reattaching +the behavior as seen in `Using behaviors `_ + +ContainableBehavior can sometimes cause issues with other behaviors or +queries that use aggregate functions and/or GROUP BY statements. If you +get invalid SQL errors due to mixing of aggregate and non-aggregate +fields, try disabling the ``autoFields`` setting. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/ja/The-Manual/Core-Behaviors/Translate.rst b/ja/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..fae121c6a9e6cfcd53a105d61e551bc1cb3c481c --- /dev/null +++ b/ja/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,341 @@ +翻訳 +#### + +翻訳ビヘイビアのセットアップは本当に簡単で、ちょっと設定するだけですぐに動作するようになります。この章では、モデルにこのビヘイビアを追加し、セットアップする方法を説明します。 + +翻訳ビヘイビアをコンテイナブルと併用するならば、クエリに'fields'キーがセットされているか確かめてください。さもないと、無効なSQLが生成されてしまうことになるでしょう。 + +i18n データベーステーブルを初期化する +===================================== + +i18n データベーステーブルは、CakePHP +コンソールを使っても生成できますし、手動でもできます。ただし、CakePHP +の将来のバージョンでレイアウトに変更があるかもしれませんので、コンソールを使うことをお勧めします。コンソールをずっと使い続けていれば、レイアウトに変更が入っても問題はおきません。 + +:: + + ./cake i18n + +i18n データベースを初期化するには、 ``[I]`` +を選択してください。既存のテーブルをドロップし、新しく作成するかどうかを聞かれます。i18n +テーブルをまだ作成していないなら yes +と答え、テーブルを作成するにはもう一度 yes と答えます。 + +モデルに翻訳ビヘイビアを追加する +================================ + +次の例のように、 ``$actAs`` +プロパティを使って、モデルに翻訳ビヘイビアを追加します。 + +:: + + + +翻訳ビヘイビアは、動作する前に一組のオプションがあることを期待するので、これだけではまだ何も起きません。まず、最初のステップで作成した翻訳テーブルで、現在のモデルのどのフィールドを辿るのかを定義する必要があります。 + +フィールドを定義する +==================== + +フィールドをセットするには、配列を使って、単純に ``'Translate'`` +の値を拡張するだけです。次のようになります。 + +:: + + array( + 'fieldOne', 'fieldTwo', 'and_so_on' + ) + ); + } + ?> + +このようにすれば、基本的なセットアップは完了です。説明で使用する例では、対象とするフィールドを「name」とします。さて、この例にならうなら、モデルの例は次のようになるはずです。 + +:: + + array( + 'name' + ) + ); + } + ?> + +翻訳ビヘイビアのために翻訳するフィールドを定義するとき、翻訳されるモデルのスキーマからこれらのフィールドを必ず省略してください。もしフィールドを残しておくと、代替ロケールでデータを検索するとき、問題が起こりうることになるでしょう。 + +最後に +====== + +これで、各レコードが更新・新規作成されたら、翻訳ビヘイビアは現在のロケールに従い、「name」の値を翻訳用のテーブル(デフォルトでは「i18n」)にコピーします。ロケールとは、いわば言語の識別子です。 + +*現在のロケール* は、現在の ``Configure::read('Config.language')`` +の値です。\ *Config.language* +の値は、まだ何も割り当てられていなければ、L10n +クラスに割り当てられます。しかしながら、翻訳ビヘイビアはロケールを動的に上書きすることもできます。これにより、ページのユーザが初期設定を変更せず、複数のバージョンを作成することができます。より詳しい情報は、以降の節を参照してください。 + +フィールドに対するすべての翻訳レコードを取得する +================================================ + +現在のモデルに対する全ての翻訳レコードを取得するには、ただ単純に、次に示す通りビヘイビアのセットアップにおける\ *フィールドの配列*\ を拡張します。名前は好きにつけてください。 + +:: + + array( + 'name' => 'nameTranslation' + ) + ); + } + ?> + +このようにセットアップすると、find() の結果は次のようになります。 + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +**注意**: +モデルのレコードは「locale」という\ *仮想的な*\ フィールドを含みます。このフィールドは、結果セットのロケールが何であるかを示します。 + +Using the bindTranslation method +-------------------------------- + +You can also retrieve all translations, only when you need them, using +the bindTranslation method + +``bindTranslation($fields, $reset)`` + +``$fields`` is a named-key array of field and association name, where +the key is the translatable field and the value is the fake association +name. + +:: + + $this->Post->bindTranslation(array ('name' => 'nameTranslation')); + $this->Post->find('all', array ('recursive'=>1)); // need at least recursive 1 for this to work. + +With this setup the result of your find() should look something like +this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +別の言語で保存する +================== + +翻訳ビヘイビアを使ったモデルが何かを保存する時に、検出したもの以外の言語で強制的に保存を行うことができます。 + +コンテンツにどの言語を使うかをモデルに伝えるには、保存前に、モデルの +``$locale`` +プロパティ値を変更するだけです。コントローラ中で定義することもできますし、モデルに直接定義することもできます。 + +**例 A:** コントローラ中での定義 + +:: + + data) { + $this->Post->locale = 'de_de'; // ドイツ語版を保存する + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**例 B:** モデル中での定義 + +:: + + array( + 'name' + ) + ); + + // オプション 1) 直接プロパティを定義する + var $locale = 'en_us'; + + // オプション 2) 簡単なメソッドを作成する + function setLanguage($locale) { + $this->locale = $locale; + } + } + ?> + +複数の翻訳テーブル +================== + +たくさんのエントリーがあることを予測しているなら、急速に成長するデータベースをどのように扱うべきかが気がかりになるかもしれません。翻訳ビヘイビアには、どのモデルを翻訳を格納するために用いるかを定義するためのプロパティが、2つあります。 + +これらのプロパティは、\ **$translateModel** と **$translateTable** +になります。 + +全ての posts +の翻訳を保存するテーブルとして、「i18n」の代わりに「post\_i18ns」を使用するとしましょう。これには、モデルを次のようにセットアップします。 + +:: + + array( + 'name' + ) + ); + + // 別のモデル(あるいはテーブル)を使用する。 + var $translateModel = 'PostI18n'; + } + ?> + +テーブル名は複数形にすることを\ **忘れないでください**\ 。これで通常のモデルとして扱え、規約にも従います。テーブルのスキーマは、CakePHP +のコンソールスクリプトが生成するものと同じある必要があります。これを間違いなく行うには、コンソールで空の +i18n テーブルを初期化し、それをリネームすると良いでしょう。 + +TranslateModel を作成する +------------------------- + +TranslateModel +を動作させるには、モデルのフォルダに実際にモデルを作成する必要があります。なぜなら、このビヘイビアを使うモデルの中で、 +displayField +ディレクトリをセットするプロパティがまだ存在しないからです。 + +``$displayField`` を ``'field'`` に変更することを忘れないでください。 + +:: + + + +これで完了です。$useTable +といったような、モデルの他の要素も追加することができます。しかし一貫性を保つために、これは実際に翻訳を行うモデルで実施するようにしましょう。この点が、\ ``$translateTable`` +の効果が発揮されるところです。 + +テーブルを変更する +------------------ + +テーブルの名前を変更したい場合は、次に示すように、ただ単純にモデル中の +$translateTable を定義します。 + +:: + + array( + 'name' + ) + ); + + // 別のモデルを使う + var $translateModel = 'PostI18n'; + + // translateModel で別のテーブルを使う + var $translateTable = 'post_translations'; + } + ?> + +**$translateTable +は単独で使用できない**\ 、ということに注意してください。独自の +``$translateModel`` +を使わない場合、このプロパティはいじらないでください。セットアップが壊れ、実行中に生成されるデフォルトの +l18n モデルで「Missing Table」メッセージが表示されてしまいます。 diff --git a/ja/The-Manual/Core-Behaviors/Tree.rst b/ja/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..d84df0a24e20e1f216ca7cdfd9dd44a3670d3a4b --- /dev/null +++ b/ja/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,588 @@ +ツリー +###### + +データベーステーブルに階層構造のデータを格納したいケースはごく一般的に存在します。例えば数に上限が無いサブカテゴリを持つカテゴリのデータ、複数のレベルを持つメニューシステムのデータ、 +ACL +のロジックのアクセスコントロールオブジェクトを保存するために使われる文字通り階層構造のデータなどです。 + +小さいツリーのデータや、少ない階層の深さを持つデータの場合、 parent\_id +フィールドをデータベーステーブルに追加したり、アイテムの親が何であるかを追跡することは簡単です。しかしながら +CakePHP にバンドルされているビヘイビアの機能は非常にパワフルです。 +`MPTT(Modified Preorder Tree Traversal) +ロジック `_\ を扱うには複雑なテクニックを駆使する必要がありますが、このビヘイビアを使用すると、それにわずらわされることなく +MPTT ロジックの恩恵を受けることができます。 + +必要なもの +========== + +ツリービヘイビアを使用するには、テーブルが次に挙げる3つのフィールドを持っている必要があります。フィールドは全て整数型です。 + +- 親 - デフォルトのフィールド名は「parent\_id」です。親オブジェクトの + id を格納するためのものです。 +- 左端 - + デフォルトのフィールド名は「lft」です。現在のオブジェクトの左端の座標を入力します。 +- 右端 - + デフォルトのフィールド名は「rght」です。現在のオブジェクトの右端の座標を入力します。 + +もし MPTT +ロジックをよく知っているなら、なぜ親フィールドが存在するのか疑問に思うでしょう。これは、親への直接的なリンクがデータベースに存在すると、いくつかのタスクがとても簡単になるためです(例えば、ある要素の直接の子を見つける時など)。 + +基本的な使い方 +============== + +ツリービヘイビアで出来ることはたくさんあります。しかし、簡単な例からはじめてみましょう。次のデータベーステーブルを作成し、データを投入してください。 + +:: + + CREATE TABLE categories ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + name VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(1, 'カテゴリ', NULL, 1, 30); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(2, '楽しみ', 1, 2, 15); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(3, 'スポーツ', 2, 3, 8); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(4, 'サーフィン', 3, 4, 5); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(5, 'エクストリーム編み物', 3, 6, 7); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(6, '友達', 2, 9, 14); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(7, 'ジェラルド', 6, 10, 11); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(8, 'グウェンドリン', 6, 12, 13); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(9, '仕事', 1, 16, 29); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(10, '報告', 9, 17, 22); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(11, '通年', 10, 18, 19); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(12, '状態', 10, 20, 21); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(13, '出張', 9, 23, 28); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(14, '国内', 13, 24, 25); + INSERT INTO `categories` (`id`, `name`, `parent_id`, `lft`, `rght`) VALUES(15, '国外', 13, 26, 27); + +正しくセットアップされたかをチェックするには、テスト用のメソッドを作成して、カテゴリツリーのコンテンツの出力がどのようになるかを確認します。簡単なコントローラを使います。 + +:: + + data = $this->Category->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +シンプルなモデルの定義例です。 + +:: + + + +これで /categories +にアクセスすると、カテゴリのツリーのデータがどのように見えるかチェックできます。 +次のようになるはずです。 + +- My カテゴリ + + - 楽しみ + + - スポーツ + + - サーフィン + - エクストリーム編み物 + + - 友人 + + - ジェラルド + - グウェンドリン + + - 仕事 + + - 報告 + + - 年次 + - 状態 + + - 出張 + + - 国内 + - 国外 + +データを追加する +---------------- + +前の章では、既存のデータを用い ``generatetreelist`` +メソッドを使うことで階層構造がどのように見えるかを確認しました。しかしながらたとえ階層構造を持ったデータであっても、通常は他のモデルとまったく同じ方法でデータを追加します。例は次の通りです。 + +:: + + // コントローラのコードの一部 + $data['Category']['parent_id'] = 3; + $data['Category']['name'] = 'スケート'; + $this->Category->save($data); + +ツリービヘイビアを用いる場合、親 ID (*parent\_id*) +をセットする以外のことは何も行う必要がありません。残りのことはツリービヘイビアが注意深く行ってくれます。もし +parent\_id +をセットしない場合は、ツリービヘイビアはツリーに、新たなトップレベルのエントリーを作成します。 + +:: + + // コントローラのコードの一部 + $data = array(); + $data['Category']['name'] = '別の人のカテゴリ'; + $this->Category->save($data); + +上述した短いコードを実行すると、ツリーは次のように変化します。 + +- カテゴリ + + - 楽しみ + + - スポーツ + + - サーフィン + - エクストリーム編み物 + - スケート **New** + + - 友人 + + - ジェラルド + - グウェンドリン + + - 仕事 + + - 報告書 + + - 年次 + - 状態 + + - 出張 + + - 国内 + - 国外 + +- 別の人のカテゴリ **New** + +データを変更する +---------------- + +データを変更することは、新しいデータを追加することと同じぐらい透過的です。何かデータを変更したいが、 +parent\_id +は変更しない場合、階層構造にかかわる箇所は何も変更されません。例は次の通りです。 + +:: + + // コントローラのコードの一部 + $this->Category->id = 5; // 「エクストリーム編み物」の ID + $this->Category->save(array('name' =>'エクストリームフィッシング')); + +上述のコードは parent\_id +フィールドに何も影響をあたえません。もし渡されたデータの中に parent\_id +が入っていても、値に変更がなければ保存されませんし、階層構造も更新されません。この結果、ツリーのデータは次ようになります。 + +- カテゴリ + + - 楽しみ + + - スポーツ + + - サーフィン + - エクストリームフィッシング **Updated** + - スケート + + - 友人 + + - ジェラルド + - グウェンドリン + + - 仕事 + + - 報告書 + + - 年次 + - 状態 + + - 出張 + + - 国内 + - 国外 + +- 別の人のカテゴリ + +ツリーの中でデータを移動することも、簡潔に行えます。エクストリームフィッシングはスポーツではないが、別の人のカテゴリに属するとする場合、次のようにします。 + +:: + + // コントローラのコードの一部 + $this->Category->id = 5; // 「エクストリームフィッシング」の ID + $newParentId = $this->Category->field('id', array('name' => '別の人のカテゴリ')); + $this->Category->save(array('parent_id' => $newParentId)); + +次のような構造に変更されることが正しい動作です。 + +- カテゴリ + + - 楽しみ + + - スポーツ + + - サーフィン + - スケート + + - 友人 + + - ジェラルド + - グウェンドリン + + - 仕事 + + - 報告書 + + - 年次 + - 状態 + + - 出張 + + - 国内 + - 国外 + +- 別の人のカテゴリ + + - エクストリームフィッシング **Moved** + +データの削除 +------------ + +ツリービヘイビアは、データの削除を管理するいくつかの方法を提供します。もっともシンプルな例からはじめてみましょう。「報告書」カテゴリが不要であるとしましょう。このカテゴリと\ *それの子要素も全て*\ 削除する場合、どのモデルであってもただ +delete() をコールします。例は次の通りです。 + +:: + + // コントローラのコードの一部 + $this->Category->id = 10; + $this->Category->delete(); + +カテゴリのツリーは次のように変更されます。 + +- カテゴリ + + - 楽しみ + + - スポーツ + + - サーフィン + - スケート + + - 友人 + + - ジェラルド + - グウェンドリン + + - 仕事 + + - 出張 + + - 国内 + - 国外 + +- 別の人のカテゴリ + + - エクストリームフィッシング + +データの問合せと利用 +-------------------- + +階層構造になったデータを取り扱い操作するのは、ややこしい作業になりがちです。コアの +find +メソッドに加え、ツリービヘイビアによって自由に使えるツリー構造の順序変更をいくつか行えます。 + +ツリービヘイビアのメソッドのほとんどは、 ``lft`` +に依存してデータを並び替え、それを返します。もし ``find()`` +メソッドをコールするときに ``lft`` +で並び替えなかったり、ツリービヘイビアのメソッドに並び替えのための値を渡すと、望ましくない結果が返ってくるでしょう。 + +Children +~~~~~~~~ + +``children`` +メソッドは列の主キー(id)の値を用いて、そのアイテムの子を返します。デフォルトの順番はツリーに出現した順です。第二引数はオプションのパラメータで、直接の子のみを返すか否かを定義します。前の章のデータ使った例を見てみましょう。 + +:: + + $allChildren = $this->Category->children(1); // 11アイテムをフラットな配列で返す + // -- または -- + $this->Category->id = 1; + $allChildren = $this->Category->children(); // 11アイテムをフラットな配列で返す + + // 直接の子のみ返す + $directChildren = $this->Category->children(1, true); // 2アイテムをフラットな配列で返す + +再帰的な配列で取得したい場合は、 ``find('threaded')`` +というようにしてください。 + +子の数を数える +~~~~~~~~~~~~~~ + +``children`` メソッドと同様に、 ``childCount`` +には列の主キー(id)の値を渡します。これにより主キーが指定されたノードの子の数が返されます。オプションの第二引数では、直接の子のみの数を返すか否かを定義できます。前の章のデータを使った例を見てみましょう。 + +:: + + $totalChildren = $this->Category->childCount(1); // 11 を出力する + // -- または -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // 11 を出力する + + // このカテゴリの直接の子のみ数える + $numChildren = $this->Category->childCount(1, true); // 2 を出力する + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist (&$model, $conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)`` + +このメソッドは、プレフィックスでインデントを付け構造が分かるようにした、 +find('list') +に似たデータを返します。次のものは、このメソッドが返すべきデータです。その他の +find に似たパラメータについては、 API リファレンスを参照してください。 + +:: + + array( + [1] => "カテゴリ", + [2] => "_楽しみ", + [3] => "__スポーツ", + [4] => "___サーフィン", + [16] => "___スケート", + [6] => "__友人", + [7] => "___ジェラルド", + [8] => "___グウェンドリン", + [9] => "_仕事", + [13] => "__出張", + [14] => "___国内", + [15] => "___国外", + [17] => "別の人のカテゴリ", + [5] => "_エクストリームフィッシング" + ) + +getparentnode +~~~~~~~~~~~~~ + +この便利な関数は、その名前が意味する通り、あるノードの親ノードを返します。ただし、指定したノードに親がない(つまり根ノードである)場合は、 +*false* を返します。例は次の通りです。 + +:: + + $parent = $this->Category->getparentnode(2); //<- 「楽しみ」の ID + // $parent にはカテゴリについての情報が入っている + +getpath +~~~~~~~ + +最も上の根ノードから最も下の葉ノードまでのパス(\ *path*)を返します。例においてカテゴリの「国外」までのパスは次のようになります。 + +- カテゴリ + + - ... + - 仕事 + + - 出張 + + - ... + - 国外 + +「国外」の ID を使って getpath +を実行すると、最も上(根ノード)からはじめて、各親を順々に返します。 + +:: + + $parents = $this->Category->getpath(15); + +:: + + // $parents の内容は次の通り + array( + [0] => array('Category' => array('id' => 1, 'name' => 'カテゴリ', ..)), + [1] => array('Category' => array('id' => 9, 'name' => '仕事', ..)), + [2] => array('Category' => array('id' => 13, 'name' => '出張', ..)), + [3] => array('Category' => array('id' => 15, 'name' => '国外', ..)), + ) + +進んだ使い方 +============ + +ツリービヘイビアはバックグラウンドだけで働くわけではありません。ビヘイビアには、階層化されたデータが必要とする処理を全て行い、このプロセス中に望まない動作が発生しないようにするための、特別なメソッドがいくつか定義されています。 + +moveDown +-------- + +ツリーの中で一つのノードを位置を下げる(葉ノードに近づける)ために使用します。移動する要素の +ID +と、そのノードを下げる階層の数を正の整数で与えてください。指定したノードの子ノードも、全て移動されます。 + +次のものは、特定のノードの位置を下げる「Categories」という名のコントローラアクションの例です。 + +:: + + function movedown($name = null, $delta = null) { + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash($name . 'という名のカテゴリが存在しません。'); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('フィールドの位置を下げる数を入力してください。'); + } + + $this->redirect(array('action' => 'index'), null, true); + } + +例えば「Sport」というカテゴリを一段下げたい場合は、「/categories/movedown/Sport/1」というリクエストを行ってください。 + +moveUp +------ + +ツリーの中で一つのノードを位置を上げる(根ノードに近づける)ために使用します。移動する要素の +ID +と、そのノードを上げる階層の数を正の整数で与えてください。全ての子ノードも、全て移動されます。 + +次のものは、ノードの位置を上げる「Categories」という名のコントローラアクションの例です。 + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash($name . 'という名のカテゴリが存在しません。'); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('カテゴリの位置を上げる数を入力してください。'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +例えば「Gwendolyn」というカテゴリを一段上げたい場合は、「/categories/moveup/Gwendolyn/1」というリクエストを行ってください。これで、友人の並び順は +Gwendolyn, Gerald となりました。 + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Using this method wil either delete or move a node but retain its +sub-tree, which will be reparented one level higher. It offers more +control than ```delete()`` `_, which for a model +using the tree behavior will remove the specified node and all of its +children. + +Taking the following tree as a starting point: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating + +Running the following code with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id); + +The Sport node will be become a top level node: + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +- Sport **Moved** + +This demonstrates the default behavior of ``removeFromTree`` of moving +the node to have no parent, and re-parenting all children. + +If however the following code snippet was used with the id for 'Sport' + +:: + + $this->Node->removeFromTree($id,true); + +The tree would become + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +This demonstrates the alternate use for ``removeFromTree``, the children +have been reparented and 'Sport' has been deleted. + +reorder +------- + +このメソッドは階層化されたデータを並び替えるために使用します。 + +データの整合性をとる +==================== + +木構造やリンクされたリストのように、自分自身を参照する複雑なデータ構造は、その性質上、まれに不用意なコールによって壊れてしまいます。気落ちしないでください。全てが失われたわけではありません!これまでの文書中には登場していませんが、Tree +Behavior はこういった状況に対処するための関数をいくつか持っています。 + +データの整合性を復旧できる可能性がある関数は、次のものになります。: + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +mode +パラメータは、有効な、あるいは正しい元情報のソースを定義するために使用します。逆側のデータソースは、先に定義した情報のソースに基づいて投入されます。例えば、$mode +が 'parent' である MPTT +のフィールドが衝突している、あるいは空である場合、 parent\_id +フィールドの値が左座標と右座標を投入するために使用されます。missingParentAction +パラメータは、"parent" モードの時にのみ使用し、parent +フィールドに有効でない ID が含まれる場合に何をすべきかを決定します。 + +reorder(&$model, $options = array()) + +木構造のデータ中のノード(と子ノード)を、パラメータで定義されたフィールドと指示によって、もう一度並び替えます。 +このメソッドは、全てのノードの親を変更しません。 + +この options 配列は、デフォルトで 'id' => null 、 'field' => +$model->displayField 、 'order' => 'ASC' 、そして 'verify' => true +という値を含みます。 + +verify(&$model) + +木構造の整合性がとれたら true を返し、そうでない場合は (type, incorrect +left/right index, message) という形式の配列を返します。 diff --git a/ja/The-Manual/Core-Components.rst b/ja/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..ef76b2dabd2068f5093ba5b2f7099ac0f4fb02a9 --- /dev/null +++ b/ja/The-Manual/Core-Components.rst @@ -0,0 +1,54 @@ +主要なコンポーネント +#################### + +CakePHP +には組み込みのコンポーネントがいくつかあります。これらにより、よく使われるタスクをすぐに扱うことができます。 + +Acl + +Acl コンポーネントはデータベースあるいは ini +ベースのアクセス制御リストを簡単に利用することを提供します。 + +Auth + +認証(\ *auth*)コンポーネントはコントローラコールバックや ACL +、オブジェクトコールバックといった様々な認証プロセスを使用する認証システムを、簡単に扱うことを提供します。 + +Session + +セッションコンポーネントはストレージから独立した PHP +のセッションのラッパーを提供します。 + +RequestHandler + +リクエストハンドラを使うと、訪問者のリクエストをより詳しく見て、アプリケーションにコンテンツタイプやリクエストの情報を知らせることができます。 + +Security + +セキュリティコンポーネントを使うと、より厳格なセキュリティのセットや +HTTP 認証の利用および管理を行うことができます。 + +Email + +PHP の mail() や smtp +を含む、ひとつあるいは複数のメール転送エージェントを利用した電子メールの送信を行うことができます。 + +Cookie + +クッキーコンポーネントは、PHP +ネイティブのクッキーサポートのラッパーを提供するという点において、 +SessionComponent と似た振る舞いをします。 + +各コンポーネントについて詳しく知りたい場合はメニュー、あるいは\ `独自のコンポーネントを作成する方法 `_\ を参照してください。 + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/ja/The-Manual/Core-Components/Access-Control-Lists.rst b/ja/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..a586f8bfe665559c7587728b9d646677716e0dab --- /dev/null +++ b/ja/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,848 @@ +アクセス制御リスト +################## + +CakePHP のアクセス制御リスト(\ *Access Control List*, +*ACL*)は、頻繁に話題になる機能のひとつです。これは、非常に多く使われる機能でありながら、とても混乱しやすいものであることが原因です。ACL +の概要を理解する近道を探しているならば、この章を読むことからはじめてください。 + +難しい所が出てきても、勇気を出して粘り強く取り組んでください。いったんコツがつかめたら、この機能はアプリケーションの開発において極めて強力なツールになります。 + +ACL の動作を理解する +==================== + +強力なアプリケーションにはアクセス制御機能が必要です。 +アクセス制御リストは、優れた保守性と管理性を保ちつつ、アプリケーションのパーミッションをきめ細かく設定する機能を提供します。 + +アクセス制御リスト(\ *Access Control List* または +*ACL*)は、主に二つの物を扱います。 +ひとつめは、何かを取り扱う「主体」、そしてもうひとつは、その主体に取り扱われる「物」です。 +ACL の専門用語では、物を扱う「主体」(たとえばユーザ)は、 +「アクセスリクエストオブジェクト(ARO)」と呼び、 +その主体(ARO)が扱う「物」(たとえばデータ)は、 +「アクセスコントロールオブジェクト(ACO)」と呼びます。 +これらが「オブジェクト」と呼ばれているのは、ARO +が人であるとは限らないからです。 +例えば、アプリケーションの別の部分でロジックを開始するために、ある特定の +Cake +のコントローラがアクセスを持っているとします。これを制限したい時があるかもしれません。 +ACO には、コントローラアクション、WEB +サービス、おばあちゃんのオンライン日記への回線など、アクセスをコントロールできる物なら何でもあてはまります。 + +復習してみましょう。 + +- ACO - アクセスコントロールオブジェクト(\ *Access Control Object*) - + ある主体が取り扱う「物」 +- ARO - アクセスリクエストオブジェクト(\ *Access Request Object*) - + ある物を取り扱う「主体」 + +つまり ACL とは、 ARO がいつ ACO +にアクセスできるかを決定するために用いるものなのです。 + +分かりやすくするために、少し具体的な例を使ってみましょう。 +ファンタジー冒険小説「指輪物語」に登場するようなグループを使うコンピュータのシステムを想像してください。 +グループのリーダーであるガンダルフは、各メンバーのプライバシーとセキュリティを十分に管理したいと考えています。 +ガンダルフがまずすべきは、次のものが含まれた ARO +のリストを作成することです。 + +- ガンダルフ +- アラゴルン +- ビルボ +- フロド +- ゴクリ +- レゴラス +- ギムリ +- ピピン +- メリー + +ACL は、ユーザを認証するシステムでは\ *ない*\ ことに注意してください。 +ACL はユーザが認証された\ *後*\ に使うものです。 +「それが誰なのか」を判断するのが「認証」で、「その人は何ができるのか」を判断するのが「ACL」です。この二つは協調して使われますが、異なる仕組みであることは意識しておきましょう。 + +ガンダルフが次に必要とするものは、システムが扱う物である ACO +の初期リストです。このリストは次のようになります。 + +- 武器 +- 一つの指輪 +- 塩漬け豚 +- 外交術 +- ビール + +伝統的な手法だと、これらはユーザとパーミッションをオブジェクトに関連付ける基本的なセットをマトリクス状に並べたもので管理されます。 +表組みであらわすと、次のようになります。 + +武器 + +一つの指輪 + +塩漬け豚 + +外交術 + +ビール + +ガンダルフ + +許可 + +許可 + +許可 + +アラゴルン + +許可 + +許可 + +許可 + +許可 + +ビルボ + +許可 + +フロド + +許可 + +許可 + +ゴクリ + +許可 + +レゴラス + +許可 + +許可 + +許可 + +許可 + +ギムリ + +許可 + +許可 + +ピピン + +許可 + +許可 + +メリー + +許可 + +一見したところ、こういう形のシステムでうまくいくように思えます。 +フロドだけが一つの指輪にアクセスできるよう、セキュリティの保護がなされています。 +また、ホビットたちは塩漬け豚や武器に触れられず、間違いが起こらないようにもなっています。 +これで十分に整理されていて、読みやすいように思えますね。しかし本当にこれで十分なのでしょうか? + +このような小さいシステムであれば、マトリクス状の設定でうまくいくでしょう。 +しかし、システムが大きくなってきたり、リソース(ACO)やユーザ(ARO)がかなりの数になる場合、 +テーブルは素早く扱えなくなり、取り回しも悪くなります。 +指輪戦争における数百という陣営のアクセスを管理する場合、そしてそれをさらに部隊ごとに管理しようとする場合を想像してください。 +マトリクスのもう一つの欠点は、ユーザのかたまりを論理的にグループ分けしたり、論理的なグループ分けに基づいてあるユーザグループのパーミッションをまとめて変更したりできないことです。 +例えば、戦闘終了後は自動的に、ホビットたちがビールや塩漬け豚へのアクセスを許可されるようにできるとよいかもしれません。 +これを個別のユーザごとに行うのは、退屈でエラーの起こりやすい作業になります。 +しかし、すべての「ホビット」に許可をまとめて与える、というのは簡単です。 + +ACL は通常、木構造で実装されます。 たいていの場合、 ARO のツリーと ACO +のツリーが存在します。 +各オブジェクトを木構造で整理すると、きめ細かいパーミッションの設定を行いつつ、全体像もしっかりと把握しながらメンテナンスできます。 +賢明なリーダーであるガンダルフは新システムで ACL を採用し、 +それぞれのオブジェクトを次のように整理し始めました + +- 旅の仲間 + + - 戦士 + + - アラゴルン + - レゴラス + - ギムリ + + - 魔法使い + + - ガンダルフ + + - ホビット + + - フロド + - ビルボ + - メリー + - ピピン + + - 来訪者 + + - ゴクリ + +ARO +に木構造を用いると、ガンダルフはグループの全てのユーザに対し、一度にパーミッションを設定することができます。 +ここでガンダルフは、ARO +ツリーを使ってグループに対するパーミッションをいくつか付け加えました。 + +- 旅の仲間 + (**Deny**: all) + + - 戦士 + (**Allow**: 武器, ビール, エルフの食べ物, 塩漬け豚) + + - アラゴルン + - レゴラス + - ギムリ + + - 魔法使い + (**Allow**: 塩漬け豚, 外交術, ビール) + + - ガンダルフ + + - ホビット + (**Allow**: ビール) + + - フロド + - ビルボ + - メリー + - ピピン + + - 来訪者 + (**Allow**: 塩漬け豚) + + - ゴクリ + +ACL +を使ってピピンがビールにアクセスできるかをどうかを確認には、まずツリーの中のどこにいるかを探します。この場合、「旅の仲間->ホビット->ピピン」というパスになります。 +次に、各ポイントのパーミッションを確認し、ピピンとビールを関連づけている一番近いパーミッションを使用します。 + ++--------------+----------------------+-------------------------------+ +| ARO ノード | パーミッション情報 | 結果 | ++==============+======================+===============================+ +| 旅の仲間 | Deny all | ビールへアクセスできない。 | ++--------------+----------------------+-------------------------------+ +| ホビット | Allow 'ビール' | ビールへアクセスできる! | ++--------------+----------------------+-------------------------------+ +| ピピン | -- | まだビールへアクセスできる! | ++--------------+----------------------+-------------------------------+ + +ACL +ツリーの「ピピン」ノードで、ACO「ビール」へのアクセスが明確に拒否されない限り、 +最終的にはその ACO に対するアクセスは許可されます。 + +木構造による ACL では ARO +グループ全体に対する設定を保ったまま、さらに細かくコントロールの調節を行うことが出来ます。 + +- 旅の仲間 + (**Deny**: all) + + - 戦士 + (**Allow**: 武器, ビール, エルフの食べ物, 塩漬け豚) + + - アラゴルン + (Allow: 外交術) + - レゴラス + - ギムリ + + - 魔法使い + (**Allow**: 塩漬け豚, 外交術, ビール) + + - ガンダルフ + + - ホビット + (**Allow**: ビール) + + - フロド + (Allow: 一つの指輪) + - ビルボ + - メリー + (Deny: ビール) + - ピピン + (Allow: 外交術) + + - 来訪者 + (**Allow**: 塩漬け豚) + + - ゴクリ + +この方法では、パーミッションの一括変更と細かな調節を両立できます。 +上述の例では、メリーを除いた全てのホビットがビールにアクセスできるようになっています。 +メリーがビールにアクセスで出来るかどうか確認するには、ツリーの中で「旅の仲間->ホビット->メリー」というパスを見つけ、ビールに関連したパーミッションを下へと追って行きます。 + ++--------------+----------------------+------------------------------+ +| ARO ノード | パーミッション情報 | 結果 | ++==============+======================+==============================+ +| 旅の仲間 | Deny all | ビールへアクセスできない。 | ++--------------+----------------------+------------------------------+ +| ホビット | Allow 'ビール' | ビールへアクセスできる! | ++--------------+----------------------+------------------------------+ +| メリー | Deny 'ビール' | ビールへアクセスできない。 | ++--------------+----------------------+------------------------------+ + +パーミッションの定義: Cake の INI-ベース ACL +============================================ + +初期における CakePHP への ACL の実装は、 CakePHP +のインストール時に保存される INI ファイルに基づいていました。 +これも便利で安定してはいますが、データベースによる ACL +ソリューションの利用をお勧めします。 そのほうが ACO と ARO +を素早く作成できるからです。 INI ファイルに基づく ACL +は、シンプルなアプリケーションや、何らかの理由でデータベースを使用できない場合などに利用するとよいでしょう。 + +デフォルトでは、 CakePHP の ACL はデータベースを利用します。 INI +ベースの ACL を有効にするには、 app/config/core.php +の次の行を上書きしてください。 + +:: + + // これらの行を変更する + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + // 変更した内容は次のとおり + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +ARO/ACO パーミッションは、 **/app/config/acl.ini.php** +で定義されています。 ARO は INI セクションで定義されており、 INI +セクションは「groups」「allow」「deny」という三つのプロパティを持つというのが基本的な考え方です。 + +- groups: その ARO が所属するグループ名。 +- allow: その ARO のアクセスを許可する ACO の名前。 +- deny: その ARO のアクセスを拒否する ACO の名前。 + +ACO は allow か deny プロパティのみを含む INI +セクションで定義されています。 + +先の例における「旅の仲間」の ARO の構造を INI +シンタックスで作成したものを見てみましょう。 +(訳注:理解しやすいよう例には日本語を含めていますが、それでは実際には動作しません。) + +:: + + ;------------------------------------- + ; ARO + ;------------------------------------- + [アラゴルン] + groups = 戦士 + allow = 外交術 + + [レゴラス] + groups = 戦士 + + [ギムリ] + groups = 戦士 + + [ガンダルフ] + groups = 魔法使い + + [フロド] + groups = ホビット + allow = 一つの指輪 + + [ビルボ] + groups = ホビット + + [メリー] + groups = ホビット + deny = ビール + + [ピピン] + groups = ホビット + + [ゴクリ] + groups = 来訪者 + + ;------------------------------------- + ; ARO のグループ + ;------------------------------------- + [戦士] + allow = 武器, ビール, 塩漬け豚 + + [魔法使い] + allow = 塩漬け豚, 外交術, ビール + + [ホビット] + allow = ビール + + [来訪者] + allow = 塩漬け豚 + +これでパーミッションが定義されました。もう ACL +コンポーネントを使いたいなら\ `パーミッションのチェックに関する章 `_\ までスキップしてください。 + +パーミッションの定義: Cake のデータベース ACL +============================================= + +INI ベースの ACL パーミッションについては説明しました。INI +ベースのものよりよく使われるデータベースを用いた ACL +について見ていきましょう。 + +はじめに +-------- + +デフォルトの ACL パーミッションの実装はデータベースを使用します。 +CakePHP のデータベースを用いる ACL +は、一連のコアモデルとコマンドラインのスクリプトで構成されています。 +これらのファイルは CakePHP をインストールすると作成されます。 モデルは、 +CakePHP がデータベースとやりとりをし、ACL +ツリーノードの保存と読み出しを行うために使用します。 +コマンドラインのスクリプトは、データベースの初期化と、ACO および ARO +ツリーのやりとりのために使います。 + +はじめる前に、 ``/app/config/database.php`` +が存在し、正しく設定されていることを確認してください。 +この設定についてのより詳しい情報は、 3.4.1 章を参照してください。 + +設定が完了したら、 CakePHP +のコマンドラインスクリプトを使ってデータベースに ACL +のテーブルを作成しましょう。 + +:: + + $ cake schema run create DbAcl + +このコマンドは、 ACO と ARO +の情報を木構造で保存するためのテーブルをドロップして再生成します。 +コマンドを実行したとき、スクリプトの出力は次のようになります。 + +:: + + --------------------------------------------------------------- + Cake Schema Shell + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +これは、古いバージョンで使われていて評判の良くなかった「initdb」を代替するコマンドです。 + +面白味に欠ける方法ではありますが、\ ``app/config/sql/db_acl.sql`` にある +SQL ファイルを実行しても同じ結果が得られます。 + +実行が完了したら、システム中に三つの新しいデータベーステーブルが作られます。 +それぞれ、「acos」「aros」そしてこのテーブルを結合しパーミッション情報を作成する「aros\_acos」となります。 + +CakePHP +がこれらのテーブルに木構造の情報をどのように保存するかについて興味があるなら、それをデータベースでどのように表現し変更するのかを調べてみてください。 +ACL コンポーネントは木構造の継承を管理するために CakePHP +の\ `ツリービヘイビア `_\ を利用します。 また、 ACL +に関するすべてのモデルクラスは、 +`db\_acl.php `_ +に集められています。 + +これで、ARO と ACO ツリーが作成できるようになったはずです。 + +アクセスリクエストオブジェクト (ARO) とアクセスコントロールオブジェクト (ACO) の作成 +------------------------------------------------------------------------------------ + +新しい ACL オブジェクト(つまり ACO と ARO) +を作成する時に、ノードに名前を付けアクセスするには、主に二つの方法があります。 +*一つ目*\ は、モデル名と外部キーの値を特定し、ACL +オブジェクトをデータベース中のレコードに直接リンクする方法です。 +*二つ目*\ はオブジェクトに対して別名を文字列で提供する方法で、これは ACL +オブジェクトが直接関連付くレコードが存在しない場合に利用します。 + +通常、別名を使うのは、グループや高いレベルのオブジェクトを作成する場合です。 +もしデータベース中の特定のアイテムやレコードへのアクセスを管理したいなら、モデル名と外部キーを使う方法を採用してください。 + +新しい ACL オブジェクトを作成するには、CakePHP コアの ACL +モデルを使用します。 +その際、データを保存する時に使うフィールドとして、「\ ``model``\ (モデル名)」「``foreign_key``\ (外部キー)」「``alias``\ (別名)」そして「``parent_id``\ (親 +ID)」というものがあります。 + +ACL +オブジェクトの「\ ``model``\ (モデル名)」と「``foreign_key``\ (外部キー)」フィールドは、オブジェクトと、あるモデルの一つのレコードをリンクするために使います。 +例えば、 ARO がデータベース中の User レコードに対応している場合です。 +ARO の ``foreign_key``\ (外部キー) に User モデルの id を設定すると、 +ARO と User の情報がリンクされます。 User の情報には、単独の +Model::find() +の実行結果が(もしアソシエーションが正しく定義されていれば、それも)含まれます。 +逆に、ある特定のブログの投稿やレシピの表などを編集する作業を管理したい場合は、これらのモデルのレコードを +ACO にリンクさせます。 + +ACL の ``alias``\ (別名) は、モデルのレコードに直接結びつかない ACL +オブジェクトに、 読んで理解できるラベルを付けるために使用します。 +別名は、ユーザのグループや ACO の集合に名前を付ける時に便利です。 + +ACL オブジェクトの ``parent_id``\ (親 ID) +は、木構造を構築するために使います。 +新しい子ノードを作成するには、ツリー中の親ノードの ID を指定します。 + +新しい ACL +オブジェクトを作成する前に、まずそれぞれのクラスを読み込まねばなりません。 +もっとも簡単な方法は、コントローラ中の $components 配列で ACL +コンポーネントを読み込むよう設定することです。 + +:: + + var $components = array('Acl'); + +これを行ったら、オブジェクトを作成する例がどのようになるのかを見てみましょう。 +次のコードをコントローラアクションのどこかに置いてください。 + +この例では ARO の作成にフォーカスしていますが、 ACO +のツリーも同様に作成できます。 + +「旅の仲間」の設定で、最初の ARO グループを作成してみましょう。 +グループは特定のレコードに結びつかないので、別名を使って ACL +オブジェクトを作成します。 +ここではあるコントローラアクション中で実行しますが、どこでも好きなところで行えます。 +またこの例においては、 ARO と ACO +を作成する方法を理解しやすくするため、やや恣意的なアプローチを採用しています。 + +特に目新しい点は何もありません。普通にモデルを使ってデータを保存しています。 + +:: + + function anyAction() + { + $aro = new Aro(); + + // 配列に全てのグループの情報を定義する + $groups = array( + 0 => array( + 'alias' => '戦士' + ), + 1 => array( + 'alias' => '魔法使い' + ), + 2 => array( + 'alias' => 'ホビット' + ), + 3 => array( + 'alias' => '来訪者' + ), + ); + + // 配列をループさせ、 ARO グループを作成する + foreach($groups as $data) + { + // ループ中でデータの保存を行う場合、 create() を忘れずに実行してください + $aro->create(); + + // データを保存する + $aro->save($data); + } + + // 別のアクションのロジックが続く + } + +これを実行したら、コマンドラインの ACL +アプリケーションを使って木構造を確認してみましょう。 + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]戦士 + + [2]魔法使い + + [3]ホビット + + [4]来訪者 + + --------------------------------------------------------------- + +この段階では大したツリーではありませんが、少なくとも四つのトップレベルノードがあることが確認できます。 +特定のユーザに対応した ARO を先ほど作成したグループに追加し、 ARO +ノードに子を追加していきましょう。 +中つ国(訳注:指輪物語の舞台)の全ての住人はこのシステムでアカウントを持っているため、 +データベース中のあるモデルの特定のレコードと ARO +レコードを結びつけることができます。 + +ツリーに子ノードを加える場合、外部キーの値ではなく ACL のノード ID +を使うようにしてください。 + +:: + + function anyAction() + { + $aro = new Aro(); + + // 新しい ARO レコードにリンクするユーザのレコードを取得します。 + // 通常、このデータには、モデルから取得したものやそれを加工したものを使います。 + // しかしこの例はデモンストレーションなので、静的な配列で定義してしまいます。 + + $users = array( + 0 => array( + 'alias' => 'アラゴルン', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'レゴラス', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'ギムリ', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'ガンダルフ', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'フロド', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'ビルボ', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'メリー', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'ピピン', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'ゴクリ', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + // 定義した配列をループでまわして ARO を子として作成する + foreach($users as $data) + { + // ループ中でデータの保存を行う場合、 create() を忘れずに実行してください + $aro->create(); + + // データを保存する + $aro->save($data); + } + + // 別のアクションのロジックが続く + } + +普通、モデル名(\ *model*)と外部キー(\ *foreign\_key*)を使って ARO +と結びつける場合、 別名(\ *alias*)は使用しません。 +この例ではデモンストレーション用にツリーを読みやすくするため、あえて別名を定義しています。 + +コマンドラインの ACL +アプリケーションの出力が少し面白くなっています。見てみましょう。 + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]戦士 + + [5]アラゴルン + + [6]レゴラス + + [7]ギムリ + + [2]魔法使い + + [8]ガンダルフ + + [3]ホビット + + [9]フロド + + [10]ビルボ + + [11]メリー + + [12]ピピン + + [4]来訪者 + + [13]ゴクリ + + --------------------------------------------------------------- + +ARO ツリーをきちんと設定できましたので、次に ACO +ツリーを構築するアプローチにどのようなものがあるのかを見ていきましょう。 +ACO はより多くの抽象的な表現を構築することができるので、 ACO +ツリーをモデルするのは CakePHP の Controller/Action +セットアップの後に行うことが実用的でしょう。 +この「旅の仲間」の筋書においては、主に五つのオブジェクトを使います。 +CakePHP +のアプリケーション中では、これらをモデルのグループとして、そして最終的にはそれらを扱うコントローラとしてセットアップするのが自然です。 +さらに進んで、アクセスの特定のアクションを制御することもできます。 + +この考えに基づき、CakePHP のアプリケーションのセットアップに似せた ACO +ツリーをセットアップしましょう。 五つの ACO を持っていますので、ACO +ツリーは次のようになります。 + +- 武器 +- 指輪 +- 豚の切り身 +- 外交努力 +- ビール + +CakePHP の ACL セットアップにおいてすばらしいことは、それぞれの ACO +が自動的に CRUD(create, read, update, delete) +アクションに関連したプロパティを持つことです。 これら五つの主な ACO +のそれぞれの下に、子ノードを作成すると、 CakePHP +の組み込みアクション管理機能の利用は、与えられたオブジェクトに対する基本的な +CRUD の操作をカバーします。 これを意識しておくと、 ACO +ツリーが簡潔で保守しやすいものになります。 +これらをどのように使うかは、パーミッションを割り当てる方法を説明する時に見ていきます。 + +これで、 ACO ツリーを作成するテクニックと同様に、 ARO +を追加することも学びました。 コアの Aco +モデルを使って、それらの上位のグループを作成してください。 + +パーミッションの割り当て +------------------------ + +ACO と ARO +を作成したら、いよいよその二つのグループ間にパーミッションを割り当てられます。これは +CakePHP コアの Acl コンポーネントで行います。例に沿って続けましょう。 + +これはコントローラのアクションの中で実行します。なぜなら、パーミッションの管理は +Acl コンポーネントが行うからです。 + +:: + + class SomethingsController extends AppController + { + // ここに指定する代わりに AppController 中で指定したいと思うかもしれませんが、 + // ここでも問題なく動きます。 + + var $components = array('Acl'); + + } + +コントローラのアクション中で AclComponent +を使い、基本的なパーミッションをセットアップしてみましょう。 + +:: + + function index() + { + // 戦士に武器への完全なアクセスを許可する。 + // これらの例では全て、別名(alias)を使って設定してみます。 + $this->Acl->allow('戦士', '武器'); + + // 王は全ての者が無制限のアクセスを持つことを + // お望みにならない + $this->Acl->deny('戦士/レゴラス', '武器', 'delete'); + $this->Acl->deny('戦士/ギムリ', '武器', 'delete'); + + die(print_r('done', 1)); + } + +まず最初の呼び出しで、 AclComponent に「戦士」という ARO +のグループに対して、「武器」という ACO +のグループへ完全なアクセスを与えています。 ここでは、 ACO と ARO +のアドレス指定に別名を使っています。 + +三つ目のパラメータはどうやって使うのでしょうか。 これが、 CakePHP +の全ての ACO +に組み込まれている便利なアクションです。このオプションのデフォルトの値は「\ ``create``\ (生成)」「``read``\ (読み込み)」「``update``\ (更新)」そして「``delete``\ 」です。が、\ ``aros_acos`` +データベーステーブル(あるいはこれを前置詞として「\_」でつなげたテーブル。例えば +``_admin``)にカラムを追加し、デフォルトのものと一緒に使うことができます。 + +二つ目の呼び出しセットでは、さらにきめ細かなパーミッションの設定を行おうとしています。アラゴルンにはフルアクセスの特権を与えたまま、同じグループの他の戦士は武器のレコードを削除(\ *delete*)できないようにしています。上述の例では、ARO +のアドレス指定に別名を用いたシンタックスを使っていますが、モデル名と外部キーを用いるシンタックスを使うこともできます。これに相当するものは、次のようになります。: + +:: + + // 6342 = レゴラス + // 1564 = ギムリ + + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342), '武器', 'delete'); + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564), '武器', 'delete'); + +別名によるノードのアドレス指定のシンタックスは、スラッシュをデリミタにした文字列(例えば +「/users/employees/developers」)を使います。モデル名と外部キーによるノードのアドレス指定を行うには、 +``array('model' => 'User', 'foreign_key' => 8282)`` +というような二つのパラメータを持つ配列を使います。 + +次の章では、AclComponent +を使ってセットアップしたパーミッションをチェックする機能を有効にする方法について説明します。 + +パーミッションのチェック: ACL コンポーネント +-------------------------------------------- + +AclComponent +を使って、ドワーフとエルフが武器庫から何も削除できないようにしましょう(訳注:アラゴルンは人間、ギムリはドワーフ、レゴラスはエルフです)。 +それにあたり、 AclComponent を使って、作成した ACO と ARO +の間の関係をチェックすることが出来ます。 +パーミッションのチェックを行う基本的なシンタックスはこのようになります。 + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +コントローラアクションの中で実行してみましょう。 + +:: + + function index() + { + // これらは全て true を返します。 + $this->Acl->check('戦士/アラゴルン', '武器'); + $this->Acl->check('戦士/アラゴルン', '武器', 'create'); + $this->Acl->check('戦士/アラゴルン', '武器', 'read'); + $this->Acl->check('戦士/アラゴルン', '武器', 'update'); + $this->Acl->check('戦士/アラゴルン', '武器', 'delete'); + + // ユーザの ARO にモデル名と外部キーを用いたシンタックスを + // 使用できることを忘れないでください。 + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), '武器'); + + // これらも全て true を返します。 + $result = $this->Acl->check('戦士/レゴラス', '武器', 'create'); + $result = $this->Acl->check('戦士/ギムリ', '武器', 'read'); + + // 一方、これらは false を返します。 + $result = $this->Acl->check('戦士/レゴラス', '武器'); + $result = $this->Acl->check('戦士/ギムリ', '武器', 'delete'); + } + +この使い方はデモンストレーション用のものですが、何が出来るかはわかるでしょう。 +実際にはパーミッションを判定したあと、何か処理を実行することを許可するのか、エラーメッセージを表示するのか、ログインページにリダイレクトするのかといった振る舞いを決定します。 diff --git a/ja/The-Manual/Core-Components/Authentication.rst b/ja/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..fe05e1b255b2320495559040b8a20b250a560679 --- /dev/null +++ b/ja/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,682 @@ +認証 +#### + +ユーザ認証システムは多くのウェブアプリケーションで一般的な仕組みです。CakePHP +には、それぞれ異なるオプションを持つユーザ認証のシステムがいくつかあります。それらの中核を担う認証コンポーネントでは、ユーザがあるサイトにアカウントを持っているかどうかをチェックします。もしアカウントが存在すれば、コンポーネントはユーザに対してサイトへのアクセスを許可します。 + +このコンポーネントは ACL (アクセス制御リスト, *Access Control +List*)コンポーネントと併せて使うことで、より複雑なサイト内のアクセス権限を作成できます。ACL +コンポーネントは、例えば、あるユーザにはサイトの一般的な領域にアクセスすることを許可し、別のユーザには保護されたサイトの管理領域へのアクセスを許可するといったことを実現できます。 + +CakePHP の AuthComponent +を使うと、こういったシステムを容易に、そして素早く作成できます。 +ごく簡単な認証システムの作成方法を見てみましょう。 + +他の全てのコンポーネントと同じく、コントローラ内で使用するコンポーネントに「Auth」を加えることで、認証コンポーネントは使用可能になります。 + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +あるいは、全てのコントローラで認証コンポーネントを利用するのなら、 +AppController に追加しても良いでしょう。 + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +AuthComponent を使うにあたり、いくつかの決まりごとがあります。 +デフォルトでは、 AuthComponent +は、「username」と「password」というフィールドを持つ「users」テーブルを使おうとします。\ *しかし状況によっては、データベースでのカラム名に「password」というものは使えない場合があります。後ほど、それぞれの環境に適合させるためデフォルトで参照するフィールド名を変更する方法を説明します。* + +では、次の SQL 文を実行して、「users」テーブルをセットアップしましょう。 + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(50), + PRIMARY KEY (id) + ); + +ユーザを認証するデータを格納するテーブルを作成する時に注意する点があります。それは、 +AuthComponent +は、データベースに格納するパスワードは平文ではなくハッシュ化されたものであることを期待する、という点です。 +そのため、パスワードを保存するフィールドには、ハッシュを格納するのに十分な長さを持たせる必要があります。(例えば、 +SHA1 だとハッシュの長さは40文字になります。) + +最も基本的なセットアップは、コントローラ中に二つのアクションを作成するだけです。 + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); // Not necessary if declared in your app controller + + /** + * AuthComponent がログインに必要な機能を提供します。 + * そのため、この関数の中身は空にしておいてください。 */ + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +login() +機能を空にしても、ログインのビューのテンプレートは作成する必要があります。 +(app/views/users/login.ctp に保存します。)UserController +のビューテンプレートとして作成する必要があるものは、次のものだけです。 +この例ではすでに Form ヘルパーを使用していると仮定しています。 + +:: + + check('Message.auth')) $session->flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +このビューは、ユーザ名とパスワードを入力する単純なログインフォームを作成します。 +このフォームを送信すると、 AuthComponent +はログインに必要な処理を行います。また、 session->flash メッセージは、 +AuthComponent が生成した全ての警告を出力します。 + +信じられないかもしれませんが、これでユーザ認証は完了です。ありえないほどシンプルですが、これが +Auth +コンポーネントとデータベースを用いた認証システムの使用方法です。とはいえ、すべきことは、まだたくさんあります。このコンポーネントのさらに進んだ使い方を見てみましょう。 + +Auth コンポーネントの変数を設定する +=================================== + +AuthComponent のデフォルトのオプションを変更する時は、コントローラに +beforeFilter() +メソッドを作成して、その中で様々な組み込みのメソッドを呼び出したり、コンポーネントの変数を設定してください。 + +例えば、パスワードを格納するフィールドの名前を「password」から「secretword」に変更するには、次のようにします。 + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'secretword' + ); + } + } + +この時、ビューテンプレートのフィールド名を変更することを忘れないでください! + +Auth +コンポーネントのその他の一般的な使い方は、あるメソッドにログインしていないユーザがアクセスすることを許可することです。 + +例えば、 index と view +メソッドには全てのユーザをアクセスさせ、他のメソッドにはアクセスさせない場合、次のようにします: + +:: + + function beforeFilter() { + $this->Auth->allow('index','view'); + } + +認証のエラーメッセージを表示する +================================ + +Auth +コンポーネントが吐き出すエラーメッセージを表示するためには、次のコードをビューに加えてください。 +このケースでは、メッセージは標準的な flash +メッセージの下に表示されます。 + +全てのビューに対して、普通のflashメッセージとauthのflashメッセージとを全て表示するには、下記の二つの行をviews/layouts/default.ctpのbodyタグの中に書き加えるようにします。content\_for\_layoutの行の前に設置するのが望ましいでしょう。 + +:: + + flash(); + $session->flash('auth'); + ?> + +認証における問題のトラブルシューティング +======================================== + +期待した動作が得られない時、問題の原因を調査することがかなり難しい場合があります。 +おぼえておくべきいくつかのポイントがあります。 + +*パスワードのハッシュ化* + +フォームからアクションへ情報が POST された時、 Auth +コンポーネントは、ユーザ名フィールドのデータはそのまま持ち、パスワードのフィールドに入力されたコンテンツは自動的にハッシュ化します。 +登録などを行うページを作成したいなら、 +「パスワード確認」というようなフィールドをユーザに入力してもらうようにし、 +パスワードと比較するようにしてください。 +サンプルコードは次のようになります。 + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +暗号化の方法を変更する +====================== + +AuthComponent は、パスワードを暗号化するために Security +クラスを使います。 Security は、デフォルトで SHA1 を使います。 Auth +コンポーネントで使用される暗号化メソッドを変更するには、\ ``setHash`` +メソッドに暗号化方法を引数で与えてください。このメソッドに引数は一つしか渡せません。 +サポートしている値は、「\ ``md5``\ 」「\ ``sha1``\ 」そして「\ ``sha256``\ 」です。 + +:: + + Security::setHash('md5'); // または sha1 か sha256 + +Security クラスはパスワードの暗号化に、 /app/config/core.php で定義した +Security.salt の値を使用します。 もし、 Security.salt +を使わず暗号化したスキーマがある既存のデータベースを利用する場合、 +``authorize`` をセットしていなければこれをセットし、そのクラスの中で +`hashPasswords `_ +メソッドを作成してください。 + +AuthComponent のメソッド +======================== + +action +------ + +``action (string $action = ':controller/:action')`` + +もし ACL を利用していて、その構造の一部として ACO を用いているなら、 +ある特定の controller/action ペアに結びついた ACO +ノードのパスを取得できます。 + +:: + + $acoNode = $this->Auth->action('users/delete'); + +何も値を渡さなければ、現在の controller/action ペアが使用されます。 + +allow +----- + +コントローラ中で認証を行わないアクション(例えば登録を行うアクション)があるのなら、 +allow メソッドを使って AuthComponent +がそのアクションを無視するようにできます。 +次の例では、「register」アクションで認証を無視するようにしています。 + +決して、 allow +メソッドに「login」という名前のアクションを適用しないでください。認証機能が誤作動します。 + +:: + + $this->Auth->allow('register'); + +複数のアクションで認証をスキップするようにするなら、それらのアクション名を +allow() メソッドのパラメータに渡してください。 + +:: + + $this->Auth->allow('foo', 'bar', 'baz'); + +ショートカット:コントローラ中の全てのアクションに allow +を実行する場合、「\*」を指定してください。 + +:: + + $this->Auth->allow('*'); + +レイアウトやエレメントで requestAction を使う場合、 +ログインページがきちんと表示されるよう、それらのアクションを allow +で許可するようにしてください。 + +認証コンポーネントは、アクション名が\ `規約に沿った `_\ ものであり、アンダースコアによる記法であることを前提とします。 + +deny +---- + +allow +で認証を行わないことを許可したアクション一覧から、一部のアクションを取り除きたいこと場合が出てくるかもしれません。 +これを行うには次のようにします。 + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +このメソッドは、 ``$data`` +にユーザ名とパスワードが含まれるかをチェックします。ユーザ名とパスワードは、 +``$userModel`` で定義されたモデル名ものにインデックスされ、\ ``$fields`` +で定義されたものを利用します。もし ``$data`` +配列がユーザ名とパスワードの両方を含む場合、このメソッドはパスワードのフィールドをハッシュ化し、同じフォーマットで +data +配列を返します。この機能は、パスワードのフィールドが発生するとき、ユーザのモデルに対する挿入や更新を行う前に実行しておくべきです。 + +:: + + $data['User']['username'] = 'me@me.com'; + $data['User']['password'] = 'changeme'; + $hashedPasswords = $this->Auth->hashPasswords($data); + print_r($hashedPasswords); + /* returns: + Array + ( + [User] => Array + ( + [email] => me@me.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +この例において、 *$hashedPasswords['User']['password']* +フィールドはコンポーネントの ``password`` +関数を使ってハッシュ化されます。 + +もしコントローラが Auth +コンポーネントを利用しており、データが前述したユーザ名やパスワードといったフィールドを保持していた場合、パスワードのフィールドはこの関数を使って自動的にハッシュ化されます。 + +mapActions +---------- + +If you are using Acl in CRUD mode, you may want to assign certain +non-default actions to each part of CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('someAction'), + 'read' => array('someAction', 'someAction2'), + 'update' => array('someAction'), + 'delete' => array('someAction') + ) + ); + +login +----- + +``login($data = null)`` + +Ajax ベースのログイン等を利用したいなら、 +このメソッドを使い、手動で利用者をシステムにログインさせることができます。 +``$data`` に値を渡さなければ、コントローラ中の POST +されたデータを自動的に使用します。 + +例えば、ユーザーに自動的にパスワードを割り当てて、登録後にログインするアプリケーションにしたい。以上の簡単な例では: + +View: + +:: + + echo $form->create('User',array('action'=>'register')); + echo $form->input('username'); + echo $form->end('Register'); + +Controller + +:: + + function register() { + if(!empty($this->data)) { + $this->User->create(); + $assigned_password = "password"; + $this->data['User']['password'] = $assigned_password; + if($this->User->save($this->data)) { + // send signup email containing password to the user + $this->Auth->login($this->data); + $this->redirect("home"); + } + } + +一点注意すべきこととしてloginRedirect呼び出されない場合は手動でログイン後にユーザーをリダイレクトする必要があります。 + +$this->Auth->login($data) は、成功時に1,失敗時に0を返します。 + +logout +------ + +利用者を認証していない状態(ログアウトした状態)にし、どこかにリダイレクトさせるための素早い方法を提供します。 +このメソッドは、アプリケーション中のメンバーだけが表示できるページ内に「ログアウト」機能を提供したい時などに便利です。 + +Example: + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +文字列を渡すと、ハッシュ化されたものを取得できます。 +この機能は、ログイン済みのユーザに対して、アプリケーションの重要な領域にアクセスさせる前にもう一度パスワードを入力してもらう時などに重要となります。 + +:: + + if ($this->data['User']['password'] == + $this->Auth->password($this->data['User']['password2'])) { + + // Passwords match, continue processing + ... + } else { + $this->flash('Typed passwords did not match', 'users/register'); + } + +認証コンポーネントは、送信されたデータの中に「username」フィールドがある場合、自動的に「password」フィールドをハッシュ化します。 + +user +---- + +:: + + user(string $key = null) + +このメソッドは、現在認証しているユーザの情報を提供します。この情報はセッションから取得されます。例は次のとおりです。 + +:: + + if ($this->Auth->user('role') == 'admin') { + $this->flash('あなたは管理者権限でアクセスしています。'); + } + +このメソッドは、ユーザのセッションデータを全て取得するためにも使えます。 + +:: + + $data['User'] = $this->Auth->user(); + +ユーザーがログインしていない場合、このメソッドは null を返します。 + +AuthComponent の変数 +==================== + +認証に関連した変数は、次のようなものになります。通常、これらはコントローラの +beforeFilter() メソッドで追加します。サイト全体に適用したい場合は、App +Controller の beforeFilter() に追加すると良いでしょう。 + +userModel +--------- + +認証に User +モデルを使いたくない場合は、この変数に使用するモデルの名前を与えてください。 + +:: + + Auth->userModel = 'Member'; + ?> + +fields +------ + +認証で使うデフォルトのユーザ名(\ *username*)とパスワード(\ *password*)のフィールド名を上書きするために使います。 + +:: + + Auth->fields = array('username' => 'email', 'password' => 'passwd'); + ?> + +userScope +--------- + +認証が成功するために必要な条件を追加するために使用します。 + +:: + + Auth->userScope = array('User.active' => 'Y'); + ?> + +loginAction +----------- + +ログインを行うアクションをデフォルトの */users/login* から変更します。 + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'members', 'action' => 'login'); + ?> + +loginRedirect +------------- + +通常、 AuthComponent +は認証が実行されるまえのコントローラ/アクションのペアを記憶しており、認証が成功したらユーザをそこにリダイレクトします。しかし、この変数に特定のコントローラ/アクションのペアを定義することで、そこへ強制的にリダイレクトするようにできます。 + +:: + + Auth->loginRedirect = array('controller' => 'members', 'action' => 'home'); + ?> + +logoutRedirect +-------------- + +デフォルトでは、ログアウト後にユーザは自動的にログインアクションへリダイレクトされます。 +このリダイレクト先のアクションを定義できます。 + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'members', 'action' => 'logout'); + ?> + +loginError +---------- + +ログインに失敗した時に表示されるデフォルトのエラーメッセージを変更できます。 + +:: + + Auth->loginError = "あらら。パスワードが違いますよ。"; + ?> + +authError +--------- + +誰かがアクセスしてはいけないオブジェクトあるいはアクションにアクセスしようとしたときに表示されるデフォルトのエラーメッセージを変更します。 + +:: + + Auth->authError = "Sorry, you are lacking access."; + ?> + +autoRedirect +------------ + +通常、 AuthComponent +は認証を実行してすぐ、自動的にリダイレクトを実行します。しかしユーザをリダイレクトする前に、さらに何かをチェックしたいかもしれません。 + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- この関数に含まれるコードは、 autoRedirect が false にセットされている時、つまり beforeFilter でだけ動作します。 + if ($this->Auth->user()) { + if (!empty($this->data)) { + $cookie = array(); + $cookie['username'] = $this->data['User']['username']; + $cookie['password'] = $this->data['User']['password']; + $this->Cookie->write('Auth.User', $cookie, true, '+2 weeks'); + unset($this->data['User']['remember_me']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.User'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // この分岐では認証のメッセージをクリアしてください。 + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +このコードの login 関数は、beforeFilter で $autoRedirect を false +にセット\ *しない限り*\ 実行されません。この login +関数のコードは、認証が試みられた\ *後*\ だけに実行されます。ここはログインが成功したかどうか確定する最もよい場所です。(例えば、最終ログイン日時をログに記録するなど) + +authorize +--------- + +通常、 AuthComponent +は入力されたログインのための情報を、ユーザモデル中のデータと比較することによって、認証するかどうか確かめようとします。しかし、認証の是非を確定するために追加の処理を行いたい時があります。この変数を何か別の値にすることで、この追加の処理を行うことができます。よくある例を次に示します。 + +:: + + Auth->authorize = 'controller'; + ?> + +authorize 変数を「controller」にセットすると、コントローラ中に +isAuthorized() +というメソッドを追加する必要が出てきて、そのメソッドは認証が成功した後に実行されます。 +このメソッドでは、追加的なチェックを行い、 true または false +を返すようにします。 + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } else { + return false; + } + } + + return true; + } + ?> + +このメソッドは、 User +モデルを用いた基本的な認証をパスした後に実行されることに注意してください。 + +:: + + Auth->authorize = 'model'; + ?> + +ACO +を利用する時など、コントローラ中での処理を行わない場合はどうすればよいのでしょうか?その場合、 +authorize 変数を「model」にセットすることで、認証が使うモデル(例えば +User) 中の isAuthorized() +メソッドがコールされます。そのメソッドの中では、例えば次のようなことを行います。 + +:: + + + +sessionKey +---------- + +認証されたユーザが保存されている現在のレコードを保持するセッション配列キー名。 + +指定されない場合はデフォルトは "Auth" です。レコードは "Auth.{$userModel +名}" に保存されています。 + +:: + + Auth->sessionKey = 'Authorized'; + ?> + +ajaxLogin +--------- + +Ajax あるいは Javascript +ベースの認証されたセッションを必要とする場合、この変数に、認証失敗もしくはセッション切れの時に表示したいビューエレメントの名前を設定してください。 + +CakePHP の他の項目と同じく、 AuthComponent についてのより詳しい情報は +`AuthComponent +class(英文) `_ +を参照してください。 + +authenticate +------------ + +デフォルトでは、AuthComponent +はパスワードをハッシュ化するためにコアユーティリティクラス ``Security`` +の ``hash`` +関数を使用します。しかし、もし必要であれば、\ ``hashPasswords`` +という関数を持つクラスのオブジェクトを ``authenticate`` +にセットすることで、独自の暗号化ロジックを使用するように設定することもできます。このプロパティは、ただのモデルではなくオブジェクトのインスタンスとして取得するために、\ ``$this->Auth->authenticate = ClassRegistry::init('ModelName');`` +というようにセットするようにしてください。この関数は、基本的に独自の暗号化ロジックを使用してコンポーネントの +``hashPasswords`` +関数の機能を置き換えます。どのように動作するかについてのより詳細な情報は、 +API や関数自身のコードをチェックしてください。 + +actionPath +---------- + +If using action-based access control, this defines how the paths to +action ACO nodes is computed. If, for example, all controller nodes are +nested under an ACO node named 'Controllers', $actionPath should be set +to 'Controllers/'. diff --git a/ja/The-Manual/Core-Components/Cookies.rst b/ja/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..0691038066ceaeef3aeaafe3156d0d65d36454bb --- /dev/null +++ b/ja/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,165 @@ +クッキー(Cookie) +################ + +CookieComponent は PHP ネイティブ関数の setcookie +のラッパーです。ただラップするだけでなく、上質な砂糖でおいしく包み込んだクッキー…というのは冗談で、コントローラの中でクッキー(\ *Cookie*)をとても便利に使えるようにしたコンポーネントです。 +CookieComponent を使うには、まずその前にコントローラ変数の $components +配列の中で 'Cookie' を指定してください。 + +コントローラのセットアップ +========================== + +クッキーを生成し管理するには、いくつかのコントローラ変数を設定します。コントローラの +beforeFilter() メソッドで、次に挙げる特別な変数を定義し、 +CookieComponent の動作を設定できます。 + +クッキー変数 + +デフォルト + +説明 + +文字列 $name + +'CakeCookie' + +クッキーの名前。 + +文字列 $key + +null + +この文字列はクッキーに変数を書き込むときに、それを暗号化するために使います。ランダムで推測しにくい文字列を使用してください。 + +文字列 $domain + +'' + +クッキーにアクセスすることを許可するドメイン名を指定します。例えば、すべてのサブドメインから許可する場合は「.yourdomain.com」としてください。 + +整数または文字列 $time + +'5 Days' + +クッキーが有効期限切れになる時間を指定します。 +整数を指定した場合は秒数として取り扱われ、 0 +は「セッションクッキー(\ *session +cookie*)」に相当するものであり、ブラウザが閉じられたときに期限切れになります。文字列がセットされた場合は、 +PHP の strtotime() によって解釈されます。この変数は、 write() +で直接セットすることができます。 + +文字列 $path + +'/' + +クッキーが適用されるサーバ上のパスを指定します。もし $cookiePath +が「/foo/」に設定されていた場合、クッキーはドメイン中の「/foo/」ディレクトリと、そこに含まれる「/foo/bar/」といった全てのサブディレクトリで有効になります。デフォルト値はドメイン全体です。この変数は、 +write() で直接セットすることができます。 + +ブール値 $secure + +false + +クッキーがセキュアな HTTPS 接続のみで送られるかどうかを示します。 true +にセットすると、クッキーはセキュアなコネクションのみで使われます。この変数は、 +write() で直接セットすることができます。 + +次の例は、クッキー名を「baker\_id」、ドメイン名を「example.com」、セキュアな接続のみで有効、「/bakers/preferences/」というパスで有効、有効期限を1時間と設定して +CookieComponent を読み込んだコントローラを抜粋したものです。 + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'baker_id'; + $this->Cookie->time = 3600; // あるいは '1 hour' + $this->Cookie->path = '/bakers/preferences/'; + $this->Cookie->domain = 'example.com'; + $this->Cookie->secure = true; // セキュアな HTTPS を使用していた場合にのみクッキーが送信されます + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +次に、 CookieComponent の特別なメソッドを使用法を見ていきましょう。 + +コンポーネントを使う +==================== + +このセクションでは CookieComponent のメソッドの概要を説明します。 + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +write() メソッドは、クッキーコンポーネントの中核部分です。 $key +はクッキー変数の名前、 $value はそれに保存する値です。 + +:: + + $this->Cookie->write('name','Larry'); + +$key 変数にドットを用いた記法を使うことで変数をグループ化できます。 + +:: + + $this->Cookie->write('User.name', 'Larry'); + $this->Cookie->write('User.role','Lead'); + +クッキーに、一回の操作で複数の値を設定したい場合、配列で渡します。 + +:: + + $this->Cookie->write( + array('name'=>'Larry','role'=>'Lead') + ); + +クッキーに保存する全ての値は、デフォルトでは暗号化されます。平文で保存したい場合は、 +write() メソッドの第三パラメータを false に設定してください。 + +:: + + $this->Cookie->write('name','Larry',false); + +write() メソッドの最後のパラメータは、 $expires +です。これはクッキーの有効期限を秒数で表すものです。便利なことに、このパラメータは +PHP の strtotime() +関数が理解できるフォーマットの文字列でも渡すことが可能です。 + +:: + + // この記述はクッキーの有効期限を1時間と定義したものです。 + // 次の表記と同等です。 $this->Cookie->write('first_name','Larry',false, 3600); + $this->Cookie->write('last_name','Masters',false, '1 hour'); + +**read(mixed $key)** + +このメソッドは $key で定義された名前のクッキー変数を読み込みます。 + +:: + + // “Larry” と出力される。 + echo $this->Cookie->read('name'); + + // 読み込みにはドットを用いた記法も使えます。 + echo $this->Cookie->read('User.name'); + + // グループ化した変数を取得するには、 + // ドットを用いた記法を配列のように使ってください。 + $this->Cookie->read('User'); + + // 出力は array('name' => 'Larry', 'role'=>'Lead') というようになります。 + +**del(mixed $key)** + +$key +で指定された名前のクッキー変数を削除します。ドットを用いた記法を使えます。 + +:: + + // 変数を削除する + $this->Cookie->del('bar') + + // bar 変数を削除するが、 foo.bar の他の foo 変数は削除しない。 + $this->Cookie->del('foo.bar') + + +**destroy()** + +現在のクッキーを壊します。 diff --git a/ja/The-Manual/Core-Components/Email.rst b/ja/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..e859ffa7667b97eb69ae1812c0a0d9cfd9a3097f --- /dev/null +++ b/ja/The-Manual/Core-Components/Email.rst @@ -0,0 +1,236 @@ +電子メール +########## + +電子メール(\ *Email*)コンポーネントは、シンプルなメール送信機能を +CakePHP アプリケーションに付け加えます。レイアウトとビューにおける ctp +ファイルと同じ考えを使って、テキスト、 HTML +またはその両方を送信できます。 PHP にビルトインされている mail 関数や +SMTP +サーバを使用する送信がサポートされています。また、フラッシュメッセージを使ったデバッグモードも備えています。ファイル添付や、いくつかの基本的なヘッダのチェックやフィルタリングをサポートしています。できないこともいろいろありますが、すぐに利用できます。 + +クラスの属性と変数 +================== + +``EmailComponent::send()`` を呼び出す前にセットする値があります。 + +to + +メッセージを送信するアドレス。文字列。 + +cc + +メッセージを CC で送信する複数のアドレスを配列でセットする。 + +bcc + +メッセージを BCC(\ *blind carbon copy*) +で送信する複数のアドレスを配列でセットする。 + +replyTo + +返信先のアドレス。文字列。 + +from + +送信元のアドレス。文字列。 + +subject + +メッセージの件名。文字列。 + +template + +``app/views/elements/email/html/`` と ``app/views/elements/email/text/`` +に設置する、 Email メッセージの要素。 + +layout + +``app/views/layouts/email/html/`` と ``app/views/layouts/email/text/`` +に設置する、 Email のレイアウト。 + +lineLength + +自動的に改行を入れる長さ。デフォルトは70。整数。 + +sendAs + +どのようにメッセージを送信するかの指定。\ ``text``\ (テキスト)、``html``\ (HTML) +または ``both``\ (両方) のいずれかを指定する。 + +attachments + +添付するファイルを配列でセットする。絶対または相対パスを指定。 + +delivery + +どのようにメッセージを送信するかをセットする。 ``mail``\ 、 +``smtp``\ (これを選ぶには smtpOptions の設定が必須。)、 ``debug`` +のいずれかを指定。 + +smtpOptions + +SMTP メーラーのための設定を連想配列でセットする。 ``port``\ 、 +``host``\ 、 ``timeout``\ 、 ``username``\ 、 ``password`` +をセットする。 + +他にも設定できる項目がありますが、詳しくは API +ドキュメントを参照してください。 + +複数の電子メールをループで送信する +---------------------------------- + +複数の電子メールをループを使って送信するには、電子メールのフィールドをリセットするため、 +Email コンポーネントの reset +メソッドを呼び、プロパティを再度設定しなおす必要があります。 + +:: + + $this->Email->reset() + +基本的なメッセージを送信する +============================ + +テンプレートを使わずメッセージを送信するには、本文を単純に文字列で +send() メソッドに渡してください。次のようになります。 + +:: + + $this->Email->from = 'Somebody '; + $this->Email->to = 'Somebody Else '; + $this->Email->subject = 'Test'; + $this->Email->send('Hello message body!'); + +レイアウトのセットアップ +------------------------ + +テキストと HTML +の両方でメールを送信する場合、それらに対するレイアウトファイルを用意する必要があり、それらのレイアウトを電子メールメッセージのデフォルトに指定しなければなりません。これらのレイアウトファイルは、ブラウザにビューで表示する時にセットアップするものと同じようなものです。最小構成で、 +``app/views/layouts/`` +ディレクトリに次のような構造でセットアップする必要があります。 + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +これらのファイルは、デフォルトのメッセージのためのレイアウトテンプレートです。例は次のようになります。 + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +メッセージ本文用に電子メールの要素をセットアップする +---------------------------------------------------- + +``app/views/elements/email/`` に ``text`` と ``html`` +というフォルダを作成します。どちらか片方だけ送信するという場合でない限り、両方作成します。両方のフォルダで、 +``$this->set()`` または send() メソッドの ``$contents`` +パラメータを参照するテンプレートを作成してください。簡単な例を次に示します。この例において、テンプレート名は +``simple_message.ctp`` とします。 + +``text`` + +:: + + Dear , + Thank you for your interest. + +``html`` + +:: + +

Dear ,
+    Thank you for your interest.

+ +コントローラ +------------ + +このコンポーネントを使うには、コントローラ中で ``$components`` +配列に要素を追記するか、配列そのものを新規作成します。 + +:: + + + +この例では、 $id +で識別したユーザに電子メールメッセージを送信するプライベートなメソッドを作成しています。(この例は +User モデルを使うコントローラ中であることを前提としています。) + +:: + + + User->read(null,$id); + $this->Email->to = $User['User']['email']; + $this->Email->bcc = array('secret@example.com'); + $this->Email->subject = 'Welcome to our really cool thing'; + $this->Email->replyTo = 'support@example.com'; + $this->Email->from = 'Cool Web App '; + $this->Email->template = 'simple_message'; // note no '.ctp' + // 'html'(HTML)、'text'(テキスト)、または'both'(両方)で送信。(デフォルトは 'text')。 + $this->Email->sendAs = 'both'; // 良い感じのメールを送りたいのでこうします。 + // ビュー変数をいつもどおりに渡す。 + $this->set('User', $User); + // send() に変数を渡さないでください。 + $this->Email->send(); + } + ?> + +メッセージが送信できたら、他のメソッドから呼び出すことが出来ます + +:: + + + $this->_sendNewUserMail( $this->User->id ); + +SMTP を使用してメールを送信する +=============================== + +SMTP +サーバを利用して電子メールを送信するには、まず基本的なメッセージの送信と同じようなセットアップを行います。そして送信するメソッドに +``smtp`` をセットし、 Email オブジェクトの ``smtpOptions`` +プロパティに必要な値をセットします。 SMTP +エラーが発生したら、コンポーネントに ``smtpError`` +プロパティが生成されますので、それでエラー内容を確認してください + +:: + + /* SMTP のオプション */ + $this->Email->smtpOptions = array( + 'port'=>'25', + 'timeout'=>'30', + 'host' => 'your.smtp.server', + 'username'=>'your_smtp_username', + 'password'=>'your_smtp_password'); + + /* 送信のメソッドをセットする */ + $this->Email->delivery = 'smtp'; + + /* send() に変数を渡さないでください。 */ + $this->Email->send(); + + /* SMTP エラーを確認する。 */ + $this->set('smtp-errors', $this->Email->smtpError); + +使用する SMTP サーバが認証を必要とするなら、例の中に登場する +``smtpOptions`` の username と password +というパラメータの定義を忘れないようにしてください。 diff --git a/ja/The-Manual/Core-Components/Request-Handling.rst b/ja/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..466ea65e58f171f7e30a3aa76ac27f646b2b8209 --- /dev/null +++ b/ja/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,272 @@ +リクエストハンドリング +###################### + +リクエストハンドラ(\ *Request +Handler*)コンポーネントは、アプリケーションに対して行われる HTTP +リクエストに関してさらなる情報を取得するために使用します。リクエストハンドラを使うことで、ファイルの拡張子が有効になっている時にクライアントが受け付けるコンテンツタイプの情報を得て適切なレイアウトを自動的に選択するだけでなく、 +Ajax による接続であるかをコントローラに伝えることが出来ます。 + +デフォルトでは、 RequestHandler は多くの JavaScript ライブラリが使用する +HTTP-X-Requested-With ヘッダに基づき、 Ajax +によるリクエストであるかどうかを自動的に判定します。 +Router::parseExtension() と一緒に使うと、 RequestHandler +は自動的にリクエストのタイプにマッチしたレイアウトとビューのファイルに切り替えます。 +さらに、リクエストされた拡張子と同じ名前のヘルパーが存在したら、 +RequestHandler はコントローラの helpers 配列にそれを加えます。 最後に、 +XML について説明します。もしコントローラに XML のデータが POST +された場合、それは自動的に解析されモデルに保存できるデータの形式で +Controller::data に割り当てられます。 +リクエストハンドラを使用したい場合は、コントローラの $components +配列にそれを加えてください。 + +:: + + + +リクエストの情報を取得する +========================== + +リクエストハンドラはクライアントとリクエストについての情報を提供する、いくつかのメソッドを持っています。 + +accepts ( $type = null) + +$type には、文字列か配列、または null を割り当てることができます。 +文字列を渡した場合、それで指定したコンテンツタイプをクライアントが受け付ける場合、 +true が返されます。 +配列で定義した場合、それらで指定したコンテンツタイプをクライアントが受け付ける場合、 +true が返されます。null +を指定すると、クライアントが受け付けるコンテンツタイプが配列で返されます。例は次のようになります。 + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // クライアントが HTML(text/html) を受け付ける場合のコードをここに書く + } elseif ($this->RequestHandler->accepts('xml')) { + // クライアントが XML を受け付ける場合のコードをここに書く + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // クライアントが XML 、 RSS 、 Atom を受け付ける場合のコードをここに書く + } + } + } + +別のリクエスト「型」を判定するメソッドは次のようになります。 + +isAjax() + +リクエストに含まれる X-Requested-Header が XMLHttpRequest である場合に +true を返します。 + +isSSL() + +現在のリクエストが SSL 接続により行われていれば true を返します。 + +isXml() + +現在のリクエストが XML のレスポンスを受け付けるなら true を返します。 + +isRss() + +現在のリクエストが RSS のレスポンスを受け付けるなら true を返します。 + +isAtom() + +現在のリクエストが Atom のレスポンスを受け付けるなら true +を返し、そうでないなら false を返します。 + +isMobile() + +ユーザエージェントがモバイルのウェブブラウザにマッチするか、クライアントが +WAP のコンテンツを受け付けるなら、 true +を返します。サポートしているモバイル端末のユーザエージェントは次のものになります。 + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +現在のクライアントが WAP のコンテンツを受け付けるなら true を返します。 + +先にあげたリクエストの型を判定する全てのメソッドは、コンテンツタイプを特定するフィルター機能に似たやり方で使うことが出来ます。例えば、 +Ajax +リクエストに対するレスポンスではブラウザのキャッシュ機能を無効にし、デバッグレベルを変更したいことが多々あるでしょう。ところがこの場合でも、 +Ajax +ではないリクエストに関してはブラウザのキャッシュを有効にしておきたいと考えるかもしれません。 +これを実現するには次のようにします。 The following would accomplish +that: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + // コントローラアクションの続きを書く + +同じ機能を持つ ``Controller::disableCache`` +を使用することでも、キャッシュを無効にすることができます。 + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + // コントローラアクションの続きを書く + +リクエストの型の判定 +==================== + +リクエストハンドラを使うと、行われた HTTP +リクエストの型についての情報を取得することと、それぞれのリクエストの型に応じたレスポンスを行うこともできます。 + +isPost() + +リクエストが POST の場合に true を返します。 + +isPut() + +リクエストが PUT の場合に true を返します。 + +isGet() + +リクエストが GET の場合に true を返します。 + +isDelete() + +リクエストが DELETE の場合に true を返します。 + +クライアントについての追加情報を取得する +======================================== + +getClientIP() + +クライアントの IP アドレスを取得します。 + +getReferrer() + +リクエストが行われたドメイン名を返します。 + +getAjaxVersion() + +Prototype ライブラリは特殊な HTTP ヘッダである「Prototype +version」をセットします。 これを用い、呼び出しが Ajax +によるものであった場合、 prototype.js のバージョンを返します。 Ajax +による呼び出しでない場合、空の文字列を返します。 + +リクエストに対するレスポンス +============================ + +リクエストの型を判定する機能に加え、 RequestHandler +は、出力の種類の変更機能とコンテンツタイプへのマッピング機能をアプリケーションに簡単に追加することもできます。 + +setContent($name, $type = null) + +- $name - + 文字列でコンテンツタイプの名前を割り当てます。この名前とは、すなわち「html」「css」「json」「xml」のことです。 +- $type - 文字列や配列で、コンテンツタイプにマップさせる MIME + タイプを割り当てます。 複数の MIME タイプを割り当てることができます。 + +setContent は与えられた名前でコンテンツタイプをセットまたは追加します。 +コンテンツタイプには、わかりやすい別名または拡張子がマップされます。 +これにより、 RequestHandler +はスタートアップメソッドの中で、リクエストの型に応じたレスポンスを自動的に返すことができます。 +またさらに、これらのコンテンツタイプは prefers() と accepts() +メソッドでも使われます。 + +コンテンツタイプの別名に対する自動的な動作の変更を効果的に行えるよう、 +setContent はコントローラの beforeFilter() で使うと良いでしょう。 + +デフォルトのマッピングは次の通りです。 + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +クライアントが好むコンテンツタイプを確定します。 +もしパラメータをセットしなければ、最も優先度の高いコンテンツタイプが返されます。 +$type +を配列で渡した場合、クライアントが受け付けるものとマッチした最初の値が返されます。 +優先度はまず、もしファイルの拡張子が指定されていたらそれを Router +で解析することにより確定されます。 次に、 HTTP\_ACCEPT +にあるコンテンツタイプのリストから選ばれます。 + +renderAs($controller, $type) + +- $controller - コントローラの参照 +- $type - 表示したいコンテンツタイプの名前。例えば「xml」「rss」など。 + +定義した型でコントローラの出力のモードを変更します。 +また、適切なヘルパーが存在し、それがコントローラ中のヘルパー配列で指定されていなければ、これを追加します。 + +respondAs($type, $options) + +- $type - + 応答したいコンテンツタイプを「xml」「rss」といった名前や、「application/x-shockwave」といったコンテンツタイプの完全な名前で指定します。 +- $options - + 指定したコンテンツタイプが複数のコンテンツに関連付いている場合、どれを使うかを + $index で指定します。 + +コンテンツタイプにマップした名前に基づき、応答するヘッダをセットします。 +DEBUG の値が2より大きい場合、ヘッダはセットされません。 + +responseType() + +現在の応答するコンテンツタイプのヘッダをの型を返します。もしセットされていなければ +null を返します。 + +mapType($ctype) + +コンテンツタイプを別名にマップします。 diff --git a/ja/The-Manual/Core-Components/Security-Component.rst b/ja/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..e0f3d4a19616f9a86056a74601800495c8e117fd --- /dev/null +++ b/ja/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,210 @@ +セキュリティコンポーネント +########################## + +セキュリティ(\ *Security*)コンポーネントを使うと、アプリケーションにさらに堅牢なセキュリティを導入できます。 +セキュリティコンポーネントをコントローラの befireFilter() で使うことで +HTTP 認証リクエストを管理することができます。 +いくつか設定できるパラメータがあり、これはプロパティと同じ名前のセッタを使うことで直接セットできます。 + +設定 +==== + +$blackHoleCallback + 何らかの理由により、処理を停止するハンドルやリクエストに対する、コントローラコールバックです。 +$requirePost + POST + によるリクエストだけで起動させるコントローラのアクションの一覧で定義します。コントローラのアクションを配列で渡すか、全てのアクションを + POST のみで起動させるために「\*」を渡します。 +$requireSecure + SSL + 接続だけで起動させるアクションの一覧を定義します。コントローラのアクションを配列で渡すか、全てのアクションを + SSL 接続のみで起動させるために「\*」を渡します。 +$requireAuth + 有効な認証キーを必要とするアクションの一覧を定義します。この認証キーはセキュリティコンポーネントによりセットされます。 +$requireLogin + HTTP + 認証のログイン(ベーシック認証またはダイジェスト認証)を必要とするアクションの一覧を定義します。「\*」も指定でき、このコントローラの全てのアクションで + HTTP 認証が必要となることを意味します。 +$loginOptions + HTTP + 認証のログインリクエストのオプションを指定します。認証タイプと、認証プロセスにおけるコントローラのコールバックをセットします。 +$loginUsers + HTTP 認証のログインで用いる usernames => passwords + という型の連想配列を指定します。ダイジェスト認証を用いる場合、パスワードは + MD5 のハッシュ値である必要があります。 +$allowedControllers + 現在のコントローラがリクエストを受けることを許可するコントローラ一覧を定義します。これは複数のコントローラ間のリクエストの制御を行う場合に使用します。 +$allowedActions + 現在のコントローラのアクションがリクエストを受け付けるアクション一覧を定義します。これは複数のコントローラ間のリクエストの制御を行う場合に使用します。 +$disabledFields + POST + されたデータをバリデートするときに、無視するフィールドのリストです。値が入っているかどうかにかかわらず、また、フォームの送信が有効であるか否かにかかわらず、その値はバリデートの評価対象になりません。 + +メソッド +======== + +requirePost() +------------- + +POST による呼び出しだけで起動するアクションをセットします。 +複数の引数を渡すことができます。 全てのアクションで POST +が必要なようにするには、引数を何も渡しません。 + +requireSecure() +--------------- + +SSL による通信のみで起動できるアクションをセットします。 +複数の引数を渡すことができます。 全てのアクションで SSL +による通信が必要なようにするには、引数を何も渡しません。 + +requireAuth() +------------- + +起動するためにセキュリティコンポーネントが生成したトークンを必要とするアクションをセットします。 +複数のアクションを引数として渡すことができます。 +全てのアクションで有効な認証が必要なようにするには、引数を何も渡しません。 + +requireLogin() +-------------- + +HTTP 認証を必要とするアクションをセットします。 +複数のアクションを引数として渡すことができます。 +全てのアクションで有効な HTTP +認証が必要なようにするには、引数を何も渡しません。 + +loginCredentials(string $type) +------------------------------ + +HTTP 認証が成功するかどうかを試します。 $type は HTTP +認証のタイプを指定し、「basic」または「digest」のいずれかを使用できます。 +これを空にするか null にすると、両方のタイプで認証を試行します。 +もし認証が成功したら、そのユーザ名とパスワードを配列にしたものが返されます。 + +loginRequest(array $options) +---------------------------- + +$options 配列で指定されたもので HTTP +認証のリクエストのヘッダに含める文字列を生成します。 + +$options には一般的に「type」と「realm」が含まれます。 「type」はどの +HTTP 認証メソッドを使用するかを表します。 「realm」のデフォルトは現在の +HTTP サーバ環境のものになります。 + +parseDigestAuthData(string $digest) +----------------------------------- + +HTTP ダイジェスト認証のリクエストを解析します。 +もし成功したら、ダイジェスト認証のデータを連想配列として返し、失敗したら +null を返します。 + +generateDigestResponseHash(array $data) +--------------------------------------- + +HTTP ダイジェスト認証のレスポンスと比較するためのハッシュを作成します。 +$data には SecurityComponent::parseDigestAuthData() +で生成された配列を渡しましょう。 + +blackHole(object $controller, string $error) +-------------------------------------------- + +されたくないリクエストを 404 +エラーかカスタムしたコールバックで代替します。 +コールバックが何も指定されていない場合、リクエストは直ちに終了します。 +もしコントローラのコールバックが SecurityComponent::blackHoleCallback +にセットされていたら、それが呼び出され全てのエラーの情報が渡されます。 + +使い方 +====== + +セキュリティコンポーネントは、一般的にコントローラの beforeFilter() +で使用します。 +行いたいセキュリティの制限をここで定義すると、セキュリティコンポーネントは起動時にそれらの制限を有効にします。 + +:: + + Security->requirePost('delete'); + } + } + ?> + +この例では、 delete アクションは POST +リクエストを受け取った場合にのみ起動します。 + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +この例では管理で用いるルーティングのアクションを全て、 SSL +による接続のみで許可するようにします。 + +ベーシック HTTP 認証 +==================== + +SecurityComponent は強力な認証機能をいくつか持っています。 +場合によって、アプリケーション中のいくつかの機能を `HTTP +ベーシック認証 `_\ によって保護したいかもしれません。 +HTTP 認証がよく使われるものとしては、 REST や SOAP の API +の保護が挙げられます。 + +このタイプの認証は、ある理由によりベーシックであると言われます。 SSL +を用いない限り、認証のための情報は平文で通信されます。 + +SecurityComponent を HTTP 認証のために使用することは簡単です。 +次のコード例ではセキュリティコンポーネントを組み込み、コントローラの +beforeFilter メソッドに何行か追加しています。 + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MyRealm' + ); + $this->Security->loginUsers = array( + 'john'=>'johnspassword', + 'jane'=>'janespassword' + ); + $this->Security->requireLogin(); + } + + function index() { + // 保護されたアプリケーションロジックが続く + } + } + +SecurityComponent の loginOptions +プロパティでは、ログインがどのようにハンドルされるかを定義するための連想配列を指定します。 +これを行うには、 **type** と **basic** +がペアになったものをただ定義するだけです。 **realm** +を定義すると、ログインしようとするユーザにメッセージを表示したり、一つのアプリケーション中で独立した複数の認証(つまり\ *realm*)を持つことができます。 + +SecurityComponent の loginUsers プロパティはこの realm +に対してアクセスを許可するユーザ名とパスワードを含んだ連想配列を指定します。 +先の例では、ユーザの情報を直接コードに書き込んでいますが、認証のための情報をより管理しやすくするためにはモデルを使用すると良いでしょう。 + +最後に、 requireLogin() について説明します。これは SecurityComponent +にこのコントローラがログインを必要とすることを教えます。 +また、上述の例で requirePost() +にメソッドの名前を指定すると、それらのメソッドは保護し他のメソッドは開放するようになります。 diff --git a/ja/The-Manual/Core-Components/Sessions.rst b/ja/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..3598c7c9514369b6118cc08278bb2b523e5b71ae --- /dev/null +++ b/ja/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,157 @@ +セッション +########## + +CakePHP +のセッション(\ *Session*)コンポーネントは、一つのクライアントが複数のページに遷移する間、ある特定のデータを継続して保持する方法を提供します。このコンポーネントは +$\_SESSION +変数に関連したいくつかの便利なメソッドを持つラッパのように振舞います。 + +セッションを保持するには、いくつかの方法があります。デフォルトは PHP +によって提供される方法で、他にも選択肢が存在します。 + +cake + セッションをファイルとして app の tmp/sessions + ディレクトリに保存します。 +database + セッションを CakePHP のデータベースに保存します。 +cache + Cache::config() + で設定されたキャッシュエンジンを使います。キャッシュしたデータとセッションを格納するために、複数のアプリケーションサーバから使えるようセットアップした + Memcache を利用する場合にとても便利です。 +php + デフォルトではこの設定になっています。php.ini + の設定に従い、セッションファイルを保存します。 + +デフォルトのセッションハンドリングのメソッドを切り替えるには、 +Session.save +の設定を変更してください。もし「database」を選択するのなら、「Session.database」設定のコメントを外し、 +app/config にある SQL ファイルを実行してください。 + +メソッド +======== + +セッションコンポーネントは、セッション情報を相互的にやりとりするために使用します。 +このコンポーネントには、基本的な CRUD +の機能、ユーザに対するフィードバックメッセージの作成機能が含まれます。 + +セッション中に配列の構造のデータを保存したい場合は、ドット区切りの記法を用いることに注意してください。 +たとえば、セッション中で「User.username」となっているデータは、次のようにすることで参照します。 + +:: + + array('User' => + array('username' => 'clarkKent@dailyplanet.com') + ); + +ドットはネストされた配列を示します。 +この記法はセッションコンポーネントのメソッドで $name +と書かれたところ全てで使用されます。 + +write +----- + +``write($name, $value)`` + +セッションの $name で指定されたものに $value の値を書き込みます。 +次の例の通り、 $name にはドット区切りで配列が指定できます。 + +:: + + $this->Session->write('Person.eyeColor', 'Green'); + +この例では、セッションの「Person => +eyeColor」に、「Green」という値を書き込んでいます。 + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +ビューで出力するために使用するセッション変数をセットします。$layout で +``/app/views/layouts`` +にあるものを指定することで、メッセージを表示する時にどのレイアウトを使用するかをコントロールできます。\ ``$layout`` +を「default」のままにしておいた場合、メッセージは次のコードでラップされます。 + +:: + +
[message]
+ +$params +では、表示されるレイアウトに追加のビュー変数を渡すことができます。$key +には Message 配列の $messages +インデックスをセットします。デフォルトは「flash」です。 + +パラメータでは、レンダリングされる div +タグに影響を与えることができます。たとえば、 $params +配列に「class」をキーとして値を与えると、 ``$session->flash()`` +で出力したレイアウトの ``div`` タグの中に class 属性を付与します。 + +:: + + $this->Session->setFlash('メッセージテキストの例', 'default', array('class' => 'example_class')) + +このとき、\ ``$session->flash()`` による出力は、次のようになります。 + +:: + +
メッセージテキストの例
+ +read +---- + +``read($name)`` + +Session の中の $name の値を返します。 $name に null +を渡すと、セッション全体を返します。例は次の通りです。 + +:: + + $green = $this->Session->read('Person.eyeColor'); + +セッションから「Green」が取得されます。 + +check +----- + +``check($name)`` + +セッション中に $name で指定した値が存在するかチェックします。 存在すれば +true を、存在しなければ false を返します。 + +delete +------ + +``delete($name) /* または */ del($name)`` + +$name +で指定したセッションのデータをクリアします。次の例を参照してください。 + +:: + + $this->Session->del('Person.eyeColor'); + +これでセッションデータの「eyeColor」というキーにはもう「Green」という値が存在しません。 +しかし、「Person」というキーはまだセッション中に存在します。 +「Person」の情報をセッションから削除するには、次のようにしてください。 + +:: + + $this->Session->del('Person'); + +destroy +------- + +``destroy`` +メソッドは、まずセッションクッキーと一時ファイルシステムに保存されていた全てのセッションデータを破棄します。 +次に、 PHP セッションを破棄し、そして新しいセッションを開始します。 + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +あるセッションで最後に発生したエラーを特定します。 diff --git a/ja/The-Manual/Core-Console-Applications.rst b/ja/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..0c1d864977420b9cf28ef40679c689e07aded03c --- /dev/null +++ b/ja/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,22 @@ +コアコンソールアプリケーション +############################## + +CakePHP +はコンソールアプリケーションがいくつかあります。これらのアプリケーションの中には他の +CakePHP (ACL や i18n +のように)と協調して使用されます。それ以外は一般的な使い方で、より素早く実行できます。 + +この章では、CakePHP +でパッケージ化されたコアコンソールアプリケーションの使用方法を説明します。 + +ここに入る前に、以前に記述した `CakePHP +コンソールセクション `_ +をチェックしてください。コンソールの構築についてはここでは扱いません。そのためこれまでにコンソールを使用したことがない場合は、チェックしてください。 + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/ja/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/ja/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..da3e809324746e83ee58d5a01a176a12447469a9 --- /dev/null +++ b/ja/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,62 @@ +Bake でコード生成する +##################### + +ここまでで、CakePHP +におけるスキャフォールドを学んできました。スキャフォールドは、データベースといくつかの簡単なクラスを用意するだけで、アプリケーションを起動し実行できる簡単な方法です。CakePHP +の Bake コンソールは、 CakePHP +をすばやく起動し実行するための、もう1つの方法です。Bake コンソールは、 +CakePHP +の基本要素である、モデル・ビュー・コントローラを生成できます。Bake +は、単なるスケルトンクラスではなく、充分な機能をもったアプリケーションを数分で生成します。実のところ、アプリケーションをスキャフォールドで組んだ後に +Bake を使用することは、一般的なステップです。 + +Bake を使用するには、/cake/console/ フォルダにある cake +スクリプトを実行します。 + +:: + + $ cd ./cake/console/ + $ cake bake + +起動時の設定次第ですが、bash +スクリプト「cake」に実行権限を付与したり、./cake bake +を使用して呼び出したりしなければならないかもしれません。cake +コンソールは、PHP +CLI(コマンドラインインターフェイス)を使用して実行します。スクリプトの実行に問題がある場合、PHP +CLI がインストールされていて、適切なモジュール(たとえば +MySQL)が有効になっているかを確認してください。 + +はじめて Bake +を実行する際に、データベース設定をまだ作成していない場合は、このファイルを作成するプロンプトが表示されます。 + +データベース設定ファイルを作成した後に Bake +を実行すると、次のようなオプションが表示されます: + +:: + + --------------------------------------------------------------- + App : app + Path: /path-to/project/app + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake?(D/M/V/C/P/Q) + > + +オプションで選択する代わりに、コマンドラインから直接これらのコマンドを選択して実行できます: + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake project + $ cake bake test + diff --git a/ja/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/ja/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..0d954fa4e0b1cb23bf14b7b2917492a390a2fa61 --- /dev/null +++ b/ja/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,12 @@ +Bake で焼かれるデフォルトの HTML を変更する +########################################### + +"bake" コマンドによって出力されるデフォルトの HTML +は、次のように簡単なステップで変更することができます。 + +#. cake/console/libs/templates/views を開く。 +#. そこに4つのファイルがあることを確認する。 +#. それらのファイルを、app/vendors/shells/templates/views にコピーする。 +#. "bake" が構築するビューを制御するために、出力される HTML + を変更します。 + diff --git a/ja/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/ja/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..15bf4552d4b15e9de1abfed3781c40b574ed66db --- /dev/null +++ b/ja/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,78 @@ +スキーマ管理とマイグレーション +############################## + +SchemaShell +は、スナップショットを生成ししたりデータベースのスナップショットをリストアしたりするだけでなく、スキーマオブジェクト・スキーマ +SQL ダンプを生成する機能を提供します。 + +スキーマファイルを生成して使用する +================================== + +生成されたスキーマファイルを使用すると、データベースに依存しないスキーマを簡単に移動できます。使用しているデータベースのスキーマファイルは、次のように生成します: + +:: + + $ cake schema generate + +これを実行すると、 ``app/config/sql`` ディレクトリに schema.php +ファイルが生成されます。 + +スキーマシェルはモデルが定義されているテーブルのみ処理します。スキーマシェルにすべてのテーブルを処理させるには、コマンドラインで +``-f`` オプションを指定する必要があります。 + +後から、すでに作成した schema.php +ファイルを用いてデータベーススキーマを再構築するには、次のようにします: + +:: + + $ cake schema run create + +これは schema.php の内容に従ってテーブルを削除し、作成します。 + +スキーマファイルは SQL +ダンプファイルを生成するために使用することもできます。\ ``CREATE TABLE`` +ステートメントを含む SQL ファイルを生成するには、次のようにします: + +:: + + $ cake schema dump filename.sql + +filename.sql は SQL ダンプ用に設計されたファイル名です。filename.sql +を省略した場合、SQL +ダンプはコンソールに出力されますが、ファイルには書き出されません。 + +CakePHP のスキーマシェルを使用したマイグレーション +================================================== + +マイグレーションはデータベーススキーマのバージョン管理をします。機能を開発した場合、データベースの変更を適用するのが簡単でデータベースに依存しない方法でできます。マイグレーションは、SCM +管理スキーマファイルかスキーマスナップショットのどちらかを通して実行されます。スキーマシェルを使用してスキーマファイルをバージョン管理するのはかなり簡単です。すでにスキーマファイルがある場合は、次を実行します。 + +:: + + $ cake schema generate + +次のような選択肢が表示されます: + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +s] (snapshot) を選択すると、バージョンアップ用の schema.php +が生成されます。schema.php がある場合は、schema\_2.php +などが生成されます。いつでも次のように実行することでこれらのスキーマファイルをリストアすることができます: + +:: + + $ cake schema run update -s 2 + +2 +は実行したいスナップショットの番号です。スキーマシェルはプロンプトを表示し、 +``ALTER`` +ステートメントを実行するか確認します。その際に既存のデータベースと実行するスキーマファイルの違いを表示します。 + +コマンドに ``-dry`` を指定することで、dyr run 実行することができます。 diff --git a/ja/The-Manual/Core-Helpers.rst b/ja/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..2212828afc11f24032f44c085ede4b9307b4aaea --- /dev/null +++ b/ja/The-Manual/Core-Helpers.rst @@ -0,0 +1,28 @@ +主要なヘルパー +############## + +ヘルパーはアプリケーションのプレゼンテーション層のための、コンポーネントに似たクラスです。これには、たくさんの +views や elements 、あるいは layouts +で共有するプレゼンテーションロジックが含まれています。 + +この章では、CakePHP に含まれている Form 、 Html 、 Javascript そして RSS +といったヘルパーについて説明します。 + +ヘルパーについての詳細と、独自のヘルパーの作成を知りたい場合は、\ `ヘルパー `_\ の章を読んでください。 + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/ja/The-Manual/Core-Helpers/AJAX.rst b/ja/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..350597af923314a5d083db1984483ef21bc3ed6b --- /dev/null +++ b/ja/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,710 @@ +AJAX +#### + +Ajax ヘルパーは、Ajax +操作やクライアントサイドのエフェクト(効果)用の人気のある Prototype や +script.aculo.us ライブラリのユーティリティです。Ajax +ヘルパーを使用するには、Javascript ライブラリの最新バージョンを +`www.prototypejs.org `_ や +`http://script.aculo.us `_ +から入手し、/app/webroot/js/ に配置しなければなりません。さらに、Ajax +ヘルパー機能を必要とするレイアウトやビュー内で、 Prototype や +script.aculo.us JavaScript ライブラリを読み込む必要があります。 + +コントローラ内で Ajax や Javascript ヘルパーを読み込む必要があります: + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +javascript ヘルパーをコントローラ内で読み込むと、Prototype や +Scriptaculous を読み込むために javascript ヘルパーの link() +メソッドを使用できます: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +これでビューで Ajax ヘルパーを使用できるようになります: + +:: + + $ajax->whatever(); + +`RequestHandler コンポーネント `_ +がコントローラに含まれている場合、CakePHP はアクションが Ajax +を通してリクエストされた際に自動的に Ajax レイアウトを適用します。 + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +AjaxHelper のオプション +======================= + +AjaxHelper のメソッドのほとんどは、$options +配列で指定できます。この配列を使用して、AjaxHelper +の振る舞いを指定することができます。ヘルパーの特定のメソッドを見る前に、この特別な配列を通して設定可能な異なるオプションを見てみましょう。後に +AjaxHelper のメソッドを使い始めると、この章を参照したくなることでしょう + +General Options +--------------- + +``$option`` のキー + +説明 + +``$options['evalScripts']`` + +戻り値として得られるコンテンツの中にある script +タグを評価するかどうかを決定します。デフォルトは *true* です。 + +``$options['frequency']`` + +インターバルベースのチェックを実行する秒数。 + +``$options['indicator']`` + +リクエストがロード中に表示され、完了したら非表示になる要素の DOM ID 。 + +``$options['position']`` + +置き換えではなく挿入するために、このオプションを使用します。挿入場所を +*top*, *bottom*, *after*, *before* で指定します。 + +``$options['update']`` + +返された内容で更新される DOM 要素の id 。 + +``$options['url']`` + +呼び出したい「コントローラ/アクション」形式の URL 。 + +``$options['type']`` + +リクエストが 'synchronous'(同期) かあるいは 'asynchronous'(非同期, +デフォルト) かを示します。 + +``$options['with']`` + +GET メソッドの URL に追加する、あるいは、その他のメソッドで送信する body +に追加する、 URL エンコードされた文字列。たとえば、 ``x=1&foo=bar&y=2`` +というようにします。パラメータは、フォーマットに依存し、\ ``$this->params['form']`` +あるいは ``$this->data`` でアクセスできます。詳細は `Prototype の +Serialize `_ +メソッドを参照してください。 + +コールバックオプション +---------------------- + +コールバックオプションを使用すると、リクエストプロセスの特定のポイントで +Javascript 関数を呼び出すことができます。AjaxHelper +操作の、前・後・実行中にちょっとしたロジックを挿入する方法を探しているなら、このコールバックを使用してください。 + +$options キー + +詳細 + +$options['condition'] + +リクエストが初期化される前に *true* をセットする必要がある JavaScript +コードスニペット + +$options['before'] + +拡張した before +リクエストが作成されます。このコールバックの通常の使用方法は、プログレスバーの表示を有効にすることです。 + +$options['confirm'] + +実行前に JavaScript の confirm メッセージに表示するテキスト + +$options['loading'] + +データがサーバから取り出されている間、実行されるコールバックコード + +$options['after'] + +リクエストが実行された後すぐに呼び出される +JavaScript。$options['loading'] +コールバックが実行される前に呼び出されます。 + +$options['loaded'] + +クライアントがリモートドキュメントが受け取った時に実行されるコールバックコード。 + +$options['interactive'] + +ユーザが、たとえ読み込みが完了していないときでもリモートドキュメントに作用する場合に呼び出されます。 + +$options['complete'] + +XMLHttpRequest が完了した際に実行される Javascript コールバック + +メソッド +======== + +link +---- + +``link(string $title, string $href, array $options, string $confirm, boolean $escapeTitle)`` + +``リンクをクリックしたときに、XMLHttpRequest を使用してバックグラウンドで呼び出される $options['url']`` +または ``$href`` +で定義されたリモートアクションへのリンクを返します。リクエストの結果は +DOM オブジェクトへ挿入され、その id は ``$options['update']`` +で指定します。 + +``$options['url']`` が空白の場合、href が代わりに使用されます。 + +サンプル: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +デフォルトでは、これらのリモートリクエストは、さまざまなコールバックが呼び出される間、非同期で実行されます。 + +サンプル: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Hello World" )' ) + ); + ?> + +同期実行を使用するには、\ ``$options['type'] = 'synchronous'`` +を指定します。 + +自動的に ajax レイアウトをセットするには、コントローラ内で +*RequestHandler* コンポーネントを読み込みます。 + +デフォルトでは、対象要素の内容は置換されます。この振る舞いを変更するには、\ ``$options['position']`` +を設定します。 + +サンプル: + +:: + +
+
+ link( + 'View Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +``$confirm`` はリクエストを実行する前に、Javascript confirm() +メッセージを呼び出します。ユーザは実行を停止できます。 + +サンプル: + +:: + +
+
+ link( + 'Delete Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Do you want to delete this post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +この関数は JavaScript +を生成しますが、それはリモートコールを作成する必要があります。link() +のヘルパーとして主に使用されます。これは、なんらかの独自スクリプトを生成する必要がない限りあまり使用されません。 + +この関数の ``$options`` は ``link`` メソッドと同じです + +サンプル: + +:: + +
+
+ + +HTML イベント属性に割り当てることもできます: + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Mouse Over This +
+ +``$options['update']`` +が渡されない場合、ブラウザはサーバのレスポンスを無視するでしょう。 + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +定期的に ``$options['frequency']`` 秒おきに ``$options['url']`` +のアクションを呼び出します。ふつう特定の div(\ ``$options['update']`` +で指定されたもの) をリモートコールの結果で更新します。 + +``remoteTimer`` は特別な ``$options['frequency']`` 以外は +``remoteMethod`` と同じです。 + +サンプル: + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post', 'complete' => 'alert( "request completed" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +デフォルトの ``$options['frequency']`` は 10 秒です。 + +form +---- + +``form(string $params, string $type, array $options)`` + +$type ('post' または 'get') を通した通常の HTTP リクエストの代わりに +XMLHttpRequest を使用してアクションに submit する form +タグを返します。もしそうでない場合は、通常の振る舞いと変わるところはまったくありません。 +submit されたデータはコントローラ内の $this->data +で利用できます。$options['update'] +が指定された場合、結果ドキュメントで更新されます。コールバックも使用できます。 + +options +配列はモデル名を含まなければいけません。例えば次のようになります。 + +:: + + $ajax->form('edit','post',array('model'=>'User','update'=>'UserInfoDiv')); + +他の方法として、フォームから、異なるコントローラへ同時にデータを post +する場合は、次のようにします。 + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'User', + 'update'=>'UserInfoDiv', + 'url' => array( + 'controller' => 'comments', + 'action' => 'edit' + ) + ) + )); + +submit +------ + +``submit(string $title, array $options)`` + +submit ボタンを返します。このボタンは XMLHttpRequest を通して +$options['with'] で指定された DOM id をもつフォームを submit します。 + +observeField +------------ + +``observeField(string $fieldId, array $options)`` + +$field\_id で指定された DOM id を持つフィールドを ($options['frequency'] +秒おきに) 監視し、その内容が変更されたときに XMLHttpRequest +を作成します。 + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'title', array( 'options' => $titles ) ) ?> + + + observeField( 'PostTitle', + array( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +``observeField`` では ``link`` と同じオプションを使います。 + +送信するフィールドの指定は、 ``$options['with']`` +使用します。\ ``Form.Element.serialize('$fieldId')`` +のデフォルト値となります。送信したデータは、コントローラ中の +``$this->data`` +で利用可能になります。この関数では、コールバックが利用できます。 + +フィールドが変更された時に、フォーム全体を送信するには、 +``$options['with'] = Form.serialize( $('Form ID') ) を使用してください。`` + +observeForm +----------- + +``observeForm(string $fieldId, array $options)`` + +observeField() と同様ですが、DOM id の $form\_id +で指定された全フォームに作用します。指定される $options は、 +$options['with'] +オプションのデフォルト値がフォームのシリアライズされた(リクエストストリング)値を評価することを除いては、 +observeField() と同じです。 + +autoComplete +------------ + +``autoComplete(string $fieldId, string $url, array $options)`` + +オートコンプリートが有効で $fieldId +をもつテキストフィールドを描画します。$url +のリモートアクションは適切なオートコンプリートの用語リストを返すべきです。よく順序づけされていないリストをこのために使用します。まず、コントローラアクションを構築し、ユーザの入力に基づいたリストに必要なデータを取り出し形成します: + +:: + + function autoComplete() { + // 部分文字列は $this->data['Post']['subject'] として + // オートコンプリートフィールドで構成されるでしょう。 + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.subject LIKE' => $this->data['Post']['subject'].'%' + ), + 'fields' => array('subject') + ))); + $this->layout = 'ajax'; + } + +つぎに、\ ``app/views/posts/auto_complete.ctp`` +を作成し、そのデータを使用して (X)HTML +に順序づけされないリストを作成します: + +:: + +
    + +
  • + +
+ +最後にビューで autoComplete() +を使用し、オートコンプリートが有効なフォームフィールドを作成します: + +:: + + create('User', array('url' => '/users/index')); ?> + autoComplete('Post.subject', '/posts/autoComplete')?> + end('View Post')?> + +autoComplete() 呼び出しが正常に動作すると、CSS +を使用してオートコンプリートが有効な選択ボックスをデザインします。結局次のようになります: + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +isAjax +------ + +``isAjax()`` + +現在のリクエストがビュー内でPrototype Ajax +リクエストかどうかをチェックします。ブール値を返します。コンテンツブロックを表示したり隠したりするプレゼンテーションロジックで使用できます。 + +drag & drop +----------- + +``drag(string $id, array $options)`` + +$id で指定された DOM 要素からドラッグ可能な要素を作成します。$options +で指定できるパラメータの詳細は +`http://github.com/madrobby/scriptaculous/wikis/draggable `_ +を見てください。 + +一般的なオプションは次です: + ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options キー | 内容 | ++==========================+==================================================================================================================================================================================================================================================================================+ +| $options['handle'] | 要素が埋め込まれたハンドルによってのみドラッグ可能にするかどうかを指定します。値は、要素のリファレンス、あるいは要素の id 、あるいは CSS クラス値を参照する文字列でなければなりません。この CSS クラス値をもつ要素内にある最初の子または孫要素は、ハンドルとして使用されます。 | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | true を指定すると、要素はドラッグが終了したときに自身の元の位置を返します。revert はドラッグ終了時に呼び出される任意の関数の参照にすることもできます。 | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | 強制的に '水平(horizontal)' または '垂直(vertical)' にドラッグさせます。空白の場合は強制されません。 | ++--------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +$id で指定された DOM 要素を作成し、ドロップ可能な要素にします。$options +でパラメータを指定できます。詳細は +`http://github.com/madrobby/scriptaculous/wikis/droppables `_ +を見てください。 + +一般的なオプションは次です: + ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options キー | 内容 | ++===========================+========================================================================================================================================================================+ +| $options['accept'] | ドロップ可能にする要素の CSS クラスを記述する文字列、あるいは javascript の文字列配列を指定します。ドロップ要素は指定された CSS クラスの要素のみ操作を受け入れます。 | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | 与えられた要素(要素の id)に含まれる場合、ドロップ可能要素はドラッグされた要素のみを受け入れます。文字列、あるいは id 参照の javascript 配列で指定できます。 | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | 'horizontal' あるいは 'vertical' を設定すると、指定された軸の 50% 以上ドロップ場所に重なっている場合に、ドロップ可能要素はドラッグ可能要素にのみ反応します。 | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | ドラッグされた要素がドロップ可能な要素にドロップされたときに、呼び出される javascript のコールバック。 | ++---------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +ドロップ対象を作成します。ドラッグ可能な要素がそこにドロップされたときに、XMLHttpRequest +を生成します。この関数の $options 配列は drop() や link() +で指定されるものと同じです。 + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +方向スライダーコントロールを作成します。詳細は、 +`http://wiki.script.aculo.us/scriptaculous/show/Slider `_ +をみてください。 + +一般的なオプションは次です: + +$options キー + +内容 + +$options['axis'] + +方向スライダーの方向('horizontal' または +'vertical')を指定します。デフォルトは horizontal です。 + +$options['handleImage'] + +ハンドルを表すイメージの +id。これはスライダーが有効な場合、無効なイメージでイメージをスワップアウトするために使用されます。handleDisabled +と合わせて使用します。 + +$options['increment'] + +ピクセルの関係を値に設定します。1 を指定すると、ピクセルごとに 1 +ずつスライダーの値を調整します。 + +$options['handleDisabled'] + +無効なハンドルを表すイメージの +id。これはスライダーが無効な場合、イメージを変更するために使用されます。handleImage +と合わせて使用します。 + +$options['change'] + $options['onChange'] + +スライダーの動作終了時、またはその値が変わったときに呼び出される +javascript +のコールバック。コールバック関数はパラメータとしてスライダーの現在の値を受け取ります。 + +$options['slide'] + $options['onSlide'] + +スライダーがドラッグによって動くときに常に呼び出される javascript +のコールバック。パラメータとしてスライダー現在の値を受け取ります。 + +editor +------ + +``editor(string $id, string $url, array $options)`` + +指定した DOM ID に in-place(その場で編集する) エディタを作成します。 +``$url`` +には要素のデータを保存する役目を担うアクションを指定します。詳細とデモは、 +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_ +を参照してください。 + +一般的なオプションは次の通りです: + +$options keys + +説明 + +``$options['collection']`` + +in-place エディタの 'collection' +モードを起動します。$options['collection'] は select +のオプションに変換する配列を受け取ります。collection についての詳細は、 +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_ +を参照してください。 + +``$options['callback']`` + +リクエストがサーバに送信される前に実行する関数を指定します。これは、サーバに送信するデータをフォーマットするために使用できます。 +``function(form, value)`` というように特徴的な書き方をします。 + +``$options['okText']`` + +編集モードの submit ボタンに表示するテキスト。 + +``$options['cancelText']`` + +編集をキャンセルするリンクに表示するテキスト。 + +``$options['savingText']`` + +テキストがサーバに送信された時に表示されるテキスト。 + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +入力フィールドの行方向の高さ。 + +``$options['cols']`` + +テキストエリアがかかる列の数。 + +``$options['size']`` + +単一の行を使用する場合における、「cols」と同じ意味のもの。 + +``$options['highlightcolor']`` + +ハイライトの色。 + +``$options['highlightendcolor']`` + +ハイライトが消えていく部分の色。 + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +例 + +:: + +
テキストの編集
+ editor( + "in_place_editor_id", + array( + 'controller' => 'Posts', + 'action' => 'update_title', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +$id +に含まれるリスト、あるいはフロートオブジェクトのグループをソート可能にします。オプション配列はいくつかのパラメータをサポートしています。sortable +についての詳細は、 +`http://wiki.github.com/madrobby/scriptaculous/sortable `_ +を参照してください。 + +一般的なオプションは次の通りです: + +$options keys + +説明 + +$options['tag'] + +コンテナのどの子要素をソート可能にするかを示します。デフォルトは 'li' +です。 + +$options['only'] + +子要素のフィルタリングをします。CSS クラスが指定可能です。 + +$options['overlap'] + +'vertical'(垂直方向) あるいは 'horizontal'(水平方向) +のいずれかを指定します。デフォルトは vertical です。 + +$options['constraint'] + +ドラッグ可能要素の動作を制限します。'horizontal'(水平方向) あるいは +'vertical'(垂直方向) が指定可能です。デフォルトは vertical です。 + +$options['handle'] + +作成した Draggables にハンドルを使用します。Draggables +のハンドルオプションを参照してください。 + +$options['onUpdate'] + +ドラッグが終了し、Sortable の順序が変わったときに呼び出されます。ある +Sortable から別のものへドラッグした場合、それぞれの Sortable +で一度コールバックが呼び出されます。 + +$options['hoverclass'] + +作成した droppable に hoverclass を付与します。 + +$option['ghosting'] + +true にすると、sortable +のドラッグした要素は複製され、元の要素を直接操作する代わりにゴーストとして出現します。 diff --git a/ja/The-Manual/Core-Helpers/Cache.rst b/ja/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..8cb6e15eebc5db1d2240ca1f27a41331905ec6c8 --- /dev/null +++ b/ja/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,144 @@ +キャッシュ +########## + +キャッシュヘルパーはレイアウトやビューをキャッシュするのを助けてくれ、繰り返しデータを取得する際に時間を節約できます。Cake +のビューキャッシングは一時的に解析したレイアウトやビューを選択したストレージエンジンに保存します。キャッシュヘルパーは他のヘルパーとはかなり異なった動作をすることに注意してください。直接呼び出せるメソッドはありません。代わりにビューはキャッシュタグでマーキングされ、コンテンツのブロックがキャッシュされていることを示します。 + +URL がリクエストされると、Cake +はそのリクエストされた文字列がすでにキャッシュされているかどうかを確認します。もしキャッシュされてれば、ディスパッチプロセスの +URL +の残りをスキップします。キャッシュされていないブロックは通常通り処理され、ビューは動作します。このおかげでキャッシュされた +URL +への各リクエストは最小限のコードだけが実行されるので実行時間を大きく節約できます。Cake +がキャッシュされたビューを見つけられない場合、あるいはキャッシュがリクエストされた +URL の期限を過ぎている場合、通常通りリクエストを処理し続けます。 + +一般的なキャッシュ +================== + +キャッシュはサーバの負荷を減らすために一時的に保存するということを意図しています。たとえば、時間のかかるデータベースクエリの結果を保存し、ページを読み込む毎に実行する必要はないとします。 + +覚えておいてほしいのは、キャッシュは永続的なストレージではないということです。決して永続的な保存に使用しないでください。必要なときに再再生できるものだけをキャッシュします。 + +Cake のキャッシュエンジン +========================= + +1.2 +では新しくいくつかのキャッシュエンジンやキャッシュバックエンドがあります。キャッシュヘルパーと透過的なこれらのインターフェイスを使用すると、メディアについて特に心配せずにビューキャッシュを保存することができます。キャッシュエンジンは +app/config/core.php +設定ファイルを通して選択します。各キャッシュエンジン用のオプションは、core.php +設定ファイル内にリストされています。各キャッシュエンジンの詳細はキャッシングの章にあります。 + +File + +ファイルエンジンは、cake +で使用されるデフォルトのキャッシュングエンジンです。ファイルシステムの通常ファイルに書き込みます。いくつかのオプションパラメータがありますが、デフォルトでも十分動作します。 + +APC + +APC エンジンは `Alternative PHP Cache `_ +オペコードキャッシュの実装です。XCache +と同様にこのエンジンはコンパイルされた PHP +オペコードをキャッシュします。 + +XCache + +XCache キャッシングエンジンは、\ `XCache `_ +オペコードキャッシングエンジンを実装している以外は機能的に APC +と同じです。適切に動作するにはユーザとパスワードが必要です。 + +Memcache + +Memcache +エンジンはメモリキャッシュングサーバとして動作し、システムメモリ内にキャッシュオブジェクトを生成します。キャッシュングについての詳細は +`php.net `_ や +`memcached `_ にあります。 + +キャッシュヘルパーの設定 +======================== + +ビューキャッシングやキャッシュヘルパーはいくつかの重要な設定要素があります。それらを以下に説明します。 + +ビューやコントローラでキャッシュヘルパーを使用するには、まず +``core.php`` の 80 行目の Configure::Cache.check を true +に設定しなければなりません。これが true +でない場合、キャッシュはチェックされず作成もされません。 + +コントローラでキャッシュする +============================ + +キャッシュ機能を使用するコントローラは $helpers 配列で CacheHelper +を読み込む必要があります。 + +:: + + var $helpers = array('Cache'); + +アクションがキャッシュを必要としていることやどのくらいの間各アクションがキャッシュされるかを示す必要があります。コントローラの +$cacheAction 変数で指定します。$cacheAction +は配列を設定し、キャッシュさせるアクションやビューにキャッシュさせたい期間を秒単位で指定します。時間の値は +strtotime() 形式で表現できます。(たとえば、"1 hour" や "3 minutes") + +ArticlesController +の例を使用します。多くのトラフィックを受けるのでキャシュが必要です。 + +キャッシュは異なる時間間隔で Articles を見ます。 + +:: + + var $cacheAction = array( + 'view/23/' => 21600, + 'view/48/' => 36000, + 'view/52' => 48000 + ); + +このような articles の広い範囲の場合、全アクションをキャッシュします。 + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +コントローラに渡るキャッシュ時間を表示するために strtotime() +形式の時間を使用してコントローラのアクション毎にキャッシュします。 + +:: + + var $cacheAction = "1 hour"; + +ビューでキャッシュされない内容をマークする +========================================== + +場合によっては、\ *完全に*\ ビューをキャッシュしたくないことがあります。たとえば、ユーザがログインしていようがゲストとしてサイトを閲覧していようが、ページの一部を確実に異なる表示にしたい場合です。 + +キャッシュ *されない* +コンテンツのブロックを表示するためには、その部分を次のように +`` `` で囲みます: + +:: + + + check('User.name')) : ?> + Welcome, read('User.name')?>. + + link('Login', 'users/login')?> + + + +その他のページのキャッシングのポイントとして、一度アクションがキャッシュされると、そのアクションのコントローラは呼び出されない、ということに注意してください。したがって、コントローラで +*null* に設定される変数を、\ `` `` +の中に入れることはできません。 + +キャッシュをクリアする +====================== + +キャッシュされたビューで使用されたモデルが変更されると、Cake +はキャッシュされたビューをクリアすることを覚えておくのは重要です。たとえば、キャッシュされたビューが +Post モデルから取得したデータを使用していて、Post で INSERT, UPDATE, +DELETE +クエリーが生成されると、そのビューのキャッシュはクリアされ新しいコンテンツが次のリクエストの際に生成されます。 + +手動でキャッシュをクリアする必要がある場合、Cache::clear() +を呼び出すことでできます。これはビューでないデータを含むキャッシュされたデータ +**すべて** をクリアします。 diff --git a/ja/The-Manual/Core-Helpers/Form.rst b/ja/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..123e748a34908a2925f08c6222d6f6768606c188 --- /dev/null +++ b/ja/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,955 @@ +フォーム +######## + +CakePHPにFormHelperが新たに追加されました。複雑なフォームの組み立てのほとんどをこの新しいクラスで行えるようになりました。しばらくの間は(非推奨ですが)HtmlHelperにもメソッドがあります。FormHelperはフォームを素早く作成する事に焦点を置き, +効率的な検証、記述の削減やレイアウトの為の方法になります。またFormHelperは柔軟でもあります。 +フォームの全てを魔法のように自動的に組み立てる事も出来る一方で、特定のメソッドだけを必要な時に利用する事もできます。 + +フォームの作成 +============== + +FormHelperを活用する為にまず利用するメソッドは ``create()`` +です。この特別なメソッドはフォームタグの開始タグを出力します。 + +``create(string $model = null, array $options = array())`` + +全てのパラメータはオプションです。\ ``create()`` +が、何のパラメータも与えられずコールされた場合、現在のコントローラーの +``add()`` もしくは ``edit()`` +アクションに対するフォームを作成しようとしていると想定します。フォームのメソッドのデフォルトは +POST です。フォームの構成要素は DOM ID 付きで返されます。ID は CamelCase +化されたコントローラーのアクション名とモデル名から生成されます。\ ``create()`` +を UsersControllerのビューの中で呼んだ場合、下記の様に出力されます。 + +:: + +
+ +しかしながら、\ ``create()`` +メソッドはパラメータを使ってさまざまなカスタマイズを行う事ができます。まず、モデル名を指定する事が出来ます。フォームに対してモデル名を指定した場合、フォームの +*context* +を指定した事になります。これにより全てのフィールドは(別に指定されない限り)このモデルに対する項目と想定され、また関連づけられた全てのモデルを参照します。特にモデルを指定しなかった場合は現在のコントローラーのデフォルトのモデルを使用すると想定します。 + +:: + + create('Recipe'); ?> + + //出力: +
+ +このフォームは RecipesController の ``add()`` アクションに対してデータを +POST します。さらに編集用のフォームとしても利用できます。FormHelper は +``$this->data`` +プロパティを利用して自動的に追加もしくは編集用のフォームを生成します。\ ``$this->data`` +はモデル名をキーとしたフォームのモデルに対するデータを保持しています。そしてモデルのプライマリキーに対して空白でない値がある場合、 +FormHelper は該当のレコードに対する編集フォームを生成します。例えば +http://site.com/recipes/edit/5 +にアクセスした場合は下記のようになります。 + +:: + + // controllers/recipes_controller.php: + data)) { + $this->data = $this->Recipe->findById($id); + } else { + // Save logic goes here + } + } + ?> + + // views/recipes/edit.ctp: + + // Since $this->data['Recipe']['id'] = 5, we should get an edit form + create('Recipe'); ?> + + //Output: +
+ + +これは編集フォームである為、デフォルトの HTTP メソッドを上書きする為の +hidden フィールドが生成されます。 + +``$options`` +はほとんどの場合の設定に利用できる配列です。この配列にはフォームタグを作成する際に指定したいオプションのキーと値のペアを必要なだけ指定できます。 + +$options[‘type’] +---------------- + +このキーは作成するフォームの種類を指定する為に使います。指定する値は「post」「get」「file」「put」「delete」です。 + +「post」「get」のいずれかを与えた場合はフォームのメソッドもこれに従います。 + +:: + + create('User', array('type' => 'get')); ?> + + //出力: + + +「file」を指定した場合は作成するフォームのメソッドは「post」になり、さらにenctypeに「multipart/form-data」が追加されます。フォームの中にfile要素が存在していない場合でも同様です。enctype属性が適切に指定されていない場合はファイルアップロード機能が正しく機能しなくなります。 + +:: + + create('User', array('type' => 'file')); ?> + + //出力: + + +「put」「delete」が指定された時はあなたのフォームは「post」のフォームと同等に機能します。 +しかし送信された後、HTTPリクエストのメソッドはそれぞれ「PUT」「DELETE」に上書きされます。これによりCakePHPは正式なRESTをWEBブラウザでエミュレートする事ができます。 + +$options[‘action’] +------------------ + +actionキーはあなたにフォームのactionに対して、現在のコントローラーのいずれかのアクションを指定する事が出来ます。たとえばあなたがフォームのあて先に現在のコントローラーのlogin()アクションを指定したい場合、あなたが指定する$options配列は下記のようになります。 + +:: + + create('User', array('action' => 'login')); ?> + + //出力: + +
+ +$options[‘url’] +--------------- + +あなたが望むフォームのアクションが現在のコントローラー内に無い場合、$options配列の「url」キーを利用してURLを指定する事が出来ます。指定するURLはCakePHPのアプリケーションと連動させる事ができます。また外部のドメインのURLを指定する事も出来ます。 + +:: + + create(null, array('url' => '/recipes/add')); ?> + // あるいは + create(null, array('url' => array('controller' => 'recipes', 'action' => 'add'))); ?> + + + //出力: +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + //出力: + + +$options[‘default’] +------------------- + +‘default’がbooleanのfalseにセットされた場合、フォームの送信ボタンが押された際にフォームを送信しなくなります。フォームをAJAXを利用して送信する場合は'default'にfalseをセットする事で、フォームの本来の振る舞いを抑制してデータと送信をAJAXが替わりに行う事が出来ます。 + +フォームの終了 +============== + +FormHelperにはフォームの記述を完了させる end() +メソッドも含まれています。多くの場合、end() +はフォームタグの終了タグを出力するのみですが、end()は他のメソッドが必要とした場合に +hidden 要素を同時に出力する事ができます。 + +:: + + create(); ?> + + + + end(); ?> + +end()の最初のパラメータに文字列が指定された場合、FormHelperはそれに沿った送信ボタンを終了タグと共に出力します。 + +:: + + end('Finish'); ?> + + 出力: + +
+ +
+
+ +フォーム要素の自動生成 +====================== + +はじめに、フォームを自動的に作成する FormHelper +のいくつかのメソッドを見ていきましょう。主なメソッドは input() +です。このメソッドは各フィールドに適切な input +を生成するために、自動的に提供されたモデルのフィールドを確認します。 + +input(string $fieldName, array $options = array()) + ++--------------------------------------------------+-------------------------------------------+ +| カラム型 | フォームフィールドの結果 | ++==================================================+===========================================+ +| string (char, varchar, etc.) | text | ++--------------------------------------------------+-------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++--------------------------------------------------+-------------------------------------------+ +| text | textarea | ++--------------------------------------------------+-------------------------------------------+ +| text, with name of password, passwd, or psword | password | ++--------------------------------------------------+-------------------------------------------+ +| date | 日、月、そして年の select | ++--------------------------------------------------+-------------------------------------------+ +| datetime, timestamp | 日、月、年、時、分そして子午線の select | ++--------------------------------------------------+-------------------------------------------+ +| time | 時、分そして子午線の select | ++--------------------------------------------------+-------------------------------------------+ + +例として、 User +モデルに「username」(varchar)、「password」(varchar)、「approved」(datetime)、「quote」(text) +というフィールドが存在するとしましょう。これらの各フォームフィールドに適切な +input を作成するために、 FormHelper の input() メソッドを使用します。 + +:: + + create(); ?> + + input('username'); //text + echo $form->input('password'); //password + echo $form->input('approved'); //日、月、年、時間、分そして子午線 + echo $form->input('quote'); //textarea + ?> + + end('Add'); ?> + +date フィールドはたくさんのオプションを持ちます。例を見てみましょう。 + +:: + + echo $form->input('birth_dt', array( 'label' => 'Date of birth' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +仕上げに、 hasAndBelongsToMany の select を生成する例を示します。User が +Group に hasAndBelongsToMany +のアソシエーションを持っていると仮定します。コントローラで、キャメル記法で複数形の変数(この例では +group -> groups あるいは ExtraFunkyModel -> extraFunkyModels)を select +オプションにセットしてください。コントローラアクション中で次のようにします。 + +:: + + $this->set('groups', $this->User->Group->find('list')); + +そして view での複数選択が簡単なコードで作成できるはずです。 + +:: + + echo $form->input('Group'); + +belongsTo あるいは hasOne の関連において、select +フィールドを生成するなら、Users +コントローラに次のコードを追加してください(User belongsTo Group +とします)。: + +:: + + $this->set('groups', $this->User->Group->find('list')); + +その後に、フォームを作成するビューに、次のコードを追加してください。 + +:: + + echo $form->input('group_id'); + +フィールドの命名に関する慣習 +---------------------------- + +フォームヘルパーはとてもスマートです。フォームヘルパーのメソッドでフィールドの命名規則を定義する時はいつでも、input +タグを構築するにあたり現在使用しているモデルの名前を使用します。例えば次のようになります。 + +:: + + + +第一引数として Modelname.fieldname +という形式を渡すことで、手動でモデル名を定義することができます。 + +:: + + echo $form->input('Modelname.fieldname'); + +同じフィールド名を使って複数のフィールドを定義する場合、次のような方法で配列を生成すると、 +saveAll() で一度に保存することができます。 + +:: + + input('Modelname.0.fieldname'); + echo $form->input('Modelname.1.fieldname'); + ?> + + + + +$options[‘type’] +---------------- + +型を定義することで、 input +タグの型を強制的にそれにし、モデルが持つフィールドの型も上書きします。テーブルで定義されうる型に加え、「file」と「password」の入力フィールドを作成することもできます。 + +:: + + input('field', array('type' => 'file')); ?> + + Output: + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +input() +メソッドの出力の中に、何か記述を挿入する必要がある場合は、これらのキーを使用してください。 + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + ));?> + + Output: + +
+ --before-- + + --between--- + + --after-- +
+ +「separator」属性はラジオボタンを作成するときに使用し、各 input/label +ペアの間に記述を挿入することができます。 + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ +$options[‘options’] +------------------- + +このキーは select +による入力、ラジオボタンのグループに対する定義を手動で行います。「type」が「radio」と定義されていない場合、 +FormHelper は対象となる出力は select +による入力フォームであると仮定します。 + +:: + + input('field', array('options' => array(1,2,3,4,5))); ?> + + Output: + +
+ + +
+ +オプションはキーと値のペアで提供することもできます。 + +:: + + input('field', array('options' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2', + 'Value 3'=>'Label 3' + ))); ?> + + Output: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +select を出力するにあたり「multiple」が true +にセットしてあった場合、その select +の入力は複数選択が許可されます。「multiple」の代わりに「checkbox」を指定することで、関連したチェックボックスのリストを出力することができます。 + +:: + + $form->input('Model.field', array( 'type' => 'select', 'multiple' => true )); + $form->input('Model.field', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +text の input において許容する最大の文字列長を定義します。 + +$options[‘div’] +--------------- + +このオプションは input タグを内包する div +タグの属性をセットします。文字列によって、 div タグの class +の名前を指定します。配列をセットすると、 div +の属性をその配列のキーと値でセットされます。これらの代わりに、このキーの値を +false にセットすると div が出力されないようになります。 + +クラス名をセットする: + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +出力: + +:: + +
+ + +
+ +複数の属性をセットする: + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +出力: + +:: + +
+ + +
+ +div の出力を無効にする: + +:: + + input('User.name', array('div' => false));?> + +出力: + +:: + + + + +$options[‘label’] +----------------- + +このキーに文字列をセットすると、 input タグにいつも付いてくる label +タグの中に、その文字列が表示されます。 + +:: + + input( 'User.name', array( 'label' => 'ユーザの別名' ) );?> + +Output: + +:: + +
+ + +
+ +このキーに文字列の代わりに false をセットすると、 label +タグの出力は無効化されます。 + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Output: + +:: + +
+ +
+ +``label`` +要素に追加的なオプションを提供する場合は、配列をセットしてください。この場合、独自の +label のテキストは、配列中の「\ ``text``\ 」キーを使用してください。 + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'ユーザの別名') ) ); ?> + +Output: + +:: + +
+ + +
+ +$options['legend'] +------------------ + +ラジオボタンのようないくつかの入力項目は、フィールド名から得られる見出しで自動的にラップされます。この見出しを +legend オプションで上書きすることができます。このオプションを false +にセットすると、フィールドセットを完全に消し去ります。 + +$options[‘id’] +-------------- + +このキーは input タグの DOM の id を指定します。 + +$options['error'] +----------------- + +このキーを使うと、デフォルトのモデルのエラーメッセージを上書きすることができ、国際化したメッセージをセットするといった使い方をすることができます。 +このオプションには、要素や要素クラス名のラッピングを制御するための、サブオプションがいくつかあります。 + +エラーメッセージの出力を行わないようにするには、「error」キーを false +にします。 + +:: + + $form->input('Model.field', array('error' => false)); + +要素をラップするタイプとそのクラス名を変更するには、次の書式を利用します。 + +:: + + $form->input('Model.field', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +モデルのエラーメッセージを上書きするには、バリデーションルールをキーの名前にした連想配列を使います。 + +:: + + $form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough', true) ))); + +前述したとおり、各バリデーションルールのエラーメッセージはモデル中でセットすることができます。追加的に、国際化したメッセージをフォームに用意することができます。 + +$options['default'] +------------------- + +select +型入力を組み合わせるために使用します。フォームがはじめに表示された時にデフォルトで選択された状態にする +option +をマークします。エラーが含まれたフォームの送信が行われた後は、選ばれた(あるいは変更された)値を保持します。 + +使用例: + +:: + + input('country', array('options'=>$countries, 'default'=>'US')); + ?> + +$options[‘selected’] +-------------------- + +選択型の入力、つまりタイプが select, date, time, datetime +の入力で使用します。入力する部分をレンダリングする時に selected +属性を設定して、デフォルトで選択状態にしたい項目の値を指定します。 + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +これらふたつのキーは textarea +の入力項目において行と列の大きさを定義します。 + +$options[‘empty’] +----------------- + +このキーを true にセットすると、 input タグの値を必ず空にします。 + +select +リストにこの値が渡された場合、空の値が入った空のオプションがドロップダウンリストに生成されます。 +ブランクオプションを使わずに空の値の text の表示が必要なら、 string +に空の文字列を渡してください。 + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(一つ選んでください)')); ?> + +出力: + +:: + +
+ + +
+ +パスワードフィールドのデフォルトの値を空にしたいなら、これの代わりに「'value' +=> ''」を使用してください。 + +オプションはキーと値のペアで渡すこともできます。 + +$options[‘timeFormat’] +---------------------- + +時刻に関連した入力に関する select +のフォーマットを定義します。有効な値は「12」、「24」そして「none」です。 + +$options[‘dateFormat’] +---------------------- + +日付に関連した入力のセットに関する input +タグのフォーマットを定義します。有効な値は「DMY」、「MDY」、「YMD」そして「NONE」です。 + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +date または datetime の入力に併せて使用します。 select +フィールドに表示する最初と最後の年を定義します。 + +$options['interval'] +-------------------- + +このオプションは分のセレクトボックスにおいて、何分間隔を空けるのかを定義します。 + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +分を選ぶ欄に、15分ごとの4つの選択肢ができたはずです。 + +$options['class'] +----------------- + +You can set the classname for an input field using ``$options['class']`` + +:: + + echo $form->input('title', array('class' => 'custom-class')); + +File フィールド +=============== + +select タイプ、すなわち select, date, time, datetime +の入力において使用します。入力する部分がレンダリングされた時にデフォルトで選択されているアイテムの値を「selected」にセットします。 + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +アップロードをバリデートする +---------------------------- + +次に示すものは、モデルに定義するバリデーションメソッドの例です。このメソッドは、アップロードが成功したかどうかをバリデートします。 + +:: + + // http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters のコメント 8 に基づきます + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty($val['tmp_name']) && $val['tmp_name'] != 'none')) + { + return is_uploaded_file($val['tmp_name']); + } else { + return false; + } + } + +Form 要素固有のメソッド +======================= + +FormHelper +で利用可能な残りのメソッドは、特定のフォーム要素の生成するためのものです。これらのメソッドの多くは特別な +$options パラメータも使用します。しかしこの場合、 $options は主に HTML +タグの属性(たとえば value あるいはフォームの要素の DOM id +など)を指定するために使用されます。 + +:: + + text('username', array('class' => 'users')); ?> + + 出力: + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +チェックボックスフォーム要素を生成します。このメソッドは指定されたフィールドのデータを +強制的に submit するために関連する hidden フォーム入力も生成します。 + +:: + + checkbox('done'); ?> + + 出力: + + + + +button +------ + +``button(string $title, array $options = array())`` + +Creates an HTML button with the specified title and a default type of +"button". Setting ``$options['type']`` will output one of the three +possible button types: + +#. button: Creates a standard push button (the default). +#. reset: Creates a form reset button. +#. submit: Same as the ``$form->submit`` method. + +:: + + button('A Button'); + echo $form->button('Another Button', array('type'=>'button')); + echo $form->button('Reset the Form', array('type'=>'reset')); + echo $form->button('Submit Form', array('type'=>'submit')); + ?> + +Will output: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, boolean $showEmpty)`` + +``$minYear`` から ``$maxYear`` までの年を表示する select +要素を生成します。デフォルトでは $selected +で指定された年が選択されます。HTML 属性は $attributes +で指定できます。\ ``$showEmpty`` が false の場合、select は空の option +を含まなくなります。 + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +月名に関する select 要素を生成します。 + +:: + + month('mob'); + ?> + +と入力すると以下のソースを出力します: + +:: + + + +'monthNames' +属性を使うことで、月の配列を指定することが出来ます。もしくは'monthNames' +属性をfalseにすることで月を数字で表示することもできます。(注意: +標準の月表示は国際化されており、ローカライズすることで翻訳されます) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +:: + + + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +日付や時間用の select 入力を生成します。$dateformat で有効な値は、‘DMY’, +‘MDY’, ‘YMD’, ‘NONE’ です。$timeFormat で有効な値は、‘12’, ‘24’, ‘NONE’ +です。 + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +月の日にち(数値)に関する select 要素を生成します。 + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +時間に関する select 要素を生成します。 + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +分に関する select 要素を生成します。 + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +‘am’ や ‘pm’ に関する select 要素を生成します。 + +error +----- + +``error(string $fieldName, string $text, array $options) `` + +バリデーションエラーが起こったイベントで、指定されたフィールドの $text +で指定したバリデーションエラーメッセージを表示します。 + +file +---- + +``file(string $fieldName, array $options)`` + +ファイル入力を生成します。 + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +hidden フォーム入力を生成します。 + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +指定された $fieldName にアクティブなバリデーションエラーがある場合に +true を返します。 + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +ラベルタグを生成します。$text でラベルを指定します。 + +password +-------- + +``password(string $fieldName, array $options)`` + +password フィールドを生成します。 + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +ラジオボタン入力を生成します。attributes['value'] +はデフォルトで選択される値をセットするために使用します。 + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +select 要素を生成します。\ ``$options`` +で選択項目を指定し、\ ``$selected`` +で指定されたオプションがデフォルトで選択状態になります。空の選択オプションを表示したくない場合、\ ``$showEmpty`` +を false にします。 + +submit +------ + +``submit(string $caption, array $options)`` + +キャプションが ``$caption`` である submit ボタンを生成します。指定された +``$caption`` は画像(‘.’ 文字を含みます)の URL である場合、submit +ボタンは画像として描画されます。 + +text +---- + +``text(string $fieldName, array $options)`` + +テキスト入力フィールドを生成します。 + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +textarea 入力フィールドを生成します。 diff --git a/ja/The-Manual/Core-Helpers/HTML.rst b/ja/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..994870e5dceb9a2ed35345f0071d92efa8be3f2e --- /dev/null +++ b/ja/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,935 @@ +HTML +#### + +CakePHP の HtmlHelper の役割は、HTML +関連のオプションの変更をより簡単に、より速く、より柔軟にすることです。このヘルパーを使用すると、アプリケーションがドメインのルートに関してどこに置かれているかについて、より明確になり、より柔軟になります。 + +HtmlHelper の役割は CakePHP 1.1 +からかなり変わってしまっています。フォームに関するメソッドは非推奨となり、新しく +FormHelper に移動しました。HTML +フォームについてのヘルプを探している場合は、新しい FormHelper +をチェックしてください。 + +HtmlHelper +のメソッドを見る前に、いくつかの設定や使用状況について知っておく必要があります。はじめに、AUTO\_OUTPUT +と呼ばれるコア定数設定です。AUTO\_OUTPUT +がアプリケーション内のコア設定ファイル(/app/config/core.php) で true +にセットされると、HtmlHelper +は値を返さずに自動的にタグの内容を出力します。これはビューコード内でショートタグ +() あるいは多くの echo() 呼び出しを嫌う用にあります。$return +パラメータを指定した関数を使用することでコア設定の内容を強制的に上書きすることができます。AUTO\_OUTPUT +の値に関係なく HtmlHelper に HTML コードを返して欲しい場合は、$return に +true を設定してください。 + +多くの HtmlHelper のメソッドは $htmlAttributes +パラメータも持っています。これを使用するとタグに特別な属性を付加することができます。ここでは +$htmlAttributes パラメータの使用方法のいくつかの例を示します: + +:: + + 期待する属性: + Array パラメータ: array('class'=>'someClass') + + 期待する属性: + Array パラメータ: array('name' => 'foo', 'value' => 'bar') + +デフォルトですべてのビューで HtmlHelper は有効になっています。HtmlHelper +がないというエラーが表示される場合、通常は 手動で設定された $helpers +コントローラ変数に名前がないということになります。 + +整形済み要素を挿入する +====================== + +HtmlHelper +が果たす最も重要な仕事は、整形済みマークアップを生成することです。使用することを恐れないでください +- ビューが描画されクライアントに出力される際にCPU +の使用を節約するためにCakePHP +のビューをキャッシュすることができます。この章では HtmlHelper +のいくつかのメソッドとその使い方を見てみます。 + +``charset(string $charset=null)`` + +ドキュメントの文字コードを指定するメタタグを生成します。デフォルトは +UTF-8 です。 + +:: + + + charset(); ?> + + // 出力 + + + charset('ISO-8859-1'); ?> + + // 出力 + + +``css(mixed $path, string $rel = null, array $htmlAttributes, boolean $inline = true)`` + +CSS スタイルシートへのリンクを生成します。$inline が false +にセットされると、リンクタグが $scripts\_for\_layout +変数に追加され、ドキュメントの head タグ内に出力することができます。 + +CSS 読み込みのこのメソッドは、指定された CSS ファイルが /app/webroot/css +ディレクトリ内に置かれていると仮定しています。 + +:: + + + css('forms'); ?> + + // 出力 + + + // 第1引数に配列も有効です + css(array('forms','tables','menu')); ?> + + // 出力 + + + + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +このメソッドは RSS/Atom フィードや favicon +のような外部リソースへのリンクを手軽にします。css() +と同様に、このタグをインライン出力にするか、第 4 引数を使用して head +タグに出力するかどうかを指定することができます。 + +"type" 属性を使用して、生成する type タグを制御します: + +$htmlAttributes パラメータを使用して "type" +属性をセットする場合、CakePHP はいくつかのショートカットがあります: + ++--------+------------------------+ +| type | 変換される値 | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> // 出力 (改行が追加されます)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + + // 出力 (改行が追加されます) + + +このメソッドはメタキーワードや詳細を追加するために使用されます。 +サンプル: + +:: + + meta( + 'keywords', + 'enter any meta keyword here', + array(), false + );?> + // 出力 + // + + meta( + 'description', + 'enter any meta description here', + array(), false + );?> + + // 出力 + +``docType(string $type = 'xhtml-strict')`` + +(X)HTML の doctype タグを出力します。次のテーブルに従って doctype +を指定します: + ++----------------+-----------------------+ +| type | 変換される値 | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +``style(array $data, boolean $inline = true) `` + +メソッドに渡された配列のキーと値に基づいて CSS +スタイル定義を構築します。CSS ファイルが動的である場合に特に便利です。 + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + + // 出力 + background:#633; + border-bottom:1px solid #000; + padding:10px; + +``image(string $path, array $htmlAttributes) = array()`` + +フォーマットされた image タグを生成します。指定されたパスは +/app/webroot/img/ からの相対パスです。 + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + + // 出力 + CakePHP + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +マークアップを div で囲んだセクションを生成するために使用します。第 1 +引数は CSS クラスを指定し、第 2 引数は div +タグで囲まれるテキストを指定します。最後のパラメータが true +にセットされると、$text は HTML エスケープされて出力されます。 + +テキストが指定されない場合、div の開始タグのみ返されます。 + +:: + + + div('error', 'Please enter your credit card number.');?> + + // 出力 +
Please enter your credit card number.
+ +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +主な目的は HTML リンクを生成することです。 + +:: + + link('Enter', '/pages/home', array('class'=>'button')); ?> + + // 出力 + Enter + + link( + 'Delete', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "Are you sure you wish to delete this recipe?" + );?> + + // 出力 + Delete + +link() と image() +を一緒に使用することで画像リンクを生成します。しかし最後のパラメータを +false にすることを忘れないでください。 + +:: + + link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "/recipes/view/6", + array(), + false, + false + ); ?> + + // 出力: + + Brownies + + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +CSS クラスのついた

+タグで囲まれたテキストを返します。テキストが指定されていない場合、開始 +

タグのみ返されます。 + +:: + + para(null, 'Hello World.');?> + // 出力 +

Hello World.

+ +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + + タグ内に配置されるテーブルヘッダーセルの行を生成します。 + +:: + + tableHeaders(array('Date','Title','Active'));?> + + // 出力 + + + tableHeaders( + array('Date','Title','Active'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + // 出力 + + + + + + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null)`` + +行内のテーブルセルを生成します。奇数/偶数行に異なる +属性を指定できます。 + +:: + + tableCells(array( + array('Jul 7th, 2007', 'Best Brownies', 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', 'No'), + )); + ?> + + // 出力 + + + + + tableCells( + array( + array('Red', 'Apple'), + array('Orange', 'Orange'), + array('Yellow', 'Banana'), + ), + array('class' => 'darker') + ); + ?> + + // 出力 + + + + +charset +------- + +``charset(string $charset=null)`` + +文書の文字コードを指定する meta +タグを生成するために使用します。デフォルトは UTF-8 です。 + +:: + + + charset(); ?> + +これは次のような出力になります: + +:: + + + +別の使用例: + +:: + + charset('ISO-8859-1'); ?> + +これは次のような出力になります: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +CSS スタイルシートへのリンクを作成します。$inline を false +にセットした場合、link タグは $scripts\_for\_layout +変数の中に出現するようになるので、この変数を文書中の head +タグの中に入れておくといった使い方ができます。 + +この CSS のリンクを作成するメソッドは、CSS ファイルが /app/webroot/css +ディレクトリの中に設置されているということを前提としています。 + +:: + + css('forms'); ?> + +これは次のような出力になります: + +:: + + + +第一引数には、複数のファイルを配列で格納することができます。 + +:: + + css(array('forms','tables','menu')); ?> + +これは次のような出力になります: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +This method is handy for linking to external resources like RSS/Atom +feeds and favicons. Like css(), you can specify whether or not you'd +like this tag to appear inline or in the head tag using the fourth +parameter. + +If you set the "type" attribute using the $htmlAttributes parameter, +CakePHP contains a few shortcuts: + ++--------+------------------------+ +| type | translated value | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> //Output (line breaks added)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + + //Output (line breaks added) + + +This method can also be used to add the meta keywords and descriptions. +Example: + +:: + + meta( + 'keywords', + 'enter any meta keyword here' + );?> + //Output + // + + meta( + 'description', + 'enter any meta description here' + );?> + + //Output + +If you want to add a custom meta tag then the first parameter should be +set to an array. To output a robots noindex tag use the following code: + +:: + + echo $html->meta(array('name' => 'robots', 'content' => 'noindex')); + +docType +------- + +``docType(string $type = 'xhtml-strict')`` + +Returns a (X)HTML doctype tag. Supply the doctype according to the +following table: + ++----------------+-----------------------+ +| type | translated value | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Builds CSS style definitions based on the keys and values of the array +passed to the method. Especially handy if your CSS file is dynamic. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Will output: + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Creates a formatted image tag. The path supplied should be relative to +/app/webroot/img/. + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Will output: + +:: + + CakePHP + +To create an image link specify the link destination using the ``url`` +option in ``$htmlAttributes``. + +:: + + image("recipes/6.jpg", array( + "alt" => "Brownies", + 'url' => array('controller' => 'recipes', 'action' => 'view', 6) + )); ?> + +Will output: + +:: + + + Brownies + + +You can also use this alternate method to create an image link, by +assigning the image to a variable (e.g. $image), and passing it to +``$html->link()`` as the first argument: + +:: + + image('recipes/6.jpg', array( + 'alt' => 'Brownies', + )); + + //$image is passed as the first argument instead of link text + echo $html->link($image, array( + 'controller' => 'recipies', + 'action' => 'view', + 6 + ), + array( + 'escape' => false //important so htmlHelper doesn't escape you image link + ) + ); + ?> + +This is useful if you want to keep your link and image a bit more +separate, or if you want to sneak some markup into your link. Be sure to +pass ``'escape' => false`` in the options array for +`` $html->link($string, $url, $options)`` to prevent htmlHelper from +escaping the code. + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +General purpose method for creating HTML links. Use ``$htmlAttributes`` +to specify attributes for the element. + +:: + + link('Enter', '/pages/home', array('class'=>'button','target'=>'_blank')); ?> + +Will output: + +:: + + + Enter + +Specify ``$confirmMessage`` to display a javascript ``confirm()`` +dialog. + +:: + + link( + 'Delete', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "Are you sure you wish to delete this recipe?" + );?> + +Will output: + +:: + + + Delete + +Query strings can also be created with ``link()``. + +:: + + link('View image', array( + 'controller' => 'images', + 'action' => 'view', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Will output: + +:: + + + View image + +HTML special characters in ``$title`` will be converted to HTML +entities. To disable this conversion, set the escape option to false in +the ``$htmlAttributes``, or set ``$escapeTitle`` to false. + +:: + + link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + null, null, false + ); + ?> + +Both will output: + +:: + + + Brownies + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns text wrapped in a specified tag. If no text is specified then +only the opening is returned. + +:: + + tag('span', 'Hello World.', array('class' => 'welcome'));?> + + //Output + Hello World + + //No text specified. + tag('span', null, array('class' => 'welcome'));?> + + //Output + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Used for creating div-wrapped sections of markup. The first parameter +specifies a CSS class, and the second is used to supply the text to be +wrapped by div tags. If the last parameter has been set to true, $text +will be printed HTML-escaped. + +If no text is specified, only an opening div tag is returned. + +:: + + + div('error', 'Please enter your credit card number.');?> + + //Output +
Please enter your credit card number.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns a text wrapped in a CSS-classed

tag. If no text is supplied, +only a starting

tag is returned. + +:: + + para(null, 'Hello World.');?> + + //Output +

Hello World.

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Creates a row of table header cells to be placed inside of
DateTitleActive
DateTitleActive
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
RedApple
OrangeOrange
YellowBanana
tags. + +:: + + tableHeaders(array('Date','Title','Active'));?> //Output + + + tableHeaders( + array('Date','Title','Active'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + //Output + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Creates table cells, in rows, assigning attributes differently for +odd- and even-numbered rows. Wrap a single table cell within an array() +for specific + + + + tableCells(array( + array('Jul 7th, 2007', array('Best Brownies', array('class'=>'highlight')) , 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', array('No', array('id'=>'special'))), + )); + ?> + + //Output + + + + + tableCells( + array( + array('Red', 'Apple'), + array('Orange', 'Orange'), + array('Yellow', 'Banana'), + ), + array('class' => 'darker') + ); + ?> + + //Output + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Returns an URL pointing to a combination of controller and action. If +$url is empty, it returns the REQUEST\_URI, otherwise it generates the +url for the controller and action combo. If full is true, the full base +URL will be prepended to the result. + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "bar"));?> + + // Output + /posts/view/bar + +Here are a few more usage examples: + +URL with named parameters + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "foo" => "bar")); + ?> + + // Output + /posts/view/foo:bar + +URL with extension + +:: + + url(array( + "controller" => "posts", + "action" => "list", + "ext" => "rss")); + ?> + + // Output + /posts/list.rss + +URL (starting with '/') with the full base URL prepended. + +:: + + url('/posts', true); ?> + + //Output + http://somedomain.com/posts + +URL with GET params and named anchor + +:: + + url(array( + "controller" => "posts", + "action" => "search", + "?" => array("foo" => "bar"), + "#" => "first")); + ?> + + //Output + /posts/search?foo=bar#first + +For further information check +`Router::url `_ in +the API. + +Changing the tags output by HtmlHelper +====================================== + +The built in tag sets for ``HtmlHelper`` are XHTML compliant, however if +you need to generate HTML for HTML4 you will need to create and load a +new tags config file containing the tags you'd like to use. To change +the tags used create ``app/config/tags.php`` containing: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +You can then load this tag set by calling ``$html->loadConfig('tags');`` diff --git a/ja/The-Manual/Core-Helpers/Javascript.rst b/ja/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..d7f13cb406d81a55b3404cdf83dd74d085d79c59 --- /dev/null +++ b/ja/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,128 @@ +Javascript +########## + +Javascript ヘルパーは 整形済み javascript +タグやコードブロックを生成します。いくつかのメソッドは +`Prototype `_ javascript +ライブラリで動作するように設計されています。 + +メソッド +======== + +``codeBlock($string, $options, $safe)`` + +- string $script - SCRIPT タグで囲まれる javascript +- boolean $options - オプション: allowCache, safe, inline +- boolean $safe - 非推奨。代わりに $options['safe'] + を使用してください。 + +codeBlock は $script を含むフォーマットされた script +要素を返します。Javascript +ヘルパーがイベントをキャッシュするように設定されている場合、null +を返すこともできます。インラインで出力するか、あるいは +$options['inline'] に false をセットすると、\ ``$scripts_for_layout`` +に出力することができます。 + +``blockEnd()`` + +キャッシュされた javascript のブロックを終了します。script +終了タグあるいは cachedEvents +配列にコンテンツを追加し空のバッファを返すことができます。返されれる値はキャッシュ設定に依存します。JavascriptHelper::cacheEvents() +をみてください。 + +``link($url, $inline)`` + +- mixed $url - JavaScript ファイルの URL 文字列、あるいは URL の配列 +- boolean $inline - true の場合、'; + echo Sanitize::html($badString); + // saída: <font size="99" color="#FF0000">HEY</font><script>...</script> + echo Sanitize::html($badString, true); + // saída: HEY... + +escape +====== + +escape(string $string, string $connection) + +Usado para tratar declarações em SQL com a adição de barras de escape, +dependendo da configuração magic\_quotes\_gpc do sistema. $connection é +o nome da base de dados, definido em seu arquivo +app/config/databasel.php, em que o tratamento deve ser aplicado. + +clean +===== + +``Sanitize::clean(mixed $data, mixed $options)`` + +Este é um método de limpeza de dados de padrão industrial e multiuso, o +que quer dizer que ele foi feito para ser utilizado em arrays inteiros +(como o $this->data, por exemplo). O método recebe um array (ou uma +string) e retorna sua versão limpa. As seguintes operações de limpeza +são executadas em cada elemento do array (recursivamente): + +- Caracteres de espaçamento diversos quaisquer (incluindo 0xCA) são + substituídos por espaços simples. +- Forte verificação de caracteres especiais e remoção de quebras de + linha para melhoria da segurança em SQL. +- Adição de barras de escape para SQL (uma chamada interna ao método + sql mostrado anteriormente). +- Troca de barras invertidas da entrada do usuário por barras + invertidas confiáveis. + +O argumnto $options pode ser tanto uma string como um array. Quando uma +string for informada, significará o nome de uma conexão de base de +dados. Se um array for informado, ele poderá ser composto das seguintes +opções: + +- connection +- odd\_spaces +- encode +- dollar +- carriage +- unicode +- escape +- backslash + +Então, chamar o método clean() com opções seria algo como: + +:: + + $this->data = Sanitize::clean($this->data, array('encode' => false)); + diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst new file mode 100644 index 0000000000000000000000000000000000000000..0357993155ba20c32e97a530aec06ab5f73cfb6e --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Data-Validation.rst @@ -0,0 +1,932 @@ +Validação de dados +################## + +A validação dos dados é uma das mais importantes partes de qualquer +aplicação, elas fazem com que os dados em um *Model* respeitem as regras +da aplicação. Por exemplo, você pode estar querendo que as senhas tenham +no mínimo oito caracteres, ou garantir que os *usernames* sejam únicos. +Definindo regras de validação você faz com que a manipulação dos +formulários fique bem mais fácil. + +Há vários aspectos diferentes no processo de validação. O quê iremos +cobrir nesta seção é a parte do *Model*. Essencialmente: o quê acontece +quando você chama o método save() do seu *model*. Para mais informações +de como manipular as mensagens de erro de validação, dê uma olhada na +`seção sobre o FormHelper `_. + +O primeiro passo para a validação de dados, é a criação de regras de +validação no seu *Model*. Para isso, use a *array* Model::validate na +definição do módulo, por exemplo: + +:: + + + +No exemplo acima, a *array* $validate foi adicionada ao *Model* User, +mas a array ainda não contém nenhuma regra de validação. Assumindo que a +tabela de usuários já tenha os campos de *login*, *password* (senha), +*email* e *born* (data de nascimento), o exemplo abaixo mostra algumas +regras de validação simples para aplicarmos à esses campos: + +:: + + 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + ?> + +Este último exemplo mostra como as regras de validação podem ser +adicionadas a campos do *model*. Para o campo de login, somente letras e +números serão aceitos, o email tem de ser válido, e a data de nascimento +deve ser uma data válida. Definindo regras de validação o CakePHP mostra +as mensagens de erro nos formulários automágicamente, se os dados não +seguirem as regras. + +O CakePHP possui várias regras de validação e usar elas é bem fácil. +Algumas das regras "de fábrica" lhe permitem verificar as formatações de +e-mails, URLs, e números de cartões de crédito - cobriremos essas regras +em detalhes em breve. + +Temos aqui um exemplo mais complexo de validação que tira vantagem de +algumas dessas regras de validação "de fábrica": + +:: + + array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Letras e números somente' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Entre 5 e 15 caracteres' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Mínimo de 8 caracteres' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Insira uma data válida', + 'allowEmpty' => true + ) + ); + } + ?> + +Duas regras de validação foram definidas para o login: ele deve conter +apenas letras e números e o tamanho deve ter entre 5 e 15 caracteres. O +campo *password* (senha) deve ter no mínimo 8 caracteres. O *email* deve +ser um endereço de email válido, e o campo *born* (data de nascimento) +deve ser uma data válida. Note também que você pode incluir mensagens +personalizadas para que o CakePHP mostre quando as regras definidas +falharem. + +Como o exemplo acima mostrou, um único campo pode haver múltiplas regras +de validação. E se as regras de validação "de fábrica" não resolverem o +seu caso, você pode adicionar suas próprias regras de validação. + +Agora que você aprendeu um pouco sobre como a validação funciona, vamos +olhar como elas são definidas em um *model*. Há três diferentes formas +que você pode usar para definir regras de validação: arrays simples, +regra única por campo, e múltiplas regras por campo. + +Regras Simples +============== + +Como o nome sugere, essa é a maneira mais simples de definir uma regra +de validação: + +:: + + var $validate = array('fieldName' => 'ruleName'); + +Sendo que 'fieldName' é o nome do campo para qual a regra está sendo +definida e 'ruleName' seria o nome de uma regra pré-definida, tal como +'alphaNumeric', 'email' ou 'isUnique'. + +Uma Regra por Campo +=================== + +Esta técnica de definição permite um melhor controle sobre como as +regras de validação funcionam. Mas antes de discutirmos isso, vamos ver +o padrão de uso geral para adicionar regras à um campo único: + +:: + + var $validate = array( + 'fieldName1' => array( + 'rule' => 'ruleName', // ou: array('ruleName', 'param1', 'param2' ...) + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // ou: 'update' + 'message' => 'Sua mensagem de erro' + ) + ); + +A chave 'rule' é obrigatória. Se você definir apenas 'required' => true, +a validação de formulário não vai funcionar corretamente. Isto porque +'required' atualmente não é uma regra. + +Como você pode ver, cada campo (apenas um campo foi mostrado acima) é +associado com um array com cinco chaves: 'rule', 'required', +'allowEmpty', 'on' e 'message'. À exceção da chave 'rule', as demais +chaves são opcionais. Vamos analisar estas chaves. + +rule +---- + +A chave 'rule' define um método de validação e aceita tanto um único +valor quanto um array. O valor da chave 'rule' deve ser o nome de um +método em seu *model*, um método da classe de validação principal, ou +uma expressão regular. Para mais informações sobre as regras disponíveis +por padrão, veja as Regras de +validação incorporadas por padrão. + +Se a regra não exigir nenhum parâmetro, a chave ‘rule’ pode conter um +único valor ex: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +Se a regra exigir mais de um parâmetro (como max, min ou range), o valor +de ‘rule’ deve ser um array: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + ); + +Lembre-se, a chave 'rule' é obrigatória para regras baseadas em array. + +required +-------- + +Para esta chave deve se dar um valor booleano. Se ‘required’ for +verdadeiro, o campo deve estar presente na array de dados. Por exemplo, +se a regra de validação for definida como a seguir: + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +Os dados enviados para o método save() do seu *model* devem conter o +campo de login. Senão, a validação irá falhar. O valor padrão para esta +chave é *false*. + +``required => true`` não é o mesmo que a regra de validação +``notEmpty()``. ``required => true`` indica que o respectivo *índice* do +array de dados deve existir - o que não quer dizer que ele precise ter +um valor. Desta forma, a validação irá falhar se o campo não estiver +presente no dataset, mas pode ter sucesso (dependendo da regra) se o +valor submetido for vazio (''). + +allowEmpty +---------- + +A chave ``allowEmpty`` deve possui um valor booleano. Se ``allowEmpty`` +for falso, os dados passados para o método ``save()`` do *model* devem +incluir o campo e ele não poderá ser vazio. Se for verdadeiro e o campo +estiver vazio, todo e qualquer tipo de validação será ignorado. + +O valor padrão para ``allowEmpty`` é ``null``. Ou seja, o campo sempre +processará as regras de validação, incluindo funções de validação +personalizada. + +Se definido para ``false``, o valor do campo deve ser "não-vazio", sendo +"não-vazio" definido como ``!empty($valor) || is_numeric($valor)``. A +verificação de dado numérico confere se o CakePHP fará a coisa certa +quando ``$valor`` for zero. + +A diferença entre ``required`` e ``allowEmpty`` pode não ser muito +clara. ``'required' => true`` significa que você não pode salvar o model +se o índice para este campo não existir no ``$this->data`` (a +verificação é feita com a função ``isset``); ao passo que +``'allowEmpty' => false`` assegura que o *valor* do campo atual seja +"não-vazio", como descrito acima. + +on +-- + +A chave 'on' pode conter os seguintes valores: 'update' ou 'create'. +Isso lhe permite aplicar uma certa regra durante a criação ou a +atualização de um registro. + +Se uma regra for definida como 'on' => 'create', a regra será executada +apenas quando algum registro for criado. Por outro lado, se estiver +definida como 'on' => 'update', ela será executada apenas quando algum +registro for atualizado. + +O valor padrão para 'on' é null. Quando 'on' estiver definido como null, +a regra será executada durante a criação e a atualização de um registro. + +message +------- + +A chave ‘message’ permite você definir mensagens de erro de validação +personalizadas para uma regra: + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => 'A senha deve ter no mìnimo 8 caracteres' + ) + ); + +last +---- + +Setting the ``'last'`` key to ``true`` will cause the validator to stop +on the rule if it fails instead of continuing with the next rule. This +is handy if you want validation to stop if the field is notEmpty in a +`multi-rule field `_. + +:: + + var $validate = array( + 'username' => array( + 'usernameRule-1' => array( + 'rule' => 'notEmpty', + 'message' => 'Please enter a username.', + 'last' => true + ), + 'usernameRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters.' + ) + ) + ); + +The default value for ``'last'`` is ``false``. + +Múltiplas regras por campo +========================== + +Esta técnica nos dá muito mais flexibilidade do que o modelo de regras +simples, mas há um passo extra que temos que seguir antes de obtermos +maior controle sobre a validação dos dados. A próxima técnica que iremos +explicar nos permite atribuir múltiplas regras de validação por campo. + +Se você desejar atribuir múltiplas regras de validaçãoo para um único +campo, seu código terá basicamente isso: + +:: + + + var $validate = array( + 'fieldName' => array( + 'ruleName' => array( + 'rule' => 'ruleName', + // chaves extras como 'on', 'required', etc. vão aqui... + ), + 'ruleName2' => array( + 'rule' => 'ruleName2', + // chaves extras como 'on', 'required', etc. vão aqui... + ) + ) + ); + +Como você pode ver, é bem similar ao que fizemos na seção anterior. Lá, +para cada campo tinhamos apenas uma matriz com parâmetros de validação. +Nesse caso, cada 'fieldName' consiste em um array com outros arrays +(índices) de regra. Cada chave 'ruleName' contém uma array separada com +parâmetros de validação. + +É mais fácil explicarmos isso com um exemplo prático: + +:: + + var $validate = array( + 'login' => array( + 'alphanumeric' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Apenas números e letras são permitidos' + ), + 'minlength' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Mínimo de 8 caracteres' + ), + ) + ); + +O exemplo acima define duas regras para o campo de login: 'alphanumeric' +e 'minLength'. Como você pode ver, cada regra é identificada por um nome +de índice. Nesse caso em especial, o nome dos índices são similares às +regras que eles empregam, mas o índice pode ter qualquer nome que você +escolher. + +*Nota:* Se você planeja usar mensagens internacionalizadas, você deverá +especificar as mensagens de erro também na sua view. + +:: + + echo $form->input('login', array( + 'label' => __('Login', true), + 'error' => array( + 'alphanumeric' => __('Apenas números e letras são permitidos', true), + 'minlength' => __('Mínimo de 8 caracteres', true) + ) + ) + ); + +Agora o campo está internacionalizado, e você pode remover as mensagens +do seu model. Para maiores informações sobre a função \_\_(), veja +"Localização & Internacionalização". + +Regras de validação incorporadas por padrão +=========================================== + +A classe de validação no CakePHP contém muitas regras de validação que +podem fazer da validação de dados para o *model* muito mais simples. +Essa classe contém as mais-usadas técnicas, assim você não precisará +reescrevêlas. Abaixo, você irá encontrar uma lista completa de todas as +regras e seus respectivos exemplos. + +alphaNumeric +------------ + +Os dados do campo devem conter apenas letras e números. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Nomes de usuário devem conter apenas letras e números.' + ) + ); + +between +------- + +O comprimento dos dados do campo deve estar entre os números +especificados (inclusive). Tanto o valor mínimo quanto o máximo devem +ser especificados. + +:: + + var $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Senhas deve ter entre 5 e 15 caracteres.' + ) + ); + +blank +----- + +Essa regra é usada para ter certeza de que o campo foi deixado em branco +ou apenas caracteres de espaço estão presentes nele. São considerados +caracteres em branco: espaço, tab, carriage return, e newline. + +:: + + var $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + +boolean +------- + +Os dados para o campo devem ser um valor booleano. Valores válidos são +true ou false, inteiros 0 ou 1 ou strings '0' ou '1'. + +:: + + var $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'Valor incorreto para myCheckbox' + ) + ); + +cc +-- + +Essa regra é usada para checar quando o campo é um cartão de crédito +válido. Ele aceita três parâmetros: 'type', 'deep' e 'regex'. + +À chave 'type' podem ser atribuidos os valores 'fast', 'all' ou qualquer +um dos seguintes: + +- bankcard +- diners +- disc +- electron +- enroute +- jcb +- maestro +- mc +- solo +- switch +- visa +- voyager + +Se a chave 'type' for setada como 'fast', ela validará o número do +cartão de crédito entre os cartões mais usados. Se o valor da chave +'type' for 'all' ele irá validar entre todos os cartões de crédito. Você +também pode setar o 'type' como uma array de tipos de cartão que você +queira validar. + +À chave 'deep' deve se dar um valor *booleano*. Se for setada como true, +a validação checará o cartão de crédito com o algoritmo Luhn +(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_). +O padrão é *false*. + +A chave 'regex' lhe permite inserir sua própria expressão regular que +irá ser usada para validar o cartão de crédito. + +:: + + var $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'O número do cartão de crédito que você forneceu é inválido.' + ) + ); + +comparison +---------- + +O 'comparison' é usado para comparar valores numéricos. Ele suporta "is +greater", "is less", "greater or equal", "less or equal", "equal to", +and "not equal". Alguns exemplos abaixo: + +:: + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Você deve ter no mínimo 18 anos.' + ) + ); + + var $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Você deve ter no mínimo 18 anos.' + ) + ); + +date +---- + +Essa regra assegura que a data enviada seja válida. Um único parâmetro +(que pode ser um array) pode ser passado para validar os dados +fornecidos. O valor desse parâmetro pode ser um dos seguitnes: + +- 'dmy' ex: 27-12-2006 or 27-12-06 (os separadores podem ser espaço, + ponto, traço e barra comum) +- 'mdy' ex: 12-27-2006 or 12-27-06 (os separadores podem ser espaço, + ponto, traço e barra comum) +- 'ymd' ex: 2006-12-27 or 06-12-27 (os separadores podem ser espaço, + ponto, traço e barra comum) +- 'dMy' ex: 27 Dezembro 2006 ou 27 Dezembro 2006 +- 'Mdy' ex: Dezembro 27, 2006 or Dez 27, 2006 (vírgula é opcional) +- 'My' ex: (Dezembro 2006 ou Dez 2006) +- 'my' ex: 12/2006 ou 12/06 (os separadores podem ser espaço, ponto, + traço e barra comum) + +Se nenhuma chave for fornecida, a chave padrão será 'ymd'. + +:: + + var $validate = array( + 'born' => array( + 'rule' => 'date', + 'message' => 'Insira uma data válida no formato AA-MM-DD.', + 'allowEmpty' => true + ) + ); + +Apesar dos bancos de dados requerirem um certo formato de data, você +deve fazer o trabalho pesado e tentar convertê-los, ao invés de forçar +os usuários a inserirem a data nesse formato. Quanto mais você puder +facilitar para os usuários, melhor. + +decimal +------- + +Esta regra garante que o dado seja um número decimal válido. Um +parâmetro pode ser passado para especificar a quantidade de casas +decimais após o ponto. Se nenhum parâmetro for passado, o dado será +validado como um número científico de ponto flutuante, que fará a +validação falhar se nenhuma dígito for encontrado após o ponto decimal. + +:: + + var $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + +email +----- + +Checa se é um e-mail válido. Passando um valor booleano true como +segundo parâmetro dessa regra fará com que tente verificar o host para o +endereço do e-mail. + +:: + + var $validate = array('email' => array('rule' => 'email')); + + var $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Insira um email válido.' + ) + ); + +equalTo +------- + +Essa regra vai garantir que o valor é igual e é do mesmo tipo do valor +dado. + +:: + + var $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'Esse valor deve ser igual a cake' + ) + ); + +extension +--------- + +Essa regra verifica se é uma extensão válida de arquivo, como .jpg ou +.png. Permite múltiplas extensões se colocadas na forma de array. + +:: + + var $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg'), + 'message' => 'Por favor, informe uma imagem válida.' + ) + ); + +file +---- + +Esta seção precisa ser reescrita. Se você tiver uma ideia sobre o que +deve constar aqui, por favor, utilize os links ao lado e submeta sua +sugestão! + +ip +-- + +Checa se um endereço IP (IPv4) válido foi enviado. + +:: + + var $validate = array( + 'clientip' => array( + 'rule' => 'ip', + 'message' => 'Por favor, insira um endereço IP válido.' + ) + ); + +isUnique +-------- + +O valor do campo deve ser único, não podendo ser usado por nenhum outro +registro. + +:: + + var $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'O nome de usuário já está em uso.' + ) + ); + +minLength +--------- + +Essa regra assegura que os dados enviados possuem o comprimento mínimo +requerido. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Nomes de usuário deve possuir no mínimo 8 caracteres.' + ) + ); + +maxLength +--------- + +Essa regra assegura que o valor tem o mínimo de caracteres requerido. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('maxLength', '15'), + 'message' => 'Nomes de usuário não podem ter mais que 15 caracteres.' + ) + ); + +money +----- + +Essa regra vai assegurar que o valor tem uma quantia monetária válida. + +O segundo parâmetro define onde o símbolo está localizado (left/right). + +:: + + var $validate = array( + 'amount' => array( + 'rule' => array('money', 'left'), + 'message' => 'Por favor, informe um valor com uma quantia monetária' + ) + ); + +multiple +-------- + +Use esta regra para validar uma entrada de seleção múltipla. Ela suporta +os parâmetros "in", "max" e "min". + +:: + + var $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array('in' => array('do', 'ray', 'me', 'fa', 'so', 'la', 'ti'), 'min' => 1, 'max' => 3)), + 'message' => 'Por favor, selecione uma, duas ou três opções' + ) + ); + +inList +------ + +Essa regra vai assegurar que é um valor permitido. Ele precisa de um +array de valores. O campo é válido se o valor do campo for um dos +valores do array. + +:: + + var $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Informe o valor Foo ou Bar.' + ) + ) + ); + +numeric +------- + +Verifica se o valor informado é um número válido. + +:: + + var $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => 'Por favor, informe o número de carros.' + ) + ); + +notEmpty +-------- + +A regra básica para garantir que um campo não seja vazio. + +:: + + var $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'Este campo não pode ser deixado em branco' + ) + ); + +Não use esta regra para uma entrada de seleção múltipla, pois do +contrário isto vai causar um erro. Ao invés disso, utilize "multiple". + +phone +----- + +Valida números de telefones dos Estados Unidos (us). Se você quer +validar um número de telefone que não seja dos Estados Unidos, você pode +fornecer uma expressão regular no segundo parâmetro. + +:: + + var $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + +postal +------ + +Postal é usado para validar códigos postais dos Estados Unidos (us), +Canadá (ca), Reino Unido (uk), Itália (it), Alemanha (de) e Bélgica +(be). Para outro formato de código postal, você pode fornecer uma +expressão regular como segundo parâmetro. + +:: + + var $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + +range +----- + +Essa regra garante que o valor está dentro da faixa númerica. Se nenhuma +faixa é fornecidade, a regra vai verificar se o valor é um número finito +válido na plataforma atual. + +:: + + var $validate = array( + 'number' => array( + 'rule' => array('range', 0, 10), + 'message' => 'Por favor coloque um número entre 0 e 10' + ) + ); + +ssn +--- + +SSN valida números de segurança social dos Estados Unidos (us), +Dinamarca (dk) e dos Países Baixos (nl). Para outro formato de número de +segurança social, você pode fornecer uma expressão regular. + +:: + + var $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + +url +--- + +Essa regra verificar por formatos de URL válidos. Suporta http(s), +ftp(s), file, news, e protocolos gopher. + +:: + + var $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + +Regras de Validação Customizadas +================================ + +Se você não está encontrando o que você precisa, você sempre poderá +criar regras para sua aplicação. Há duas maneiras de fazer isso: +definindo expressões regulares customizadas, ou criando métodos +customizados de validação. + +Validação com Expressão Regular Customizada +------------------------------------------- + +Se a técnica de validação que você precisa pode ser completada usando +expressão regular, você pode definir uma expressão regular como um campo +na regra de validação. + +:: + + var $validate = array( + 'login' => array( + 'rule' => array('custom', '/[a-z0-9]{3,}$/i'), + 'message' => 'Apenas letras e números, mínimo de 3 caracteres' + ) + ); + +O exemplo acima verifica se *login* contem apenas letras e números, com +o mínimo de três caracteres. + +Validação com Métodos Customizados +---------------------------------- + +As vezes verificar valores com expressões regulares não é o suficiente. +Por exemplo, você precisa garantir que um código promocional possa ser +usado apenas 25 vezes, você precisa adicionar um método customizado de +validação, como mostrado abaixo: + +:: + + array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'Esse código promocional já foi usados muitas vezes.' + ) + ); + + function limitDuplicates($data, $limite) { + $quantidade_existente = $this->find('count', array('conditions' => $data, 'recursive' => -1)); + return $quantidade_existente < $limite; + } + } + ?> + +Se você quer passar parâmetros para seus métodos customizados, adicione +mais elementos no *array* ‘rule’, e depois acesse esses parâmetros +(depois do parâmetro principal ``$data``) no seu método. + +Seu método pode estar no *model* (como mostrado acima), ou em um +*behavior* que o *model* implemente. Isso inclui métodos mapeados. + +Métodos no *model/behavior* são verificados primeiro para depois +procurar por um método na classe ``Validation``. Isso significa que você +pode sobrescrever um método existente (como ``alphaNumeric()``) a nível +de aplicação (adicionando o método no ``AppModel``), ou em qualquer +*model*. + +Validando Valores no Controle +============================= + +Enquanto normalmente você apenas usa o método *save* do *model*, talvez +as vezes você queira validar os valores sem salva-los. Por exemplo, +talvez você queira mostrar alguma informação extra para o usuário antes +de salvar os valores no banco de dados. Validar estes valores requer um +processo um pouco diferente do que simplesmente salva-los. + +Primeiro, defina os valores no *model*. + +:: + + $this->ModelName->set( $this->data ); + +Então verifique se os valores validaram, use o método ``validates()`` do +*model*, ele irá retornar *true* se validar e *false* se não validar. + +:: + + if ($this->ModelName->validates()) { + // lógica de validado + } else { + // lógica de não validado + } + +O método ``validates()`` chama o método ``invalidFields()`` para +preencher o ``validationErrors`` no *model*. O método +``invalidFields()`` também retorna os resultados. + +:: + + $errors = $this->ModelName->invalidFields(); // contem o array validationErrors + +É importante notar que os valores têm que estar definidos no *model* +antes para poderem ser validados. É diferente do método ``save()`` pois +permite que as informações sejam passados como parâmetro. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst new file mode 100644 index 0000000000000000000000000000000000000000..cbd1342c73cdb90c0cda93a7a4d18de889a4515a --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Debugging.rst @@ -0,0 +1,156 @@ +Depuração +######### + +Depuração é uma parte necessária e inevitável de qualquer ciclo de +desenvolvimento. Ainda que o CakePHP não ofereça quaisquer ferramentas +diretamente relacionada a qualquer IDE ou editor, o CakePHP dispôe de +várias ferramentas de depuração e exibição do que está sendo executado +internamente por sua aplicação. + +Depuração Básica +================ + +debug($var, $showHTML = false, $showFrom = true) + +A função debug() é uma função que está disponível globalmente e que +funciona de maneira semelhante à função print\_r() do PHP. A função +debug() permite que você exibe o conteúdo de uma variável de diferentes +maneiras. Primeiro, se você quiser que os dados sejam exibidos num +formato HTML, defina o segundo parâmetro como true. A função também +exibe a linha e o arquivo que originaram o conteúdo por padrão. + +A saída desta função é exibida apenas se a variável debug do core +estiver definida para um valor maior que 0. + +Usando a Classe Debugger +======================== + +Para usar o depurador Debugger, primeiro certifique-se de que +Configure::read('debug') esteja definida com um valor maior que 0. + +dump($var) + +Despeja o conteúdo de uma variável para a tela. Este método irá exibir +todas as propriedades e métodos (se existirem) para a variável +informada. + +:: + + $foo = array(1,2,3); + + Debugger::dump($foo); + + // saída + array( + 1, + 2, + 3 + ) + + // objeto simples + $car = new Car(); + + Debugger::dump($car); + + // saída + Car:: + Car::colour = 'red' + Car::make = 'Toyota' + Car::model = 'Camry' + Car::mileage = '15000' + Car::acclerate() + Car::decelerate() + Car::stop() + +log($var, $level = 7) + +Cria um log detalhado da pilha de execução até o momento da chamada. O +método log() exibe dados de forma semelhante à que é feita pelo método +Debugger::dump(), mas para um arquivo debug.log ao invés de para o +buffer de saída. Note que seu diretório app/tmp directory (e seu +conteúdo) deve ter permissão de escrita pelo usuário do servidor web +para que o método log() funcione corretamente. + +trace($options) + +Retorna a pilha de execução atual. Cada linha da pilha inclui a listagem +dos métodos executados, incluindo o arquivo e a linha a partir de que a +chamada se originou. + +:: + + // Em PostsController::index() + pr( Debugger::trace() ); + + // saída + PostsController::index() - APP/controllers/downloads_controller.php, line 48 + Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 265 + Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 237 + [main] - APP/webroot/index.php, line 84 + +Acima está a pilha de execução gerada pela chamada a Debugger::trace() +em uma action do controller. A leitura da pilha de execução de baixo +para cima mostra a ordem das funções atualmente em execução (stack +frames). No exemplo acima, index.php chamou Dispatcher::dispatch(), que +por sua vez chamou Dispatcher::\_invoke(). O método \_invoke() então +chamou PostsController::index(). Esta informação é útil ao trabalhar com +operações recursivas ou pilhas de execução mais profundas, já que +identifica quais funções estão atualmente em execução no momento da +chamada à trace(). + +excerpt($file, $line, $context) + +Pega um excerto do arquivo em $path (que é um nome de arquivo absoluto), +destaca a linha de número $line com uma quantidade de linhas de contexto +($context) a seu redor. + +:: + + pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); + + // vai exibir o seguinte. + Array + ( + [0] => * @access public + [1] => */ + [2] => function excerpt($file, $line, $context = 2) { + + [3] => $data = $lines = array(); + [4] => $data = @explode("\n", file_get_contents($file)); + ) + +Por mais que este método seja usado internamente, ele pode ser útil se +você estiver criando suas próprias mensagens de erro ou entradas de log +para situações específicas. + +exportVar($var, $recursion = 0) + +Converte uma variável de qualquer tipo para uma string para uso na saída +de depuração. Este método também é usado pela maioria das conversões de +variáveis internas de Debugger e pode ser usada em seus próprios +depuradores também. + +invoke($debugger) + +Substitui o Debugger do CakePHP por um novo manipulador de erros. + +A Classe Debugger +================= + +A classe Debugger passou a fazer parte do CakePHP 1.2 e oferece ainda +mais opções para obtenção de informação de depuração. Há diversas +funções que são invocadas estaticamente e provêem exibição, log e função +de manipulação de erros. + +A Classe Debugger sobrescreve a manipulação de erros padrão do PHP, +substituindo-a com muito mais relatórios de erros. O manipulador de +erros do Debugger é usado por padrão no CakePHP. Como para todas as +funções de depuração, o Configure::debug deve estar definida com um +valor maior que 0. + +Quando um erro for lançado, o Debugger exibe informação para a página e +também cria uma entrada no arquivo error.log. O relatório de erro que é +gerado contém tanto uma pilha de execução quanto um excerto do código a +partir do qual o erro foi lançado. Clique no link "Error" para mostrar a +pilha de execução e no link "Code" para mostrar as linhas que provocaram +o erro. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..5c9ba665898e37e313d50a30add3c9058da6b647 --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Error-Handling.rst @@ -0,0 +1,89 @@ +Manipulação de Erros +#################### + +No caso de um erro irrecuperável em sua aplicação, é comum para o +processamento e mostrar uma página de erro ao usuário. Para poupar-lhe o +trabalho de ter código de manipulação de erros para isto em cada um de +seus controllers e componentes, você pode usar o método disponível no +CakePHP: + +``$this->cakeError(string $errorType [, array $parameters]);`` + +Chamar este método irá exibir uma página de erro ao usuário e +interromper qualquer processamento subsequente em sua aplicação. + +``parameters`` deve ser um array de strings. Se este array contiver +objetos (incluindo objetos do tipo Exception), eles serão convertidos em +strings. + +O CakePHP predefine um conjunto de tipos de erro, mas no momento em que +este manual era escrito, a maioria é utilizada apenas pelo próprio +framework. Um dos mais úteis para o programador em geral pode ser o bom +e velho erro 404. Este erro pode ser disparado chamando-se o método +desta forma: + +:: + + $this->cakeError('error404'); + +Ou, alternativamente, você pode fazer com que a página que reporte o +erro esteja em uma URL específica passando o parâmetro ``url``: + +:: + + $this->cakeError('error404', array('url' => 'alguma/outra.url')); + +Pode ser um pouco mais útil ao estender o manipulador de erros para +incluir tratamento para tipos de erros definidos pelo programador. +Manipuladores de erro de aplicação são semelhantes a ações em um +controller; você normamente passa parâmetros com o método set() que +estarão disponível na view e então renderiza o arquivo da view a partir +do diretório ``app/views/errors``. + +Crie um arquivo ``app/app_error.php`` com o seguinte conteúdo. + +:: + + + +Manipuladores para novos tipos de erro podem ser implementados +adicionando-se métodos nesta classe. Simplesmente crie um novo método +com o nome que você quiser usar como seu tipo de erro. + +Digamos que temos uma aplicação que escreve alguns arquivos para o disco +e que seja adequado reportar erros de gravação para o usuário. Não +queremos incluir o código para fazer isto em todas as diversas partes de +nossa aplicação, então é uma boa oportunidade de usar um novo tipo de +erro. + +Adicione um novo método em sua classe ``AppError``. Vamos incluir um +parâmetro chamado ``file`` que será o caminho do arquivo cuja escrita +teve problemas. + +:: + + function cannotWriteFile($params) { + $this->controller->set('file', $params['file']); + $this->_outputMessage('cannot_write_file'); + } + +Crie a view em ``app/views/errors/cannot_write_file.ctp`` + +:: + +

Erro ao gravar no arquivo

+

Não foi possível gravar o arquivo para o disco.

+ +...e lance o erro em seu controller/componente + +:: + + $this->cakeError('cannotWriteFile', array('file'=>'somefilename')); + +A implementação padrão de ``$this->_outputMessage()`` irá +apenas mostrar a view em ``views/errors/.ctp``. Se você +quiser sobrescrever este comportamento, você também pode redefinir o +método ``_outputMessage($template)`` em sua classe AppError. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst new file mode 100644 index 0000000000000000000000000000000000000000..fe718708f888b2d63f5ee50c9280bed66f022824 --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Internationalization-Localization.rst @@ -0,0 +1,209 @@ +Internacionalização & Localização +################################# + +Um dos melhores caminhos para que suas aplicações atinjam a um +público-alvo maior é fazer com que sua aplicação funcione em diversos +idiomas. Isso pode representar uma tarefa árdua, mas os recursos de +internacionalização e localização do CakePHP tornam isto muito mais +fácil. + +Primeiro, é importante que você entenda a terminologia. +*Internacionalização* refere-se à capacidade de uma aplicação ser +localizada. Já *Localização* refere-se à adaptação de uma aplicação para +atender as necessidades de um idioma específico (ou cultura), como, por +exemplo, um locale. Internacionalização e localização são abreviadas +como i18n e l10n, respectivamente; 18 e 10 representam a quantidade de +caracteres entre o primeiro e o último caractere. + +Internacionalizando Sua Aplicação +================================= + +Há alguns poucos passos a serem executados para transformar uma +aplicação mono-idioma para uma aplicação multi-idioma, sendo que o +primeiro passo é usar a função +`\_\_() `_ em +seu código. Abaixo segue um exemplo de algum código para uma aplicação +mono-idioma: + +:: + +

Posts

+ +Para internacionalizar seu código, tudo o que você precisa é demarcar as +strings com `a função de +tradução `_ como +assim: + +:: + +

+ +Se você não fizer mais nada, esses dois trechos de código são +funcionalmente idênticos - ambos irão enviar o mesmo conteúdo para o +navegador web. A `função +``__()`` `_ vai +traduzir a string passada se houver uma tradução disponível, ou então +apenas retorná-la inalterada em caso contrário. Funciona de forma +semelhante a qualquer outra implementação de +`Gettext `_ (tal como outras +funções de tradução, +como\ ```__d()`` `_, +```__n()`` `_ etc). + +Com seu código pronto para se tornar multi-idioma, o próximo passo é +criar seu `arquivo pot `_, que é o +modelo para todas as strings traduzíveis de sua aplicação. Para gerar +seu(s) arquivo(s) pot, apenas execute a tarefa `i18n +console `_, +que irá vasculhar todos os locais em que você utilizou a função de +traduçãoo em seu código e gerar o(s) arquivo(s) pot para você. Você +também pode re-executar esta tarefa de console a qualquer momento para +modificar as traduções em seu código. + +O(s) arquivo(s) pot em si não são usados pelo CakePHP, eles são modelos +usados para criar ou atualizar seus `arquivos +po `_, que contém efetivamente as +traduções. O Cake irá procurar por seus arquivos po dados no seguinte +local: + +:: + + /app/locale//LC_MESSAGES/.po + +O domínio padrão chama-se 'default', já sua pasta locale deve ser algo +parecido com: + +:: + + /app/locale/eng/LC_MESSAGES/default.po (Inglês) + /app/locale/fre/LC_MESSAGES/default.po (Francês) + /app/locale/por/LC_MESSAGES/default.po (Português) + +Para criar um editar seus arquivos po é recomendado que você *não* +utilize seu editor de texto. Para criar um arquivo po pela primeira vez +é possível copiar o arquivo pot para o local correto e apenas mudar a +extensão; *porém*, a menos que você esteja familiarizado com seu +formato, é muito fácil corromper o arquivo e torná-lo inválido ou mesmo +salvá-lo com o conjunto de caracteres incorreto (se você estiver +editando-o manualmente, utilize sempre UTF-8 para evitar problemas). +Existem ferramentas livres, como o `PoEdit `_, +que tornam a edição e atualização de seus arquivos po uma tarefa mais +fácil; especialmente no caso da atualização de um arquivo po existente a +partir de um recém-atualizado arquivo pot. + +Os códigos de três letras do locale estão de acordo com o padrão `ISO +639-2 `_, +apesar de que se você for criar locales regionais (en\_US, en\_GB, etc.) +o Cake vai usá-los apropriadamente. + +Há um limite de 1041 caracteres para cada valor de msgstr (favor citar a +fonte desta informação). + +Lembre-se que os arquivos po são úteis para mensagens curtas, se você +vir que terá que traduzir parágrafos longos ou mesmo páginas inteiras - +você deveria considerar implementar uma solução diferente, p.ex.: + +:: + + // Código em AppController. + function beforeFilter() { + $locale = Configure::read('Config.language'); + if ($locale && file_exists(VIEWS . $locale . DS . $this->viewPath)) { + // p.ex. usa /app/views/fre/pages/tos.ctp ao invés de /app/views/pages/tos.ctp + $this->viewPath = $locale . DS . $this->viewPath; + } + } + +ou + +:: + + // Código na view. + echo $this->element(Configure::read('Config.language') . '/tos') + +Localização no CakePHP +====================== + +Para modificar ou definir o idioma de sua aplicação, tudo o que você +precisa fazer é o seguinte: + +:: + + Configure::write('Config.language', 'fre'); + +Isso diz ao Cake que locale usar (se você usar um locale regional, como +fr\_FR, ele irá usar o código do locale do padrão `ISO +639-2 `_ como +segunda opção caso o locale regional principal não exista), você pode +modificar o idioma a qualquer momento, p.ex., em seu bootstrap, se você +estiver definindo o idioma padrão da aplicação, ou no beforeFilter de +seu (App) Controller se o idioma estiver especificado na requisição do +usuário, ou de fato em qualquer momento antes de você querer exibir uma +mensagem num idioma diferente. + +É uma boa ideia disponibilizar conteúdo público em múltiplos idiomas a +partir de uma url única - isso torna mais fácil para seus usuários (e +para os mecanismos de busca) encontrar o que elees estão procurando no +idioma que eles esperam. Há diversas maneira de se fazer isso, pode ser +com subdomínios específicos de idioma (como http://en.example.com, +http://fra.example.com, etc.) ou usando-se prefixos para a url, como é o +caso deste manual do Cookbook. Você também pode querer pegar a +informação de idioma a partir do user-agent do navegador, entre outras +coisas. + +Como mencionado na seção anterior, a exibição de conteúdo localizado é +feita usando-se a função de conveniência \_\_() ou uma das outras +funções de tradução que estão disponíveis globalmente, e provavelmente +elas serão melhor utilizadas em suas views. O primeiro parâmetro da +função é usado como o msgid definido nos arquivos po. + +Lembre-se de usar o parêmtro de retorno para os vários métodos ``__*`` +se você não quiser que a string seja exibida diretamente (echo interno). +Por exemplo: + +:: + + error( + 'Card.cardNumber', + __("errorCardNumber", true), + array('escape' => false) + ); + ?> + +Se você gostaria de ter todas as suas mensagens de erro de validação +traduzidas por padrão, uma solução simples poderia ser adicionar o +seguinte código a seu app\_model.php: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __($value, true)); + } + +O tarefa de console i18n não é capaz de determinar o id da mensagem para +o código do trecho acima, o que significa que você vai precisar +adicionar manualmente as entradas para seu arquivo pot (ou através de +seu próprio script). Para previnir a necessidade de editar seu arquivo +default.po(t) a cada vez que a tarefa de console i18n for executada, +você pode usar um domínio diferente como em: + +:: + + function invalidate($field, $value = true) { + return parent::invalidate($field, __d('validation_errors', $value, true)); + } + +Este trecho irá procurar uma entrada para ``$value`` no arquivo +validation\_errors.po. + +Há um outro aspecto sobre localização de sua aplicação que não foi +abordado pelo uso das funções de tradução, que são os formatos de data e +moeda. Não se esqueça de que o CakePHP é PHP :), sendo assim, para +definir o formato para tais coisas você vai precisar usar o +```setlocale`` `_. + +Se você passar um locale que não exista em seu computador para a função +```setlocale`` `_, sua chamada não terá +efeito. Você pode obter a lista de locales disponíveis executando o +comando $ locale -a em um terminal. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Logging.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Logging.rst new file mode 100644 index 0000000000000000000000000000000000000000..5f54bdac6819f337331f3529d9bb00a4023c272b --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Logging.rst @@ -0,0 +1,70 @@ +Log +### + +Por mais que as configurações da classe Configure do CakePHP possam +realmente ajudar você a visualizar o que está acontecendo por dentro de +sua aplicação, algumas vezes é importante ter logs de dados no disco +para descobrir o que está acontecendo. Num mundo que está se tornando +cada vez mais dependente de tecnologias como SOAP e AJAX, a depuração +pode ser bem dificultada. + +Log de erros também pode ser uma maneira de visualizar o que acontece +com sua aplicação ao longo do tempo. Que termos de busca estão sendo +usados? Que tipos de erros estão sendo mostrados a meus usuários? Quão +frequente executada é uma determinada consulta? + +Fazer log de dados no CakePHP é fácil - a função the log() é parte da +classe Object, que é o ancestrarl comum para a maioria das classes do +CakePHP. Se o contexto for uma classe do CakePHP (Models, Controllers, +Components... praticamente tudo), você pode registrar um log dos dados. + +Usando a função log +=================== + +A função log() leva dois parâmetros. O primeiro é a mensagem que você +gostaria de escrever no arquivo de log. Por padrão, esta mensagem de +erro é escrita para o arquivo log de erro encontrado em +app/tmp/logs/error.log. + +:: + + // Executar isto dentro de uma classe CakePHP... + + $this->log("Alguma coisa não deu certo!"); + + // ...resulta nisto sendo anexado ao arquivo app/tmp/logs/error.log + + 2007-11-02 10:22:02 Error: Alguma coisa não deu certo! + +O segundo parâmetro é usado para definir o tipo do log de erro cuja +mensagem você quer escrever. Se não informado, o padrão para este +segundo parâmetro é LOG\_ERROR, que registra o log de erro previamente +mencionado. Você pode definir este segundo parâmetro, p.ex., para +LOG\_DEBUG para escrever suas mensagem para um arquivo de log +alternativo de depuração que será encontrado em app/tmp/logs/debug.log: + +:: + + // Executar isto dentro de uma classe CakePHP... + + $this->log('Uma mensagem de depuração.', LOG_DEBUG); + + // ...resulta nisto sendo anexado ao arquivo app/tmp/logs/debug.log (ao invés de error.log) + + 2007-11-02 10:22:02 Error: Uma mensagem de depuração. + +Você também pode especificar um nome diferente para o arquivo de log +definindo um nome de arquivo como segundo parâmetro. + +:: + + // Executar isto dentro de uma classe CakePHP... + + $this->log('Uma mensagem especial do registro de log da atividade.', 'activity'); + + // ...resulta nisto sendo anexado ao arquivo app/tmp/logs/activity.log (ao invés de error.log) + + 2007-11-02 10:22:02 Activity: Uma mensagem especial do registro de log da atividade. + +Seu diretório app/tmp deve ter permissão de escrita para o usuário do +servidor web para que os recursos de log funcionem corretamente. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst new file mode 100644 index 0000000000000000000000000000000000000000..b894192e9d9f7ee0577e7ebcd8c723a2a1001b56 --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Pagination.rst @@ -0,0 +1,364 @@ +Paginação +######### + +Uma das principais dificuldades de criar aplicações Web flexíveis e +amigáveis é a de criar uma interface de usuário intuitiva. Muitas +aplicações tendem a crescer rapidamente em tamanho e complexidade e +tanto designers como programadores acabam tendo que lidar com o problema +de mostrar centenas ou milhares de registros. Refatoração leva tempo e o +desempenho e a satisfação do usuário podem ser afetados. + +Mostrar um número razoável de registros por página tem sido uma parte +crítica das aplicações e geralmente causam muitas dores de cabeça para +os desenvolvedores. CakePHP facilita o trabalho do desenvolvedor +provendo uma forma rápida e fácil para paginar os dados. + +O PaginatorHelper oferece uma ótima solução porque é muito fácil de +usar. Além da paginação ele também traz consigo algumas funcionalidades +de ordenação. Por último, mas não menos importante, ordenação e +paginação Ajax também são suportadas. + +Configuração no Controller +========================== + +No controller, começamos definindo os padrões de paginação na variável +*$paginate* do controller. É importante notar aqui que a entrada "order" +deve estar presente na estrutura dada do array. + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +Você também pode incluir outras opções do find(), como, p.ex., *fields*: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'fields' => array('Post.id', 'Post.created'), + 'limit' => 25, + 'order' => array( + 'Post.title' => 'asc' + ) + ); + } + +Outras chaves que podem ser incluídas no array *$paginate* são +semelhantes aos parâmetros do método *Model->find('all')*, quais sejam: +*conditions*, *fields*, *order*, *limit*, *page*, *contain*, e +*recursive*. De fato, você pode definir mais do que um conjunto de +padrões de paginação no controller, você apenas denomina as peças do +array depois do model que você deseja configurar: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'Recipe' => array (...), + 'Author' => array (...) + ); + } + +Exemplo de sintaxe usando o ContainableBehavior: + +:: + + class RecipesController extends AppController { + + var $paginate = array( + 'limit' => 25, + 'contain' => array('Article') + ); + } + +Uma vez a variável *$paginate* tenha sido definida, podemos chamar o +método *paginate()* nas actions do controller. Este método retorna +resultados de um *find()* paginados a partir do model e obtém +estatísticas adicionais de paginação que são passadas para a View por +debaixo dos panos. Este método também adiciona o PaginatorHelper à lista +de helpers em seu controller, se ainda não tiver sido adicionado. + +:: + + function list_recipes() { + // semelhante a find('all'), mas com resultados paginados + $data = $this->paginate('Recipe'); + $this->set('data', $data); + } + +Você pode filtrar os registros passando condições como segundo parâmetro +para o método ``paginate()``. + +:: + + $data = $this->paginate('Recipe', array('Recipe.title LIKE' => 'a%')); + +Ou você também pode definir a chave *conditions* no array ``$paginate``. + +Paginação nas Views +=================== + +Fica a seu critério decidir como exibir os registros para o usuário, mas +na maioria das vezes isso normalmente é feito com tabelas HTML. Os +exemplos abaixo assumem um layout tabular, mas o PaginatorHelper +disponível nas views não se restringe em absoluto a nenhum formato de +exibição. + +Veja os detalhes sobre o +`PaginatorHelper `_ na +API. + +Como mencionado, o PaginatorHelper também fornece recursos de ordenação +que podem ser facilmente integrados às colunas de suas tabelas de dados: + +:: + + // app/views/recipes/list_recipes.ctp +
DateTitleActive
DateTitleActive
-attributes. + +:: + + tableCells(array( + array('Jul 7th, 2007', 'Best Brownies', 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', 'No'), + )); + ?> + + //Output +
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
RedApple
OrangeOrange
YellowBanana
+ + + + + + + + + + +
sort('ID', 'id'); ?>sort('Title', 'title'); ?>
+ +Os links exibidos a partir do método sort() do PaginatorHelper permite +que os usuários cliquem nos cabeçalhos da tabela para trocar a ordenação +de dados para o campo dado. + +Também é possível ordenar uma coluna baseada em associações: + +:: + + + + + + + + + + + + +
sort('Title', 'title'); ?>sort('Author', 'Author.name'); ?>
+ +O ingrediente final para a exibição da paginação nas views é a adição do +navegador de páginas, também disponível no PaginationHelper. + +:: + + + numbers(); ?> + + prev('« Anterior ', null, null, array('class' => 'disabled')); + echo $paginator->next(' Próximo »', null, null, array('class' => 'disabled')); + ?> + + counter(); ?> + +Os dizeres exibidos pelo método counter() também podem ser +personalizados com o uso de marcadores especiais: + +:: + + counter(array( + 'format' => 'Página %page% de %pages%, exibindo %current% registros de um total de %count%, exibindo do registro %start% até o %end%' + )); + ?> + +Para passar todos os argumentos URL para funções do paginador, adicione +o seguinte à sua view: + +:: + + $paginator->options(array('url' => $this->passedArgs)); + +Elementos de rota que não forem argumentos parametrizados (named +arguments) devem ser mesclados manualmente com ``$this->passedArgs``: + +:: + + // para urls como http://www.example.com/en/controller/action + // que são roteadas como Router::connect('/:lang/:controller/:action/*', array(),array('lang'=>'ta|en')); + $paginator->options(array('url'=>array_merge(array('lang'=>$lang),$this->passedArgs))); + +Ou você pode especificar quais parâmetros serão passados manualmente: + +:: + + $paginator->options(array('url' => array("0", "1"))); + +AJAX Pagination +=============== + +É muito simples incorporar funcionalidades Ajax na paginação. O único +código extra requerido é a inclusão da biblioteca Javascript Prototype, +e a especificação de uma DIV para ser atualizada com o conteúdo de +paginação (ao invés de reler a página). Pode-se também definir o +parâmetro indicator contendo o id de uma DIV que será mostrada quando o +conteúdo da paginação estiver sendo lido e ocultado ao término de sua +leitura. + +Não se esqueça de adicionar o componente RequestHandler para detectar as +chamadas Ajax em seu controller. + +:: + + var $components = array('RequestHandler'); + +Mudanças de Layout +------------------ + +Primeiramente, nós vamos incluir a biblioteca Prototype no cabeçalho, +definir nossa imagem indicadora de status (spinner.gif) e definir o DIV +principal que conterá nossos dados, "content". + +Aqui você vê como layout que inclui estes elementos se parece +(parcialmente): + +:: + + + <?php echo $title_for_layout; ?> + link(array('prototype')); ?> + + + +
+ +
+ +
+
+ + + +Mudanças na View +---------------- + +A única configuração extra para a paginação Ajax é feita usando-se o +método options() do PaginationHelper, a partir do qual se especificam os +parâmetros Ajax requeridos. Neste caso, estamos definindo que todos os +links de paginação devem atualizar o elemento com o ID 'content' com os +dados resultantes e queremos que 'spinner' apareça como indicador de +carregamento. + +Se a chave 'update' não for especificada, o PaginationHelper irá exibir +links não-Ajax para ordenação e paginação. + +:: + + options(array('update' => 'content', 'indicator' => 'spinner')); + + echo $paginator->prev('<< Anterior', null, null, array('class' => 'disabled')); + + echo $paginator->next('Próximo >>', null, null, array('class' => 'disabled')); + ?> + + + counter(); ?> + +Paginação com Consultas Personalizadas +====================================== + +FIXME: Por favor, incluir um exemplo que demonstre a necessidade de se +personalizar as consultas de paginação. + +Se você precisar criar consultas personalizadas para gerar os dados que +quer paginar, você pode sobrescrever os métodos ``paginate()`` e +``paginateCount()`` do model, usados pela lógica de paginação do +controller. + +Antes de prosseguir, verifique se você realmente não consegue fazer o +que você deseja com os métodos padrão do core model. + +O método ``paginate()`` utiliza os mesmos parâmetros que o +``Model::find()``. Para usar sua própria versão/sua própria lógica, +sobrescreva este método no model a partir do qual você quer obter os +dados. + +:: + + /** + * Método paginate sobrescrito - agrupa pelos campos "week", "away_team_id" e "home_team_id" + */ + function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) { + $recursive = -1; + $group = $fields = array('week', 'away_team_id', 'home_team_id'); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')); + } + +Você também precisa sobrescrever o método ``paginateCount()``, que é um +método que utiliza os mesmos argumentos que o ``Model::find('count')``. +O exemplo abaixo utiliza alguns recursos específicos para banco +Postgres, então atente para fazer ajustes para o banco de dados que você +estiver utilizando. + +:: + + /** + * Método paginateCount sobrescrito. + */ + function paginateCount($conditions = null, $recursive = 0, $extra = array()) { + $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games"; + $this->recursive = $recursive; + $results = $this->query($sql); + return count($results); + } + +O leitor mais atento vai perceber que o método paginate que definimos +não era realmente necessário - tudo que você precisaria fazer seria +adicionar a palavra-chave na variável de classe ``$paginate`` do +controller. + +:: + + /** + * Inclui a cláusula GROUP BY. + */ + var $paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + /** + * Ou, fazendo "on-the-fly" dentro da própria action. + */ + function index() { + $this->paginate = array( + 'MyModel' => array('limit' => 20, + 'order' => array('week' => 'desc'), + 'group' => array('week', 'home_team_id', 'away_team_id')) + ); + +No entanto, ainda seria necessário sobrescrever o método +``paginateCount()`` para obter um valor correto. diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/REST.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/REST.rst new file mode 100644 index 0000000000000000000000000000000000000000..de3e4bd4ae4e9dd312d0ce5ddef46366165c0651 --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/REST.rst @@ -0,0 +1,188 @@ +REST +#### + +Muitos novos programadores estão compreendendo a necessidade de abrir +suas funcionalidades de suas aplicações a um público maior. Oferecer +acesso fácil e ilimitado à API principal de sua aplicação pode ajudar na +aceitação de sua plataforma, além de permitir a criação de produtos +derivados (mashups) e facilitar a integração com outros sistemas. + +Por mais que existam outras soluções, REST é uma grande maneira de +prover acesso fácil à lógica que você criou em sua aplicação. É simples, +normalmente baseado em XML (e estamos falando de XML bem simples, nada +parecido com um envelope SOAP) e depende dos cabeçalhos HTTP para +direcionamento. Expor uma API usando-se REST no CakePHP é simples. + +Configuração Simples +==================== + +O jeito mais rápido para utilizar REST em sua aplicação é adicionar +algumas linhas em seu arquivo routes.php, que é econtrado em app/config. +O objeto Router possui um método chamado mapResources(), que é utilizado +para configurar algumas rotas padrões, estas possibilitam o acesso REST +em suas classes de controle (controllers). Se desejamos permitir acesso +REST para um banco de dados de receitas (recipe), nós faríamos algo +assim: + +:: + + //Em app/config/routes.php... + + Router::mapResources('recipes'); + Router::parseExtensions(); + +A primeira linha cria uma série de rotas padrões para acesso REST de +forma simples. Estas rotas são sensíveis ao método HTTP solicitado. A +tabela abaixo descreve as rotas criadas. + ++---------------+----------------+----------------------------------+ +| Método HTTP | URL | Ação do Controle invocada | ++===============+================+==================================+ +| GET | /recipes | RecipesController::index() | ++---------------+----------------+----------------------------------+ +| GET | /recipes/123 | RecipesController::view(123) | ++---------------+----------------+----------------------------------+ +| POST | /recipes | RecipesController::add() | ++---------------+----------------+----------------------------------+ +| PUT | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ +| DELETE | /recipes/123 | RecipesController::delete(123) | ++---------------+----------------+----------------------------------+ +| POST | /recipes/123 | RecipesController::edit(123) | ++---------------+----------------+----------------------------------+ + +A classe de rotas do CakePHP utiliza diferentes indicadores para +detectar o método HTTP utilizado. Abaixo é demonstrado a ordem de +avaliação deste indicadores: + +#. A variável *\_method* enviada junto ao POST +#. O X\_HTTP\_METHOD\_OVERRIDE +#. O cabeçalho REQUEST\_METHOD + +A variável do POST *\_method* é útil ao utilizar o navegador como um +cliente REST (ou qualquer outra coisa que possa enviar um POST +facilmente). Apenas atribua o valor da variável \_method para o tipo da +requisição HTTP você deseja emular. + +Com as rotas prontas para mapear as requisições REST para as ações de um +certo controle, nós podemos criar a lógica em nossas ações do controle. +Uma classe de controle básica é parecida com está aqui: + +:: + + // controllers/recipes_controller.php + + class RecipesController extends AppController { + + var $components = array('RequestHandler'); + + function index() { + $recipes = $this->Recipe->find('all'); + $this->set(compact('recipes')); + } + + function view($id) { + $recipe = $this->Recipe->findById($id); + $this->set(compact('recipe')); + } + + function edit($id) { + $this->Recipe->id = $id; + if ($this->Recipe->save($this->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + + function delete($id) { + if($this->Recipe->delete($id)) { + $message = 'Deleted'; + } else { + $message = 'Error'; + } + $this->set(compact("message")); + } + } + +Uma vez que foi adicionado a chamada ao método +Router::parseExtensions(), as rotas do CakePHP estão prontas para servir +diferentes visões baseadas em diferentes tipos de requisições. Como +estamos lidando com requisições REST, as visões são do tipo XML. Nós +gravaremos as arquivos da camada de visão para nosso RecipesController +dentro de app/views/xml. Nós podemos utilizar também o XmlHelper para +proporcinar uma saída rápida e simples de XML em nossa visão. Abaixo é +demonstrado como a nossa index ficará: + +:: + + // app/views/recipes/xml/index.ctp + + + serialize($recipes); ?> + + +Usuários experientes em CakePHP notam que nós não incluimos o XmlHelper +no array de helpers do RecipesController. Isto foi por propósito - +quando servimos diferentes tipos de *content type* usando +parseExtensions(), o CakePHP automaticamente procura o helper que se +adequa com o tipo. Como nós estamos usando XML como o *content type*, o +XmlHelper é automaticamente lido para nosso uso na visão. + +O XML renderizado seguirá o modelo abaixo: + +:: + + + + + + + + + + + + +Criar a lógica para a ação *edit* é um pouco mais complicado, mas não +tanto. Uma vez que sua API retorna XML, é uma escolha natural receber +XML como entrada. Nada a se preocupar, pois o RequestHandler e a classe +Router fazem as coisas muito mais simples. Se uma requisição POST ou PUT +contém um *content-type* XML, então a entrada é passada para uma +instancia do objeto Xml do Cake, este faz o parse do xml e o transforma +em um array, que é atribuido a propriedade $data do controle. Por causa +deste recurso, lidar com XML e dados POST em paralelo é muito simples: +nenhuma alteração é necessária no código da classe de controle ou na +classe de modelo. Tudo o que ocê precisa estará em $this->data. + +Roteamento REST Personalizado +============================= + +Se as rotas padrão criadas pelo mapResources() não funcionarem para +você, utilize o método Router::connect() para definir um conjunto +específico de rotas REST. O método connect() permite a você definir um +conjunto de diferentes opções para uma dada URL. O primeiro parâmetro é +a URL em si e o segundo parâmetro permite a você informar tais opções. O +terceiro parâmetro deste método permite que você especifique padrões de +expressão regular para ajudar o CakePHP a identificar certos marcadores +na URL especificada. + +Vamos apresentar um exemplo simples aqui e deixar que você ajuste esta +rota para seus outros propósitos RESTful. Aqui você vê como nossa rota +REST deve se parecer, sem utilizar mapResources(): + +:: + + Router::connect( + "/:controller/:id", + array("action" => "edit", "[method]" => "PUT"), + array("id" => "[0-9+]") + ) + +Técnicas avançadas de roteamento também já foram abordadas noutras +oportunidades neste Cookbook, então iremos nos focar na parte mais +importante para nossos propósitos aqui: a chave [method] do array +options no segundo parâmetro. Uma vez que esta chave esteja definida, a +rota especificada irá funcionar apenas para o método de requisição HTTP +dado (que pode ser GET, POST, DELETE, etc.) diff --git a/pt/The-Manual/Common-Tasks-With-CakePHP/Testing.rst b/pt/The-Manual/Common-Tasks-With-CakePHP/Testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..d2a944b61cd7daf99ba32be8da24f189dea6f74c --- /dev/null +++ b/pt/The-Manual/Common-Tasks-With-CakePHP/Testing.rst @@ -0,0 +1,1096 @@ +Testes +###### + +A partir da versão 1.2, o CakePHP passou a dar suporte a um intuitivo +framework de testes embutido dentro do próprio CakePHP. Este framework +de testes é uma extensão do SimpleTest para PHP. Esta seção irá discutir +como preparar para realização de testes e como criar e executar testes +em suas aplicações CakePHP. + +Preparando para testar +====================== + +Pronto para começar a testar? Ótimo! Então vamos lá! + +Instalando o SimpleTest +----------------------- + +Instalando o SimpleTest + +O framework de testes disponível com o CakePHP 1.2 foi construído com +base no framework de testes SimleTest. O SimpleTest não é +disponibilizado numa instalação padrão do CakePHP, então primeiramente +precisamos fazer obtê-lo. Você pode fazer o download do SimpleTest aqui: +`http://simpletest.sourceforge.net/ `_. + +Pegue a versão mais recente e descompacte o conteúdo para sua pasta +vendors, ou para app/vendors, dependendo de sua preferência. Agora então +deve haver um diretório vendors/simpletest dentro do qual estão todos os +arquivos e pastas do SimpleTest. Lembre-se de ter definido o nível de +DEBUG de sua aplicação pelo menos em 1 em seu arquivo +app/config/core.php antes de executar quaisquer testes! + +Se você não tiver uma conexão de testes definida em seu +app/config/database.php, as tabelas de testes serão criadas com um +prefixo ``test_suite_`` no nome. Você pode criar uma conexão ``$test`` +na sua base de dados para conter suas tabelas de testes com uma +configuração parecida com esta abaixo: + +:: + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'dbhost', + 'login' => 'dblogin', + 'password' => 'dbpassword', + 'database' => 'databaseName' + ); + +Se a base de dados test estiver disponível e se o CakePHP puder conectar +a ela, todas as tabelas de testes serão criadas nesta base de dados. + +Executando casos de teste principais +------------------------------------ + +Os pacotes do CakePHP 1.2 não vêm com casos de testes principais do +núcleo do framework. Para obter estes testes você precisa fazer o +download do pacote do CakePHP disponibilizado diariamente (nightly) +disponível em +`http://cakephp.org/downloads/nightly/1.2.x.x `_ +ou obtê-los dos fontes do Subversion, fazendo checkout do ramo 1.2.x.x +do repositório. (Para mais detalhes sobre como obter o CakePHP, veja +`/view/30/Getting-CakePHP `_ + +Para adicionar os tests principais do CakePHP à sua aplicação, +descompacte o pacote diário (nightly) em algum local temporário. +Verifique a subpasta ``1.2.x.x_dd.mm.yyyy/cake/tests`` e copie-a +inteiramente para dentro da pasta ``/cake/tests`` de sua aplicação +CakePHP. + +Feito isto, então os testes podem ser acessados via browser acessando-se +http://seu.dominio.cake/test.php - dependendo de sua instalação +específica do CakePHP. Tente executar um dos grupos de testes do núcleo +(Core Tests) clicando no link correspondente. Executr um grupo de testes +pode demorar um pouco, mas ao final você eventualmente deve ver algo +como "2/2 test cases complete: 49 passes, 0 fails and 0 exceptions.". + +Parabéns, agora você está pronto para começar a escrever seus próprios +casos de testes para sua aplicação! + +Visão geral sobre testes - Testes unitários e Testes funcionais (web) +===================================================================== + +O framework de testes do CakePHP dá suporte a dos tipos de testes. Sejam +Testes Unitários, em que você testa pequenas partes de seu código, tais +como um método dentro de um componente ou uma action em um controller. O +outro tipo de testes suportado é o Teste Funcional (Web Test), em que +você automatiza o trabalho de testar sua aplicação navegando nas págnas, +preenchendo formulários, clicando em links e assim por diante. + +Preparando dados de teste +========================= + +Entendendo fixtures +------------------- + +Ao testar código que depende de modelos e de dados, pode-se usar o que +chamamos de **fixtures** como uma forma de gerar dados temporários em +tabelas carregadas com dados de registros de exemplo que podem ser +usados apenas para teste. A vantagem de usar fixtures é que seu teste +não tem como corromper dados reais em produção. Além disso, você pode +começar a testar seu código antes carregar conteúdo de dados efetivos em +sua aplicação. + +O CakePHP tenta usar a conexão ``$test`` definida em seu arquivo de +configuração app/config/database.php. Se esta conexão não existir ou não +for possível usá-la, a conexão ``$default`` será então utilizada. Neste +caso, o CakePHP adicionará "test\_suite\_" como prefixo de suas próprias +tabelas para evitar colisão com de nomes com tabelas existentes. + +Durante o curso de um caso de testes baseado em fixtures, o CakePHP +realiza os seguintes passos: + +#. Cria tabelas para cada um dos fixtures necessários +#. Popula as tabelas com dados, se houver dados definidos no fixture +#. Executa os métodos de teste +#. Empties the fixture tables +#. Remove as tabelas de fixtures da base de dados + +Criando fixtures +---------------- + +Para criar um fixture, você vai definir principalmente duas coisas: como +a tabela é criada (quas campos são parte da tabela) e quais registros de +testes serão inicialmente populados nas tabelas. Vamos então criar nosso +primeiro fixture, que será usado para testar nosso model Article. Crie +um arquivo chamado **article\_fixture.php** em seu diretório +**app/tests/fixtures** com o seguinte conteúdo: + +:: + + array('type' => 'integer', 'key' => 'primary'), + 'title' => array('type' => 'string', 'length' => 255, 'null' => false), + 'body' => 'text', + 'published' => array('type' => 'integer', 'default' => '0', 'null' => false), + 'created' => 'datetime', + 'updated' => 'datetime' + ); + var $records = array( + array ('id' => 1, 'title' => 'Primeiro artigo', 'body' => 'Texto do primeiro artigo', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Segundo artigo', 'body' => 'Texto do segundo artigo', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Terceiro artigo', 'body' => 'Texto do terceiro artigo', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Usamos o atribut $fields para especificar quais campos serão parte desta +tabela e como são definidos. O formato usado para definir estes campos é +o mesmo usado pela função **generateColumnSchema()** definida nas +classes do mecanismo de base de dados do Cake (por exemplo, no arquivo +dbo\_mysql.php). Vejamos os atributos disponíveis que um campo pode +levar e seus significados: + +type + tipo de dados interno do CakePHP. Atulmente são suportdos: string + (mapeia para VARCHAR), text (mapeia para TEXT), integer (mapeia para + INT), float (mapeia para FLOAT), datetime (mapeia para DATETIME), + timestamp (mapeia para TIMESTAMP), time (mapeia para TIME), date + (mapeia para DATE) e binary (mapeia para BLOB) +key + defina como "primary" para fazer o campo como AUTO\_INCREMENT e seja + a PRIMARY KEY da tabela. +length + define o comprimento específico que o campo deve ter. +null + atribua true (para dizer que o campo pode conter valores NULL) ou + false (se o campo dever ser NOT NULL) +default + o valor padrão que o campo deve ter se nada for informado. + +Por último podemos definir um conjunto de registros que serão populador +depois que a tabela de teste for criada. O formato para definição de +registros é bastante intuitivo e não precisa de muita explicação. Apenas +tenha em mente que cada registro no array $records deve ter um índice +para **cada um dos campos** especificados no array $fields. Se um campo +em um dado registro precisar assumir um valor NULL, defina +explicitamente o campo específico como índice e seu valor como NULL. + +Importando definições e registros das tabelas +--------------------------------------------- + +Sua aplicação pode já ter models funcionando com dados reais associados +a eles e você pode decidir testar seus models com tais dados. Seria um +grande retrabalho ter de replicar a definição da tabela e/ou os +registros presentes para seus fixtures. Felizmente, é possível fazer com +que a definição de tabelas e/ou os registros presentes sejam utilizados +por um dado fixture a partir de um model exstente ou mesmo a partir +diretamente das tabelas no banco de dados. + Vamos começar com um exemplo. Considerando que você já tenha um model +chamado Article disponível em sua aplicação (referente à tabela no banco +chamada articles), modifique o fixture criado na seção anterior +(**app/tests/fixtures/article\_fixture.php**) para: + +:: + + + + +Esta declaração indica que a suíte de testes deve importar a definição +de tabela a partir da tabela que está linkada ao model chamado Article. +Você pode usar qualquer model disponível em sua aplicação. A declaração +acima não vai importar os registros presentes na tabela, mas se quiser, +você pode importar também os dados da tabela alterando o fixture: + +:: + + 'Article', 'records' => true); + } + ?> + +Se por outro lado você já tiver uma tabela criada mas sem o respectivo +model para ela, você pode então fazer com que que as informações sejam +importadas diretamente da tabela. Por exemplo: + +:: + + 'articles'); + } + ?> + +Isto irá importar a definição de tabela para o fixture a partir de uma +tabela chamada 'articles' usando a conexão 'default' do CakePHP. Se você +quiser trocar a conexão a ser utilizada, você pode faze: + +:: + + 'articles', 'connection' => 'other'); + } + ?> + +Como é usada a conexão da base de dados do CakePHP, caso haja algum +prefixo de tabela ele será usado automaticamente quando as informações +forem lidas da tabela. Os dois exemplos de código mostrados acima não +importam os registros da tabela. Para forçar que o fixture importe +também os registros de dados, modifique-o para: + +:: + + 'articles', 'records' => true); + } + ?> + +Você também pode importar suas definições de tabelas a partir de um +model ou de uma tabela existentes mas tendo seus registros definidos +diretamente no fixture tal como visto na seção anterior. Por exemplo: + +:: + + 1, 'title' => 'Primeiro artigo', 'body' => 'Texto do primeiro artigo', 'published' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + array ('id' => 2, 'title' => 'Segundo artigo', 'body' => 'Texto do segundo artigo', 'published' => '1', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'), + array ('id' => 3, 'title' => 'Terceiro artigo', 'body' => 'Texto do terceiro artigo', 'published' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31') + ); + } + ?> + +Criando testes +============== + +Primeiro, vamos conferir o conjunto de regras ou orientações +relacionadas a testes: + +#. Os arquivos PHP contendo testes devem ficar em + **app/tests/cases/[alguma\_pasta]**. +#. Os nomes dos arquivos de teste devem terminar em **.test.php** ao + invés de apenas em .php. +#. As classes contendo testes deve estender de **CakeTestCase** ou de + **CakeWebTestCase**. +#. O nome de qualquer método contendo um teste (quer dizer, contendo + asserções) devem começar com **test**, como por exemplo + **testPublished()**. + +Quando você criar um caso de teste, você pode executá-lo navegando até +**http://your.cake.domain/cake\_folder/test.php** (dependendo de como +esteja sua instalação), clicando em casos de teste da aplicação (App +Test Cases) e então clicando no link de seu arquivo especifico. + +Métodos de Callback do CakeTestCase +----------------------------------- + +Se você quiser definir alguma lógica logo antes ou depois de um método +individual do CakeTestCase, ou mesmo antes ou depois de seu CakeTestCase +todo, os seguintes callbacks estão disponíveis: + +**start()** + Primeiro método chamado em um *caso de teste*. + +**end()** + Último método chamado em um *caso de teste*. + +**startCase()** + Chamado logo antes de um *caso de teste* ser iniciado. + +**endCase()** + Chamado logo depois que um *caso de teste* tenha sido executado. + +**before($method)** + Anuncia o início de um *caso de teste*. + +**after($method)** + Anuncia o final de um *caso de teste*. + +**startTest($method)** + Chamado logo antes que um *método de teste* seja executado. + +**endTest($method)** + Chamado logo depois que um *método de teste* tenha terminado. + +Testando models +=============== + +Criando um caso de teste +------------------------ + +Digamos que já temos nosso model Article definido em +app/models/article.php, parecido com isto: + +:: + + name . '.published' => 1 + ); + + return $this->findAll($conditions, $fields); + } + } + ?> + +Agora queremos configurar um teste que iremos usar para este model, mas +através de fixtures, para testar alguma funcionalidade no model. A suíte +de teste do CakePHP carrega um conjunto mínimo de arquivos (para manter +os testes isolados), assim temos de começar carregando o model em +questão a ser testado (neste caso, o model Article) e então informar a +suíte de teste que queremos testar este model especificando que +configuração de banco de dados deve ser usada. A suíte de teste do +CakePHP habilita uma configuração de banco de dados chamada +**test\_suite** que é usada por todos os models que dependem de +fixtures. Definir $useDbConfig para esta configuração fará com que o +CakePHP saiba que este model usa a conexão de banco de dados da suíte de +teste. + Como queremos reutilizar todo o código de nosso model existente, vamos +criar um model de teste que estenda de Article, definindo $useDbConfig e +$name apropriadamente. Agora crie um arquivo chamado +**article.test.php** em seu diretório **app/tests/cases/models**, com o +seguinte conteúdo: + +:: + + + +Criamos assim o ArticleTestCase. Na veriável **$fixtures**, definimos o +conjunto de fixtures que iremos usar. + +Se seu model estiver associado com outros models, você precisará incluir +**TODOS** os fixtures para cada model associado, mesmo se você não for +utilzá-los. Por exemplo: dados os models A, B, C e D, se tivermos os +relacionamentos que A hasMany B, B hasMany C e C hasMany D, ao criar um +caso de teste ATestCase, você terá que incluir fixtures para os models +A, B, C e D. + +Criando um método de teste +-------------------------- + +Vamos agora adicionar um método para testar a função published() no +model Article. Edite o arquivo +**app/tests/cases/models/article.test.php** deixando-o para algo como o +mostrado abaixo: + +:: + + Article = ClassRegistry::init('Article'); + + $result = $this->Article->published(array('id', 'title')); + $expected = array( + array('Article' => array( 'id' => 1, 'title' => 'Primeiro artigo' )), + array('Article' => array( 'id' => 2, 'title' => 'Segundo artigo' )), + array('Article' => array( 'id' => 3, 'title' => 'Terceiro artigo' )) + ); + + $this->assertEqual($result, $expected); + } + } + ?> + +Como você pode ver, adicionamos um método chamado **testPublished()** ao +nosso caso de teste. Começamos criando uma instância de nosso fixture +baseada no model **Article**, e então executamos nosso método +**published()**. Atribuímos à **$expected** aquilo que esperamos ser o +resultado correto (que já sabemos, uma vez que nós mesmos definimos +quais registros são inicialmente populados na tabela articles). Então +verificamos se o resultado é igual a o que esperamos com uma chamada ao +método **assertEqual**. Veja a seção `Criando +testes `_ para informações sobre como +executar os testes. + +Testando controllers +==================== + +Criando um caso de teste +------------------------ + +Digamos que você tenha um típico ArticlesController, com seu model +correspondente, como algo parecido com isto: + +:: + + data)) { + $this->Article->save($this->data); + } + if (!empty($short)) { + $result = $this->Article->findAll(null, array('id', + 'title')); + } else { + $result = $this->Article->findAll(); + } + + if (isset($this->params['requested'])) { + return $result; + } + + $this->set('title', 'Articles'); + $this->set('articles', $result); + } + } + ?> + +Crie um arquivo chamado articles\_controller.test.php no seu diretório +app/tests/cases/controllers com o seguinte código: + +:: + + Iniciando Caso de Teste'; + } + function endCase() { + echo '

Terminando Caso de Teste

'; + } + function startTest($method) { + echo '

Iniciando método ' . $method . '

'; + } + function endTest($method) { + echo '
'; + } + function testIndex() { + $result = $this->testAction('/articles/index'); + debug($result); + } + function testIndexShort() { + $result = $this->testAction('/articles/index/short'); + debug($result); + } + function testIndexShortGetRenderedHtml() { + $result = $this->testAction('/articles/index/short', + array('return' => 'render')); + debug(htmlentities($result)); + } + function testIndexShortGetViewVars() { + $result = $this->testAction('/articles/index/short', + array('return' => 'vars')); + debug($result); + } + function testIndexFixturized() { + $result = $this->testAction('/articles/index/short', + array('fixturize' => true)); + debug($result); + } + function testIndexPostFixturized() { + $data = array('Article' => array('user_id' => 1, 'published' + => 1, 'slug'=>'new-article', 'title' => 'Novo Artigo', 'body' => 'Novo Texto de Artigo')); + $result = $this->testAction('/articles/index', + array('fixturize' => true, 'data' => $data, 'method' => 'post')); + debug($result); + } + } + ?> + +O método testAction +------------------- + +A novidade agora é o método **testAction**. O primeiro argumento deste +método é a url (no formato do Cake) do controller e da action a ser +testada, como em '/articles/index/short'. + +O segundo argumento é um array de parâmetros, contendo: + +return + defina para o que você quer retornar. + Valores válidos são: + + - 'vars' - Devolve os valores das variáveis da view (view vars) + atribuídas após da execução da action + - 'view' - Devolve o conteúdo da view renderizada em si, sem o + layout + - 'contents' - Devolve o conteúdo html completo da view, incluindo + o layout + - 'result' - Devolve o valor retornado quando a action utiliza + $this->params['requested']. + + O default é 'result'. +fixturize + defina como true se você quiser que seus models sejam + autofixturizados (ou seja, que as tabelas de sua aplicação sejam + copiadas juntamente com seus dados, para testar tabelas; assim se + você modificar seus dados eles não irão afetar sua aplicação real). + Se você definir 'fixturize' para um array de models, apenas os + models discriminados serão autofixturizados enquanto que os demais + continuarão usando dados das tabelas reais. Entretanto, se você + quiser usar seus arquivos de fixtures com o método testAction() + então não utilize este recurso de fixturize. Ao invés disso, apenas + utilize fixtures como você faria normalmente. +method + atribua para 'post' ou 'get' informando como você quer passar os + dados para o controller +data + os dados a serem passados. Defina-o como um array associativo + contendo pares campo => valor. Dê uma olhada no método + ``function testIndexPostFixturized()`` no caso de teste acima para + ver como emulamos dados do formulário para submissão de um novo + artigo. + +Problemas conhecidos +-------------------- + +Se você usar testAction para testar um método que faça um redirect em um +controller, seu teste irá encerrar imediatamente sem sequem mostrar +nenhum resultado. + Veja +`https://trac.cakephp.org/ticket/4154 `_ +para uma possível correção. + +Testando helpers +================ + +Como classes de Helper também contém uma considerável quantidade de +códgo, é importante ter certeza de que elas também estejam cobertas por +casos de testes. + +O teste de helpers é um pouco semelhante à abordagem para componentes. +Suponha que temos um helper chamado CurrencyRendererHelper localizado em +``app/views/helpers/currency_renderer.php`` com seu respectivo arquivo +de caso de teste em +``app/tests/cases/helpers/currency_renderer.test.php`` + +Criando testes de helper, parte I +--------------------------------- + +Antes de mais nada, nós vamos definir as responsabilidades de nosso +CurrencyRendererHelper. Basicamente, este helper terá dois métodos +apenas para fins de demonstração: + +function usd($amount) + +Este método irá receber o total a renderizar. Ele leva 2 casa decimais, +preenchendo espaços vazios com zeros e incluindo o prefixo 'USD'. + +function euro($amount) + +Este método fará o mesmo que o usd() exceto que o prefixo incluído na +saída será 'EUR'. Só para deixar as cosas um pouco mais complexas, +também vamos exibir o resultado dentro de uma tag span: + +:: + + + +Vamos criar os testes primeiro: + +:: + + currencyRenderer = new CurrencyRendererHelper(); + } + + // testando o método usd(). + public function testUsd() { + $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); + // Devemos sempre ter 2 casas decimais. + $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); + $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); + // Testando o separador de milhar. + $this->assertEqual('USD 12,000.70', $this->currencyRenderer->usd(12000.70)); + } + +Aqui, chamamos ``usd()`` com diferentes parâmetros e dizemos para a +suíte de testes checar se os valores retornados são iguais aos +esperados. + +Executar o teste agora irá resultar em erros (já que o +currencyRendererHelper nem sequer existe ainda), mostrando que temos 3 +falhas. + +Uma vez que saibamos o que nosso método deve fazer, podemos escrever o +método propriamente dito: + +:: + + `_, nós precisamos de um controller +para acessar os dados no model. + +Se o método startup() do componente for parecido com isto: + +:: + + public function startup(&$controller){ + $this->Transporter = $controller->Transporter; + } + +...então nós podemos criar uma classe falsa bem simples: + +:: + + class FakeTransporterController {} + +...e associar valores nela dessa forma: + +:: + + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + +Criando um método de teste +-------------------------- + +Apenas crie uma classe que estenda CakeTestCase e comece a escrever seus +testes! + +:: + + class TransporterTestCase extends CakeTestCase { + var $fixtures = array('transporter'); + function testGetTransporter() { + $this->TransporterComponentTest = new TransporterComponent(); + $controller = new FakeTransporterController(); + $controller->Transporter = new TransporterTest(); + $this->TransporterComponentTest->startup(&$controller); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); + $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); + + $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); + $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); + + $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); + $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); + + $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); + $this->assertEqual($result, 0, "Noone can service Norway"); + } + } + + +Web testing - Testando views +============================ + +A maior parte dos (se não todos os) projetos em CakePHP resultam em uma +aplicação web. Ainda que testes unitários sejam uma forma excelente de +testar partes pequenas de funcionaidade, você também pode querer testar +a funcionalidade em larga escala. A classe **CakeWebTestCase** +representa uma boa maneira de fazer testes a partir do ponto de vista de +um usuário da aplicação. + +Sobre a CakeWebTestCase +----------------------- + +**CakeWebTestCase** é uma extensão direta da classe WebTestCase do +SimpleTest, sem qualquer funcionalidade extra. Toda a funcionalidade +encontrada na `documentação do SimpleTest para testes +Web `_ +também está disponível. Isto também significa que nenhuma funcionalidade +além das presentes no SimpleTest está disponível. Isso quer dizer que +você não pode usar fixtures, e que **todos os casos de teste que +envolvam atualizar/inserir registros na base de dados irá modificar +permanentemente os registros em sua base de dados**. Os resultados dos +testes quase sempre serão baseados nos valores presentes na base de +dados, então uma etapa para certificar-se de que sua base de dados +contenha os valores que você espera faz parte de seu procedimento de +teste. + +Criando um teste +---------------- + +Para se ater às prévias convenções de testes, você deve criar seus +testes de views na pasta tests/cases/views. Obviamente você pode colocar +estes testes em qualquer lugar, mas seguir as convenções sempre que +possível é sempre uma boa ideia. Então vamos criar o arquivo +tests/cases/views/complete\_web.test.php + +Primeiramente, quando você quiser criar testes web, você deve se lembrar +de estender **CakeWebTestCase** ao invés de CakeTestCase: + +:: + + class CompleteWebTestCase extends CakeWebTestCase + +Se você precisar de algum preparativo antes de iniciar o teste, crie um +construtor: + +:: + + function CompleteWebTestCase(){ + // Seus preparativos iniciais aqui + } + +Para escrever casos para testes web, a primeira coisa que você precisa +fazer é obter alguma saída à qual analisar. Isto pode ser feito com uma +requisição **get** ou **post**, usando-se os métodos **get()** ou +**post()**, respectivamente. Ambos métodos recebem uma url completa como +primeiro parâmetro. Esta url pode ser obtida dinamicamente. Neste caso, +supondo que o script de teste esteja localizado em +http://seu.dominio/cake/pasta/webroot/test.php , poder-se-ia obter esta +url com: + +:: + + $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); + +Você pode, então, fazer requisições gets e posts usando urls no formato +do Cake, como por exemplo: + +:: + + $this->get($this->baseurl."/products/index/"); + $this->post($this->baseurl."/customers/login", $data); + +O segundo parâmetro para o método post, **$data**, é um array +associativo contendo os dados para a requisição no formato do Cake: + +:: + + $data = array( + "data[Customer][mail]" => "user@user.com", + "data[Customer][password]" => "userpass"); + +Quando você tiver requisitado a página, você pode fazer diversos tipos +de asserções com ela, usando os métodos disponíveis por padrão no +próprio SimpleTest. + +Navegando-se por uma página +--------------------------- + +A classe CakeWebTest também permite navegar-se por uma página, clicando +em links ou imagens, preenchendo formulários e clicando em botões. Por +favor, confira a `documentação do +SimpleTest `_ +para mais informações sobre isto. + +Testando plugins +================ + +Testes para plugins são criados em seu próprio diretório dentro da pasta +plugins. + +:: + + /app + /plugins + /pizza + /tests + /cases + /fixtures + /groups + +Testes para plugins são como testes normais, exceto que você deve se +lembrar de usar a convenção de nomenclatura para plugins ao importar as +classes. Este é um exemplo de um caso de teste para o model PizzaOrder +criado `no capítulo de plugins `_ deste +manual. Uma diferença para os outros testes é a primeira linha que faz a +importação de 'Pizza.PizzaOrder'. Você também precisa prefixar os +fixtures de seu plugin com '``plugin.plugin_name.``\ '. + +:: + + PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); + + // faz algum teste útil aqui + $this->assertTrue(is_object($this->PizzaOrderTest)); + } + } + ?> + +Se você quiser usar o fixtures de plugin nos testes da aplicação, você +pode referenciá-los usando a sintaxe 'plugin.pluginName.fixtureName' no +array $fixtures. + +E isso é tudo! + +Miscelânea +========== + +Personalizando o relatório de testes +------------------------------------ + +O relatório de testes padrão é **muito** minimalista. Se você quiser uma +saída mais elaborada para impressionar alguém, não tema. Atualmente é +muito fácil estender a saída do relatório de testes do CakePHP. + O único risco é que você vai precisar dispender mais tempo com o código +do núcleo do Cake, especificamente o +**/cake/tests/libs/cake\_reporter.php**. + +Para modificar a saída dos testes você pode sobrescrever os seguintes +métodos: + +paintHeader() + Exibe conteúdo antes do teste ser iniciado. +paintPass() + Exibe conteúdo a cada caso de teste que passe. Utilize + $this->getTestList() para obter um array de informações pertinentes + ao teste, e $message para obter o resultado do teste. Lembre-se de + fazer uma chamada a parent::paintPass($message). +paintFail() + Exibe conteúdo a cada caso de teste que falhe. Lembre-se de fazer + uma chamada a parent::paintFail($message). +paintFooter() + Exibe conteúdo quando o texte tiver terminado, i.e., quando todos os + casos de teste tiverem sido executados. + +Se, ao chamar paintPass e paintFail, você quiser esconder a saída um +nível acima, faça a chamada dentro de tags html de comentário, como em: + +:: + + echo "\n\n"; + +Uma configuração de exemplo de **cake\_reporter.php** que cria uma +tabela para armazenar os resultados dos testes poderia ser a seguinte: + +:: + + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + */ + class CakeHtmlReporter extends HtmlReporter { + function CakeHtmlReporter($characterSet = 'UTF-8') { + parent::HtmlReporter($characterSet); + } + + function paintHeader($testName) { + $this->sendNoCacheHeaders(); + $baseUrl = BASE; + print "

$testName

\n"; + print "\n"; + flush(); + } + + function paintFooter($testName) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
Res.Test caseMessage
\n"; + print "
"; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
\n"; + } + + function paintPass($message) { + parent::paintPass($message); + echo "\n\t\n"; + print "\t\tPass: \n"; + echo "\t\n\t\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + $message = split('at \[', $message); + print "->$message[0]
\n\n"; + echo "\n\t\n\n\n"; + } + + function paintFail($message) { + echo "\n\n"; + echo "\n\t\n"; + print "\t\tFail: \n"; + echo "\n\t\n\t\n"; + $breadcrumb = $this->getTestList(); + print implode("->", $breadcrumb); + echo "\n\t\n\t\n"; + print "$message"; + echo "\n\t\n\n\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } + + } + ?> + +Agrupando testes +---------------- + +Se você quiser que vários de seus testes sejam executados ao mesmo +tempo, você pode tentar criar um grupo de testes. Crie um arquivo em +**/app/tests/groups/** e dê-lhe um nome como +**nome\_de\_seu\_grupo\_de\_teste.group.php**. Neste arquivo, estenda a +classe **GroupTest** e importe seus testes como se segue: + +:: + + + +O código acima irá agrupar todos os casos de teste encontrados na pasta +**/app/tests/cases/models/**. Para adicionar um arquivo individual, +utilize **TestManager::addTestFile**\ ($this, filename). + +Executando os testes a partir da linha de comandos +================================================== + +Se você tiver o SimpleTest instalado, você pode executar seus testes em +sua aplicação a partir da linha de comando. + +no diretório **app/**, digite + +:: + + cake testsuite help + +:: + + Usage: + cake testsuite category test_type file + - category - "app", "core" or name of a plugin + - test_type - "case", "group" or "all" + - test_file - file name with folder prefix and without the (test|group).php suffix + + Examples: + cake testsuite app all + cake testsuite core all + + cake testsuite app case behaviors/debuggable + cake testsuite app case models/my_model + cake testsuite app case controllers/my_controller + + cake testsuite core case file + cake testsuite core case router + cake testsuite core case set + + cake testsuite app group mygroup + cake testsuite core group acl + cake testsuite core group socket + + cake testsuite bugs case models/bug + // for the plugin 'bugs' and its test case 'models/bug' + cake testsuite bugs group bug + // for the plugin bugs and its test group 'bug' + + Code Coverage Analysis: + + + Append 'cov' to any of the above in order to enable code coverage analysis + +Como o menu de ajuda sugere, você será capaz de executar todos os +testes, apenas um subconjunto deles ou até um único caso de teste de sua +aplicação, de seu plugin ou do core, direto da linha de comando. + +Se você tiver um teste de model em **test/models/meu\_model.test.php**, +para executar apenas este caso de teste você poderia digitar o seguinte +na linha de comando: + +:: + + cake testsuite app models/meu_model + diff --git a/pt/The-Manual/Core-Behaviors.rst b/pt/The-Manual/Core-Behaviors.rst new file mode 100644 index 0000000000000000000000000000000000000000..9115949576cb4d4a69273405fcc318943c24e5a5 --- /dev/null +++ b/pt/The-Manual/Core-Behaviors.rst @@ -0,0 +1,14 @@ +Behaviors Principais +#################### + +Behaviors adicionam funcionalidades extras a seus models. O CakePHP já +vem com alguns behaviors inclusos tais como Tree e Containable. + + +.. toctree:: + :maxdepth: 1 + + Core-Behaviors/ACL + Core-Behaviors/Containable + Core-Behaviors/Translate + Core-Behaviors/Tree \ No newline at end of file diff --git a/pt/The-Manual/Core-Behaviors/ACL.rst b/pt/The-Manual/Core-Behaviors/ACL.rst new file mode 100644 index 0000000000000000000000000000000000000000..6f3ce649e38ee3261b9f5f9a1e85a892c68fe06e --- /dev/null +++ b/pt/The-Manual/Core-Behaviors/ACL.rst @@ -0,0 +1,103 @@ +ACL +### + +The Acl behavior provides a way to seamlessly integrate a model with +your ACL system. It can create both AROs or ACOs transparently. + +To use the new behavior, you can add it to the $actsAs property of your +model. When adding it to the actsAs array you choose to make the related +Acl entry an ARO or an ACO. The default is to create AROs. + +:: + + class User extends AppModel { + var $actsAs = array('Acl' => array('type' => 'requester')); + } + +This would attach the Acl behavior in ARO mode. To join the ACL behavior +in ACO mode use: + +:: + + class Post extends AppModel { + var $actsAs = array('Acl' => array('type' => 'controlled')); + } + +You can also attach the behavior on the fly like so: + +:: + + $this->Post->Behaviors->attach('Acl', array('type' => 'controlled')); + +Using the AclBehavior +===================== + +Most of the AclBehavior works transparently on your Model's afterSave(). +However, using it requires that your Model has a parentNode() method +defined. This is used by the AclBehavior to determine parent->child +relationships. A model's parentNode() method must return null or return +a parent Model reference. + +:: + + function parentNode() { + return null; + } + +If you want to set an ACO or ARO node as the parent for your Model, +parentNode() must return the alias of the ACO or ARO node. + +:: + + function parentNode() { + return 'root_node'; + } + +A more complete example. Using an example User Model, where User +belongsTo Group. + +:: + + function parentNode() { + if (!$this->id && empty($this->data)) { + return null; + } + $data = $this->data; + if (empty($this->data)) { + $data = $this->read(); + } + if (!$data['User']['group_id']) { + return null; + } else { + $this->Group->id = $data['User']['group_id']; + $groupNode = $this->Group->node(); + return array('Group' => array('id' => $groupNode[0]['Aro']['foreign_key'])); + } + } + +In the above example the return is an array that looks similar to the +results of a model find. It is important to have the id value set or the +parentNode relation will fail. The AclBehavior uses this data to +construct its tree structure. + +node() +====== + +The AclBehavior also allows you to retrieve the Acl node associated with +a model record. After setting $model->id. You can use $model->node() to +retrieve the associated Acl node. + +You can also retrieve the Acl Node for any row, by passing in a data +array. + +:: + + $this->User->id = 1; + $node = $this->User->node(); + + $user = array('User' => array( + 'id' => 1 + )); + $node = $this->User->node($user); + +Will both return the same Acl Node information. diff --git a/pt/The-Manual/Core-Behaviors/Containable.rst b/pt/The-Manual/Core-Behaviors/Containable.rst new file mode 100644 index 0000000000000000000000000000000000000000..3f41f2bc505b538e0e7d0c2979bb7703f4b41eca --- /dev/null +++ b/pt/The-Manual/Core-Behaviors/Containable.rst @@ -0,0 +1,380 @@ +Containable +########### + +A new addition to the CakePHP 1.2 core is the ``ContainableBehavior``. +This model behavior allows you to filter and limit model find +operations. Using Containable will help you cut down on needless wear +and tear on your database, increasing the speed and overall performance +of your application. The class will also help you search and filter your +data for your users in a clean and consistent way. + +Containable allows you to streamline and simplify operations on your +model bindings. It works by temporarily or permanently altering the +associations of your models. It does this by using the supplied +containments to generate a series of ``bindModel`` and ``unbindModel`` +calls. + +To use the new behavior, you can add it to the $actsAs property of your +model: + +:: + + class Post extends AppModel { + var $actsAs = array('Containable'); + } + +You can also attach the behavior on the fly: + +:: + + $this->Post->Behaviors->attach('Containable'); + +Usando o Containable +==================== + +Para ver como o Containable funciona, vamos ver alguns exemplos. +Primeiramente vamos começar com uma chamada find() em um model chamado +Post. Digamos que nosso Post possa ter vários comentários (Post hasMany +Comment) e que possa ter e pertence a muitas tgs (Post +hasAndBelongsToMany Tag). A quantidade de dados recuperados em uma +chamada find() normal poderá ser bem extensa: + +:: + + debug($this->Post->find('all')); + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => Primeiro post + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => Primeiro comentário + [created] => 2008-05-18 00:00:00 + ) + [1] => Array + ( + [id] => 2 + [post_id] => 1 + [author] => Sam + [email] => sam@example.net + [website] => http://example.net + [comment] => Segundo comentário + [created] => 2008-05-18 00:00:00 + ) + ) + [Tag] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => Incrível + ) + [1] => Array + ( + [id] => 2 + [name] => Fermento + ) + ) + ) + [1] => Array + ( + [Post] => Array + (... + +Para algumas telas de sua aplicação, você pode não precisar de tanta +informação a partir do model Post. Uma das coisas que o +``ContainableBehavior`` faz é ajudar você a enxugar o retorno de uma +chamada find(). + +Por exemplo, para obter apenas as informações referentes ao Post em si, +você pode fazer o seguinte: + +:: + + $this->Post->contain(); + $this->Post->find('all'); + +Você também pode invocar a mágica do Containable de dentro de uma +chamada ao find(): + +:: + + $this->Post->find('all', array('contain' => false)); + +Feito isto, você obtem um resultado bem mais conciso: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => Primeiro post + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + ) + [1] => Array + ( + [Post] => Array + ( + [id] => 2 + [title] => Segundo post + [content] => bbb + [created] => 2008-05-19 00:00:00 + ) + ) + +Este tipo de ajuda não é algo novo: na verdade, você já poderia fazer +isso mesmo sem o ``ContainableBehavior`` com algo assim: + +:: + + $this->Post->recursive = -1; + $this->Post->find('all'); + +O Containable realmente vai mostrar sua importância quando você tiver +associações compleas e quiser filtrar as coisas que estiverem num mesmo +nível. A propriedade ``$recursive`` do model é útil se você quiser +remover um nível completo de recursão, mas não vai adiantar se você +quser selecionar e escolhar o que manter em cada nível. Vejamos como as +coisas funcionam ao se usar o método ``contain()``. + +O primeiro argumento do métod contain aceita o nome ou um array de nomes +do models que queremos manter na operação de busca. Se quisermos +recuperar todos os posts e suas respectivas tags (sem as informações de +comentários), poderíamos fazer algo como: + +:: + + $this->Post->contain('Tag'); + $this->Post->find('all'); + +Novamente, podemos também usar o índice contain dentro de uma chamada +find(): + +:: + + $this->Post->find('all', array('contain' => 'Tag')); + +Sem o Containable, você acabaria precisaria usar o método +``unbindModel()`` do model várias vezes para remover diversos models do +resultado. O Containable cria uma maneira simples e clara de se obter o +mesmo resultado. + +Containing deeper associations +============================== + +Containable also goes a step deeper: you can filter the data of the +*associated* models. If you look at the results of the original find() +call, notice the author field in the Comment model. If you are +interested in the posts and the names of the comment authors — and +nothing else — you could do something like the following: + +:: + + $this->Post->contain('Comment.author'); + $this->Post->find('all'); + + //or.. + + $this->Post->find('all', array('contain' => 'Comment.author')); + +Here, we've told Containable to give us our post information, and just +the author field of the associated Comment model. The output of the find +call might look something like this: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [author] => Daniel + [post_id] => 1 + ) + [1] => Array + ( + [author] => Sam + [post_id] => 1 + ) + ) + ) + [1] => Array + (... + +As you can see, the Comment arrays only contain the author field (plus +the post\_id which is needed by CakePHP to map the results). + +You can also filter the associated Comment data by specifying a +condition: + +:: + + $this->Post->contain('Comment.author = "Daniel"'); + $this->Post->find('all'); + + //or... + + $this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')); + +This gives us a result that gives us posts with comments authored by +Daniel: + +:: + + [0] => Array + ( + [Post] => Array + ( + [id] => 1 + [title] => First article + [content] => aaa + [created] => 2008-05-18 00:00:00 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 1 + [post_id] => 1 + [author] => Daniel + [email] => dan@example.com + [website] => http://example.com + [comment] => First comment + [created] => 2008-05-18 00:00:00 + ) + ) + ) + +Additional filtering can be performed by supplying the standard +``Model->find()`` options: + +:: + + $this->Post->find('all', array('contain' => array( + 'Comment' => array( + 'conditions' => array('Comment.author =' => "Daniel"), + 'order' => 'Comment.created DESC' + ) + ))); + +Here's an example of using the ``ContainableBehavior`` when you've got +deep and complex model relationships. + +Let's consider the following model associations: + +:: + + User->Profile + User->Account->AccountSummary + User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes + User->Post->Tag + +This is how we retrieve the above associations with Containable: + +:: + + $this->User->find('all', array( + 'contain'=>array( + 'Profile', + 'Account' => array( + 'AccountSummary' + ), + 'Post' => array( + 'PostAttachment' => array( + 'fields' => array('id', 'name'), + 'PostAttachmentHistory' => array( + 'HistoryNotes' => array( + 'fields' => array('id', 'note') + ) + ) + ), + 'Tag' => array( + 'conditions' => array('Tag.name LIKE' => '%happy%') + ) + ) + ) + )); + +Keep in mind that ``contain`` key is only used once in the main model, +you don't need to use 'contain' again for related models + +When using 'fields' and 'contain' options - be careful to include all +foreign keys that your query directly or indirectly requires. Please +also note that because Containable must be attached to all models used +in containment, you may consider attaching it to your AppModel. + +Using Containable with pagination +================================= + +Here's an example of how to contain associations when paginating. + +:: + + $this->paginate['User'] = array( + 'contain' => array('Profile', 'Account'), + 'order' => 'User.username' + ); + + $users = $this->paginate('User'); + +By including the 'contain' parameter in the ``$paginate`` property it +will apply to both the find('count') and the find('all') done on the +model + +ContainableBehavior options +=========================== + +The ``ContainableBehavior`` has a number of options that can be set when +the Behavior is attached to a model. The settings allow you to fine tune +the behavior of Containable and work with other behaviors more easily. + +- **recursive** (boolean, optional) set to true to allow containable to + automatically determine the recursiveness level needed to fetch + specified models, and set the model recursiveness to this level. + setting it to false disables this feature. The default value is + ``true``. +- **notices** (boolean, optional) issues E\_NOTICES for bindings + referenced in a containable call that are not valid. The default + value is ``true``. +- **autoFields**: (boolean, optional) auto-add needed fields to fetch + requested bindings. The default value is ``true``. + +You can change ContainableBehavior settings at run time by reattaching +the behavior as seen in `Using behaviors `_ + +ContainableBehavior can sometimes cause issues with other behaviors or +queries that use aggregate functions and/or GROUP BY statements. If you +get invalid SQL errors due to mixing of aggregate and non-aggregate +fields, try disabling the ``autoFields`` setting. + +:: + + $this->Post->Behaviors->attach('Containable', array('autoFields' => false)); + diff --git a/pt/The-Manual/Core-Behaviors/Translate.rst b/pt/The-Manual/Core-Behaviors/Translate.rst new file mode 100644 index 0000000000000000000000000000000000000000..68ff885ac85da95c7c0e0a7827f3e39f00a07df9 --- /dev/null +++ b/pt/The-Manual/Core-Behaviors/Translate.rst @@ -0,0 +1,367 @@ +Translate +######### + +TranslateBehavior é bastante fácil de configurar e funciona bem com +pouca configuração. Nesta seção, você vai aprender como adicionar e +configurar o comportamento de utilização em qualquer model. + +Initializing the i18n Database Tables +===================================== + +Você pode usar o console CakePHP ou você pode criá-lo manualmente. É +aconselhado a usar o console para isso, porque pode acontecer que o +esquema alterações em futuras versões do CakePHP. Cumprindo com o +console irá certificar-se de que você tem a disposição correta. + +:: + + ./cake i18n + +Selecione ``[I]``, que irá executar o script i18n dados intialization. +Você será perguntado se você quer largar qualquer existente e se você +deseja criá-lo. Resposta: Sim, se você estiver com certeza não existe +uma tabela i18n já, e responder com sim para criar novamente a tabela. + +Anexando o Translate Behavior nos seus Models +============================================= + +Para adicionar adicioná-lo ao seu model, use a propriedade ``$actsAs``, +como no exemplo a seguir. + +:: + + + +Isso não fará nada ainda, porque se espera algumas configurações antes +de começar a trabalhar. É necessário definir quais os campos do model +deve ser monitorada na tabela de tradução que criamos na primeira etapa. + +Defining the Fields +=================== + +Você pode configurar os campos, simplesmente, que prorroga o +``'Traduzir' `` com outro valor array, assim: + +:: + + array( + 'fieldOne', 'fieldTwo', 'and_so_on' + ) + ); + } + ?> + +Depois de ter feito isso (por exemplo, colocar "nome", como um dos +campos) que você já terminou a configuração básica. Ótimo! De acordo com +o nosso exemplo, o actual modelo deve agora olhar algo parecido com +isto: + +:: + + array( + 'name' + ) + ); + } + ?> + +Conclusion +========== + +A partir de agora, cada registro atualização/criação irá causar +TranslateBehavior para copiar o valor do "nome" para a tabela de +tradução (por defeito: i18n), juntamente com o actual local. A +localidade é o identificador do idioma, por assim dizer. + +O actual *locale* é o valor actual dos +`` Configure:: read ( 'Config.language') ``. O valor da +*Config.language* é atribuído na l10n classe - a menos que ele já está +definido. No entanto, o TranlateBehavior lhe permite substituir esta +on-the-fly, que permite que o usuário da sua página para criar múltiplas +versões sem a necessidade de alterar as suas preferências. Mais sobre +este assunto na próxima seção. + +Retrieve all translation records for a field +============================================ + +If you want to have all translation records attached to the current +model record you simply extend the *field array* in your behavior setup +as shown below. The naming is completely up to you. + +:: + + array( + 'name' => 'nameTranslation' + ) + ); + } + ?> + +With this setup the result of $this->Post->find() should look something +like this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +**Note**: The model record contains a *virtual* field called "locale". +It indicates which locale is used in this result. + +Note that only fields of the model you are directly doing \`find\` on +will be translated. Models attached via associations won't be translated +because triggering callbacks on associated models is currently not +supported. + +Using the bindTranslation method +-------------------------------- + +You can also retrieve all translations, only when you need them, using +the bindTranslation method + +``bindTranslation($fields, $reset)`` + +``$fields`` is a named-key array of field and association name, where +the key is the translatable field and the value is the fake association +name. + +:: + + $this->Post->bindTranslation(array ('name' => 'nameTranslation')); + $this->Post->find('all', array ('recursive'=>1)); // need at least recursive 1 for this to work. + +With this setup the result of your find() should look something like +this: + +:: + + Array + ( + [Post] => Array + ( + [id] => 1 + [name] => Beispiel Eintrag + [body] => lorem ipsum... + [locale] => de_de + ) + + [nameTranslation] => Array + ( + [0] => Array + ( + [id] => 1 + [locale] => en_us + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Example entry + ) + + [1] => Array + ( + [id] => 2 + [locale] => de_de + [model] => Post + [foreign_key] => 1 + [field] => name + [content] => Beispiel Eintrag + ) + + ) + ) + +Saving in another language +========================== + +You can force the model which is using the TranslateBehavior to save in +a language other than the one detected. + +To tell a model in what language the content is going to be you simply +change the value of the ``$locale`` property on the model before you +save the data to the database. You can do that either in your controller +or you can define it directly in the model. + +**Example A:** In your controller + +:: + + data) { + $this->Post->locale = 'de_de'; // we are going to save the german version + $this->Post->create(); + if ($this->Post->save($this->data)) { + $this->redirect(array('action' => 'index')); + } + } + } + } + ?> + +**Example B:** In your model + +:: + + array( + 'name' + ) + ); + + // Option 1) just define the property directly + var $locale = 'en_us'; + + // Option 2) create a simple method + function setLanguage($locale) { + $this->locale = $locale; + } + } + ?> + +Multiple Translation Tables +=========================== + +If you expect a lot of entries you probably wonder how to deal with a +rapidly growing database table. There are two properties introduced by +TranslateBehavior that allow you to specify which "Model" to bind as the +model containing the translations. + +These are **$translateModel** and **$translateTable**. + +Lets say we want to save our translations for all posts in the table +"post\_i18ns" instead of the default "i18n" table. To do so you need to +setup your model like this: + +:: + + array( + 'name' + ) + ); + + // Use a different model (and table) + var $translateModel = 'PostI18n'; + } + ?> + +**Important** to note is that you have to pluralize the table. It is now +a usual model and can be treated as such and thus comes with the +conventions involved. The table schema itself must be identical with the +one generated by the CakePHP console script. To make sure it fits one +could just initialize an empty i18n table using the console and rename +the table afterwards. + +Create the TranslateModel +------------------------- + +For this to work you need to create the actual model file in your models +folder. The reason is that there is no property to set the displayField +directly in the model using this behavior yet. + +Make sure that you change the ``$displayField`` to ``'field'``. + +:: + + + +That's all it takes. You can also add all other model stuff here like +$useTable. But for better consistency we could do that in the model +which actually uses this translation model. This is where the optional +``$translateTable`` comes into play. + +Changing the Table +------------------ + +If you want to change the name of the table you simply define +$translateTable in your model, like so: + +:: + + array( + 'name' + ) + ); + + // Use a different model + var $translateModel = 'PostI18n'; + + // Use a different table for translateModel + var $translateTable = 'post_translations'; + } + ?> + +Please note that **you can't use $translateTable alone**. If you don't +intend to use a custom ``$translateModel`` then leave this property +untouched. Reason is that it would break your setup and show you a +"Missing Table" message for the default I18n model which is created in +runtime. diff --git a/pt/The-Manual/Core-Behaviors/Tree.rst b/pt/The-Manual/Core-Behaviors/Tree.rst new file mode 100644 index 0000000000000000000000000000000000000000..25bd38f053b83a22b46f4998bfc9d3468634f5be --- /dev/null +++ b/pt/The-Manual/Core-Behaviors/Tree.rst @@ -0,0 +1,622 @@ +Tree (Árvore) +############# + +É comum necessitar exibir dados hierárquiamente de alguma tabela do +banco de dados. Exemplos de tais dados podem ser categorias com +subcategorias, com diversos níveis, dados relativos a um sistema de menu +com multiníveis ou assim como uma representação de hirearquia, como é +utilizado para armazenar os objetos do ACL. + +Para pequenas árvores de dados, ou quando os dados possuem apenas alguns +níveis, é simples adicionar campos como parent\_id, e utilizar esse +campo para determinar o parente do dados, assim conseguindo exibir em +uma estrutura hierárquica. Bundled com o cake no entanto, provem um +poderoso behavior que permite usar os benefícios do `MPTT +logic `_, +sem se preocupar com com nada de técnica, ao menos que você quiser ;) + +Requisitos +========== + +Para usar o Tree behaviour, sua tabela da base de dados precisa dos três +campos que estão listados a seguir (todos são inteiros): + +- pai - nome do campo padrão é parent\_id, usado para armazenar o id do + objeto pai +- esquerda - nome do campo padrão é lft, usado para armazenar o valor + da esquerda da atual linha. +- direita - nome do campo padrão é rght, usado para armazenar o valor + da direita da atual linha. + +Se você está acostumado com a lógica MPTT, irá se perguntar o porquê de +haver um campo pai - simplesmente porque é mais fácil fazer determinadas +tarefas se existir um link para o pai armazenado na base de dados - +como, por exemplo, quais filhos estão ligados diretamente a um item pai. + +Uso Básico +========== + +O behavior tree tem bastante funcionalidade dentro dele, porém vamos +começar com um simples exemplo - criamos a tabela no banco de dados e +vamos por alguns dados: + +:: + + CREATE TABLE categorias ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + nome VARCHAR(255) DEFAULT '', + PRIMARY KEY (id) + ); + + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(1, 'Minhas categorias', NULL, 1, 30); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(2, 'Divertidos', 1, 2, 15); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(3, 'Esportes', 2, 3, 8); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(4, 'Surf', 3, 4, 5); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(5, 'Padel', 3, 6, 7); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(6, 'Amigos', 2, 9, 14); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(7, 'Thiago', 6, 10, 11); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(8, 'Geraldo', 6, 12, 13); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(9, 'Trabalho', 1, 16, 29); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(10, 'Relatórios', 9, 17, 22); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(11, 'Anuais', 10, 18, 19); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(12, 'Status', 10, 20, 21); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(13, 'Viagens', 9, 23, 28); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(14, 'Nacionais', 13, 24, 25); + INSERT INTO `categorias` (`id`, `nome`, `parent_id`, `lft`, `rght`) VALUES(15, 'Internacionais', 13, 26, 27); + +Com o intuito de verificar se tudo está correto, nós podemos criar um +método de teste e mostrar os conteúdos da nossa árvore de categoria para +ver o que aparece. Com um simples controller: + +:: + + data = $this->Categoria->generatetreelist(null, null, null, '   '); + debug ($this->data); die; + } + } + ?> + +an an even simpler model definition: + +:: + + + +Nós podemos verificar que a nossa árvore de categoria mostra, acessando +/categorias Você ira ver algo do tipo: + +- Minhas Categorias + + - Divertidos + + - Esportes + + - Surf + - Padel + + - Amigos + + - Thiago + - Geraldo + + - Trabalho + + - Relátorios + + - Anual + - Status + + - Viagens + + - Nacionais + - Internacionais + +Adicionar dados +--------------- + +Na sessão anterior, nos usamos uma data existente e verificamos como +ficou a hirearquia usando o méotodo ``generatetreelist``. No entando, +geralmente é necessário você adicionar os dados da mesma maneira que +qualquer outro modelo. Por exemplo: + +:: + + // pseudo controller code + $data['Categoria']['parent_id'] = 3; + $data['Categoria']['name'] = 'Skating'; + $this->Categoria->save($data); + +Quando usamos o tree behavior isto não é necessário fazer nada a mais, +apenas setar o parent\_id e o tree behavior vai cudar do resto. Se você +não setar o parent\_id, o tree behavior vai adicionar na árvore uma nova +entrada no nível do topo: + +:: + + // pseudo controller code + $data = array(); + $data['Category']['name'] = 'Other People\'s Categories'; + $this->Category->save($data); + +Rodando os dois códigos acima, você ira receber os seguintes resultados: + +- Minhas Categorias + + - Divertidos + + - Esportes + + - Surf + - Padel + - Skating **New** + + - Amigos + + - Thiago + - Geraldo + + - Trabalho + + - Relátorios + + - Anual + - Status + + - Viagens + + - Nacionais + - Internacionais + +- Nova Categoria **New** + +Modificando dados +----------------- + +Modificar dados é tão transparente quanto adicionar novos dados. Se você +modificar alguma coisa, mas não quiser alterar o campo parent\_id - a +estrutura dos dados vai permanecer inalterada. Por exemplo: + +:: + + // pseudo código de controller + $this->Category->id = 5; // id de Extreme knitting + $this->Category->save(array('name' =>'Extreme fishing')); + +O código acima não afeta o campo parent\_id - mesmo que o o parent\_id +seja incluído nos dados passados para o método save se o valor não +mudar, a estrutura dos dados também não mudará. Portanto a árvore de +dados deve esta assim: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme fishing **Atualizado** + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + +Mover dados na árvore também é uma tarefa simples. Vamos dizer que +Extreme fishing não pertence mais a Sport, mas ao invés, deve ser +alocada dentro de Other People's Categories. Com o seguinte código: + +:: + + // pseudo código de controller + $this->Category->id = 5; // id de Extreme fishing + $newParentId = $this->Category->field('id', array('name' => 'Other People\'s Categories')); + $this->Category->save(array('parent_id' => $newParentId)); + +Como o esperado a estrutura foi modificada para: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Skating + + - Friends + + - Gerald + - Gwendolyn + + - Work + + - Reports + + - Annual + - Status + + - Trips + + - National + - International + +- Other People's Categories + + - Extreme fishing **Movida** + +Deletando dados +--------------- + +O tree behaviour provem maneiras de deletar dados. Para começar podemos +fazer um simples exemplo para testar; vamos dizer que a categoria +"relatórios", não é tão usada. Para remover isso *e todas os filhos que +este tem* apenas chame o delete igual usamos para qualquer modelo. Para +exemplificar segue o código: + +:: + + // pseudo controller code + $this->Categoria->id = 10; + $this->Categoria->delete(); + +A árvore de categoria deve ficar como em baixo: + +- Minhas Categorias + + - Divertidos + + - Esportes + + - Surf + - Padel + - Skating + + - Amigos + + - Thiago + - Geraldo + + - Trabalho + + - Viagens + + - Nacionais + - Internacionais + +- Nova Categoria + +Requisitando e usando seus dados +-------------------------------- + +Usar e manipular dados hierárquicos pode ser um negócio de artimanhas. +Em adição aos métodos core find, com o tree behavior existe um pouco +mais de permutações orientadas a árvore a sua disposição. + +A maioria dos métodos do tree behavior retornam e dependem dos dados +armazenados no campo ``lft``. Se você chamar o método ``find()`` e não +ordenar pelo campo ``lft``, ou chamar um método do tree behavior e +especificar um sort order, você poderá receber resultados indesejados. + +Filhos +~~~~~~ + +o Método ``children`` usa o valor da chave primária(id) de uma tupla e +retorna seus filhos, na ordem que eles aparecem na árvore por padrão. O +segundo parametro, que é opicional define se deve ou não ser retornados +somente filhos diretos. Usando os dados de exemplo da sessão anterior: + +:: + + $allChildren = $this->Category->children(1); // um array plano com 11 itens + // -- ou -- + $this->Category->id = 1; + $allChildren = $this->Category->children(); // um array plano com 11 itens + + // Somente retorna filhos diretos + $directChildren = $this->Category->children(1, true); // um array plano com 2 itens + +Se você quiser um array recursivo use ``find('threaded')`` + +Contando filhos +~~~~~~~~~~~~~~~ + +Tal como acontece com o método ``children``, ``childCount`` assume o +valor de chave primária (o id) de uma linha e retorna quantos filhos ele +tem. O segundo parâmetro opcional define ou não se os filhos direto +serão contados. Usando os dados de exemplo da seção anterior: + +:: + + $totalChildren = $this->Category->childCount(1); // resultara 11 + // -- ou -- + $this->Category->id = 1; + $directChildren = $this->Category->childCount(); // resultara 11 + + // Só conta os descendentes diretos desta categoria + $numChildren = $this->Category->childCount(1, true); // resultara 2 + +generatetreelist +~~~~~~~~~~~~~~~~ + +``generatetreelist ($conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)`` + +This method will return data similar to find('list'), with an indented +prefix to show the structure of your data. Below is an example of what +you can expect this method to return see the api for the other find-like +parameters. + +:: + + array( + [1] => "My Categories", + [2] => "_Fun", + [3] => "__Sport", + [4] => "___Surfing", + [16] => "___Skating", + [6] => "__Friends", + [7] => "___Gerald", + [8] => "___Gwendolyn", + [9] => "_Work", + [13] => "__Trips", + [14] => "___National", + [15] => "___International", + [17] => "Other People's Categories", + [5] => "_Extreme fishing" + ) + +getparentnode +~~~~~~~~~~~~~ + +This convenience function will, as the name suggests, return the parent +node for any node, or *false* if the node has no parent (its the root +node). For example: + +:: + + $parent = $this->Category->getparentnode(2); //<- id for fun + // $parent contains All categories + +getpath +~~~~~~~ + +The 'path' when refering to hierachial data is how you get from where +you are to the top. So for example the path from the category +"International" is: + +- My Categories + + - ... + - Work + + - Trips + + - ... + - International + +Using the id of "International" getpath will return each of the parents +in turn (starting from the top). + +:: + + $parents = $this->Category->getpath(15); + +:: + + // contents of $parents + array( + [0] => array('Category' => array('id' => 1, 'name' => 'My Categories', ..)), + [1] => array('Category' => array('id' => 9, 'name' => 'Work', ..)), + [2] => array('Category' => array('id' => 13, 'name' => 'Trips', ..)), + [3] => array('Category' => array('id' => 15, 'name' => 'International', ..)), + ) + +Utilização Avançada +=================== + +O comportamento da árvore não é só trabalho no fundo, há uma série de +métodos específicos definidos no comportamento para satisfazer todas as +suas necessidades de dados hierárquicos, bem como quaisquer problemas +inesperados que possam surgir no processo. + +moveDown +-------- + +Utilizado para mover um único nó para baixo da árvore. Você precisará +fornecer a identificação do elemento a ser movido e um número positivo +de quantas posições o nó deve ser movido para baixo. Todos nós filho +para o nó especificado também será movido. + +Aqui está um exemplo de uma ação do controlador (em um controlador +chamado Categorias) que move um nó especificado pela árvore: + +:: + + function movedown($name = null, $delta = null) { + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('Não existe uma categoria chamada ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveDown($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Por favor, forneça o número de posições do campo para ser movido para baixo.'); + } + + $this->redirect(array('action' => 'index'), null, true); + } + +Por exemplo, se você deseja mover a categoria "Sport" uma posição +abaixo, você terá que pedir: /categories/movedown/Sport/1. + +moveUp +------ + +Utilizado para mover um único nó na árvore. Você precisará fornecer a +identificação do elemento a ser movido e um número positivo de quantas +posições o nó deve ser movida para cima. Todos nós filho também serão +movidos. + +Aqui está um exemplo de uma ação do controlador (em um controlador +chamado Categorias) que move um nó na árvore: + +:: + + function moveup($name = null, $delta = null){ + $cat = $this->Category->findByName($name); + if (empty($cat)) { + $this->Session->setFlash('Não existe uma categoria chamada ' . $name); + $this->redirect(array('action' => 'index'), null, true); + } + + $this->Category->id = $cat['Category']['id']; + + if ($delta > 0) { + $this->Category->moveup($this->Category->id, abs($delta)); + } else { + $this->Session->setFlash('Por favor, forneça um número de posições que a categoria deve ser deslocado para cima.'); + } + + $this->redirect(array('action' => 'index'), null, true); + + } + +Por exemplo, se você gostaria de mover a categoria "Gwendolyn" até uma +posição que você gostaria de pedir /categories/moveup/Gwendolyn/1. +Agora, a ordem de Amigos sera Gwendolyn, Gerald. + +removeFromTree +-------------- + +``removeFromTree($id=null, $delete=false)`` + +Use este método se quiser apagar ou mover um nó, mas manter a sua +sub-árvore, que será reparentado um nível superior. Ele oferece mais +controle do que ```delete()`` `_, que para um +modelo com o comportamento árvore irá remover o nó especificado e todos +os seus filhos. + +Tendo a seguinte árvore como ponto de partida: + +- My Categories + + - Fun + + - Sport + + - Surfing + - Extreme knitting + - Skating + +Executando o código a seguir com o id 'Sport' + +:: + + $this->Node->removeFromTree($id); + +O nó do Sport irá tornar-se um nó de nível superior: + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +- Sport **Movido** + +Isso demonstra o comportamento padrão do ``removeFromTree`` de mover o +nó para não ter nenhum pai, e reparentear todos os filhos. + +Se, contudo, o seguinte trecho de código foi utilizado com o id 'Sport' + +:: + + $this->Node->removeFromTree($id,true); + +A árvore se tornaria + +- My Categories + + - Fun + + - Surfing + - Extreme knitting + - Skating + +Isso demonstra o uso alternativo para ``removeFromTree``, os filhos +foram reparentados e 'Sport' foi excluido. + +reorder +------- + +This method can be used to sort hierarchical data. + +Integridade de dados +==================== + +Devido à natureza complexa da auto-estruturas de dados referenciais, +como árvores e listas ligadas, podem, ocasionalmente, tornar-se quebrado +por uma chamada descuidada. Coragem, nem tudo está perdido! O +comportamento de cada árvore contém vários recursos não documentados +anteriormente projetado para se recuperar de tais situações. + +Estas funções que você pode economizar um bom tempo são: + +recover(&$model, $mode = 'parent', $missingParentAction = null) + +O parâmetro mode é usado para especificar a fonte de informação que é +válido / correto. A fonte de dados oposto será preenchida com base em +que fonte de informação. Por exemplo, se os campos MPTT são corruptos ou +vazio, com o $mode 'parent' os valores do campo parent\_id será usado +para preencher os campos esquerdo e direito. O parâmetro +missingParentAction só se aplica ao "parent" de modo que determina o que +fazer se o campo pai contém um id que não está presente. + +reorder(&$model, $options = array()) + +Reordena os nós (e nós filhos) da árvore de acordo com o campo e direção +especificada nos parâmetros. Este método não altera o pai de qualquer +nó. + +O array de opções contém os valores 'id' => null, 'field' => +$model->displayField, 'order' => 'ASC', e 'verify' => true, por padrão. + +verify(&$model) + +Retorna true se a árvore é válida caso contrário um array de (type, +incorrect left/right index, message). diff --git a/pt/The-Manual/Core-Components.rst b/pt/The-Manual/Core-Components.rst new file mode 100644 index 0000000000000000000000000000000000000000..7600506149b4682bec16f6828411f241afb34ce0 --- /dev/null +++ b/pt/The-Manual/Core-Components.rst @@ -0,0 +1,57 @@ +Componentes Principais +###################### + +CakePHP tem alguns acessórios para os componentes. Eles fornecem uma +saída para as tarefas mais frequentes. + +Acl + +O Acl componente fornece de maneira fácil relação entre acesso de +controle entre banco de dados e ini. + +Auth + +O auth componente fornece de maneira fácil o uso de autenticação no +sistema com variedades, tais como chamadas de retorno de controller, +Acl, ou chamadas de retorno de Objetos. + +Session + +O componente da sessão fornece um envoltório independente do +armazenamento às sessões do PHP. + +RequestHandler + +O request handler permite que você filtre as requisições de seus +visitantes e informe sua aplicação sobre os tipos favoráveis e as +informações pedidas. + +Security + +O security componente permite que você ajuste uma segurança mais alta e +controle a autenticação de HTTP. + +Email + +Uma relação usada para emitir email usando de diversos agentes de +transferência de email incluindo php mail() e smtp. + +Cookie + +O cookie componente comporta-se de forma similar ao SessionComponent que +fornece um envoltório para o apoio nativo do cookie. + +Para saber mais sobre cada componente veja o menu a direita, ou aprenda +mais sobre `criar seus próprios componentes `_. + + +.. toctree:: + :maxdepth: 1 + + Core-Components/Access-Control-Lists + Core-Components/Authentication + Core-Components/Cookies + Core-Components/Email + Core-Components/Request-Handling + Core-Components/Security-Component + Core-Components/Sessions \ No newline at end of file diff --git a/pt/The-Manual/Core-Components/Access-Control-Lists.rst b/pt/The-Manual/Core-Components/Access-Control-Lists.rst new file mode 100644 index 0000000000000000000000000000000000000000..37d79b3256fbe0f6bdcd3b2f01d152add97ea8ac --- /dev/null +++ b/pt/The-Manual/Core-Components/Access-Control-Lists.rst @@ -0,0 +1,875 @@ +Lista de Controle de Acesso +########################### + +Lista de controle de acesso do CakePHP é uma das funcionalidades mais +discutidas, mais provável porque é a mais procurada, mas mesmo porque +ela pode ser bastante confusa. Se vocês está procurando uma boa forma de +começar com as ACLs em geral, continue lendo. + +Seja corajoso e fique com ela, mesmo que o caminho seja difícil. Depois +que você pegar o jeito dela, é uma ferramenta extremamente poderosa para +ter à mão quando desenvolver sua aplicação. + +Entendendo como ACL trabalha +============================ + +Poderosas coisas requerem controle de acesso. Lista de controle de +acesso são uma forma de gerenciar as permissões da aplicação com alta +granularidade, ainda de fácil manutenção e sustentável. + +Lista de Controle de Acesso ou ACL (Access Control List), lida com dois +conceitos principais: coisas que querem coisas inúteis, e coisas que são +desejadas. No ACL, coisas (na maioria das vezes usuários) que pretendem +usar coisas são chamados acesso de requisição de objetos ou AROs. Coisas +no sistema que são desejadas (na maioria das vezes ações ou dados) são +chamados de Controle de Acesso de Objetos ou ACOs. As entidades são +chamados 'objetos' porque algumas vezes a requisição de objetos não é +uma pessoa - as vezes você pode quer limitar o acesso a certos +controladores do Cake tem que iniciar a lógica em outras partes da sua +aplicação. ACOs pode ser qualquer coisa que você queira controlar, de +uma action de uma controladora, até um web service, para uma linha em +seu blog grandma's. + +Revisando: + +- ACO - Controle de acesso de objetos - Qualquer coisa que é necessária +- ARO - Requisição de acesso a objetos - Qualquer coisa que necessita + de algo + +Essencialmente, ACL é o que é usado para decidir quando um ARO quer ter +acesso a um ACO. + +A fim de ajuda-lo aentender como tudo funciona em conjunto, vamos usar +um exemplo prático. Imagine, por um momento, um sistema de computador +usado por um grupo familiar de aventureiros do *Senhor dos Anéis Rings*. +O líder do grupo, Gandalf, quer gerenciar os bens do grupo enquando +mantém uma boa quantidade de privacidade e segurança para os outros +membros do grupo. A primeira coisa ele precisa criar uma lista de AROs +envolvidos: + +- Gandalf +- Aragorn +- Bilbo +- Frodo +- Gollum +- Legolas +- Gimli +- Pippin +- Merry + +Perceba que o ACL *não* é a mesma coisa que autenticação. ACL é o que +acontece *depois* que um usuário está autenticado. Embora os dois são +geralmente usados em conjunto, ele é importante para realizar a +diferença entre saber quem é (autenticação) e saber o que ele fez (ACL). + +A próxima coisa que Gandalf precisa fazer é uma lista inicial de coisas, +ou ACOs, o sistema irá tratar. Essa lista pode ser algo como: + +- Weapons +- The One Ring +- Salted Pork +- Diplomacy +- Ale + +Tradicionalmente, sistemas eram gerenciados usando uma matriz, que +mostrava basicamente os usuários e permissões relacionado a objetos. Se +essa informação estava armazenada numa tabela, que poderia parecer com a +seguinte tabela: + +Weapons + +The Ring + +Salted Pork + +Diplomacy + +Ale + +Gandalf + +Allow + +Allow + +Allow + +Aragorn + +Allow + +Allow + +Allow + +Allow + +Bilbo + +Allow + +Frodo + +Allow + +Allow + +Gollum + +Allow + +Legolas + +Allow + +Allow + +Allow + +Allow + +Gimli + +Allow + +Allow + +Pippin + +Allow + +Allow + +Merry + +Allow + +A primeira vista, parece que esse tipo de sistema poderia funcionar bem. +Atribuições podem ser feitas para proteger a segurança (apenas Frodo +pode ter acesso ao anel) e proteger contra acidentes. Parece de +granularidade suficiente, e mesmo assim fácil de ler, certo? + +Para um sistema pequeno como esse, talvez uma matriz iria trabalhar. Mas +com o crescimento do sistema, ou o sistema com uma grande quantidade de +recursos (ACOs) e usuários (AROs), uma tabela pode se tornar pesada +rapidamente. Imagine tentando controlar o acesso de centenas de +acampamentos de guerra e tentando gerenciá-los por unidade. Outro +inconveniente de matrizes é que você não pode agrupar logicamente por +grupo de usuários ou fazer permissão em cascata mudando para grupos de +usuários baseados nesses agrupamentos lógicos. Por exemplo, seria bom +permitir automaticamente aos hobbits acesso para o ale e pork uma vez +que a batalha acabou. Fazendo isso um usuário indivualmente, seria +tediosa e propensa a erros. Fazendo permissão em cascata mudando para +todos 'hobbits' seria mais fácil. + +ACL é geralmente implementado numa estrutura de árvore. Existe +geralmente uma árvore de AROs e uma árvore de ACOs. Para organizar seus +objetos em árvores, permissões podem ser tratadas de forma granulada. +Como líder, Gandalf elege ACL para usar no seu novo sistema, e organiza +seus objetos como nas seguintes linhas: + +- Fellowship of the Ring™ + + - Warriors + + - Aragorn + - Legolas + - Gimli + + - Wizards + + - Gandalf + + - Hobbits + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitors + + - Gollum + +Usando uma estrutura de árvore ARO permite Gandalf definir permissões +que são aplicadas para entrada de grupos de usuários de uma só vez. +Então, usando sua árvore ARO, Gandalf pode mudar um pouco as permissões +baseado em grupo: + +- Fellowship of the Ring + (**Deny**: all) + + - Warriors + (**Allow**: Weapons, Ale, Elven Rations, Salted Pork) + + - Aragorn + - Legolas + - Gimli + + - Wizards + (**Allow**: Salted Pork, Diplomacy, Ale) + + - Gandalf + + - Hobbits + (**Allow**: Ale) + + - Frodo + - Bilbo + - Merry + - Pippin + + - Visitors + (**Allow**: Salted Pork) + + - Gollum + +Se nós queremos usar o ACL para ver se o Pippin tinha permissão para +acessar a cerveja, nós primeiro temos que obter o caminho na árvore, que +é Fellowship->Hobbits->Pippin. Então nós vemos diferentes permissões que +se encontram presente em cada um desses pontos, e usa a mais específica +permissão relacionada com Pippin e Cerveja. + ++--------------------------+-------------------+---------------------------+ +| ARO Node | Permission Info | Result | ++==========================+===================+===========================+ +| Fellowship of the Ring | Deny all | Denying access to ale. | ++--------------------------+-------------------+---------------------------+ +| Hobbits | Allow 'ale' | Allowing access to ale! | ++--------------------------+-------------------+---------------------------+ +| Pippin | -- | Still allowing ale! | ++--------------------------+-------------------+---------------------------+ + +Desde que o nó 'Pippin' na árvore ACL não especifica permissão negada +para a cerveja ACO, o resultado final é que nós permitimos acesso para +aquele ACO. + +A mesma árvore nos permite fazer finos ajustes para um controle mais +granular - embora ainda mantendo a habilidade de fazer grandes mudanças +para os grupos AROs: + +- Fellowship of the Ring + (**Deny**: all) + + - Warriors + (**Allow**: Weapons, Ale, Elven Rations, Salted Pork) + + - Aragorn + (Allow: Diplomacy) + - Legolas + - Gimli + + - Wizards + (**Allow**: Salted Pork, Diplomacy, Ale) + + - Gandalf + + - Hobbits + (**Allow**: Ale) + + - Frodo + (Allow: Ring) + - Bilbo + - Merry + (Deny: Ale) + - Pippin + (Allow: Diplomacy) + + - Visitors + (**Allow**: Salted Pork) + + - Gollum + +Esta abordagem permite-nos tanto a capacidade de fazer alterações de +grande alcance nas permissões, mas também ajustes finos. Isso +permite-nos dizer que todos os hobbits podem ter acesso a cerveja, com +uma exceção; Merry. Para ver se Merry pode acessar a cerveja, nós temos +que procurar seu caminho na árvore: Fellowship->Hobbits->Merry e +trabalhar nosso caminho, e manter o rasto de permissões relacionadas a +cerveja: + ++--------------------------+-------------------+---------------------------+ +| ARO Node | Permission Info | Result | ++==========================+===================+===========================+ +| Fellowship of the Ring | Deny all | Denying access to ale. | ++--------------------------+-------------------+---------------------------+ +| Hobbits | Allow 'ale' | Allowing access to ale! | ++--------------------------+-------------------+---------------------------+ +| Merry | Deny 'ale' | Denying ale. | ++--------------------------+-------------------+---------------------------+ + +Definindo Permissões: ACL do Cake baseado em arquivo INI +======================================================== + +A primeira implementação do ACL do Cake foi baseada em arquivos INI na +instalação do Cake. Enquanto ele é útil e estável, nós recomendamos que +você use a solução de base de dados do ACL, principalmente porque ele é +fácil para criar novos ACOs e AROs. Nos significou para usarmos em +aplicações simples - e especialmente para aquelas pessoas que talvez não +estejam usando uma base de dados por alguma razão. + +Por padrão, o ACL do CakePHP é controlado banco de dados. Para habilitar +ACL baseado em arquivo INI, você precisará dizer ao CakePHP qual sistema +você está usando atualizando as seguintes linhas em app/config/core.php + +:: + + //Mude essas linhas: + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + + //Fica assim: + Configure::write('Acl.classname', 'IniAcl'); + //Configure::write('Acl.database', 'default'); + +Permissões ARO/ACO são especificadas em **/app/config/acl.ini.php**. A +idéia básica é que AROs são especificados na seção INI que possui três +propriedades: grupos, permissão e restrição. + +- grupos: nome de grupos ARO, esse ARO é membro de +- permissão: nome de ACOs, esse ARO tem acesso à +- restrição: nome de ACOs, esse ARO deve ser acesso restrito para + +ACOs são especificados em seções INI que apenas incluem as propriedades +de permissão e restrição. + +Como um exemplo, vamos ver a estrutura de companherismo ARO, a +elaboraçao seria semelhante a sintaxe INI: + +:: + + ;------------------------------------- + ; AROs + ;------------------------------------- + [aragorn] + groups = warriors + allow = diplomacy + + [legolas] + groups = warriors + + [gimli] + groups = warriors + + [gandalf] + groups = wizards + + [frodo] + groups = hobbits + allow = ring + + [bilbo] + groups = hobbits + + [merry] + groups = hobbits + deny = ale + + [pippin] + groups = hobbits + + [gollum] + groups = visitors + + ;------------------------------------- + ; ARO Groups + ;------------------------------------- + [warriors] + allow = weapons, ale, salted_pork + + [wizards] + allow = salted_pork, diplomacy, ale + + [hobbits] + allow = ale + + [visitors] + allow = salted_pork + +Agora que você tem suas permissões definidas, você pode pular para `a +seção de controle de +permissão `_ usando o +componente ACL. + +Definindo Permissões: Base de dados ACL do Cake +=============================================== + +Agora que você já sabe como funciona as permissões do ACL baseado em +arquivo INI, vamos analisar como usar permissões ACL através de banco de +dados (mais comumente utilizado). + +Começando +--------- + +O padrão da implementação de permissões ACL é em base de dados. A base +de dados ACL do Cake consiste de um conjunto de modelos, e uma aplicação +console que vem com a instalação do Cake. Os modelos são usados pelo +Cake para intergir com a base de dados para armazenar e recuperar nós em +formato de árvore. A aplicação console é usada para inicializar sua base +de dados e interagir com suas árvores ACO e ARO. + +Para começar, você precisa primeiro ter certeza que o arquivo +``/app/config/database.php`` está presente e configurado corretamente. +Veja a seção 4.1 para maiores informações sobre a configuração da base +de dados. + +Mesmo que você tenha finalizado, use o console CakePHP para criar suas +tabelas da base de dados ACL: + +:: + + $ cake schema run create DbAcl + +Rodando esse comando, ele irá apagar e re-criar as tabelas necessárias +para armazenar as informações em formato de árvore de ACO e ARO. A saída +da aplicação console deveria ser como a seguir: + +:: + + --------------------------------------------------------------- + Cake Schema Shell + --------------------------------------------------------------- + + The following tables will be dropped. + acos + aros + aros_acos + + Are you sure you want to drop the tables? (y/n) + [n] > y + Dropping tables. + acos updated. + aros updated. + aros_acos updated. + + The following tables will be created. + acos + aros + aros_acos + + Are you sure you want to create the tables? (y/n) + [y] > y + Creating tables. + acos updated. + aros updated. + aros_acos updated. + End create. + +Isso substitui um comando depreciado, "initdb". + +Você pode mesmo usar o arquivo SQL que pode ser encontrado em +``app/config/sql/db_acl.sql``, mas isso não tem muita graça. + +Quando terminado, você deveria ter três novas tabelas na sua base de +dados do seu sistema: acos, aros e aros\_acos (uma junção das tabelas +para criar informações de permissões entre as duas árvores). + +Se você está curioso sobre como o Cake armazena as informações em àrvore +nessas tabelas, leia sobre modificação transversal em base de dados em +formato de árvore. O componente ACL usa o `Behavior +Tree `_ do Cake para gerenciar as heranças +das árvores. Os arquivos de modelo de classes para ACL estão todos +compilados em um único arquivo +`db\_acl.php `_. + +Agora que você está sabendo tudo, vamos começar a trabalhar criando +algumas árvores ARO e ACO. + +Criado Acesso de Requisição de Objetos (AROs) e Controle de Acesso a Objetos (ACOs) +----------------------------------------------------------------------------------- + +Criando nos objetos ACL (ACOs e AROs), verifique que existem duas formas +principais de nomear e chamar nodes. A *primeira* forma é linkar o +objeto diretamente ao registro na sua base de dados, especificando um +nome de molde e um valor de chave estrangeira. A *segunda* forma pode +ser usada quando um objeto não tem uma relação direta com o registro em +sua base de dados - você pode fornecer um álias para o objeto. + +Comumente, quando você está criando um grupo ou objeto de nível alto, +use um álias. Se você está gerenciando acesso para um item específico ou +registro na base de dados, use a forma modelo/chave estrangeira. + +Você cria novos objetos ACL usando o núcleo ACL do CakePHP. Fazendo +isso, existem um número de campos que você precisará usar quando estiver +salvando dados: ``model``, ``foreign_key``, ``alias``, e ``parent_id``. + +Os campos ``model`` e ``foreign_key`` para um objeto ACL permite que +você ligue o objeto para o seu modelo correspondente ao registro (se +existir algum). Por exemplo, muitos AROs terão registros User +correpondentes na base de dados. Setando um ARO ``foreign_key`` para o +User ID irá permitir você ligar ARO e informações User com um simples +modelo User find() chamado se você tiver setado corretamente as +associações de modelo. Inversamente, se você quer gerenciar operações de +edição em um post de blog específico ou listar receitas, você pode +escolher ligar um ACO a um registro específico de modelo. + +O ``álias`` para um objeto ACL é apenas um rótulo que você pode usar +para identificar um objeto ACL que não tem correlação de registro modelo +direta. Álias são geralmente úteis nomeando grupo de usuários ou +coleções de ACO. + +O ``parent_id`` para um objeto ACL permite você preencher a estrutura da +árvore. Forneça o ID do node filho na árvore para criar um novo filho. + +Antes nós podemos criar novos objeto ACL, nós precisamos carregar essas +respectivas classes. A forma mais fácil para fazer isso é incluir o +componente ACL no seu array controladora $components array: + +:: + + var $components = array('Acl'); + +Depois de feito isso, vamos ver o que alguns desses exemplos de criação +de objetos se parecem. O código a seguir pode ser colocado em qualquer +lugar na action: + +Enquanto os exemplos aqui focam a criação de ARO, as mesmas técnicas +podem ser usadas para criar uma árvore ACO. + +Mantendo com a nossa configuração de Sociedade, vamos primeiro criar +nossos grupos ARO. Porque nossos grupos não têm registros específicos +vinculados a eles, nós iremos usar álias para criar esses objetos ACL. O +que nós temos que fazer aqui é a partir da perspectiva da action da +controladora, mas pode ser feito em outro lugar. O que nós vamos cobrir +aqui é um pedaço da abordagem artificial, mas você deve se sentir +confortável para usar estas técnicas para construir AROs e ACOs em +situções reais. + +Isso não deve ser algo drasticamente novo - nós estamos apenas usando +modelos para salvar dados como nós sempre fazemos: + +:: + + function anyAction() + { + $aro =& $this->Acl->Aro; + + //Aqui estão todas informações sobre nosso grupo num array + $groups = array( + 0 => array( + 'alias' => 'warriors' + ), + 1 => array( + 'alias' => 'wizards' + ), + 2 => array( + 'alias' => 'hobbits' + ), + 3 => array( + 'alias' => 'visitors' + ), + ); + + //Iterar e criar grupos ARO + foreach($groups as $data) + { + //Lembre-se de chamar create() quando salvar em loops... + $aro->create(); + + //Save data + $aro->save($data); + } + + //Outras lógicas da action vão aqui... + } + +Uma vez que nós temos eles lá dentro, podemos usar a aplicação de +console do ACL para verificar a estrutura da árvore. + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [2]wizards + + [3]hobbits + + [4]visitors + + --------------------------------------------------------------- + +Eu suponho que não é muito de uma árvore esse ponto, mas pelo menos +temos alguma verificação que nós temos para os quatros principais +níveis. Vamos adicionar alguns filhos para esse node ARO, adicionando +seus usuários AROs específicos, debaixo desses grupos. Todos bons +cidadãos de Middle Earth tem uma conta no nosso sistema, então nós +iremos amarrar esses registros ARO para um modelo específico de +registros na nossa base de dados. + +Quando adicionar nodes filhos para uma árvore, tenha certeza de usar o +node ID do ACL, invés de um valor foreign\_key. + +:: + + function anyAction() + { + $aro =new Aro(); + + // Aqui são seus registros de usuário, lidos para serem ligados para os novos + // registros ARO. + // Esses dados devem vir do modelo e modificado, mas nós estamos usando estáticos + // arrays aqui apenas para demostração + + $users = array( + 0 => array( + 'alias' => 'Aragorn', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 2356, + ), + 1 => array( + 'alias' => 'Legolas', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 6342, + ), + 2 => array( + 'alias' => 'Gimli', + 'parent_id' => 1, + 'model' => 'User', + 'foreign_key' => 1564, + ), + 3 => array( + 'alias' => 'Gandalf', + 'parent_id' => 2, + 'model' => 'User', + 'foreign_key' => 7419, + ), + 4 => array( + 'alias' => 'Frodo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 7451, + ), + 5 => array( + 'alias' => 'Bilbo', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5126, + ), + 6 => array( + 'alias' => 'Merry', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 5144, + ), + 7 => array( + 'alias' => 'Pippin', + 'parent_id' => 3, + 'model' => 'User', + 'foreign_key' => 1211, + ), + 8 => array( + 'alias' => 'Gollum', + 'parent_id' => 4, + 'model' => 'User', + 'foreign_key' => 1337, + ), + ); + + //Itera e cria AROs (como filhos) + foreach($users as $data) + { + //Lembre-se de chamar o create() quando estiver salvando em loops... + $aro->create(); + + //Salvar os dados + $aro->save($data); + } + + //Outras lógicas da action vão aqui... + } + +Tipicamente você não pode fornecer álias e um modelo/chave estrangeira, +mas nós estamos usando ambos aqui para fazer a estrutura de árvore mais +fácil para ler, mas apenas como demonstração. + +A saída da aplicação de console deve ser agora um pouco mais +interessante. Vamos ver a possibilidade: + +:: + + $ cake acl view aro + + Aro tree: + --------------------------------------------------------------- + [1]warriors + + [5]Aragorn + + [6]Legolas + + [7]Gimli + + [2]wizards + + [8]Gandalf + + [3]hobbits + + [9]Frodo + + [10]Bilbo + + [11]Merry + + [12]Pippin + + [4]visitors + + [13]Gollum + + --------------------------------------------------------------- + +Agora que nós tempos nossa árvore ARO contruída corretamente, vamos +discutir uma possível abordagem para estruturar uma árvore ACO. Embora +possamos estruturar mais de uma representação abstrata de nossa ACO, é +muitas vezes mais prático modelar uma árvore ACO depois de configurar as +Controladoras/Action do Cake. Nós temos cinco objetos principais que +temos de controlar em nosso cenário de Sociedade, e a configuração +natural para que em uma aplicação Cake está um grupo de modelos, e +finalmente os controladores que os manipulam. Passado os controladores, +nós vamos querer controlar acesso a actions específicas naquelas +controladoras. + +Com base nessa idéia, vamos configurar uma árvore ACo que irá imitar o +setup de uma aplicação Cake. Pois temos cinco ACOs, nós iremos criar uma +árvore ACO que deve acabar procurando algo como a seguir: + +- Weapons +- Rings +- PorkChops +- DiplomaticEfforts +- Ales + +Um detalhe legal sobre a configuração do ACL do Cake é que cada ACO +contém automaticamente quatro propriedades relacionadas a actions CRUD +(criar, pesquisar, atualizar e deletar). Você pode criar nodes filhos +abaixo de cada um desses cinco principais ACOs, mas usando o construtor +de gerenciamento de actions do Cake que abrange as operações básicas do +CRUD sobre um determinado objeto. + +Uma vez que você está agora adicionando AROs, use aquelas mesmas +técnicas para criar essa árvore ACO. Criar esses níveis de grupos usando +o modelo ACO. + +Atribuindo Permissões +--------------------- + +Depois de criar seus ACOs e AROs, nós podemos finalmente atribuir +permissões entre os dois grupos. Esse é feito usando o núcleo do +componente ACL. Vamos continuar com nosso exemplo. + +Aqui nós iremos trabalhar no contexto da action da controladora. Nós +faremos porque as permissões são gerenciadas pelo componente ACL. + +:: + + class SomethingsController extends AppController + { + // Você pode querer colocar isso no AppController + // mas aqui funciona muito bem também + + var $components = array('Acl'); + + } + +Vamos confugurar algumas permissões básicas usando o componente +AclComponent na action dentro dessa controladora. + +:: + + function index() + { + // Permite guerreiros (warriors) a acesso completa a armas (weapons) + // Ambos exemplos usam a sintaxe de álias + $this->Acl->allow('warriors', 'Weapons'); + + // Embora o rei não possa querer todos + // tem acesso ilimitado + $this->Acl->deny('warriors/Legolas', 'Weapons', 'delete'); + $this->Acl->deny('warriors/Gimli', 'Weapons', 'delete'); + + die(print_r('done', 1)); + } + +A primeiro conjuneto de chamadas nós fazemos para o AclComponent permir +que qualquer usuário abaixo do grupo ARO 'warriors' tenha acesso +completo a qualquer coisa abaixo do grupo ACO 'Weapons'. Aqui estamos +apenas abordando os ACOs e AROs pelo seus álias. + +Observou o uso do terceiro parâmetro? Isso é onde nós usamos aqueles +controles que estão dentro de todos ACO Cake. A opção padrão para que os +parâmetros são ``create``, ``read``, ``update``, e ``delete`` mas você +pode adicionar uma coluna na tabela ``aros_acos`` (prefixado com \_ - +por exemplo ``_admin``) e usá-lo juntamente com os padrões. + +O segundo conjunto de chamadas é uma tentativa de fazer decisão de +permissão de granularidade alta. Nós queremos que Aragorn mantenha seu +privilégio de acesso, mas recusar que outros guerreiros do grupo tenha +permissão para deletar registros de armas. Nós estamos usando álias para +resolver o AROs acima, mas você pode querer usar a sintaxe modelo/chave +estrangeira. O que nós temos acima é equivalente a isso: + +:: + + // 6342 = Legolas + // 1564 = Gimli + + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342), 'Weapons', 'delete'); + $this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564), 'Weapons', 'delete'); + +Endereçando um node usando a sintaxe álias usa o delimitador '/' +('/users/employees/developers'). Endereçando um node usando a sintaxe +modelo/chave estrangeira usa um array com dois parâmetros: +``array('model' => 'User', 'foreign_key' => 8282)``. + +A próxima seção irá nos ajudar a validar nossa configuração usando o +AclComponent para checar as permissões que acabamos de criar. + +Checando Permissões: O Componente ACL +------------------------------------- + +Vamos usar o AclComponent para ter certeza que dwarves e elves não podem +remover coisas do depósito de armas. Nesse ponto, deveríamos ser capazes +de utilizar o AlcComponent para fazer a verificação entre os ACOs e AROs +que criamos. O comando básico para fazer a verificação das permissões é: + +:: + + $this->Acl->check( $aro, $aco, $action = '*'); + +Vamos tentar dar-lhe uma action dentro da controladora: + +:: + + function index() + { + //Isso tudo retorna true: + $this->Acl->check('warriors/Aragorn', 'Weapons'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'create'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'read'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'update'); + $this->Acl->check('warriors/Aragorn', 'Weapons', 'delete'); + + //Lembre-se, nós podemos usar a sintaxe model/foreign key + //para nossos usuários AROs + $this->Acl->check(array('model' => 'User', 'foreign_key' => 2356), 'Weapons'); + + //Isso também vai retornar true: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'create'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'read'); + + //Mas isso retorna false: + $result = $this->Acl->check('warriors/Legolas', 'Weapons', 'delete'); + $result = $this->Acl->check('warriors/Gimli', 'Weapons', 'delete'); + } + +O uso aqui é demonstração, mas espero que você pode ver como é verificar +como esta pode ser utilizada para decidir se quer ou não permitir que +aconteça alguma coisa, mostrar uma mensagem de erro, ou redirecionar o +usuário para um login diff --git a/pt/The-Manual/Core-Components/Authentication.rst b/pt/The-Manual/Core-Components/Authentication.rst new file mode 100644 index 0000000000000000000000000000000000000000..241c9455f59fcb35bafc430460aab4fc52bdff4c --- /dev/null +++ b/pt/The-Manual/Core-Components/Authentication.rst @@ -0,0 +1,753 @@ +Autenticação +############ + +O Sistema de autenticação de usuário é uma parte comum de muitas +aplicações web. No CakePHP, existem vários sistemas de autenticação de +usuários, sendo que cada uma oferece diferentes opções. Na sua essência +o componente de autenticação irá verificar se um usuário tem uma conta +com um site. Se o tiver, o componente lhes dará acesso a esse usuário +para o site. + +Este componente pode ser combinado com o ACL (Access Control List) para +criar componentes mais complexos em níveis de acesso dentro de um site. +O Componente ACL, por exemplo, poderia permitir-lhe conceder o acesso do +usuário as áreas públicas de um site, enquanto que outro usuário o +acesso à concessão as partes protegidas e administrativas do site. + +O AuthComponent pode ser usado para criar o tal sistema de forma fácil e +rápida. Vamos dar uma olhada em como você iria construir um simples +sistema de autenticação. + +Como todos os componentes, você usá-lo por 'Auth' para a lista de +componentes no seu controller: + +:: + + class FooController extends AppController { + var $components = array('Auth'); + +Ou adicioná-lo ao seu AppController para que todos os controladores +possa utilizá-lo: + +:: + + class AppController extends Controller { + var $components = array('Auth'); + +Agora, há alguns padrões sobre quando utilizar AuthComponent. Por +padrão, o AuthComponent espera que você tenha uma tabela chamada 'users' +com campos denominados' username' e 'senha' para ser utilizada. *Em +algumas situações, as bases de dados não permitem que você use +'password' como nome de uma coluna, mais tarde, você vai ver como mudar +o padrão campo nomes para trabalhar com o seu próprio ambiente.* + +Vamos criar a tabela com os nossos usuários utilizando o seguinte SQL: + +:: + + CREATE TABLE users ( + id integer auto_increment, + username char(50), + password char(40), + PRIMARY KEY (id) + ); + +Algo para segurar na mente ao criar uma tabela para armazenar todos os +seus dados de autenticação do usuário. O AuthComponent espera que a +senha armazenada no banco de dados seja ciptografada ao invés de ser +armazenada como texto puro. Tenha certeza que o campo que você estará +usando para armazenar senhas é grnade o suficiente para armazenar hash +(40 caracteres for SHA1, por exemplo). + +Se você quer adicionar um usuário manualmente para o banco de dados - o +método simples de obter os dados corretos é a tentativa de login e olhar +o log sql.log + +Para a configuração mais básica, você precisa apenas criar duas actions +na sua controladora: + +:: + + class UsersController extends AppController { + + var $name = 'Users'; + var $components = array('Auth'); // Não é necessário se declarado na sua AppController + + /** + * O AuthComponent disponibiliza as funções necessárias para login, + * então você pode deixar essa função em branco. + */ + function login() { + } + + function logout() { + $this->redirect($this->Auth->logout()); + } + } + +Enquanto você deixar a função login() em branco, você precisa criar a +template de visão da action login (salvo em app/views/users/login.ctp). +Isso é a única template de visão da classe UsersController que é +necessária criar. O exemplo abaixo assume que você já esteja usando o +Helper Form: + +:: + + flash('auth'); + echo $form->create('User', array('action' => 'login')); + echo $form->input('username'); + echo $form->input('password'); + echo $form->end('Login'); + ?> + +Essa visão criar um simples formulário de login, onde você informa o +usuário e senha. Mesmo você submetendo esse formulário, o AuthComponent +cuida do resto para você. A mensagem da sessão será mostrada quando +qualquer notificação for gerada pelo AutoComponent. + +Acredite ou não, está feito! Essa é a forma de implementar incrivelmente +simples, sistema de autenticação usando o componente Auth. Entretanto, +existe muito mais que podemos fazer. Dê uma olhada em algumas +utilizações avançadas do componente + +Setando Variáveis do Auth Component +=================================== + +Toda vez que você alterar uma opção padrão para o AuthComponent, você +tem quefazer isso através da criação do método beforeFilter no seu +controlador, então chamando vários métodos embarcados ou setar as +variáveis do componente. + +Por exemplo, para mudar o nome do campo usado para senhas de 'password' +para 'secretword', você deve fazer o seguinte: + +:: + + class UsersController extends AppController { + var $components = array('Auth'); + + function beforeFilter() { + $this->Auth->fields = array( + 'username' => 'username', + 'password' => 'secretword' + ); + } + } + +Nesse caso em particular, você deve além disso é necessário lembrar para +mudar o nome do campo na view! + +Outro uso comum das variáveis do Auth Component é permitir acesso a +certos métdods sem que o usuário esteja logado. + +Por exemplo, se nós queremos permitir que todos os usuários acessem os +métodos index e view (mas não qualquer outro), nós devemos fazer o +seguinte: + +:: + + function beforeFilter() { + $this->Auth->allow('index','view'); + } + +Mostrando mensagens do Auth Error +================================= + +Para exibir as mensagens de erros do Auth, você precisa adicionar o +seguinte código no seu view. Neste caso, a mensagem aparecerá abaixo das +mensagens regulares "flash": + +Em ordem para mostrar todas menssagens "flash" normais e as menssagens +"flash auth" para todos os views, adicione as seguintes linhas no seu +seu arquivo /views/layouts/default.ctp na secão body de preferência +antes da linha content\_for\_layout. + +:: + + flash(); + $session->flash('auth'); + ?> + +Solucionando problemas do Auth +============================== + +Em alguns casos pode ser bastante difícil diagnosticar problemas quando +o mesmo não se comporta como esperado, então aqui existem alguns pontos +para lembrar + +*Senha hashing* + +Quando enviar informação para uma action através de um form, o Auth +component automaticamente criptografa o conteúdo de seu campo de entrada +senha se você informado algum dado no campo usuário. Então, se você está +tentando criar alguma página de registro, tenha certeza de ter feito o +usuário preencher um campo chamado 'confirmar senha' para que possa +comparar os dois valores. Aqui está um exemplo de código: + +:: + + data) { + if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) { + $this->User->create(); + $this->User->save($this->data); + } + } + } + ?> + +Mudando a função Hash +===================== + +O AuthComponent usa a classe Security para criptografar a senha. A +classe Security usa o esquema SHA1 por padrão. Para mudar outra função +hash para ser utilizado pelo componente Auth use o método ``setHash`` +passando ``md5``, ``sha1`` ou ``sha256`` como único parâmetro. + +:: + + Security::setHash('md5'); // ou sha1 ou sha256. + +A classe Security usa um valor forte (setar em /app/config/core.php) +para criptografar a senha. + +Se você quer usar uma lógica de hashing diferente para senha além de +md5/sha1, você irá precisar sobrescrever o mecanismo padrão hashPassword +- Você talvez precise fazer isso se por exemplo você tem uma base de +dados existente que anteriormente utilizava um esquema de diferente. +Para fazer isso, criar o método ``hashPasswords`` na classe que você +quer se responsável por criptografar suas senhas (geralmente no modelo +User) e setar ``authenticate`` para o objeto que você está autenticando +(geralmente esse é User) como: + +:: + + function beforeFilter() { + $this->Auth->authenticate = ClassRegistry::init('User'); + ... + parent::beforeFilter(); + } + +Como o código acima, o método hashPasswords() do modelo User será +chamado toda vez que o Cake chamar AuthComponent::hashPasswords(). + +Métodos do AuthComponent +======================== + +action +------ + +``action (string $action = ':controller/:action')`` + +Se você está utilizando ACO's como parte da estrutura do seu ACL, você +pode recuperar o caminho para o nó ACO para um par controlador +particular controller/action: + +:: + + $acoNode = $this->Auth->action('users/delete'); + +Se você não passar qualquer valor, ele usa o valor atual do par +controller / action + +allow +----- + +Se você tem algumas actions em sua controladora que não precisam ser +autenticada, você pode adicionar métodos ao AuthComponent para que ele +ignore. O exemplo a seguir mostra como permitir uma action chamada +'register'. + +:: + + $this->Auth->allow('register'); + +Se você deseja permitir que múltiplas actions ignorem autenticação, você +tem que fornecê-los como parâmetros para o método allow(): + +:: + + $this->Auth->allow('foo', 'bar', 'baz'); + +Atalho: você talvez precise permitir que todas as actions da sua +controladora, nesse caso utilize '\*'. + +:: + + $this->Auth->allow('*'); + +Se você está usando requestAction no seu layout ou elementos, você +precisa permitir que essas actions, a fim de ser capaz de abrir a página +de login corretamente. + +O AuthComponent assume que o nome das suas actions `está seguindo as +convenções `_ e +estão em caixa baixa. + +deny +---- + +Existem algumas vezes onde você quer remover actions da sua lista de +actions permitidas (setar usando $this->Auth->allow()). Aqui está um +exemplo: + +:: + + function beforeFilter() { + $this->Auth->authorize = 'controller'; + $this->Auth->allow('delete'); + } + + function isAuthorized() { + if ($this->Auth->user('role') != 'admin') { + $this->Auth->deny('delete'); + } + + ... + } + +hashPasswords +------------- + +``hashPasswords ($data)`` + +Esse método checa se a ``$data`` contém os campos username e password +como especificado pela variável ``$fields`` indexada pelo nome de modelo +como especificado por ``$userModel``. Se o array ``$data`` contém o +username e password, ele criptografa o campo password no array e retorna +o array ``data`` no mesmo formato. Essa função deve ser usada antes das +chamadas do usuário para inserir ou alterar. + +:: + + $data['User']['username'] = 'me@me.com'; + $data['User']['password'] = 'changeme'; + $hashedPasswords = $this->Auth->hashPasswords($data); + print_r($hashedPasswords); + /* returns: + Array + ( + [User] => Array + ( + [username] => me@me.com + [password] => 8ed3b7e8ced419a679a7df93eff22fae + ) + ) + + */ + +O campo *$hashedPasswords['User']['password']* agora deveria ser +criptografada usando o ``password``, na função do componente. + +Se seu controlador usa o AuthComponent e enviou os dados contidos nos +campos como explicado acima, ele irá automaticamente criptografar o +campo password usando essa função. + +mapActions +---------- + +Se você está utiliznado o ACL em modo CRUD, você talvez precise atribuir +ations não-padrão para cada parte do CRUD. + +:: + + $this->Auth->mapActions( + array( + 'create' => array('algumaAction'), + 'read' => array('algumaAction', 'algumaAction2'), + 'update' => array('algumaAction'), + 'delete' => array('algumaAction') + ) + ); + +login +----- + +``login($data = null)`` + +Se você está fazendo algum login baseado em Ajax, você pode usar esse +método para registrar alguém manualmente no sistema. Se você não passar +qualquer valor para ``$data``, ele irá automaticamente usar POST para os +dados passados dentro do controller. + +logout +------ + +Provê uma forma rápida para retirar a autenticação de alguém e +redirecionar para onde ele precisa ir. Esse método também é útil se você +quer prover um link 'Me tire daqui' para membros, dentro de uma área da +sua aplicação + +Exemplo: + +:: + + $this->redirect($this->Auth->logout()); + +password +-------- + +``password (string $password)`` + +Passe uma string, e você recebe a senha criptografada. Isso é uma +funcionalidade essencial se você está criando um usuário numa página +onde você tem usuários que entram com suas senhas uma segunda vez para +confirmá-las. + +:: + + if ($this->data['User']['password'] == + $this->Auth->password($this->data['User']['password2'])) { + + // Passwords match, continue processing + ... + } else { + $this->flash('Typed passwords did not match', 'users/register'); + } + +O componente Auth irá automaticamente criptografar o campo senha se o +campo username também estiver presente nos dados submetidos. + +O Cake junta sua senha a um valor e então criptografa-os. A função de +criptografia usada depende de como está configurardo a classe utilitária +do núcles ``Security`` (sha1 por padrão). Você pode usar o método +``Security::setHash`` para mudar o método de criptografia. O valor usado +para sua aplicação é definido em ``core.php`` + +user +---- + +``user(string $key = null)`` + +Esse método provê informação sobre o usuário autenticado no momento. A +informação é recuperada a partir da sessão. Por exemplo: + +:: + + if ($this->Auth->user('role') == 'admin') { + $this->flash('Você tem acesso de administrador'); + } + +Pode ser usado também para retornar a sessão completa do usuário como: + +:: + + $data['User'] = $this->Auth->user(); + +Se esse método retornar null, o usuário não está logado na aplicação. + +Na view você pode usar o helper Session para obter informações sobre o +usuário autenticado no momento: + +:: + + + $session->read('Auth.User'); // retorna o registro completo do usuário + $session->read('Auth.User.first_name') // retorna o valor de um campo em específico + +A chave da sessão pode ser diferente dependendo de qual model o Auth +está configurado para usar. P.ex., se você usasse o model ``Account`` ao +invés de ``User``, então a chave da sessão seria ``Auth.Account``. + +Variáveis AuthComponent +======================= + +Agora, existem algumas variáveis relacionadas que você pode usar como +bem entender. Geralmente você pode adicionar essas definições na sua +controladora no método beforeFilter(). Ou, se você precisar aplicar como +definições site-wide, você deveria adicionar elas no classe +AppController, no método beforeFilter() + +userModel +--------- + +Não quer utilizar o modelo User para fazer autenticação? Sem problemas, +apenas mude-o setando esse valor para o nome do modelo que você quer +usar. + +:: + + Auth->userModel = 'Member'; + ?> + +fields +------ + +Sobrescreva os campos padrões 'username' e 'password' usados para +autenticação. + +:: + + Auth->fields = array('username' => 'email', 'password' => 'passwd'); + ?> + +userScope +--------- + +Use isso para prover requisitos para autenticação ter sucesso. + +:: + + Auth->userScope = array('User.active' => true); + ?> + +loginAction +----------- + +Você pode mudar o login padrão de */users/login* para ser qualquer +action que você escolher. + +:: + + Auth->loginAction = array('admin' => false, 'controller' => 'members', 'action' => 'login'); + ?> + +loginRedirect +------------- + +O AuthComponent lembra qual par controlador/action você estava tentando +obter, para antes fazer sua própria autenticação e armazenar esse valor +na sessão, abaixo a chave ``Auth.redirect``. Entretanto, se esse valor +de sessão não está setado (se você está vindo para a página de login a +partir de um link externo, por exemplo), então o usuário será +redirecionado para a URL especificada no loginRedirect. + +Exemplo: + +:: + + Auth->loginRedirect = array('controller' => 'members', 'action' => 'home'); + ?> + +logoutRedirect +-------------- + +Você mesmo pode especificar onde você quer que o usuário vá depois que +ele for desconectado, com o padrão sendo a action login. + +:: + + Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'members', 'action' => 'logout'); + ?> + +loginError +---------- + +Mude a mensagem padrão de erro mostrada quando algupem não consegue +logar. + +:: + + Auth->loginError = "Não, você errou! A senha não está correta!"; + ?> + +authError +--------- + +Mude a mensagem de erro padrão quando alguém tenta acessar um objeto ou +action que ele não tenha acesso. + +:: + + Auth->authError = "Desculpe, você está sem acesso!"; + ?> + +autoRedirect +------------ + +Normalmente, o AuthComponent automaticamente redireciona você logo que +você é autenticado. Algumas vezes você quer fazer alguma validação a +mais antes de redirecionar o usuário: + +:: + + Auth->autoRedirect = false; + } + + ... + + function login() { + //-- código dentro dessa função irá executar apenas quando autoRedirect estiver setado como false. + if ($this->Auth->user()) { + if (!empty($this->data)) { + $cookie = array(); + $cookie['username'] = $this->data['User']['username']; + $cookie['password'] = $this->data['User']['password']; + $this->Cookie->write('Auth.User', $cookie, true, '+2 weeks'); + unset($this->data['User']['remember_me']); + } + $this->redirect($this->Auth->redirect()); + } + if (empty($this->data)) { + $cookie = $this->Cookie->read('Auth.User'); + if (!is_null($cookie)) { + if ($this->Auth->login($cookie)) { + // Limpa a mensagem auth, apenas nesse caso usamos ela. + $this->Session->del('Message.auth'); + $this->redirect($this->Auth->redirect()); + } + } + } + } + ?> + +O código na função de login não irá executar *a menos que* você marque +$autoRedirect para falso em beforeFilter. O código presente na função de +login apenas executa *depois* que a autenticação foi tentada. Esse é o +melhor lugar para determinar se o login ocorreu com sucesso ou não pelo +AuthComponent (você pode querer logar o último acesso com sucesso do +login, etc). + +authorize +--------- + +Normalmente, o AuthComponent tentará verificar se as credenciais de +login que você digitou são precisas, comparando-os com as quais estão +armazenadas no seu modelo User. Entretanto, existem vezes em que você +quer fazer um trabalho adicional na determinação correta das +credenciais. Para setar essa variável para um dos vários valores +diferentes, você pode fazer diferentes coisas. Aqui são algumas das mais +comuns que você pode querer utilizar. + +:: + + Auth->authorize = 'controller'; + ?> + +Quando authorize é setado para 'controller', você vai precisar adicionar +um método chamado isAuthorized() para seu controlador. Esse método +permite você fazer mais algumas verificações de autenticação e então +retornar true ou false. + +:: + + action == 'delete') { + if ($this->Auth->user('role') == 'admin') { + return true; + } else { + return false; + } + } + + return true; + } + ?> + +Lembre que esse método será checado depois que você tenha passado pela +autenticação básica do modelo user. + +:: + + Auth->authorize = 'model'; + ?> + +Não quero adicionar nada ao seu controlador e pode estar usando ACO's? +Você pode obter o AuthComponent chamar um método no seu modelo user, +chamando isAuthorized() para fazer o mesmo tipo de coisa: + +:: + + + +Finalmente, você pode usar autorização como nas actions a seguir: + +:: + + Auth->authorize = 'actions'; + ?> + +Ao utilizar actions, Auth fará uso da ACL e checar com +AclComponent::check(). A função isAuthorized não é necessária. + +:: + + Auth->authorize = 'crud'; + ?> + +Ao utilizar crud, Auth fará o uso de ACL e checar com +AclComponent::check(). Actions devem ser mapeadas para CRUS (veja +`mapActions `_). + +sessionKey +---------- + +Nome da chave do array de sessão onde o registro do atual usuário +autorizado está armazenado + +Padrão para "Auth", se não especificado, o registro é armazenado em +"Auth.{$userModel name}". + +:: + + Auth->sessionKey = 'Authorized'; + ?> + +ajaxLogin +--------- + +Se você está fazendo uso de Ajax ou Javascript baseado em requisições +que requerem sessões autenticadas, marque essa variável para o nome da +view que você gostaria que fosse renderizada e retornada quando você tem +uma inválida ou sessão expirada. + +Como qualquer parte do CakePHP, As with any part of CakePHP, +certifique-se de dar uma olhada na `classe do +AuthComponent `_ para mais +informações sobre este componente. + +authenticate +------------ + +Essa variável possui uma referência para o objeto responsável por +criptografar senhas se ela é necessária para mudar/sobrescrever o +mecanismo de criptografia de senha padrão. Veja `Mudando o Tipo de +Criptografia `_ para mais +informações. + +actionPath +---------- + +Se for utilizar um controle de acesso baseado em actions, esta variável +define como os caminhos para a ação são determinados a partir dos nós +ACO. Se, por exemplo, todos os nós dos controllers estiverem aninhados +dentro de um nó ACO chamado 'Controllers', então $actionPath deverá ser +definida para 'Controllers/'. diff --git a/pt/The-Manual/Core-Components/Cookies.rst b/pt/The-Manual/Core-Components/Cookies.rst new file mode 100644 index 0000000000000000000000000000000000000000..99771f0bd8cf36eccf901c96d3498533c8504592 --- /dev/null +++ b/pt/The-Manual/Core-Components/Cookies.rst @@ -0,0 +1,177 @@ +Cookies +####### + +O CookieComponent é um empacotador em torno do método nativo do PHP +setcookie. Antes de tentar utilizar o CookieComponent, você deve ter +certeza que 'Cookie' está listado em seus arrays $components das suas +controladoras. + +Configurar Controladora +======================= + +Existe um número de variáveis controladoras que permitem você configurar +a forma que os cookies são criados e gerenciados. Definindo essas +variávies especiais no método beforeFilter() da sua controladora, +permite você definir como o CookieComponent trabalha. + +Variável Cookie + +padrão + +descrição + +string $name + +'CakeCookie' + +O nome do cookie. + +string $key + +null + +Essa string é usada para criptografar o valor escrito no cookie. É +recomendado que essa string seja randômica e difícil de adivinhar. + +string $domain + +'' + +O nome do domínio permite acessar o cookie. Por exemplo: Use +'seudominio.com.br' para permitir acesso para todos seus subdomínios. + +int ou string $time + +'5 Dias' + +O momento em que seu cookie expirará. Inteiros são interpretados como +segundos e um valor 0 é equivalente para um 'session.cookie': por +exemplo, o cookie expira quando seu browser é fechado. Se uma string +estiver setada, isso será interpretado com a função PHP strtotime(). +Você pode setar isso diretamente dentro do método write(). + +string $path + +'/' + +O caminho do servidor em que o cookie será aplicado. Se $cookiePath é +setado para '/foo/', o cookie ira apenas estar liberado dentro do +diretório /foo/ e todos seus sub-diretórios como /foo/bar/ do seu +domínio. O valor padrão é a entrada do domínio. Você pode setar isso +diretamente dentro do método write(). + +boolean $secure + +false + +Indica que o cookie deveria ser apenas transmitido dentro de uma conexão +segura. Quando setado para true (verdadeiro), o cookie irá apenas estar +serado se uma conexão existir. Você pode setar isso diretamente dentro +do método write(). + +A seguir um pedaço de código de uma controladora mostra como incluir o +CookieComponent e configurar as variáveis controladoras necessárias para +escrever um cookie chamado 'baker\_id' para o domínio 'exemplo.com.br' +que precisa de uma conexão segura, está disponível no caminho +‘/bakers/preferences/’, e expira em uma hora. + +:: + + var $components = array('Cookie'); + function beforeFilter() { + $this->Cookie->name = 'baker_id'; + $this->Cookie->time = 3600; // ou '1 hora' + $this->Cookie->path = '/bakers/preferences/'; + $this->Cookie->domain = 'exemplo.com.br'; + $this->Cookie->secure = true; //i.e. apenas envia se usar uma conexão segura (HTTPS) + $this->Cookie->key = 'qSI232qs*&sXOw!'; + } + +Agora, vamos procurar como usar os diferentes métodos do componente +Cookie. + +Usando o componente +=================== + +Essa seção descreve os métodos do CookieComponent. + +**write(mixed $key, mixed $value, boolean $encrypt, mixed $expires)** + +O método write() é o coração do componente cookie, $key é o nome da +variável cookie que você quer, e o $value é a informação para ser +armazenada. + +:: + + $this->Cookie->write('name','Larry'); + +Você pode também agrupar suas variáveis fornecendo uma pequena notação +no parâmetro key. + +:: + + $this->Cookie->write('User.name', 'Larry'); + $this->Cookie->write('User.role','Lead'); + +Se você quer escrever mais que um valor no cookie, como um horário, você +pode passar um array: + +:: + + $this->Cookie->write( + array('name'=>'Larry','role'=>'Lead') + ); + +Todos os valores no cookie são criptografados por padrão. Se você quer +armazenar esses valores como texto puro, sete o terceiro parâmetro do +método writer() para false. + +:: + + $this->Cookie->write('name','Larry',false); + +O último parâmetro para write é $expires – o número de segundos antes do +seu cookir expirar. Por conveniência, esse parâmetro pode também ser +passado como uma string que a função PHP strtotime() entenda: + +:: + + //Ambos cookies expiram em uma hora + $this->Cookie->write('first_name','Larry',false, 3600); + $this->Cookie->write('last_name','Masters',false, '1 hour'); + +**read(mixed $key)** + +Esse método é usado para ler o valor da variável cookie com o nome +especificado por $key. + +:: + + // Saídas “Larry” + echo $this->Cookie->read('name'); + + //Você pode também usar a notação curta para ler + echo $this->Cookie->read('User.name'); + + //Obter as variáveis que você tem agrupada + //usando uma notação curta como um array, usando algo como + $this->Cookie->read('User'); + + // algo como a saída do array('name' => 'Larry', 'role'=>'Lead') + +**del(mixed $key)** + +Deleta uma variável cookie do nome em $key. Trabalha com notação curta. + +:: + + //Delete a variable + $this->Cookie->del('bar') + + //Deleta a variável cookie bar, mas não todos debaixo de foo + $this->Cookie->del('foo.bar') + + +**destroy()** + +Destroy o cookie atual. diff --git a/pt/The-Manual/Core-Components/Email.rst b/pt/The-Manual/Core-Components/Email.rst new file mode 100644 index 0000000000000000000000000000000000000000..c25cd2a5bc761087cfdd271b6e62ccf8169cb9df --- /dev/null +++ b/pt/The-Manual/Core-Components/Email.rst @@ -0,0 +1,244 @@ +Email +##### + +O emailComponent é a forma para você adicionar uma simples +funcionalidade de envio de email para sua aplicação CakePHP. Usando +alguns conceitos de layout e arquivos view (ctp) para enviar mensagens +formatadas como texto, html ou ambos. Ela suporta envios através de +funções mail do PHP, via servidores smtp ou um modo debug onde escreve +mensagens para uma sessão de mensagem flash. Suporta anexo de arquivos e +alguns cabeçalhos básicos de injeção checar/filtrar para você. Existem +muitos que ele não faz para você, mas já ajuda a começar. + +Variávies e Atributos da Classe +=============================== + +Existem os valores que você pode setar antes de chamar +``EmailComponent::send()`` + +to + +endereço que será enviada a mensagem (string) + +cc + +coleção de endereços com cópia que será enviada a mensagem + +bcc + +coleção de endereços ocultos que receberão a mensagem + +replyTo + +responder para o endereço (string) + +from + +endereço do remetente (string) + +subject + +título da mensagem (string) + +template + +Elemento de email usado para as mensagens (localizado em +``app/views/elements/email/html/`` e ``app/views/elements/email/text/``) + +layout + +O layout usado para o email (localizado em +``app/views/layouts/email/html/`` e ``app/views/layouts/email/text/``) + +lineLength + +Tamanho que as linhas devem ser quebradas. Padrão é 70. (inteiro) + +sendAs + +como você quer que seja enviada a mensagem ``text``, ``html`` ou +``ambas (both)`` + +attachments + +coleção de arquivos para enviar (caminhos absolutos ou relativos) + +delivery + +como enviar a mensagem (``mail``, ``smtp`` [exigiria smtpOptions como +abaixo] e ``debug``) + +smtpOptions + +array associativo de opções para mailer smtp (``port``, ``host``, +``timeout``, ``username``, ``password``, ``client``) + +Existem algumas outras coisas que podem ser setadas, mas você deveria +verificar a referência da documentação da API para mais informações. + +Enviando múltiplos emails em um loop +------------------------------------ + +Se você deseja enviar vários email usando um loop, você vai precisar +resetar os campos de email usando o método reset() do componente Email. +Você vai precisar resetar antes setando a propriedade email. + +:: + + $this->Email->reset() + +Enviando uma mensagem básica +============================ + +Para enviar uma mensagem sem usar um template, apenas passe o corpo da +mensagem como uma string (ou um array de linhas) para o método send(). +Por exemplo: + +:: + + $this->Email->from = 'Fulano '; + $this->Email->to = 'Ciclano '; + $this->Email->subject = 'Teste'; + $this->Email->send('Corpo da mensagem!'); + +Configurar os layouts +--------------------- + +Para usar envio de mensagem em texto e html você precisa criar arquivos +de layout para eles, assim como na configuração de seus layouts padrão +para visualização de suas visualizações no navegador, você precisa +configurar layouts padrão para suas mensagens de email. No diretório +``app/views/layouts/`` você precisa configurar (no mínimo) a seguinte +estrutura + +:: + + email/ + html/ + default.ctp + text/ + default.ctp + +Esses são os arquivos que possuem os modelos de apresentação das +mensagens. Alguns exemplos de conteúdo estão a seguir + +``email/text/default.ctp`` + +:: + + + +``email/html/default.ctp`` + +:: + + + + + + + + +Configuração do elemento de email para o corpo da mensagem +---------------------------------------------------------- + +No diretório ``app/views/elements/email/`` você precisa configurar +pastas para ``text`` e ``html`` a menos que você planeja apenas enviar +um ou outro. Em cada uma dessas pastas você precisa criar modelos para +os dois tipos de mensagens referente ao conteúdo que você enviar para a +view qualquer uma das duas usando $this->set() ou usando o parâmetro +$contents do método send(). Alguns simples exemplos são mostrados +abaixo. Para esse exemplo nós iremos chamar o modelo simple\_message.ctp + +``text`` + +:: + + , + Obrigado pelo seu interesse. + +``html`` + +:: + +

,
+    Obrigado pelo seu interesse.

+ +Controladora +------------ + +Na sua controladora você precisa adicionar o componente para seu array +``$components`` ou adicionar um array $components pra sua controladora +como: + +:: + + + +Nesse exemplo nós iremos configurar um método private para manipular o +envio de mensagens de email para um usuário identificado por um $id. Em +nossa controladora (vamos usar a controladora User nesse exemplo) + +:: + + + User->read(null,$id); + $this->Email->to = $User['User']['email']; + $this->Email->bcc = array('secret@example.com'); + $this->Email->subject = 'Welcome to our really cool thing'; + $this->Email->replyTo = 'support@example.com'; + $this->Email->from = 'Cool Web App '; + $this->Email->template = 'simple_message'; // note que sem o '.ctp' + //Send as 'html', 'text' or 'both' (default is 'text') + $this->Email->sendAs = 'both'; // porque nós queremos enviar emails bacanas + //Configura as variáveis da view como normal + $this->set('User', $User); + //Não passa qualquer argumento para o método send() + $this->Email->send(); + } + ?> + +Você tem uma nova mensagem, você poderia chamar isso a partir de outro +método como + +:: + + + $this->_sendNewUserMail( $this->User->id ); + +Enviando uma mensagem usando SMTP +================================= + +Enviar email usando um servidor SMTP, os passos são similares para o +envio de uma mensagem básica. Defina o método de entrega para ``smtp`` e +especificar qualquer opção para a propriedade ``smtpOptions`` do objeto +Email. Você também pode obter erros gerados durante a sessão lendo a +propriedade ``smtpError`` do componente. + +:: + + /* SMTP Options */ + $this->Email->smtpOptions = array( + 'port' => '25', + 'timeout' => '30', + 'host' => 'your.smtp.server', + 'username' => 'your_smtp_username', + 'password' => 'your_smtp_password', + 'client' => 'smtp_helo_hostname'); + + /* Define a forma de entrega */ + $this->Email->delivery = 'smtp'; + + /* Não passa qualquer argumento para o método send() */ + $this->Email->send(); + + /* Checa por erros SMTP. */ + $this->set('smtp-errors', $this->Email->smtpError); + +Se seu servidor smtp requer autenticação, tenha certeza de especificar +os parâmetros usuário e senha para ``smtpOptions`` como mostrado no +exemplo. diff --git a/pt/The-Manual/Core-Components/Request-Handling.rst b/pt/The-Manual/Core-Components/Request-Handling.rst new file mode 100644 index 0000000000000000000000000000000000000000..9684ff41e3a900f55c3104acd59629fb9b093146 --- /dev/null +++ b/pt/The-Manual/Core-Components/Request-Handling.rst @@ -0,0 +1,276 @@ +Manipulando Requisições +####################### + +O componente RequestHandler é usado para se obter informações adicionais +sobre as requisições HTTP feitas a sua aplicação CakePHP. Você pode +usá-lo para informar seus controllers sobre Ajax bem como obter dados +adicionais sobre os tipos de conteúdo que o cliente aceita e modificar +automaticamente o layout quando as extensões de arquivo estiverem +habilitadas. + +Por padrão o RequestHandler vai detectar automaticamente requisições +Ajax com base no cabeçalho HTTP-X-Requested-With que muitas das +bibliotecas Javascript usam. Quando utilizado em conjunto com o +Router::parseExtensions(), o RequestHandler vai modificar +automaticamente os arquivos de layout e de views para aqueles que +correspondam ao tipo requisitado. Além disso, se um helper com o mesmo +nome da extensão requisitada existir, ele será adicionado ao array de +helpers do controller. Por fim, se dados XML forem submetidos para seus +controllers, eles serão convertidos em objetos XML os quais são +associados a Controller:data, podendo então serem salvos como dados de +model normalmente. Para fazer uso do RequestHandler ele deve estar +incluído no seu array de $components. + +:: + + + +Obtendo Informações da Requisição +================================= + +O componente RequestHandler possui vários métodos que proveem +informações sobre o cliente e sua requisição. + +accepts ( $type = null) + +$type pode ser uma string, um array ou null. Se for uma string, o método +accepts retornará verdadeiro se um cliente aceitar o tipo de conteúdo +dado. Se um array for especificado como parâmetro, o método accepts +retorna verdadeiro se qualquer um dos tipos de conteúdo for aceito pelo +cliente. Se o parâmetro for null, o método retorna um array de todos os +content-types que o cliente aceita. Por exemplo: + +:: + + class PostsController extends AppController { + + var $components = array('RequestHandler'); + + function beforeFilter () { + if ($this->RequestHandler->accepts('html')) { + // Executa código só se o cliente aceita uma resposta em HTML (text/html) + } elseif ($this->RequestHandler->accepts('xml')) { + // Executa código com operações em XML + } + if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { + // Executa se o cliente aceita qualquer um dos tipos: XML, RSS ou Atom + } + } + } + +Outros 'tipos' (type) de deteção incluem: + +isAjax() + +Retorna verdadeiro se a requisição contiver um cabeçalho +X-Requested-Header igual a XMLHttpRequest. + +isSSL() + +Retorna verdadeiro se a requisição atual tiver sido feita sobre uma +conexão SSL. + +isXml() + +Retorna verdadeiro se a requisição atual aceitar XML como resposta. + +isRss() + +Retorna verdadeiro se a requisição atual aceitar RSS como resposta. + +isAtom() + +Retorna verdadeiro se a chamada atual aceitar Atom como resposta e falso +em caso contrário. + +isMobile() + +Retorna verdadeiro se a string do agente de usuário (user agent) +corresponder a um navegador web móvel, ou se o cliente aceitar conteúdo +WAP. As strings de agentes de usuário móveis aceitas são: + +- iPhone +- MIDP +- AvantGo +- BlackBerry +- J2ME +- Opera Mini +- DoCoMo +- NetFront +- Nokia +- PalmOS +- PalmSource +- portalmmm +- Plucker +- ReqwirelessWeb +- SonyEricsson +- Symbian +- UP.Browser +- Windows CE +- Xiino + +isWap() + +Retorna verdadeiro se o cliente aceitar conteúdo WAP. + +Todos os métodos de deteção de requisição podem ser usados de forma +semelhante à funcionalidade de filtragem pretendida para dados tipos de +conteúdo. Por exemplo, ao responder a requisições Ajax, você quase +sempre vai querer desabilitar o cache do navegador web e mudar o nível +de debug. No entanto, você vai querer permitir cache para requisições +não-Ajax. O código a seguir deve bastar para fazer isso: + +:: + + if ($this->RequestHandler->isAjax()) { + Configure::write('debug', 0); + $this->header('Pragma: no-cache'); + $this->header('Cache-control: no-cache'); + $this->header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + } + // Continua a ação do controller + +Você também pode desabilitar o cache com o método análogo +``Controller::disableCache`` + +:: + + if ($this->RequestHandler->isAjax()) { + $this->disableCache(); + } + // Contrinua a ação do controller + +Deteção do Tipo de Requisição +============================= + +O RequestHandler também dispõe de informação sobre o tipo de requisição +HTTP que é feita, permitindo a você dar uma resposta específica a cada +tipo de requisição.. + +isPost() + +Retorna verdadeiro se a requisição for uma requisição do tipo POST. + +isPut() + +Retorna verdadeiro se a requisição for uma requisição do tipo PUT. + +isGet() + +Retorna verdadeiro se a requisição for uma requisição do tipo GET. + +isDelete() + +Retorna verdadeiro se a requisição for uma requisição do tipo DELETE. + +Obtendo Informações Adicionais do Cliente +========================================= + +getClientIP() + +Obtém o endereço IP remoto do cliente. + +getReferrer() + +Retorna o nome de domínio a partir do qual a requisição foi originada. + +getAjaxVersion() + +Obtém a versão do Prototype no caso de uma chamada Ajax, caso contrário +retorna uma string vazia. A biblioteca Prototype define o cabeçalho HTTP +especial "Prototype version". + +Respondendo a Requisições +========================= + +Além da deteção de requisição, o RequestHandler também possibilita fácil +acesso a alteração da saída e dos mapeamentos de tipo de conteúdo para +sua aplicação. + +setContent($name, $type = null) + +- $name string - O nome do Content-type ie. html, css, json, xml. +- $type mixed - O(s) mime-type(s) para os quais o conteúdo são + mapeados. + +O método setContent define/adiciona os tipos de conteúdo (Content-types) +para um dado nome. Ele permite que os tipos de conteúdo sejam mapeados +para aliases amigáveis e/ou extensões. Isto possibilita ao +RequestHandler respondere automaticamente às requisições de cada tipo em +seu método de inicialização. Além do mais, estes tipos de conteúdo são +usados pelos métodos prefers() e accepts(). + +O setContent é mais adequado a ser usado dentro do beforeFilter() de +seus controllers, que é o lugar em que a automágica do mapeamento de +conteúdo acontece. + +Os mapeamentos já definidos por padrão são: + +- **javascript** text/javascript +- **js** text/javascript +- **json** application/json +- **css** text/css +- **html** text/html, \*/\* +- **text** text/plain +- **txt** text/plain +- **csv** application/vnd.ms-excel, text/plain +- **form** application/x-www-form-urlencoded +- **file** multipart/form-data +- **xhtml** application/xhtml+xml, application/xhtml, text/xhtml +- **xhtml-mobile** application/vnd.wap.xhtml+xml +- **xml** application/xml, text/xml +- **rss** application/rss+xml +- **atom** application/atom+xml +- **amf** application/x-amf +- **wap** text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp +- **wml** text/vnd.wap.wml +- **wmlscript** text/vnd.wap.wmlscript +- **wbmp** image/vnd.wap.wbmp +- **pdf** application/pdf +- **zip** application/x-zip +- **tar** application/x-tar + +prefers($type = null) + +Determina por qual tipo de conteúdo o cliente tem preferência. Se nenhum +parâmetro for dado, o tipo de conteúdo com mais afinidade é retornado. +Se $type for um array, o primeiro tipo que o cliente aceitar será +retornado. A preferência é determinada principalmente pela extensão de +arquivo tratada pelo Router se alguma for informada, e em segundo lugar, +pela lista de content-types no cabeçalho HTTP\_ACCEPT. + +renderAs($controller, $type) + +- $controller - Referência ao controller +- $type - nome amigável do content-type com o qual o conteúdo será + renderizado, p.ex., xml, rss. + +Modifica o modo de renderização de um controller para um tipo +específico. Também irá anexar o helper apropriado ao array de helpers do +controller, se estiver disponível e já não estiver incluso no array. + +respondAs($type, $options) + +- $type - Nome amigável do content-type, p.ex., xml rss ou uma + descrição completa do tipo, como em application/x-shockwave +- $options - Se $type for um nome de tipo amigável que tenha mais de um + conteúdo mapeado, $index é usado para selecionar o tipo de conteúdo. + +Define o cabeçalho de resposta baseado nos mapeamentos dos tipos de +conteúdo. Se DEBUG for maior que 2, o header não é definido. + +responseType() + +Retorna o tipo de resposta atual do cabeçalho Content-type ou null se +este ainda estiver para ser definido. + +mapType($ctype) + +Mapeia um tipo de conteúdo de volta para um alias. diff --git a/pt/The-Manual/Core-Components/Security-Component.rst b/pt/The-Manual/Core-Components/Security-Component.rst new file mode 100644 index 0000000000000000000000000000000000000000..42cb0adb1d0b5771f199cbf2663e3d6724916133 --- /dev/null +++ b/pt/The-Manual/Core-Components/Security-Component.rst @@ -0,0 +1,261 @@ +O Componente Security +##################### + +O componente Security (SecurityComponent) do CakePHP cria uma maneira +fácil de integrar uma segurança mais reforçada à sua aplicação. Uma +interface para gerência de requisições HTTP autenticadas pode ser criada +com o SecurityComponent. Ele é configurado dentro do beforeFilter() de +seus controllers. Este componente possui diversos parâmetros +configuráveis. Todas estas propriedades podem atribuídades diretamente +ou por meio de métodos modificadores com mesmo nome. + +Se uma ação for restringida utilizando-se o SecurityComponent, a +requisição é marcada numa lista negra que irá resultar num erro 404 por +padrão. Você pode configurar este comportamento definindo a propriedade +$this->Security->blackHoleCallback para uma função de callback no +controller. Tenha em mente de que a lista negra de todos os métodos do +SecurityComponent serão executadas por este método de callback. + +Ao usar o SecurityComponente, você **deve** usar o FormHelper para criar +seus formulários. O SecurityComponente procura por certos indicadores +que são criados e gerenciados pelo FormHelper (especialmente àqueles +criados entre as chamadas create() e end()). + +Configuração +============ + +$blackHoleCallback + Um callback de controller que irá manipular as requisições que forem + para a lista negra. +$requirePost + Uma lista de ações de controller que necessitam de uma requisição + POST para ocorrer. Um array de acitons de controller ou '\*' para + forçar todas as requisições a demandarem POST. +$requireSecure + Lista de ações que necessitam de uma conexão SSL para ocorrer. Um + array de ações do controller ou '\*' para forçar todas as + requisições a demandarem uma conexão SSL. +$requireAuth + Lista de ações que necessitam de uma chave de autenticação válida. A + chave de validação é definida pelo SecurityComponent. +$requireLogin + Lista das ações que necessitam de logins de uma sessão + HTTP-Autenticada (basic ou digest). Também aceita '\*' indicando que + todas as requisições deste controller demandam autenticação HTTP. +$loginOptions + Opções para requisições de autenticação HTTP. Permite que você + defina a autenticação e o callback de controller para o processo de + autenticação. +$loginUsers + Um array associativo de usernames => passwords que são usados para + logins em autenticação HTTP. Se você estiver usando autenticação + digest, suas senhas devem estar em formato MD5. +$allowedControllers + Uma lista de controllers cujas ações têm permissão para receber + requisições. Isto pode ser usado para controlar requisições entre + controllers cruzados. +$allowedActions + Ações a partir das quais as ações do controller atual têm permissão + de receber requisições. Isto pode ser usado para controlar + requisições entre controllers cruzados. +$disabledFields + Lista dos campos de formulário que devem ser ignoradas na validação + POST - o valor, presença ou ausência destes campos de formulário não + serão levados em conta na avaliação de que a submissão do formulário + é válida. Especifique os campos como você faria com o + FormHelper(\ ``Model.fieldname``). + +Métodos +======= + +requirePost() +------------- + +Define as ações que uma requisição POST exige. Pega qualquer número de +argumentos. Pode ser chamada sem argumentos para forçar todas ações +requisitar um POST. + +requireSecure() +--------------- + +Define as ações que exigem uma requisição SSL segunra. Pega qualquer +número de argumentos. Pode ser chamada sem argumentos para forçar todas +ações para requisitar um SSL seguro. + +requireAuth() +------------- + +Define as ações que exigem um token válido gerado por um componente de +segurança. Pode ser chamado sem argumentos para forçar todas ações para +requisitar uma autenticação válida. + +requireLogin() +-------------- + +Define as ações que exige uma requisição HTTP-Autenticada válida. Pega +qualquer número de argumentos. Pode ser chamada sem argumentos para +forçar todas as ações para requerir uma HTTP-autenticação válida. + +loginCredentials(string $type) +------------------------------ + +Attempt to validate login credentials for a HTTP-authenticated request. +$type is the type of HTTP-Authentication you want to check. Either +'basic', or 'digest'. If left null/empty both will be tried. Returns an +array with login name and password if successful. + +loginRequest(array $options) +---------------------------- + +Generates the text for an HTTP-Authenticate request header from an array +of $options. + +$options generally contains a 'type', 'realm' . Type indicate which +HTTP-Authenticate method to use. Realm defaults to the current HTTP +server environment. + +parseDigestAuthData(string $digest) +----------------------------------- + +Parse an HTTP digest authentication request. Returns and array of digest +data as an associative array if succesful, and null on failure. + +generateDigestResponseHash(array $data) +--------------------------------------- + +Creates a hash that to be compared with an HTTP digest-authenticated +response. $data should be an array created by +SecurityComponent::parseDigestAuthData(). + +blackHole(object $controller, string $error) +-------------------------------------------- + +Black-hole an invalid request with a 404 error or a custom callback. +With no callback, the request will be exited. If a controller callback +is set to SecurityComponent::blackHoleCallback, it will be called and +passed any error information. + +Modo de usar +============ + +A maneira mais comum de se utilizar o componente security é através do +método beforeFilter() do respectivo controller. Você especifica as +restrições de segurança que deseja e o componente Security as aplicará +em sua inicialização. + +:: + + Security->requirePost('delete'); + } + } + ?> + +Nesse exemplo, a action delete pode ser disparada com sucesso apenas se +receber um POST. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->requireSecure(); + } + } + } + ?> + +Esse exemplo força todas as actions que possuem admin routing a requerer +requests seguros através de SSL. + +:: + + params[Configure::read('Routing.admin')])){ + $this->Security->blackHoleCallback = '_forceSSL'; + $this->Security->requireSecure(); + } + } + + function _forceSSL() { + $this->redirect('https://' . env('SERVER_NAME') . $this->here); + } + } + ?> + +Esse exemplo força todas as actions que possuem admin routing a requerer +requests seguros via SSL. Quando o request estiver "black holed", o +componente chamará o método callback \_forceSSL() que redirecionará +requests inseguros para requests seguros automaticamente. + +Basic HTTP Authentication +========================= + +O SecurityComponent tem algumas características de autenticação muito +poderoso. Às vezes pode ser necessário para proteger algumas +funcionalidades dentro de sua aplicação usando\ `HTTP Basic +Authentication `_. +Uma utilização comum para HTTP Auth é proteger um REST ou SOAP API. + +Este tipo de autenticação é chamado de base para uma razão. A menos que +você está transferindo informações sobre SSL, credenciais serão +transferidas em texto puro. + +Usando o SecurityComponent para autenticação HTTP é fácil. O exemplo de +código a seguir inclui as SecurityComponent e acrescenta algumas linhas +de código dentro do método do controlador beforeFilter.p> + +:: + + class ApiController extends AppController { + var $name = 'Api'; + var $uses = array(); + var $components = array('Security'); + + function beforeFilter() { + $this->Security->loginOptions = array( + 'type'=>'basic', + 'realm'=>'MyRealm' + ); + $this->Security->loginUsers = array( + 'john'=>'johnspassword', + 'jane'=>'janespassword' + ); + $this->Security->requireLogin(); + } + + function index() { + //protected application logic goes here... + } + } + +The loginOptions property of the SecurityComponent is an associative +array specifying how logins should be handled. You only need to specify +the **type** as **basic** to get going. Specify the **realm** if you +want display a nice message to anyone trying to login or if you have +several authenticated sections (= realms) of your application you want +to keep separate. + +The loginUsers property of the SecurityComponent is an associative array +containing users and passwords that should have access to this realm. +The examples here use hard-coded user information, but you'll probably +want to use a model to make your authentication credentials more +manageable. + +Finally, requireLogin() tells SecurityComponent that this Controller +requires login. As with requirePost(), above, providing method names +will protect those methods while keeping others open. diff --git a/pt/The-Manual/Core-Components/Sessions.rst b/pt/The-Manual/Core-Components/Sessions.rst new file mode 100644 index 0000000000000000000000000000000000000000..91a1444691b3ff9830ec407a8e0d49ae03ff56d9 --- /dev/null +++ b/pt/The-Manual/Core-Components/Sessions.rst @@ -0,0 +1,194 @@ +Sessions +######## + +O componente Session do CakePHP oferece uma maneira de persistir dados +do cliente entre requisições de páginas. Este componente funciona como +um encapsulador para a variável $\_SESSION além de oferecer métodos de +conveniência para diversas funções relacionadas ao $\_SESSION. + +Sessões podem ser persistidas de diferentes maneiras. O padrão é usar as +configurações definidas pelo PHP; no entanto existem outras opções. + +cake + Salva os arquivos de sessão do diretório tmp/sessions de sua + aplicação. +database + Utiliza sessões da base de dados do CakePHP. +cache + Utiliza o mecanismo de cache configurado em Cache::config(). Muito + útil para ser usado em conjunto com o Memcache (em ambientes + servindo múltiplas aplicações) para armazenar tanto dados em cache + quanto dados de sessões. +php + A configuração padrão. Salva os arquivos de sessão conforme definido + no php.ini. + +Para modificar o método padrão de manipulação de sessões, altere a +configuração de Session.save para refletir a opção de sua preferência. +Se você escolher 'database', você deveria também descomentar as +configurações de Session.database e rodar o arquivo de script SQL +localizado em app/config. + +Para usar uma configuração personalizada, defina o valor de Session.save +para um nome de um arquivo. O CakePHP vai considerar este arquivo +informado no diretório CONFIGS. + +:: + + // app/config/core.php + Configure::write('Session.save','my_session'); + +Isto permite que você personalize a manipulação de sessões. + +:: + + // app/config/my_session.php + // + // Reverte o valor e força checagem do referrer mesmo quando + // Security.level for medium + ini_restore('session.referer_check'); + + ini_set('session.use_trans_sid', 0); + ini_set('session.name', Configure::read('Session.cookie')); + + // Cookies agora são destruídos quando o navegador é fechado, + // não persiste a informação por dias e é o padrão para nível + // de segurança em low ou medium + ini_set('session.cookie_lifetime', 0); + + // Cookie path agora é '/', mesmo se sua aplicação estiver + // em um subdiretório no domínio + $this->path = '/'; + ini_set('session.cookie_path', $this->path); + + // Cookies de sessão agora são persistidos para todos + // os subdomínios + ini_set('session.cookie_domain', env('HTTP_BASE')); + +Métodos +======= + +O componente Session é usado para interagir com as informações na +sessão. Ele inclui funções CRUD básicas bem como recursos para criação +de mensagens de feedback para os usuários. + +Deve-se notar que estruturas de array podem ser criadas na sessão +utilizando-se a notação de ponto. Assim, User.username deve referenciar +a seguinte estrutura: + +:: + + array('User' => + array('username' => 'clarkKent@dailyplanet.com') + ); + +Pontos são usados para indicar arrays dentro de arrays. Esta notação +pode ser usada para todos os métodos do componente Session onde $name +for usado. + +write +----- + +``write($name, $value)`` + +Escreve na sessão, atribuindo $value para $name. $name pode referenciar +um array na notação de ponto. Por exemplo: + +:: + + $this->Session->write('Person.eyeColor', 'Green'); + +Isto escrever o valor 'Green' na chave de sessão sob Person => eyeColor. + +setFlash +-------- + +``setFlash($message, $layout = 'default', $params = array(), $key = 'flash')`` + +Usado para definir uma variável de sessão que pode ser usada para +exibição na view. O parâmtro $layout lhe permite selecionar o layout +(presente em ``/app/views/layouts``) a ser usado para renderizar a +mensagem. Se você mantiver ``$layout`` como 'default', a mensagem será +encapsulada da seguinte maneira: + +:: + +
[message]
+ +$params lhe permite passar variáveis adicionais para a view para o +layout renderizado. $key atribui o índice de $messages no array Message. +Seu valor padrão é 'flash'. + +Podem ser passados parâmetros que afetem a div renderizada, por exemplo, +incluir uma chave "class" no array $params irá aplicar aquela classe à +``div`` de saída usando ``$session->flash()`` em seu layout ou view. + +:: + + $this->Session->setFlash('Texto de uma mensagem de exemplo', 'default', array('class' => 'example_class')) + +A saída ao usar ``$session->flash()`` com o código de exemplo acima será +algo como: + +:: + +
Texto de uma mensagem de exemplo
+ +read +---- + +``read($name)`` + +Retorna o valor armazenado em $name na sessão. Se $name for null, os +valores de toda a sessão seráo retornado. P.ex.: + +:: + + $green = $this->Session->read('Person.eyeColor'); + +Retorna o valor Green a partir da sessão. + +check +----- + +``check($name)`` + +Usado para verificar se a variável de sessão está definida. Retorna true +se a variável dada existir e false em caso contrário. + +delete +------ + +``delete($name) /*or*/ del($name)`` + +Limpa o valor da sessão data em $name. Ex. + +:: + + $this->Session->del('Pessoa.CorDoOlho'); + +Sua sessão não contém o valor 'Verde', ou CorDoOlho não está definida. +No entanto, a Pessoa existe na sessão. Para deletar a informação da +Pessoa completa da sessão use. + +:: + + $this->Session->del('Pessoa'); + +destroy +------- + +O método ``destroy`` irá excluir o cookie de sessão e todos os dados +armazenados no sistema de arquivo temporário. Este método vai destruir a +sessão PHP e então criar uma nova sessão limpa. + +:: + + $this->Session->destroy() + +error +----- + +``error()`` + +Usado para determinar o último erro na sessão. diff --git a/pt/The-Manual/Core-Console-Applications.rst b/pt/The-Manual/Core-Console-Applications.rst new file mode 100644 index 0000000000000000000000000000000000000000..4817e59a741246a1e9bd7dad483c6999324855f7 --- /dev/null +++ b/pt/The-Manual/Core-Console-Applications.rst @@ -0,0 +1,23 @@ +Aplicações de Console Principais +################################ + +O CakePHP dispõe de um conjunto de aplicações de console. Algumas destas +aplicações são usadas em conjunto com outros recursos do CakePHP (como +ACL ou i18n) enquanto outros são de uso geral para lhe ajudar a acelerar +seu desenvolvimento. + +Esta seção explica como usar as aplicações de console principais +disponíveis no CakePHP. + +Antes de prosseguirmos, você pode querer conferir a `seção sobre o +Console do CakePHP `_, abordada +anteriormente. A configuração do console não é mostrada aqui, assim, se +você nunca tiver usado o console antes, não deixe de conferir. + + +.. toctree:: + :maxdepth: 1 + + Core-Console-Applications/Code-Generation-with-Bake + Core-Console-Applications/Schema-management-and-migrations + Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates \ No newline at end of file diff --git a/pt/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst b/pt/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst new file mode 100644 index 0000000000000000000000000000000000000000..75c8b27be7f84a120b89c46ede0e460940a0c711 --- /dev/null +++ b/pt/The-Manual/Core-Console-Applications/Code-Generation-with-Bake.rst @@ -0,0 +1,60 @@ +Geração de código com o Bake +############################ + +Você já aprendeu sobre *scaffolding* no CakePHP: uma maneira simples de +pegar e executar com apenas um banco de dados e algumas poucas classes. +O console do Bake no CakePHP é outra tentativa para que você pegue e +execute no CakePHP – rapidamente. O console do Bake pode criar qualquer +um dos ingredientes básicos do CakePHP: *models*, *views* e +*controllers*. E não estamos falando apenas de classes *skeleton*: O +Bake pode criar uma aplicação completa e funcional em poucos minutos. De +fato, o Bake é um passo natural a seguir uma vez que foi feito o +*scaffold* da aplicação. + +Aqueles que são novos no Bake (especialmente usuários de Windows) podem +achar o `Bake screencast `_ útil +para esclarecer as coisas antes de continuar. + +Dependendo da configuração da sua instalação, você terá que setar os +direitos de execução no script bash do cake ou chamá-lo utilizando +./cake bake. O console do cake é executado utilizando o CLI (*command +line interface*) do PHP. Caso você tenha problemas executando o script, +certifique-se de ter o CLI do PHP instalado e que ele tenha os módulos +apropriados habilitados (Ex: MySQL). + +Quando executar o Bake pela primeira vez, você será solicitado a criar o +arquivo de configuração do banco de dados, caso você já não o tenha +criado. + +Depois de criar o arquivo de configuração do banco de dados, ao executar +o Bake serão apresentadas a você as seguintes opções: + +:: + + --------------------------------------------------------------- + App : app + Path: /path-to/project/app + --------------------------------------------------------------- + Interactive Bake Shell + --------------------------------------------------------------- + [D]atabase Configuration + [M]odel + [V]iew + [C]ontroller + [P]roject + [Q]uit + What would you like to Bake? (D/M/V/C/P/Q) + > + +Alternativamente, você pode executar qualquer um destes comandos +diretamente da linha de comando: + +:: + + $ cake bake db_config + $ cake bake model + $ cake bake view + $ cake bake controller + $ cake bake project + $ cake bake test + diff --git a/pt/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst b/pt/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst new file mode 100644 index 0000000000000000000000000000000000000000..3c60df094ab9af81755041fb397cd84df55002ad --- /dev/null +++ b/pt/The-Manual/Core-Console-Applications/Modify-default-HTML-produced-by-baked-templates.rst @@ -0,0 +1,39 @@ +Alterando o HTML produzido pelos templates "bakeados" +##################################################### + +Se voce quer modificar o layout padrão de saída HTML produzido pelo +comando "bake", siga estes passos: + +**Para views específicas:** + +#. Abra a pasta: cake/console/libs/templates/views +#. Note os 4 arquivos presentes +#. Copie os arquivos para: app/vendors/shells/templates/views +#. Faça as alterações desejadas na saída HTML a ser gerada pelo "bake". + +**Para projetos específicos:** + +#. Abra a pasta: cake/console/libs/templates/skel +#. Note que os arquivos básicos da aplicação se encontram aqui +#. Copie os arquivos para: app/vendors/shells/templates/skel +#. Faça as alterações desejadas na saída HTML a ser gerada pelo "bake". +#. Inclua o parâmetro com o caminho dos arquivos de template (skel) para + a tarefa "project" do bake + + :: + + cake bake project -skel vendors/shells/templates/skel + +Notas + +- Você precisa executar a tarefa específica (no caso, "project") + ``cake bake project`` para poder informar o parâmetro com o caminho + dos arquivos. +- O caminho dos templates é relativo ao diretório atual em que você + estiver na linha de comando. +- Uma vez é necessário informar o caminho completo para os arquivos de + template, você pode especificar qualquer diretório que contenha tais + arquivos, podendo até mesmo usar múltiplos templates. (Pelo menos + enquanto o Cake ainda não sobrescreve uma pasta skel da aplicação, da + mesma forma como faz para views) + diff --git a/pt/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst b/pt/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst new file mode 100644 index 0000000000000000000000000000000000000000..c01bacbfd4f942f7cf150c4bd67526f03c222eee --- /dev/null +++ b/pt/The-Manual/Core-Console-Applications/Schema-management-and-migrations.rst @@ -0,0 +1,94 @@ +Gerenciamento de esquema e migrações +#################################### + +O SchemaShell disponibiliza uma funcionalidade para criar objetos de +esquema, geração de sql de um esquema, bem como criação de instantâneos +de banco de dados (snapshots) e restauração de banco de dados para dados +a partir de tais instantâneos. + +Gerando e utilizando arquivos schema +==================================== + +Gerar um arquivo de schema permite a você converter facilmente uma base +de dados para um formato neutro, independente do gerenciador de banco de +dados. Você pode gerar um arquivo de schema de sua base de dados com: + +:: + + $ cake schema generate + +Isto irá gerar um arquivo schema.php em seu diretório +``app/config/sql``. + +O shell schema vai processar apenas as tabelas para as quais já haja +model definido. Para forçar o shell schema a processar todas as tabelas, +você deve adicionar a opção ``-f`` à linha de comando. + +Para reconstruir o esquema de base de dados a partir de um arquivo +schema.php previamente existente, execute: + +:: + + $ cake schema run create + +Isto irá apagar e criar as tabelas definidas a partir do conteúdo do +arquivo schema.php. + +Arquivos de schema também podem ser usados para gerar conteúdos de +arquivos sql. Para gerar um arquivo sql contendo declarações +``CREATE TABLE``, execute: + +:: + + $ cake schema dump filename.sql + +Em que filename.sql é o nome de arquivo desejado que terá o conteúdo +sql. Se você omitir este nome de arquivo, o conteúdo sql gerado será +exibido na tela do console e não será criado nenhum arquivo. + +Migrações com o CakePHP schema shell +==================================== + +Migrações (ou migrations) permitem que você versione o esquema de sua +base de dados, então, conforme você prossegue no desenvolvimento de sua +aplicação, você tem a possibilidade de distribuir as modificações em sua +base de dados de uma forma neutra e independente de um gerenciador de +banco de dados específico. Migrações podem ser obtidas tanto colocando +os arquivos de schema em um sistema de controle de versão ou ainda +através de instantâneos. Versionar um arquivo de schema gerado com o +schema shell é bem fácil. Se você já tiver um arquivo de schema gerado, +ao executar novamente + +:: + + $ cake schema generate + +Serão mostradas a você as seguintes opções: + +:: + + Generating Schema... + Schema file exists. + [O]verwrite + [S]napshot + [Q]uit + Would you like to do? (o/s/q) + +Selecionar [s] (snapshot, ou instantâneo) irá gerar um arquivo +schema.php incrementado. Então, se você já tiver um schema.php, ele irá +gerar um arquivo schema\_2.php e assim por diante. Você pode então +restaurar qualquer um desses arquivos de schema a qualquer momento +executando: + +:: + + $ cake schema run update -s 2 + +Neste caso, 2 é o número do instantâneo para o qual você quer recuperar. +O schema shell vai lhe solicitar uma confirmação antes de executar os +comandos ``ALTER`` que representem a diferença entre a configuração +atual de sua base de dados e as definições no arquivo de schema em +questão. + +Você também pode fazer uma execução "a seco" adicionando a opção +``-dry`` ao comando. diff --git a/pt/The-Manual/Core-Helpers.rst b/pt/The-Manual/Core-Helpers.rst new file mode 100644 index 0000000000000000000000000000000000000000..786a3e306e677ee723919fc457c8740cabbfdd3a --- /dev/null +++ b/pt/The-Manual/Core-Helpers.rst @@ -0,0 +1,30 @@ +Helpers Principais +################## + +**Helpers** ("ajudantes", em tradução livre) são classes parecidas com +componentes para a camada de apresentação (view) da sua aplicação. Eles +contém lógicas de apresentação que são compartilhadas entre muitos +views, elementos ou layouts. + +Esta seção descreve cada um dos Helpers que vêm com o CakePHP, tais como +Form, Html, JavaScript e RSS. + +Leia `Helpers `_ (em inglês) para aprender mais +sobre Helpers e como construir seus próprios. + + +.. toctree:: + :maxdepth: 1 + + Core-Helpers/AJAX + Core-Helpers/Cache + Core-Helpers/Form + Core-Helpers/HTML + Core-Helpers/Javascript + Core-Helpers/Number + Core-Helpers/Paginator + Core-Helpers/RSS + Core-Helpers/Session + Core-Helpers/Text + Core-Helpers/Time + Core-Helpers/XML \ No newline at end of file diff --git a/pt/The-Manual/Core-Helpers/AJAX.rst b/pt/The-Manual/Core-Helpers/AJAX.rst new file mode 100644 index 0000000000000000000000000000000000000000..ee2e70e6c900999e409fcda654b3d38ab0ec5533 --- /dev/null +++ b/pt/The-Manual/Core-Helpers/AJAX.rst @@ -0,0 +1,741 @@ +AJAX +#### + +O AjaxHelper utiliza as populares bibliotecas Javascript Prototype e +script.aculo.us para operações Ajax e efeitos no lado do cliente. Para +usar o AjaxHelper, você precisa que a versão atual destas bibliotecas +Javascript (obtidas a partir de +`www.prototypejs.org `_ e +`http://script.aculo.us `_) estejam presentes +na pasta /app/webroot/js/. Além disso, você deve incluir as bibliotecas +Javascript Prototype e script.aculo.us em todos os layouts ou views que +utilizarem funcionalidade do AjaxHelper. + +Você vai precisar incluir os helpers Ajax e Javascript em seus +controllers: + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + } + +Uma vez que você tenha o JavascriptHelper incluído em seu controller, +você pode usar o método link() do helper javascript para incluir as +bibliotecas Prototype e Scriptaculous: + +:: + + echo $javascript->link('prototype'); + echo $javascript->link('scriptaculous'); + +Agora você já pode usar o AjaxHelper em sua view: + +:: + + $ajax->whatever(); + +Se o componente `RequestHandler `_ for +incluído no controller, então o CakePHP poderá aplicar automaticamente o +layout Ajax quando uma ação for requisitada via AJAX + +:: + + class WidgetsController extends AppController { + var $name = 'Widgets'; + var $helpers = array('Html','Ajax','Javascript'); + var $components = array( 'RequestHandler' ); + } + +Opções do AjaxHelper +==================== + +A maioria dos métodos do AjaxHelper precisam que você informe um array +$options. Você pode usar este array para configurar como o AjaxHelper se +comporta. Antes de abordarmos os métodos específicos do helper, vamos +dar uma olhada nas diferentes opções disponíveis através deste array +especial. Você vai querer consultar esta seção quando começar a usar os +métodos do AjaxHelper. + +Opções Gerais +------------- + +chaves do array ``$option`` + +Descrição + +``$options['evalScripts']`` + +Determina se as tags do script no conteúdo retornado são avaliadas. +Definido para *true* por padrão. + +``$options['frequency']`` + +O intervalo de tempo em segundos entre verificações baseadas em algum +intervalo. + +``$options['indicator']`` + +O id do elemento DOM a ser mostrado enquanto uma requisição estiver +sendo carregada e a ser escondido quando a requisição estiver concluída. + +``$options['position']`` + +Para inserir, ao invés de substituir, utilize esta opção para +especificar a posição de inserção como *top*, *bottom*, *after*, ou +*before*. + +``$options['update']`` + +O id do elemento DOM a ser atualizado com o conteúdo retornado pela +requisição. + +``$options['url']`` + +A URL do controller/action que você quer chamar. + +``$options['type']`` + +Indica se uma requisição deve ser 'syncrhonous' (síncrona) ou +'asynchronous' (assíncrona). 'asynchronous' é o valor default. + +``$options['with']`` + +Uma string, no formato de URL, a qual será adicionada à URL para métodos +get, ou no corpo da requisição post para qualquer outro método. Exemplo: +``x=1&foo=bar&y=2``. Os parâmetros estarão disponíveis através de +``$this->params['form']`` ou de ``$this->data`` dependendo do formato. +Para mais informações, leia sobre o método `serialize do +Prototype `_. + +Opções de Callback +------------------ + +As opções de callback permitem que você chame funções Javascript a +partir de pontos específicos de seu processo de requisição. Se você +estiver procurando por uma maneira de inserir um pouco de lógica antes, +depois ou durante suas operações com o AjaxHelper, você pode utilizar +estes callbacks para isso. + +opções do array $options + +Descrição + +$options['condition'] + +Trecho de código Javascript que precisa resultar em verdadeiro (*true*) +antes da requisição ser iniciada. + +$options['before'] + +Executado antes da requisição ser realizada. Um uso comum para este +callback é permitir a visibilidade de um indicador de progresso. + +$options['confirm'] + +Texto a ser exibido num diálogo de confirmação Javascript antes de +proceder com a requisição. + +$options['loading'] + +Código de callback a ser executado enquanto os dados estiverem sendo +obtidos do servidor. + +$options['after'] + +Javascript chamado imediatamente depois da requisição; é disparado antes +que o callback $options['loading'] execute. + +$options['loaded'] + +Código de callback a ser executado quando o documento remoto tiver sido +recebido pelo cliente. + +$options['interactive'] + +Chamado quando o usuário puder interagir com o documento remoto, mesmo +que ainda não tenha terminado de carregar o documento. + +$options['complete'] + +Callback de Javascript a ser executado quando o XMLHttpRequest estiver +concluído. + +Métodos +======= + +link +---- + +``link(string $title, string $href, array $options, string $confirm, boolean $escapeTitle)`` + +Retorna um link para uma action remota definida por ``$options['url']`` +ou ``$href`` e que será chamada em segundo plano usando-se o +XMLHttpRequest quando o link for clicado. O resultado da requisição pode +ser posto dentro de um objeto DOM cujo id pode ser dado por +``$options['update']``. + +Se a chave ``$options['url']`` estiver em branco, href será considerada +em seu lugar + +Exemplo: + +:: + +
+
+ link( + 'Visualizar Post', + array( 'controller' => 'posts', 'action' => 'view', 1 ), + array( 'update' => 'post' ) + ); + ?> + +Por padrão, essas requisições remotas são processadas de maneira +assíncrona, durante a qual vários callbacks podem ser disparados + +Exemplo: + +:: + +
+
+ link( + 'Visualizar Post', + array( 'controller' => 'posts', 'action' => 'post', 1 ), + array( 'update' => 'post', 'complete' => 'alert( "Olá Mundo!" )' ) + ); + ?> + +Para processar as requisições de maneira síncrona, especifique a chave +``$options['type'] = 'synchronous'``. + +Para definir automaticamente o layout ajax, inclua o componente +*RequestHandler* em seu controller + +Por padrão, o conteúdo do elemento alvo é substituído. Para modificar +isto, defina a chave ``$options['position']`` + +Exemplo: + +:: + +
+
+ link( + 'Visualizar Post', + array( 'controller' => 'posts', 'action' => 'view', 1), + array( 'update' => 'post', 'position' => 'top' ) + ); + ?> + +A chave ``$confirm`` pode ser usada para disparar uma mensagem com +Javascript confirm() antes de a requisição ser executada, permitindo ao +usuário evitar a execução + +Exemplo: + +:: + +
+
+ link( + 'Excluir Post', + array( 'controller' => 'posts', 'action' => 'delete', 1 ), + array( 'update' => 'post' ), + 'Você quer mesmo excluir este post?' + ); + ?> + +remoteFunction +-------------- + +``remoteFunction(array $options);`` + +Este método cria o código Javascript necessário para fazer uma chamada +remota. É usada principalmente como um auxiliar para link(). Assim, +acaba não sendo muito utilizado, a menos que você precise gerar scripts +personalizados. + +O array ``$options`` para este método são os mesmos disponíveis para o +método ``link`` + +Exemplo: + +:: + +
+
+ + +Também pode-se associar este método a atrivutos de evento HTML: + +:: + + remoteFunction( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post' ) + ); + ?> +
+ Passe o Mouse Aqui +
+ +Se a chave ``$options['update']`` não for definida, o navegador irá +ignorar a resposta do servidor. + +remoteTimer +----------- + +``remoteTimer(array $options)`` + +Chama periodicamente a ação definida em ``$options['url']``, a cada +``$options['frequency']`` segundos. Este método normalmente é usado para +atualizar um div específico (definido por ``$options['update']``) com o +resultado da chamada remota. Callbacks podem ser usados. + +O método ``remoteTimer`` é o mesmo que o ``remoteMethod``, exceto pelo +parâmetro extra ``$options['frequency']`` + +Exemplo: + +:: + +
+
+ remoteTimer( + array( + 'url' => array( 'controller' => 'posts', 'action' => 'view', 1 ), + 'update' => 'post', 'complete' => 'alert( "requisição concluída" )', + 'position' => 'bottom', 'frequency' => 5 + ) + ); + ?> + +Por padrão ``$options['frequency']`` tem o valor de 10 segundos. + +form +---- + +``form(string $action, string $type, array $options)`` + +Retorna uma tag form que submete para a $action usando XMLHttpRequest ao +invés de uma requisição HTTP normal via $type ('post' ou 'get'). Fora +isso, as submissões do formulário funcionam exatamente da mesma maneira: +os dados submetidos ficam disponível como $this->data dentro de seus +controllers. Se $options['update'] for especificada, o elemento referido +será atualizado com o documento resultante. Callbacks podem ser usados. + +O array de opções deve incluir o nome do model, p.ex. + +:: + + $ajax->form('edit','post',array('model'=>'User','update'=>'UserInfoDiv')); + +Alternativamente, se você precisar fazer um post cruzado para outro +controller a partir de seu form: + +:: + + $ajax->form(array('type' => 'post', + 'options' => array( + 'model'=>'User', + 'update'=>'UserInfoDiv', + 'url' => array( + 'controller' => 'comments', + 'action' => 'edit' + ) + ) + )); + +submit +------ + +``submit(string $title, array $options)`` + +Retorna um botão submit que submete o formulário especificado pelo id +DOM por $options['with'] via XMLHttpRequest. + +observeField +------------ + +``observeField(string $fieldId, array $options)`` + +Observa o campo cujo id DOM for especificado por $fieldId (a cada +$options['frequency'] segundos), criando um XMLHttpRequest quando seu +conteúdo for modificado. + +Quando nenhum intervalo de frequência ou um intervalo de frequência +pequeno (entre 0 e 1) é especificado, um ``Form.Element.EventObserver`` +será usado ao invés de um ``Form.Element.Observer``. O +``Form.Element.EventObserver`` não é temporizado e vai executar assim +que o valor do elemento observado mudar. + +:: + + create( 'Post' ); ?> + 'Tom', 2 => 'Dick', 3 => 'Harry' ); ?> + input( 'title', array( 'options' => $titles ) ) ?> + + + observeField( 'PostTitle', + array( + 'url' => array( 'action' => 'edit' ), + 'frequency' => 0.2, + ) + ); + ?> + +O método ``observeField`` utiliza as mesmas opções que o método ``link`` + +O campo o qual será enviado pode ser definido com ``$options['with']``. +O valor padrão neste caso é ``Form.Element.serialize('$fieldId')``. Os +dados submetidos ficam disponível em ``$this->data`` dentro de seus +controllers. Callbacks podem ser usados com este método. + +Para enviar o formulário inteiro quando o campo for modificado, utilize +``$options['with'] = Form.serialize( $('Form ID') )`` + +observeForm +----------- + +``observeForm(string $fieldId, array $options)`` + +Semelhante ao observeField(), mas opera em cima de um formulário inteiro +cujo id DOM seja identificado por $form\_id. O array de $options neste +caso é o mesmo que para observeField(), exceto que o valor padrão para a +chave $options['with'] avalia para o conteúdo serializado (string de +requisição) do formulário. + +autoComplete +------------ + +``autoComplete(string $fieldId, string $url, array $options)`` + +Renderiza um campo text cujo id é dado por $fieldId com recurso de +autocompletar. A ação remota dada por $url deve retornar uma lista de +termos adequados em questão. Quase sempre, uma lista não ordenada é +usada para isto. Em primeiro lugar, você precisa definir uma action de +um controller que obtenha e organize os dados que você vai precisar para +sua lista, baseado na entrada do usuário: + +:: + + function autoComplete() { + // Strings parciais virão com o campo autocomplete como + // $this->data['Post']['subject'] + $this->set('posts', $this->Post->find('all', array( + 'conditions' => array( + 'Post.subject LIKE' => $this->data['Post']['subject'].'%' + ), + 'fields' => array('subject') + ))); + $this->layout = 'ajax'; + } + +A seguir, crie um ``app/views/posts/auto_complete.ctp`` que utilize +estes dados e crie uma lista não-ordenada em (X)HTML: + +:: + +
    + +
  • + +
+ +Finalmente, utilize o método autoComplete() em uma view para criar seu +campo de formulário autocompletável: + +:: + + create('User', array('url' => '/users/index')); ?> + autoComplete('Post.subject', '/posts/autoComplete')?> + end('Visualizar Post')?> + +Uma vez que você tenha uma chamada autoComplete() funcionando +corretamente, utilize CSS para estilizar a caixa de sugestão do +autocompletar. Você vai acabar usando algo como: + +:: + + div.auto_complete { + position :absolute; + width :250px; + background-color :white; + border :1px solid #888; + margin :0px; + padding :0px; + } + li.selected { background-color: #ffb; } + +isAjax +------ + +``isAjax()`` + +Permite a você conferir se a requisição atual é uma requisição de Ajax +do Prototype dentro de uma view. Retorna um booleano. Por ser usado para +lógica de apresentação para ocultar/exibir blocos de conteúdos. + +drag & drop +----------- + +``drag(string $id, array $options)`` + +Torna arrastável (do inglês, *drag*) o elemento cujo id DOM é +especificado por $id. Para mais informações sobre os parâmetros aceitos +por $options, veja +`http://github.com/madrobby/scriptaculous/wikis/draggable `_. + +Opções comuns podem incluir: + ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| chaves do array $options | Descrição | ++============================+=========================================================================================================================================================================================================================================================================================================================================+ +| $options['handle'] | Define se o elemento deve ser arrastável apenas por um manipulador embutido. Seu valor deve ser uma referência a um elemento, um id ou uma string referenciando uma classe CSS. O primeiro elemento filho/neto/etc. que for encontrado dentro do elemento e que tenha o valor da classe CSS especificada será usado como manipulador. | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['revert'] | Se definida para true, o elemento retorna à sua posição original quando o arraste terminar. Esta opção revert também pode ser uma referência a uma função arbitrária, que será chamada quando o arraste terminar. | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['constraint'] | Restringe o arraste apenas ao eixo 'horizontal' ou 'vertical'. Deixe branco para não impor qualquer restrição. | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``drop(string $id, array $options)`` + +Faz com que o elemento, cujo id DOM é especificado por $id, aceite +elementos soltos (do inglês, *drop*) depois de se arrastar. Parâmetros +adicionais podem ser especificados em $options. Para mais informações, +veja +`http://github.com/madrobby/scriptaculous/wikis/droppables `_. + +Opções comuns incluem: + ++----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| chaves do array $options | Descrição | ++============================+================================================================================================================================================================================================================================================+ +| $options['accept'] | Defina para uma string ou um array de strings em Javascript descrevendo as classes CSS que os elementos soltáveis irão aceitar. O elemento em questão irá aceitar apenas os elementos em questão que contenham as classes CSS especificadas. | ++----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['containment'] | O elemento zona de soltura irá aceitar apenas elementos arrastáveis se estiverem contidos nos elementos dados (ids de elementos). Pode ser uma string ou um array de strings em Javascript de ids de referências. | ++----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['overlap'] | Se definido com os valores 'horizontal' ou 'vertical', o elemento zona de soltura só irá reagir aos elementos arrastáveis se estes já estiverem cobrindo mais de 50% por cima do eixo referido. | ++----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| $options['onDrop'] | Um callback de Javascript que é chamado quando o elemento arrastável for solto dentro do elemento zona de soltura. | ++----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +``dropRemote(string $id, array $options)`` + +Faz um alvo de soltura que cria um XMLHttpRequest quando um elemento +arrastável for solto dentro dele. O array $options para este método é o +mesmo que pode ser usado pelos métodos drop() e link(). + +slider +------ + +``slider(string $id, string $track_id, array $options)`` + +Cria um controle deslizante direcional. Para mais informações, veja +`http://wiki.github.com/madrobby/scriptaculous/slider `_. + +Opções comuns incluem: + +chaves do array $options + +Descrição + +$options['axis'] + +Define o eixo no qual o controle deslizante se apresenta, se +'horizontal' ou 'vertical'. O padrão é horizontal. + +$options['handleImage'] + +O id da imagem que representa o manipulador. É usado para trocar entre o +src da imagem desabilitada pelo da imagem normal quando o controle +deslizante for habilitado. É usado em conjunto com handleDisabled. + +$options['increment'] + +Define a relação de pixels entre os valores. Definir o valor 1 fará com +que cada pixel que o controle for movido, ajustará o valor do elemento +em uma unidade. + +$options['handleDisabled'] + +O id da imagem que vai representar o manipulador quando estiver +desabilitado. É usado para trocar entre o src da imagem desabilitada +pelo da imagem normal quando o controle deslizante for habilitado. É +usado em conjunto com handleDisabled. É usado em conjunto com +handleImage. + +$options['change'] + $options['onChange'] + +Callbacks de Javascript disparados quando o elemento deslizante tiver +terminado de ser movido ou tiver seu valor modificado. A função de +callback recebe o valor atual do slides com um parâmetro. + +$options['slide'] + $options['onSlide'] + +Callbacks de Javascript que são chamados toda vez que o elemento +deslizando for movido por arraste. A função de callback recebe o valor +atual do slides com um parâmetro. + +editor +------ + +``editor(string $id, string $url, array $options)`` + +Cria um editor de texto no elemento com id DOM especificado. O parâmetro +``$url`` deve ser uma action que seja responsável por salvar os dados do +elemento. Para mais informações e demonstrações, veja +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor `_. + +Opções comuns incluem: + +chaves do array $options + +Descrição + +``$options['collection']`` + +Ativa o modo 'collection' de edição. $options['collection'] leva um +array que é convertido em opções para o select. Para aprender mais sobre +collection veja +`http://github.com/madrobby/scriptaculous/wikis/ajax-inplacecollectioneditor `_. + +``$options['callback']`` + +Uma função a ser executada antes da requisição ser enviada ao servidor. +Isto pode ser usado para formatar a informação enviada ao servidor. A +assinatura da função de callback é ``function(form, value)``. + +``$options['okText']`` + +O texto do botão submit no modo edit. + +``$options['cancelText']`` + +O texto do link que cancela a edição. + +``$options['savingText']`` + +O texto exibido enquanto o conteúdo do texto editado for enviado ao +servidor. + +``$options['formId']`` + +``$options['externalControl']`` + +``$options['rows']`` + +A altura do campo, em linhas de texto. + +``$options['cols']`` + +A quantidade de colunas que o campo textarea deve ocupar. + +``$options['size']`` + +Sinônimo de ‘cols’, quando utilizando uma única linha. + +``$options['highlightcolor']`` + +A cor de destaque de texto. + +``$options['highlightendcolor']`` + +A cor que é interpolada para a cor de destaque (efeito fade). + +``$options['savingClassName']`` + +``$options['formClassName']`` + +``$options['loadingText']`` + +``$options['loadTextURL']`` + +Exemplo + +:: + +
Texto a ser editado
+ editor( + "in_place_editor_id", + array( + 'controller' => 'Posts', + 'action' => 'update_title', + $id + ), + array() + ); + ?> + +sortable +-------- + +``sortable(string $id, array $options)`` + +Torna ordenáveis uma lista ou grupo de objetos flutuantes que estejam +contidos no elemento dado por $id. O array $options suporta diversos +parâmetros. Para saber mais sobre sortable, veja +`http://wiki.github.com/madrobby/scriptaculous/sortable `_. + +Opções comuns incluem: + +chaves do array $options + +Descrição + +$options['tag'] + +Indica que tipo de elementos filhos do container serão tornados +ordenáveis. O padrão é 'li'. + +$options['only'] + +Permite filtragem a posteriori dos elementos filhos. Aceita uma classe +CSS. + +$options['overlap'] + +Assume os valores 'vertical' ou 'horizontal'. O padrão é vertical. + +$options['constraint'] + +Restringe o movimento dos elementos arrastáveis. Aceita os valores +'horizontal' ou 'vertical'. O padrão é vertical. + +$options['handle'] + +Faz com que os elementos arrastáveis (Draggables) utilizem +manipuladores. Consulte sobre a opção handle em Draggables. + +$options['onUpdate'] + +Chamado quando o arraste terminar e a ordem do elemento ordenável tiver +sido modificada de alguma maneira. Ao arrastar algum elemento ordenável +para outro, o callback é chamado uma vez para cada um dos elementos. + +$options['hoverclass'] + +Inclui ao elemento zona de soltura uma classe hover. + +$options['ghosting'] + +Se definido para true, os elementos ordenáveis arrastados sejam clonados +e apareçam como fantasmas, ao invés de manipular-se diretamente o +elemento original. diff --git a/pt/The-Manual/Core-Helpers/Cache.rst b/pt/The-Manual/Core-Helpers/Cache.rst new file mode 100644 index 0000000000000000000000000000000000000000..d6f497dc5b3d606e5063a273c78d71ff1678e786 --- /dev/null +++ b/pt/The-Manual/Core-Helpers/Cache.rst @@ -0,0 +1,174 @@ +Cache +##### + +O helper Cache ajuda a manter caches de todo o conteúdo de layouts e +views, economizando o tempo da obtenção de dados. O cache de views no +CakePHP armazena temporariamente o código renderizado dos layouts e +views usando o mecanismo de armazenamento definido. Deve-se atentar que +o helper Cache funciona de forma diferente de de outros helpers. Ele não +possui métodos a serem chamados diretamente. Ao invés disso, uma view é +marcada com tags de cache indicando quais blocos de conteúdo não devem +ser cacheados. + +Quando uma URL é requisitada, o Cake verifica se a string da requisição +já está em cache. Se já estiver, o restante do processo de expedição da +URL ("dispatch") é ignorado. Quaisquer blocos nocache são processados +normalmente e a view é então servida. Isto cria uma grande economia de +tempo de processamento para cada requisição à URL cacheada uma vez que +um mínimo de código é de fato executado. Se o Cake não encontrar uma +view no cache ou se o cache para aquela view já estiver expirado, ele +continua o prossesso da requisição normalmente. + +Cache em Geral +============== + +Cache é uma espécie de armazenamento temporário que ajuda a reduzir a +carga no servidor. Por exemplo, você poderia armazenar resultados de uma +consulta demorada de banco de dados de forma que não precisasse ser +executada a cada vez que a página fosse carregada. + +Com isto em mente, recurso de cache não é um armazenamento permanente e +nunca deveria ser usado para armazenar nada permanentemente. Além disso, +deveriam ser mantidas em cache apenas aquelas coisas que possam ser +geradas novamente sempre que preciso. + +Mecanismos de Cache no Cake +=========================== + +A versão 1.2 do CakePHP dispõe de diversos mecanismos de cache. Tais +mecanismos interagem transparentemente com o helper cache, permitindo a +você armazenar caches de views em vários tipos de mídia sem se preocupar +com as peculiaridades de cada uma. A escolha do mecanismo de cache é +controlada através do arquivo de configuração app/config/core.php config +file. A maioria das opções para cada mecanismo de cache são listadas no +arquivo core.php e informações mais detalhadas sobre cada mecanismo +podem ser encontradas na seção sobre Cache. + +File + +O mecanismo File (arquivo) é o padrão usado pelo Cake. Ele utiliza +arquivos comuns no sistema de arquivos e possui diversos parâmetros +opcionais, mas funciona bem com os valores padrão. + +APC + +O mecanismo APC implementa o `Alternative PHP +Cache `_ em opcode. Como o XCache, este mecanismo +faz cache do código opcode de PHP compilado. + +XCache + +O mecanismo XCache é funcionalmente semelhante ao APC e é outro que +implementa cache em opcode, via `XCache `_. +Este mecanismo precisa de um usuário e senha para funcionar +adequadamente + +Memcache + +O mecanismo Memcache funciona com um servidor que permite a você criar +um cache de objetos na memória do sistema. Mas informações sobre cache +em memória podem ser encontradas em +`php.net `_ and +`memcached `_ + +Configuração do Cache Helper +============================ + +O cache de views e o Cache helper possuem importantes elementos de +configuração. Configurações estas que estão detalhadas abaixo. + +Para usar o Cache helper em qualquer view ou controller, você primeiro +precisa descomentar a linha Configure::Cache.check e definí-la para o +valor true no arquivo ``core.php`` de sua pasta app/config. Se este +valor não for definido para true, então o cache não será verificado ou +criado. + +Manipulando Cache no Controller +=============================== + +Qualquer controller que utilize a funcionalidade de cache precisa +incluir o CacheHelper em seu array $helpers. + +:: + + var $helpers = array('Cache'); + +Você também precisa indicar que actions precisam de cache, e como será +feito o cache de cada action. Isto é feito por meio da variável +$cacheAction em seus controllers. $cacheAction deve ser atribuída com um +array que contenha as actions as quais você queira manter em cache e a +duração, em segundos, indicando por quanto tempo você quer que as views +permaneçam em cache. Este tempo pode ser informado também no mesmo +formato que para o strtotime() (p.ex., "1 hour" ou "3 minutes"). + +Considere um exemplo de um ArticlesController, que recebe muito tráfego +e que precisa de cache. + +O código abaixo faz cache dos artigos frequentemente visitados por +diferentes períodos de tempo. + +:: + + var $cacheAction = array( + 'view/23' => 21600, + 'view/48' => 36000, + 'view/52' => 48000 + ); + +Aqui, o cache de uma action completa, neste caso, uma listagem grande de +artigos. + +:: + + var $cacheAction = array( + 'archives/' => '60000' + ); + +E agora, um cache para cada action no controller usando o formato +strtotime(), mais amigável, para indicar o período de duração do cache +no controller. + +:: + + var $cacheAction = "1 hour"; + +Marcando Conteúdo Não-Cacheado nas Views +======================================== + +Haverá momentos em que você não vai querer que *toda* a view seja +mantida em cache. Por exemplo, certas partes de uma página podem ficar +diferentes dependendo se o usuários já estiver autenticado ("logado") ou +se estiver navegando no site como usuário anônimo. + +Para indicar blocos de conteúdo que *não* sejam mantidos em cache, +delimite-os em `` `` dessa forma: + +:: + + + check('User.name')) : ?> + Bem-vindo, read('User.name')?>. + + link('Login', 'users/login')?> + + + +Deve-se atentar que uma vez que a action estiver em cache, o método do +controller para a ação não é chamado - do contrário, não haveria tanto +sentido em manter a página em cache. Dessa maneira, não é possível +marcar `` `` em trechos que contenham +variáveis que são definidas no controller, já que elas terão o valor +*null*. + +Limpando o Cache +================ + +É importante lembrar de que o Cake irá limpar uma view em cache se um +model usado na view em questão for modificado. Por exemplo, se uma view +em cache utilizar dados a partir do model Post, e se tiver havido uma +operação de INSERT, UPDATE ou DELETE em um Post, o cache para aquela +view é limpo e o novo conteúdo é gerado para a próxima requisição. + +Se você precisar limpar o cache manualmente, você pode fazer isso +chamando o método Cache::clear(). Isto irá limpar **todos** os dados em +cache, incluindo dados que não sejam views. diff --git a/pt/The-Manual/Core-Helpers/Form.rst b/pt/The-Manual/Core-Helpers/Form.rst new file mode 100644 index 0000000000000000000000000000000000000000..20b4f28e2dd93bd86022140bdd1f6926581b7ea6 --- /dev/null +++ b/pt/The-Manual/Core-Helpers/Form.rst @@ -0,0 +1,1374 @@ +Form +#### + +O FormHelper é uma das novidades nesta versão do CakePHP. A maior parte +do trabalho pesado relativo à criação de formulários agora pode ser +feita por esta nova classe, no lugar dos (agora obsoletos) métodos do +HtmlHelper. O FormHelper foca na criação rápida de formulários, +possibilitando valdação de dados, atribuição de valores de campos e +layout. O FormHelper também é flexível - ele vai fazer quase todo o +trabalho automagicamente, ou você pode usar os métodos específicos para +ter apenas o que você precise. + +Criando Forms +============= + +O primeiro método que você vai precisar para começar a usar o FormHelper +é o ``create()``. Este método especial retorna uma tag de abertura de +formulário HTML. + +``create(string $model = null, array $options = array())`` + +Todos os parâmetros são opcionais. Se o ``create()`` for chamado sem +nenhum parâmetro, ele assume que você está criando um formulário que +submete para o controller atual por meio de uma action chamada ``add()`` +ou ``edit()``. O método padrão para submissão de formulários é o POST. A +tag form também é retornada com um DOM ID. O ID é gerado a partir do +nome do model e do nome do controller, no formato CamelCase. Se +chamarmos ``create()`` dentro de uma view do UsersController, veremos +uma saída semelhante à esta no código renderizado da view: + +:: + +
+ +Você também pode passar ``false`` para o parâmetro ``$model``. Isso faz +com que os dados de seu formulário sejam submetidos diretamente dentro +do array ``$this->data`` (ao invés de dentro de um sub-array +``$this->data['Model']``). Isto pode ser útil para formulários pequenos +e que não tenham uma correspondência direta com nenhuma entidade em sua +base de dados. + +O método ``create()`` nos permite ainda mais personalizações através de +seus parâmetros. Primeiro, você pode especificar um nome para o model. +Ao especificar um model para o form, você está criando um *contexto* de +formulário. Assume-se que todos os campos dentro de um contexto +pertençam ao model especificado (a menos que outro model seja +explicitamente definido no nome do campo), além de presumir-se que +demais models associados façam referência ao model em questão. Se você +não especificar um model, então o Cake vai assumir que você está usando +o model padrão para o controller atual. + +:: + + create('Recipe'); ?> + + // saída: + + +Isto irá submeter os dados do formulários via POST para a action +``add()`` do RecipesController. Mas você também pode usar a mesma lógica +para criar um formulário de edição. O FormHelper utiliza a propriedade +``$this->data`` para detectar automaticamente se deve criar um form de +inserção (add) ou de edição (edit). Se o ``$this->data`` contiver um +elemento de array com o nome do model, e se este array for composto por +um outro array contendo um índice com o mesmo nome da chave primária do +model, então o FormHelper irá criar um formulário de edição para o +registro atual. Por exemplo, se acessarmos +http://example.com/recipes/edit/5, vamos obter o seguinte: + +:: + + // controllers/recipes_controller.php: + data)) { + $this->data = $this->Recipe->findById($id); + } else { + // Lógica para salvar o registro aqui + } + } + ?> + + // views/recipes/edit.ctp: + + // Como $this->data['Recipe']['id'] = 5, um form de edição é que será gerado + create('Recipe'); ?> + + // saída: + + + +Como este é um formulário de edição, um campo escondido é gerado para +sobrescrever o método HTTP padrão. + +O array ``$options`` é o parâmetro que contém a maioria das +configurações do formulário. Este array pode conter diversos pares de +chave-valor que afetam a maneira como a tag form é gerada. + +$options[‘type’] +---------------- + +Esta chave é usada para especificar o tipo do formulário a ser criado. +Valores válidos incluem ‘post’, ‘get’, ‘file’, ‘put’ e ‘delete’. + +Ao informar ‘post’ ou ‘get’, o método de submissão do formulário também +é modificado de acordo. + +:: + + create('User', array('type' => 'get')); ?> + + // saída: + + +Especificar o valor ‘file’ modifica o método de submissão do formulário +para ‘post’ e inclui o parâmetro enctype com o valor +“multipart/form-data” na tag form. Isto deve ser usado se você tiver +algum campo input do tipo file dentro de seu formulário. Sem o atributo +enctype na tag form, o browser não é capaz de realizar uploads de +arquivos adequadamente. + +:: + + create('User', array('type' => 'file')); ?> + + // saída: + + +Se você utilizar os valores ‘put’ ou ‘delete’, seu formulário será +funcionalmente equivalente a um form 'post', mas ao ser submetido, o +método da requisição HTTP será respectivamente sobrescrito por 'PUT' ou +'DELETE'. Isto permite o CakePHP emular o suporte a REST para o browser. + +$options[‘action’] +------------------ + +Esta chave permite que você defina uma action específica em seu +controller atual para a qual o formulário irá submeter. Por exemplo, se +você quiser que o formulário apontasse para a action login() do +controller atual, você deve informar isso um array $options desta +maneira: + +:: + + create('User', array('action' => 'login')); ?> + + // saída: + +
+ +$options[‘url’] +--------------- + +Se a action desejada para o form não estiver no controller atual, você +pode especificar uma URL para a action do formulário usando a chave +‘url’ do array $options. A URL informada deve ser relativa à sua +aplicação CakePHP, ou pode ainda apontar para um domínio externo. + +:: + + create(null, array('url' => '/recipes/add')); ?> + // ou + create(null, array('url' => array('controller' => 'recipes', 'action' => 'add'))); ?> + + + // saída: +
+ + create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); ?> + + // saída: + + +$options[‘default’] +------------------- + +Se ‘default’ for definido para o valor false, a action do formulário é +modificada de forma que ao se pressionar o botão de submit (ou se teclar +ENTER) o formulário não seja submetido. Se se pretende que o formulário +seja submetido via AJAX, definir ‘default’ para false evita o +comportamento padrão do formulário, e então você tem a possibilidade de +capturar os dados e submeter o form via AJAX. + +Fechando o Form +=============== + +O FormHelper também inclui um método end() que completa a marcação do +formulário. Normalmente, o end() gera apenas a tag form de fechamento, +mas o end() também é um bom lugar para incluir campos escondidos em seu +formulário que possam ser necessários na lógica de sua aplicação. + +:: + + create(); ?> + + + + end(); ?> + +Se uma string form informada como primeiro parâmetro para o método +end(), o FormHelper vai gerar um botão de submit com o nome +correspondente à string informada. + +:: + + end('Finalizar'); ?> + + saída: + +
+ +
+
+ +Automagic Form Elements +======================= + +First, let’s look at some of the more automatic form creation methods in +the FormHelper. The main method we’ll look at is input(). This method +will automatically inspect the model field it has been supplied in order +to create an appropriate input for that field. + +input(string $fieldName, array $options = array()) + ++--------------------------------------------------+--------------------------------------------------------+ +| Column Type | Resulting Form Field | ++==================================================+========================================================+ +| string (char, varchar, etc.) | text | ++--------------------------------------------------+--------------------------------------------------------+ +| boolean, tinyint(1) | checkbox | ++--------------------------------------------------+--------------------------------------------------------+ +| text | textarea | ++--------------------------------------------------+--------------------------------------------------------+ +| text, with name of password, passwd, or psword | password | ++--------------------------------------------------+--------------------------------------------------------+ +| date | day, month, and year selects | ++--------------------------------------------------+--------------------------------------------------------+ +| datetime, timestamp | day, month, year, hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ +| time | hour, minute, and meridian selects | ++--------------------------------------------------+--------------------------------------------------------+ + +For example, let’s assume that my User model includes fields for a +username (varchar), password (varchar), approved (datetime) and quote +(text). I can use the input() method of the FormHelper to create +appropriate inputs for all of these form fields. + +:: + + create(); ?> + + input('username'); //text + echo $form->input('password'); //password + echo $form->input('approved'); //day, month, year, hour, minute, meridian + echo $form->input('quote'); //textarea + ?> + + end('Add'); ?> + +A more extensive example showing some options for a date field: + +:: + + echo $form->input('birth_dt', array( 'label' => 'Date of birth' + , 'dateFormat' => 'DMY' + , 'minYear' => date('Y') - 70 + , 'maxYear' => date('Y') - 18 )); + +Besides the specific input options found below you can specify any html +attribute (for instance onfocus). For more information on $options and +$htmlAttributes see `HTML Helper `_. + +And to round off, here's an example for creating a hasAndBelongsToMany +select. Assume that User hasAndBelongsToMany Group. In your controller, +set a camelCase plural variable (group -> groups in this case, or +ExtraFunkyModel -> extraFunkyModels) with the select options. In the +controller action you would put the following: + +:: + + $this->set('groups', $this->User->Group->find('list')); + +And in the view a multiple select can be expected with this simple code: + +:: + + echo $form->input('Group'); + +If you want to create a select field while using a belongsTo- or +hasOne-Relation, you can add the following to your Users-controller +(assuming your User belongsTo Group): + +:: + + $this->set('groups', $this->User->Group->find('list')); + +Afterwards, add the following to your form-view: + +:: + + echo $form->input('group_id'); + +If your model name consists of two or more words, e.g., "UserGroup", +when passing the data using set() you should name your data in a +pluralised and camelCased format as follows: + +:: + + $this->set('userGroups', $this->UserGroup->find('list')); + // or + $this->set('reallyInappropriateModelNames', $this->ReallyInappropriateModelName->find('list')); + +Field naming convention +----------------------- + +The Form helper is pretty smart. Whenever you specify a field name with +the form helper methods, it'll automatically use the current model name +to build an input with a format like the following: + +:: + + + +You can manually specify the model name by passing in +Modelname.fieldname as the first parameter. + +:: + + echo $form->input('Modelname.fieldname'); + +If you need to specify multiple fields using the same field name, thus +creating an array that can be saved in one shot with saveAll(), use the +following convention: + +:: + + input('Modelname.0.fieldname'); + echo $form->input('Modelname.1.fieldname'); + ?> + + + + +$opcoes[‘type’] +--------------- + +Utilizando o ``FormHelper``, você pode forçar um campo a mudar o seu +tipo (e substituir a conduta do model) especificando-o. Em adição aos +tipos da tabela acima, você pode criar campos do tipo 'file' e +'password'. + +:: + + input('campo', array('type' => 'file')); ?> + + //A saída será: + +
+ + +
+ +$options[‘before’], $options[‘between’], $options[‘separator’] and $options[‘after’] +------------------------------------------------------------------------------------ + +Use these keys if you need to inject some markup inside the output of +the input() method. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + ));?> + + Output: + +
+ --before-- + + --between--- + + --after-- +
+ +For radio type input the 'separator' attribute can be used to inject +markup to separate each input/label pair. + +:: + + input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + ));?> + + Output: + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ +For ``date`` and ``datetime`` type elements the 'separator' attribute +can be used to change the string between select elements. Defaults to +'-'. + +$options[‘options’] +------------------- + +This key allows you to manually specify options for a select input, or +for a radio group. Unless the ‘type’ is specified as ‘radio’, the +FormHelper will assume that the target output is a select input. + +:: + + input('field', array('options' => array(1,2,3,4,5))); ?> + +Output: + +:: + +
+ + +
+ +Options can also be supplied as key-value pairs. + +:: + + input('field', array('options' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2', + 'Value 3'=>'Label 3' + ))); ?> + +Output: + +:: + +
+ + +
+ +If you would like to generate a select with optgroups, just pass data in +hierarchical format. Works on multiple checkboxes and radio buttons too, +but instead of optgroups wraps elements in fieldsets. + +:: + + input('field', array('options' => array( + 'Label1' => array( + 'Value 1'=>'Label 1', + 'Value 2'=>'Label 2' + ), + 'Label2' => array( + 'Value 3'=>'Label 3' + ) + ))); ?> + +Output: + +:: + +
+ + +
+ +$options[‘multiple’] +-------------------- + +If ‘multiple’ has been set to true for an input that outputs a select, +the select will allow multiple selections. Alternatively set ‘multiple’ +to ‘checkbox’ to output a list of related check boxes. + +:: + + $form->input('Model.field', array( 'type' => 'select', 'multiple' => true )); + $form->input('Model.field', array( 'type' => 'select', 'multiple' => 'checkbox' )); + +$options[‘maxLength’] +--------------------- + +Define o número máximo de caracteres permitidos em uma input do tipo +texto. + +$options[‘div’] +--------------- + +Use this option to set attributes of the input's containing div. Using a +string value will set the div's class name. An array will set the div's +attributes to those specified by the array's keys/values. Alternatively, +you can set this key to false to disable the output of the div. + +Setting the class name: + +:: + + echo $form->input('User.name', array('div' => 'class_name')); + +Output: + +:: + +
+ + +
+ +Setting multiple attributes: + +:: + + echo $form->input('User.name', array('div' => array('id' => 'mainDiv', 'title' => 'Div Title', 'style' => 'display:block'))); + +Output: + +:: + +
+ + +
+ +Disabling div output: + +:: + + input('User.name', array('div' => false));?> + +Output: + +:: + + + + +$options[‘label’] +----------------- + +Set this key to the string you would like to be displayed within the +label that usually accompanies the input. + +:: + + input( 'User.name', array( 'label' => 'The User Alias' ) );?> + +Output: + +:: + +
+ + +
+ +Alternatively, set this key to false to disable the output of the label. + +:: + + input( 'User.name', array( 'label' => false ) ); ?> + +Output: + +:: + +
+ +
+ +Set this to an array to provide additional options for the ``label`` +element. If you do this, you can use a ``text`` key in the array to +customize the label text. + +:: + + input( 'User.name', array( 'label' => array('class' => 'thingy', 'text' => 'The User Alias') ) ); ?> + +Output: + +:: + +
+ + +
+ +$options['legend'] +------------------ + +Some inputs like radio buttons will be automatically wrapped in a +fieldset with a legend title derived from the fields name. The title can +be overridden with this option. Setting this option to false will +completely eliminate the fieldset. + +$options[‘id’] +-------------- + +Set this key to force the value of the DOM id for the input. + +$options['error'] +----------------- + +Using this key allows you to override the default model error messages +and can be used, for example, to set i18n messages. It has a number of +suboptions which control the wrapping element, wrapping element class +name, and whether HTML in the error message will be escaped. + +To disable error message output set the error key to false. + +:: + + $form->input('Model.field', array('error' => false)); + +To modify the wrapping element type and its class, use the following +format: + +:: + + $form->input('Model.field', array('error' => array('wrap' => 'span', 'class' => 'bzzz'))); + +To prevent HTML being automatically escaped in the error message output, +set the escape suboption to false: + +:: + + $form->input('Model.field', array('error' => array('escape' => false))); + +To override the model error messages use an associate array with the +keyname of the validation rule: + +:: + + $form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough', true) ))); + +As seen above you can set the error message for each validation rule you +have in your models. In addition you can provide i18n messages for your +forms. + +$options['default'] +------------------- + +Usada para atribuir um valor default para o campo input. O valor é usado +se os dados passados para o form não contiverem um valor correspondente +para o campo (ou se nenhum dado for passado). + +Exemplo de utilização: + +:: + + input('ingrediente', array('default'=>'Açúcar')); + ?> + +Exemplo com um campo select (o tamanho "Médio" será selecionado como +padrão): + +:: + + 'Pequeno', 'm'=>'Médio', 'g'=>'Grande'); + echo $form->input('tamanho', array('options'=>$sizes, 'default'=>'m')); + ?> + +Você não pode usar ``default`` para marcar um checkbox - ao invés disso +você deve atribuir o valor para o campo correspondente em +``$this->data`` no seu controller ou em ``$form->data`` na sua view. + +Valores default para campos do tipo date e datetime podem ser atribuídos +utilizando-se a chave 'selected'. + +$options[‘selected’] +-------------------- + +Used in combination with a select-type input (i.e. For types select, +date, time, datetime). Set ‘selected’ to the value of the item you wish +to be selected by default when the input is rendered. + +:: + + echo $form->input('close_time', array('type' => 'time', 'selected' => '13:30:00')); + +The selected key for date and datetime inputs may also be a UNIX +timestamp. + +$options[‘rows’], $options[‘cols’] +---------------------------------- + +These two keys specify the number of rows and columns in a textarea +input. + +:: + + echo $form->input('textarea', array('rows' => '5', 'cols' => '5')); + +Output: + +:: + +
+ + +
+ +$options[‘empty’] +----------------- + +If set to true, forces the input to remain empty. + +When passed to a select list, this creates a blank option with an empty +value in your drop down list. If you want to have a empty value with +text displayed instead of just a blank option, pass in a string to +empty. + +:: + + input('field', array('options' => array(1,2,3,4,5), 'empty' => '(choose one)')); ?> + +Output: + +:: + +
+ + +
+ +If you need to set the default value in a password field to blank, use +'value' => '' instead. + +Options can also supplied as key-value pairs. + +$options[‘timeFormat’] +---------------------- + +Used to specify the format of the select inputs for a time-related set +of inputs. Valid values include ‘12’, ‘24’, and ‘none’. + +$options[‘dateFormat’] +---------------------- + +Usado para especificar o formato do seletor de entrada para datas. +Valores válidos incluem ‘DMY’, ‘MDY’, ‘YMD’, e ‘NONE’. + +$options['minYear'], $options['maxYear'] +---------------------------------------- + +Used in combination with a date/datetime input. Defines the lower and/or +upper end of values shown in the years select field. + +$options['interval'] +-------------------- + +This option specifies the number of minutes between each option in the +minutes select box. + +:: + + input('Model.time', array('type' => 'time', 'interval' => 15)); ?> + +Would create 4 options in the minute select. One for each 15 minutes. + +$options['class'] +----------------- + +You can set the classname for an input field using ``$options['class']`` + +:: + + echo $form->input('title', array('class' => 'custom-class')); + +Campos do tipo File(Arquivo) +============================ + +Para adicionar campos do tipo arquivo (file), você primeiro deve se +certificar que o formulário está definido como "multipart/form-data". +Para isso crie o seu formulário usando uma dessas formas abaixo. + +:: + + echo $form->create('Arquivo', array('enctype' => 'multipart/form-data') ); + + // OUTRA ALTERNATIVA + + echo $form->create('Arquivo', array('type' => 'file')); + +Em seguida, adicione uma das duas linhas ao seu arquivo de exibição do +formulário. + +:: + + echo $form->input('Arquivo.file', array('between'=>'
','type'=>'file')); + + // ou + + echo $form->file('Arquivo.file'); + +Devido a limitações do próprio HTML não é possível adicionar valores nos +campos do tipo arquivo (file). Cada vez que o formulário for exibido o +seu valor sempre será vazio (em branco). + +Após o envio, os campos do tipo arquivo (file) mostram um array com os +dados para o script que recebe os dados do formulário. + +Para o exemplo acima, os valores enviados sempre estará organizado da +forma como mostrado abaixo. O caminho físico do arquivo na variável +'tmp\_name' pode váriar de acordo com o sistema operacional usado pelo +CakePHP. + +:: + + $this->data['Arquivo']['file'] = array( + 'name' => nome_do_arquivo.pdf + 'type' => application/pdf + 'tmp_name' => C:/WINDOWS/TEMP/php1EE.tmp + 'error' => 0 + 'size' => 41737 + ); + +Esse array é gerado automaticamente pelo próprio PHP. Para mais detalhes +sobre os dados de arquivo passados pelo PHP `leia a documentação no site +oficial do PHP `_. + +Validando Uploads +----------------- + +Abaixo é um exemplo do método de validação que você pode definir no seu +model para validar se um arquivo foi enviado com sucesso. + +:: + + // Baseado no comentário 8 de: http://bakery.cakephp.org/articles/view/improved-advance-validation-with-parameters + + function isUploadedFile($params){ + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty($val['tmp_name']) && $val['tmp_name'] != 'none')) + { + return is_uploaded_file($val['tmp_name']); + } else { + return false; + } + } + +Elementos Específicos de Formulários +==================================== + +O restante dos métodos diponíveis no FormHelper são para criar elementos +específicos de formulários. Muitos desses métodos também fazem uso de um +segundo parâmetro especial de opções. Nesse caso, o parâmetro $opcoes é +usado para especificar elmentos e atributos HTML (como o valor ou o id +do DOM do elemento do formulário). + +:: + + text('nome', array('class' => 'users')); ?> + +A saída será: + +:: + + + + +checkbox +-------- + +``checkbox(string $fieldName, array $options)`` + +Cria um elemento de form checkbox. Este método também gera uma +associação oculta de form input para forçar a submição (submit) do dado +para o campo específico. + +:: + + checkbox('done'); ?> + +Will output: + +:: + + + + +button +------ + +``button(string $titulo, array $opcoes= array())`` + +Cria um elemento HTML "button" com o título especificado. Definindo o +``$opcoes['type']`` a saída será uma dessas três possibilidades: + +#. button: Cria um botão padrão do HTML. +#. reset: Cria um botão padrão de limpar o formulário. +#. submit: Cria um botão padrão de submeter um formulário. (O mesmo que + o método ``$form->submit``). + +:: + + button('Teste'); + echo $form->button('Outro Botão', array('type'=>'button')); + echo $form->button('Limpar', array('type'=>'reset')); + echo $form->button('Enviar formulário', array('type'=>'submit')); + ?> + +A saída será: + +:: + + + + + + +year +---- + +``year(string $fieldName, int $minYear, int $maxYear, mixed $selected, array $attributes, mixed $showEmpty)`` + +Creates a select element populated with the years from ``$minYear`` to +``$maxYear``, with the ``$selected`` year selected by default. +``$selected`` can either be a four-digit year (e.g. 2004) or string +``'now'``. HTML attributes may be supplied in ``$attributes``. + +:: + + year('purchased', 2005, 2009); + ?> + +Will output: + +:: + + + +If ``$showEmpty`` is false, the select will not include an empty option. +If ``$showEmpty`` is a string, it will be used as empty option's name. + +:: + + year('returned', 2008, 2010, null, null, 'Select a year'); + ?> + +Will output: + +:: + + + +month +----- + +``month(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with month names. + +:: + + month('mob'); + ?> + +Will output: + +:: + + + +You can pass in your own array of months to be used by setting the +'monthNames' attribute (CakePHP 1.3 only), or have months displayed as +numbers by passing false. (Note: the default months are +internationalized and can be translated using localization.) + +:: + + month('mob', null, array('monthNames' => false)); + ?> + +dateTime +-------- + +``dateTime(string $fieldName, string $dateFormat = ‘DMY’, $timeFormat = ‘12’, mixed $selected, array $attributes, boolean $showEmpty)`` + +Cria um conjunto de entradas de seleção para a data e hora. Valores +válidos para $dateformat são ‘DMY’, ‘MDY’, ‘YMD’ ou ‘NONE’. Valores +válidos para $timeFormat são ‘12’, ‘24’, e ‘NONE’. + +day +--- + +``day(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the (numerical) days of the +month. + +To create an empty option with prompt text of your choosing (e.g. the +first option is 'Day'), you can supply the text as the final parameter +as follows: + +:: + + day('created'); + ?> + +Will output: + +:: + + + +hour +---- + +``hour(string $fieldName, boolean $format24Hours, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the hours of the day. + +minute +------ + +``minute(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with the minutes of the hour. + +meridian +-------- + +``meridian(string $fieldName, mixed $selected, array $attributes, boolean $showEmpty)`` + +Creates a select element populated with ‘am’ and ‘pm’. + +error +----- + +``error(string $fieldName, string $text, array $options)`` + +Shows a validation error message, specified by $text, for the given +field, in the event that a validation error has occurred. + +Options: + +- 'escape' bool Whether or not to html escape the contents of the + error. +- 'wrap' mixed Whether or not the error message should be wrapped in a + div. If a string, will be used as the HTML tag to use. +- 'class' string The classname for the error message + +file +---- + +``file(string $fieldName, array $options)`` + +Creates a file input. + +:: + + create('User',array('type'=>'file')); + echo $form->file('avatar'); + ?> + +Will output: + +:: + +
+ + +When using ``$form->file()``, remember to set the form encoding-type, by +setting the type option to 'file' in ``$form->create()`` + +hidden +------ + +``hidden(string $fieldName, array $options)`` + +Cria um caixa no form input hidden. Exemplo: + +:: + + hidden('id'); + ?> + +A saída: + +:: + + + +isFieldError +------------ + +``isFieldError(string $fieldName)`` + +Returns true if the supplied $fieldName has an active validation error. + +:: + + isFieldError('gender')){ + echo $form->error('gender'); + } + ?> + +When using ``$form->input()``, errors are rendered by default. + +label +----- + +``label(string $fieldName, string $text, array $attributes)`` + +Creates a label tag, populated with $text. + +:: + + label('status'); + ?> + +Will output: + +:: + + + +password +-------- + +``password(string $fieldName, array $options)`` + +Creates a password field. + +:: + + password('password'); + ?> + +Will output: + +:: + + + +radio +----- + +``radio(string $fieldName, array $options, array $attributes)`` + +Creates a radio button input. Use ``$attributes['value']`` to set which +value should be selected default. + +Use ``$attributes['separator']`` to specify HTML in between radio +buttons (e.g.
). + +Radio elements are wrapped with a label and fieldset by default. Set +``$attributes['legend']`` to false to remove them. + +:: + + 'Male','F'=>'Female'); + $attributes=array('legend'=>false); + echo $form->radio('gender',$options,$attributes); + ?> + +Will output: + +:: + + + + + + + +If for some reason you don't want the hidden input, setting +``$attributes['value']`` to a selected value or boolean false will do +just that. + +select +------ + +``select(string $fieldName, array $options, mixed $selected, array $attributes, boolean $showEmpty)`` + +Cria um elemento select com as opções do ``$options``, usando o +``$selected`` como opção selecionada por padrão. Use ``$showEmpty`` como +falso se você não quiser mostrar a opção em branco. + +:: + + 'Masculino','F'=>'Feminino'); + echo $form->select('sexo',$opcoes); + ?> + +A saída será: + +:: + + + +Usando o ``$showEmpty`` como falso a saída será: + +:: + + + +submit +------ + +``submit(string $caption, array $options)`` + +Creates a submit button with caption ``$caption``. If the supplied +``$caption`` is a URL to an image (it contains a ‘.’ character), the +submit button will be rendered as an image. + +It is enclosed between ``div`` tags by default; you can avoid this by +declaring ``$options['div'] = false``. + +:: + + submit(); + ?> + +Will output: + +:: + +
+ +You can also pass a relative or absolute url to an image for the caption +parameter instead of caption text. + +:: + + submit('ok.png'); + ?> + +Will output: + +:: + +
+ +text +---- + +``text(string $fieldName, array $options)`` + +Creates a text input field. + +:: + + text('first_name'); + ?> + +Will output: + +:: + + + +textarea +-------- + +``textarea(string $fieldName, array $options)`` + +Creates a textarea input field. + +:: + + textarea('notes'); + ?> + +Will output: + +:: + + + diff --git a/pt/The-Manual/Core-Helpers/HTML.rst b/pt/The-Manual/Core-Helpers/HTML.rst new file mode 100644 index 0000000000000000000000000000000000000000..b88221df3942df49b3467971b177b6fbbbd1e53a --- /dev/null +++ b/pt/The-Manual/Core-Helpers/HTML.rst @@ -0,0 +1,611 @@ +HTML +#### + +O papel do HtmlHelper no CakePHP é fazer as ações relacionadas a HTML +mais fáceis, rápidas e mais resistente a mudanças. O uso deste helper +permitirá que suas aplicações sejam mais leves e mais flexíveis. + +O objetivo do HtmlHelper foi significativamente modificado desde o +CakePHP 1.1. Seus métodos relacionados a formulários tornaram-se +obsoletos em favor do novo FormHelper. Se você estiver querendo auxílio +para criação de elementos de formulários HTML, você deve conferir o +FormHelper. + +Antes de vermos os métodos do HtmlHelper, você precisará conhecer sobre +as poucas configurações e situações que vão lhe ajudar a utilizar melhor +esta classe. Primeiro de tudo, num esforço para acalmar aqueles que não +gostam de tags curtas () nem de muitas chamadas echo() no código +de suas views, todos os métodos do HtmlHelper são passados para o método +output(). Se você quiser permitir a exibição automática do conteúdo HTML +gerado, você pode simplesmente implementar o método output() em seu +AppHelper. + +:: + + function output($str) { + echo $str; + } + +Fazendo isto você pode eliminar a necessidade de adicionar declarações +echo no código de suas views. + +Muitos métodos do HtmlHelper também incluem um parâmetro +$htmlAttributes, que permite que você inclua atributos extras em suas +tags. Aqui estão alguns poucos exemplo de como usar o parâmetro +$htmlAttributes: + +:: + + Atributos desejados: + Array de parâmetros: array('class'=>'someClass') + + Atributos desejados: + Array de parâmetros: array('name' => 'foo', 'value' => 'bar') + +O HtmlHelper está disponível em todas as views por padrão. Se você +estiver recebendo um erro dizendo que ele não pôde ser encontrado, é +provável que ele esteja faltando numa definição manual que você tenha +feito da variável $helpers em um controller. + +Inserindo Elementos Bem-Formatados +================================== + +A tarefa mais importante que o HtmlHelper realiza é a criação de +marcações bem formadas. Não tenha medo de usá-lo muitas vezes - você +pode armazenar em cache views em CakePHP, a fim de salvar alguns ciclos +do processamento quando as views estão sendo renderizadas e liberadas. +Esta seção irá cobrir alguns dos métodos da HtmlHelper e como usá-los. + +charset +------- + +``charset(string $charset=null)`` + +Utilizado para criar uma meta tag especificando os caracteres do +documento. O padrão é UTF-8. + +:: + + + charset(); ?> + +Saída: + +:: + + + +Alternativamente, + +:: + + charset('ISO-8859-1'); ?> + +Saída: + +:: + + + +css +--- + +``css(mixed $path, string $rel = null, array $htmlAttributes = array(), boolean $inline = true)`` + +Creates a link(s) to a CSS style-sheet. If $inline is set to false, the +link tags are added to the $scripts\_for\_layout variable which you can +print inside the head tag of the document. + +This method of CSS inclusion assumes that the CSS file specified resides +inside the /app/webroot/css directory. + +:: + + css('forms'); ?> + +Will output: + +:: + + + +The first parameter can be an array to include multiple files. + +:: + + css(array('forms','tables','menu')); ?> + +Will output: + +:: + + + + + +meta +---- + +``meta(string $type, string $url = null, array $attributes = array(), boolean $inline = true)`` + +This method is handy for linking to external resources like RSS/Atom +feeds and favicons. Like css(), you can specify whether or not you'd +like this tag to appear inline or in the head tag using the fourth +parameter. + +If you set the "type" attribute using the $htmlAttributes parameter, +CakePHP contains a few shortcuts: + ++--------+------------------------+ +| type | translated value | ++========+========================+ +| html | text/html | ++--------+------------------------+ +| rss | application/rss+xml | ++--------+------------------------+ +| atom | application/atom+xml | ++--------+------------------------+ +| icon | image/x-icon | ++--------+------------------------+ + +:: + + meta( + 'favicon.ico', + '/favicon.ico', + array('type' => 'icon') + );?> //Output (line breaks added)

+ + + meta( + 'Comments', + '/comments/index.rss', + array('type' => 'rss')); + ?> + + //Output (line breaks added) + + +This method can also be used to add the meta keywords and descriptions. +Example: + +:: + + meta( + 'keywords', + 'enter any meta keyword here' + );?> + //Output + // + + meta( + 'description', + 'enter any meta description here' + );?> + + //Output + +If you want to add a custom meta tag then the first parameter should be +set to an array. To output a robots noindex tag use the following code: + +:: + + echo $html->meta(array('name' => 'robots', 'content' => 'noindex')); + +docType +------- + +``docType(string $type = 'xhtml-strict')`` + +Returns a (X)HTML doctype tag. Supply the doctype according to the +following table: + ++----------------+-----------------------+ +| type | translated value | ++================+=======================+ +| html | text/html | ++----------------+-----------------------+ +| html4-strict | HTML4 Strict | ++----------------+-----------------------+ +| html4-trans | HTML4 Transitional | ++----------------+-----------------------+ +| html4-frame | HTML4 Frameset | ++----------------+-----------------------+ +| xhtml-strict | XHTML1 Strict | ++----------------+-----------------------+ +| xhtml-trans | XHTML1 Transitional | ++----------------+-----------------------+ +| xhtml-frame | XHTML1 Frameset | ++----------------+-----------------------+ +| xhtml11 | XHTML 1.1 | ++----------------+-----------------------+ + +:: + + docType(); ?> + + + docType('html4-trans'); ?> + + +style +----- + +``style(array $data, boolean $inline = true) `` + +Builds CSS style definitions based on the keys and values of the array +passed to the method. Especially handy if your CSS file is dynamic. + +:: + + style(array( + 'background' => '#633', + 'border-bottom' => '1px solid #000', + 'padding' => '10px' + )); ?> + +Will output: + +:: + + background:#633; + border-bottom:1px solid #000; + padding:10px; + +image +----- + +``image(string $path, array $htmlAttributes = array())`` + +Cria uma tag formatada de imagem. O caminho fornecido deve ser relativo +a /app/webroot/img/ . + +:: + + image('cake_logo.png', array('alt' => 'CakePHP'))?> + +Saída será: + +:: + + CakePHP + +Para criar um link de imagem especificando o link de destino usando a +opção de ``url`` em ``$htmlAttributes. `` + +:: + + image("recipes/6.jpg", array( + "alt" => "Brownies", + 'url' => array('controller' => 'recipes', 'action' => 'view', 6) + )); ?> + +Saída será: + +:: + + + Brownies + + +link +---- + +``link(string $title, mixed $url = null, array $htmlAttributes = array(), string $confirmMessage = false, boolean $escapeTitle = true)`` + +General purpose method for creating HTML links. Use ``$htmlAttributes`` +to specify attributes for the element. + +:: + + link('Enter', '/pages/home', array('class'=>'button','target'=>'_blank')); ?> + +Will output: + +:: + + + Enter + +Specify ``$confirmMessage`` to display a javascript ``confirm()`` +dialog. + +:: + + link( + 'Delete', + array('controller'=>'recipes', 'action'=>'delete', 6), + array(), + "Are you sure you wish to delete this recipe?" + );?> + +Will output: + +:: + + + Delete + +Query strings can also be created with ``link()``. + +:: + + link('View image', array( + 'controller' => 'images', + 'action' => 'view', + 1, + '?' => array( 'height' => 400, 'width' => 500)) + ); + +Will output: + +:: + + + View image + +HTML special characters in ``$title`` will be converted to HTML +entities. To disable this conversion, set the escape option to false in +the ``$htmlAttributes``, or set ``$escapeTitle`` to false. + +:: + + link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + array('escape'=>false) + ); + + echo $html->link( + $html->image("recipes/6.jpg", array("alt" => "Brownies")), + "recipes/view/6", + null, null, false + ); + ?> + +Both will output: + +:: + + + Brownies + + +Also check `HtmlHelper::url `_ +method for more examples of different types of urls. + +tag +--- + +``tag(string $tag, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns text wrapped in a specified tag. If no text is specified then +only the opening is returned. + +:: + + tag('span', 'Hello World.', array('class' => 'welcome'));?> + + //Output + Hello World + + //No text specified. + tag('span', null, array('class' => 'welcome'));?> + + //Output + + +div +--- + +``div(string $class, string $text, array $htmlAttributes, boolean $escape = false) `` + +Used for creating div-wrapped sections of markup. The first parameter +specifies a CSS class, and the second is used to supply the text to be +wrapped by div tags. If the last parameter has been set to true, $text +will be printed HTML-escaped. + +If no text is specified, only an opening div tag is returned. + +:: + + + div('error', 'Please enter your credit card number.');?> + + //Output +
Please enter your credit card number.
+ +para +---- + +``para(string $class, string $text, array $htmlAttributes, boolean $escape = false)`` + +Returns a text wrapped in a CSS-classed

tag. If no text is supplied, +only a starting

tag is returned. + +:: + + para(null, 'Hello World.');?> + + //Output +

Hello World.

+ +tableHeaders +------------ + +``tableHeaders(array $names, array $trOptions = null, array $thOptions = null)`` + +Creates a row of table header cells to be placed inside of tags. + +:: + + tableHeaders(array('Date','Title','Active'));?> //Output + + + tableHeaders( + array('Date','Title','Active'), + array('class' => 'status'), + array('class' => 'product_table') + );?> + + //Output + + + + + + +tableCells +---------- + +``tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true)`` + +Creates table cells, in rows, assigning attributes differently for +odd- and even-numbered rows. Wrap a single table cell within an array() +for specific + + + + tableCells(array( + array('Jul 7th, 2007', array('Best Brownies', array('class'=>'highlight')) , 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', array('No', array('id'=>'special'))), + )); + ?> + + //Output + + + + + tableCells( + array( + array('Red', 'Apple'), + array('Orange', 'Orange'), + array('Yellow', 'Banana'), + ), + array('class' => 'darker') + ); + ?> + + //Output + + + + +url +--- + +``url(mixed $url = NULL, boolean $full = false)`` + +Returns an URL pointing to a combination of controller and action. If +$url is empty, it returns the REQUEST\_URI, otherwise it generates the +url for the controller and action combo. If full is true, the full base +URL will be prepended to the result. + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "bar"));?> + + // Output + /posts/view/bar + +Here are a few more usage examples: + +URL with named parameters + +:: + + url(array( + "controller" => "posts", + "action" => "view", + "foo" => "bar")); + ?> + + // Output + /posts/view/foo:bar + +URL with extension + +:: + + url(array( + "controller" => "posts", + "action" => "list", + "ext" => "rss")); + ?> + + // Output + /posts/list.rss + +URL (starting with '/') with the full base URL prepended. + +:: + + url('/posts', true); ?> + + //Output + http://somedomain.com/posts + +URL with GET params and named anchor + +:: + + url(array( + "controller" => "posts", + "action" => "search", + "?" => array("foo" => "bar"), + "#" => "first")); + ?> + + //Output + /posts/search?foo=bar#first + +For further information check +`Router::url `_ in +the API. + +Alterando a saída de tags pelo HtmlHelper +========================================= + +A definição de tags para o ``HtmlHelper`` segue o modelo XHTML. No +entanto, se você precisa gerar HTML para HTML4 você vai precisar criar e +carregar um arquivo de configuração contendo as tags que você gostaria +de usar. Para mudar as tags utilizadas crie o arquivo +``app/config/tags.php`` com o conteúdo: + +:: + + $tags = array( + 'metalink' => '', + 'input' => '', + //... + ); + +Assim, você pode carregar a tag definida chamando +``$html->loadConfig('tags');`` diff --git a/pt/The-Manual/Core-Helpers/Javascript.rst b/pt/The-Manual/Core-Helpers/Javascript.rst new file mode 100644 index 0000000000000000000000000000000000000000..babfeb7106b73dd4eec5383d3bea67b03cac50c6 --- /dev/null +++ b/pt/The-Manual/Core-Helpers/Javascript.rst @@ -0,0 +1,151 @@ +Javascript +########## + +O helper Javascript é usado para ajudar na criação de tags e blocos de +código javascript bem-formados. Ele contém diversos métodos, alguns dos +quais foram desenvolvidos para funcionar com a biblioteca de Javascript +`Prototype `_. + +Métodos +======= + +``codeBlock($script, $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), $safe)`` + +- string $script - o código Javascript a ser embutido em tags SCRIPT +- array $options - um conjunto de opções: + + - allowCache: booleano, define se este é um bloco armazenável com as + configurações atuais de cache. + - safe: booleano, se este bloco deve ser delimitado por tags CDATA. + O padrão é o definido na configuração do objeto helper. + - inline: booleano, se este bloco deve ser exibido inline, ou + guardado para ser exibido depois na seção específica no cabeçalho + do documento (i.e. $scripts\_for\_layout). + +- boolean $safe - OBSOLETO. Prefira utilizar $options['safe'] em seu + lugar + +O método codeBlock retorna um elemento de script formatado contendo o +código dado por $script. Mas também retorna null se o helper Javascript +estiver configurado para armazenar eventos em cache. Veja +JavascriptHelper::cacheEvents(). Este método também pode escrever na +variável de layout ``$scripts_for_layout`` se você definir a chave +$options['inline'] para false. + +``blockEnd()`` + +Encerra um bloco de Javascript em cache. Pode retornar tanto uma tag de +fechamendo de script ou esvaziar o buffer, adicionando o conteúdo para o +array cachedEvents. Este método retorna um valor dependendo das +configurações de cache. Veja JavascriptHelper::cacheEvents() + +``link($url, $inline)`` + +- mixed $url - uma string URL para um arquivo Javascript ou um array de + URLs. +- boolean $inline - se for true, a tag
DateTitleActive
DateTitleActive
-attributes. + +:: + + tableCells(array( + array('Jul 7th, 2007', 'Best Brownies', 'Yes'), + array('Jun 21st, 2007', 'Smart Cookies', 'Yes'), + array('Aug 1st, 2006', 'Anti-Java Cake', 'No'), + )); + ?> + + //Output +
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
Jul 7th, 2007Best BrowniesYes
Jun 21st, 2007Smart CookiesYes
Aug 1st, 2006Anti-Java CakeNo
RedApple
OrangeOrange
YellowBanana