PHP:Cakephp-Framework
Einstellungen der Tools
Wenn die Webseite nicht nur Englisch, sondern auch andere Sprachen verwenden soll, dann ist es notwendig einen einheitlichen Zeichensatz festzulegen. Wenn man das nicht macht, können Sonderzeichen falsch dargestellt werden. Meist wird die UTF-8-Zeichenkodierung dafür verwendet. Im Folgenden zeige ich, wie man die Tools, die ich verwendet auf den UTF-8-Zeichensatz umstellt.
Tools | Einstellung |
---|---|
Browser | <meta charset="utf-8" /> (HTML5)
bzw.
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> (XHTML)
|
Datenbank (mysql) | CREATE TABLE user (
id INT UNSIGNED AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
modified DATETIME DEFAULT NULL,
created DATETIME DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; --InnoDB ist langsamer
|
IDE (Netbeans) | Rechtsklick auf das Projekt --> Properties --> Sources/Encoding |
IDE (Eclipse) | Window/Preferences --> General/Editors/Text Editors/Spelling --> Encoding |
Editor (jedit) | Extras/Globale Optionen --> Zeichenkodierung --> Standardzeichenkodierung |
Editor (notepad++) | Kodierung |
Spracheditor (poedit) | Wenn ein Katalog geöffnet ist. Katalog/Eigenschaften --> Zeichensatz |
SQL-Developer | Extras/Voreinstellungen --> Umgebung --> Codierung |
Installation
Pear/Pecl
PHPUnit
Xdebug
Installation mit PEAR/PECL:
pecl install xdebug
In die php.ini Folgendes (mit richtigem Pfad) hinzufügen:
zend_extension="/usr/local/php/modules/xdebug.so"
Note: You should ignore any prompts to add "extension=xdebug.so" to php.ini — this will cause problems.
Links
CakePHP
git clone git://github.com/cakephp/cakephp.git
In das Webserververzeinis kopieren und die URL aufrufen.
CakePHP-Webseite zur Installation
Konventionen
Convention over configuration ist ein fester Bestandteil von CakePHP. Deshalb sollte man sich an die Regel halten. Einige davon sind:
Überschrift | Überschrift | Überschrift |
---|---|---|
Model-Klassennamen |
|
|
Tabellenname |
|
|
Beispiel | Beispiel | Beispiel |
Beispiel | Beispiel | Beispiel |
Beispiel | Beispiel | Beispiel |
Model-Klassennamen ||
- Beispiel
- Beispiel
Aufbau der Webseite / Layout
Wie die Webseite aufgebaut wird, beschreibt die Datei app/Views/Layout/default.ctp. Den HTML-Code, den die Views zu den entsprechenden Controllern erzeugen wird durch:
echo $this->fetch('content');
eingefügt.
Man kann weitere dieser Einfügestellen erstellen indem man in einer View einen Abschnitt definiert, der wie folgt aussehen würde:
$this->start('Abschnitt');
//erzeuge HTML-Text
$this->end();
Auf diesen Namen bezieht man sich dann in der app/Views/Layout/default.ctp durch Einfügen von:
echo $this->fetch('Abschnitt');
Das Standardlayout wird durch die default.ctp beschrieben, man kann aber auch weiter Layoutdateien erzeugen, z.B. alternativ.ctp. Um auf dieses Layout umzuschalten gibt es zwei Möglichkeiten:
- Im Controller
function ... {
$this->layout = 'alternativ';
}
- In der View
$this->layout = 'alternativ';
Speichern und Laden von Daten
Die ID einer neu gespeicherten Zeile erhalten
if($this->MyModel->save($this->data)){
//the id is here
echo $this->MyModel->id;
}
Gruppierung
array(
'conditions' => array('Model.field' => $thisValue), //array of conditions
'recursive' => 1, //int
//array of field names
'fields' => array('Model.field1', 'DISTINCT Model.field2'),
//string or array defining order
'order' => array('Model.created', 'Model.field3 DESC'),
'group' => array('Model.field'), //fields to GROUP BY
'limit' => n, //int
'page' => n, //int
'offset' => n, //int
'callbacks' => true //other possible values are false, 'before', 'after'
)
Konstruktoren
Componente
App::uses('PropertiesComponent', 'Controller/Component');
App::uses('PropertiesController', 'Controller');
...
// Eine andere 'Component', falls man nicht Vererbung nutzt
public $components = array('Properties');
...
public function __construct(ComponentCollection $collection, $settings = array()) {
parent::__construct($collection, $settings);
// Ein 'Controller', sollte man aber eher nicht machen
$this->PropertiesController = new PropertiesController();
// Ein Model
$this->Property = ClassRegistry::init('Property');
}
Controller
App::uses('PropertiesComponent', 'Controller/Component');
...
function __construct($request = null, $response = null) {
parent::__construct($request, $response);
// Eine andere 'Component'
$this->PropertiesComponent = $this->Components->load('Properties');
}
Model
App::uses('InMemoryProperties', 'Utility');
...
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
// Eine Utility-Klasse
$this->InMemoryProperties = InMemoryProperties::Instance();
}
Zugriffe
vom Controller
- auf das eigene Model
$this->MyModel->modelfkt($parameter);
- auf ein fremdes Model
App::uses('OtherModelName', 'Model');
$this->OtherModelName = new OtherModelName();
$this->OtherModelName->modelfkt($parameter);
oder
App::uses('OtherModelName', 'Model');
$this->Controller->loadModel('OtherModelName');
$this->Controller->OtherModelName->modelfkt($parameter);
oder
$this->OtherModelName = ClassRegistry::init('OtherModelName');
$this->OtherModelName->modelfkt($parameter);
- auf eine Komponete
App::uses('ExampleComponent', 'Controller/Component');
$this->Example = $this->Components->load('Example');
$this->Example->function();
- auf einen anderen Controller
- über Vererbung
- über eine Komponente
App::uses('ExampleController', 'Controller');
$this->Example = new ExampleController();
$this->Example->function();
Benutzerrechte / ACLs
ToDo: ACLs einrichten
Die Rechte werden über die Tabelle aros_acos vergeben, die eine Verknüpfung der Tabellen aros und arcos darstellt. In aros findet eine Zuweisung zu den Gruppen (und Nutzern) statt. In acos sind alle Controller und Views in einem Baum aufgeführt, die durch die Kommandozeilenoperation
cake AclExtras.AclExtras aco_sync
aktualisiert werden kann. Um nun die Rechte auf Gruppenebene zu verteilen empfiehlt sich folgendes Datenbankskript:
INSERT INTO aros_acos (aro_id, aco_id, _create, _read, _update, _delete)
VALUES ( (SELECT id FROM aros WHERE foreign_key=
(SELECT id FROM groups WHERE NAME='<Benutzergruppe>' AND MODEL='Group')),
(SELECT id FROM acos WHERE ALIAS='<Controllername ohne Controller>'),
1, 1, 1, 1
);
Internationalisierung
Sprachinformationen in /app/locale/deu/
Kategorie | Beschreibung |
---|---|
LC_MESSAGES | Übersetzung von Ausgaben an den Benutzer |
LC_CTYPE | Spezielle Sonderzeichen einer Region |
LC_NUMERIC | Lokale Formatierung von Zahlen |
LC_TIME | Lokale Formatierung von Zeitangaben |
LC_COLLATE | Angabe für den Umgang mit Zeichenketten, z.B. Sortierung |
LC_MONETARY | Lokale Währungsangaben |
LC_ALL | Überschreibt alle Angaben aus den anderen Kategorien |
Die Datei LC_TIME spezifiziert die Datums-/Zeitdarstellung. Die Buchstaben sind in Unicode(Hex) dargestellt. <U004D><U00E4><U0072><U007A> heißt z.B. März. Genauer wird es hier beschrieben. Meine Version für deutsch sieht so aus:
comment_char #
escape_char /
#
#
#Abbreviated weekday names (%a)
abday "<U0053><U006F>";"<U004D><U006F>";/
"<U0044><U0069>";"<U004D><U0069>";/
"<U0044><U006F>";"<U0046><U0072>";/
"<U0053><U0061>"
#
#
#Full weekday names (%A)
day "<U0053><U006F><U006E><U006E><U0074><U0061><U0067>";/
"<U004D><U006F><U006E><U0074><U0061><U0067>";/
"<U0044><U0069><U0065><U006E><U0073><U0074><U0061><U0067>";/
"<U004D><U0069><U0074><U0074><U0077><U006F><U0063><U0068>";/
"<U0044><U006F><U006E><U006E><U0065><U0072><U0073><U0074><U0061><U0067>";/
"<U0046><U0072><U0065><U0069><U0074><U0061><U0067>";/
"<U0053><U0061><U006D><U0073><U0074><U0061><U0067>"
#
#
#Abbreviated month names (%b)
abmon "<U004A><U0061><U006E>";"<U0046><U0065><U0062>";/
"<U004D><U00E4><U0072>";"<U0041><U0070><U0072>";/
"<U004D><U0061><U0069>";"<U004A><U0075><U006E>";/
"<U004A><U0075><U006C>";"<U0041><U0075><U0067>";/
"<U0053><U0065><U0070>";"<U004F><U006B><U0074>";/
"<U004E><U006F><U0076>";"<U0044><U0065><U007A>"
#
#
#Full month names (%B)
mon "<J><a><n><u><a><r>";/
"<F><e><b><r><u><a><r>";/
"<U004D><U00E4><U0072><U007A>";/
"<U0041><U0070><U0072><U0069><U006C>";/
"<U004D><U0061><U0069>";/
"<U004A><U0075><U006E><U0069>";/
"<U004A><U0075><U006C><U0069>";/
"<U0041><U0075><U0067><U0075><U0073><U0074>";/
"<U0053><U0065><U0070><U0074><U0065><U006D><U0062><U0065><U0072>";/
"<U004F><U006B><U0074><U006F><U0062><U0065><U0072>";/
"<U004E><U006F><U0076><U0065><U006D><U0062><U0065><U0072>";/
"<U0044><U0065><U007A><U0065><U006D><U0062><U0065><U0072>"
#
#
#
am_pm "";""
#
#
# Appropriate date and time representation (%c)
# "%a %d %b %Y %T %Z"
d_t_fmt "<U0025><U0061><U0020><U0025><U0064><U0020><U0025><U0062><U0020><U0025><U0059><U0020><U0025><U0054><U0020><U0025><U005A>"
#
#
# Appropriate date representation (%x)
# "%d.%m.%Y"
d_fmt "<U0025><U0064><U002E><U0020><U0025><U0042><U0020><U0025><U0059>"
#
# Appropriate time representation (%X)
# "%T"
t_fmt "<U0025><U0054>"
t_fmt_ampm ""
date_fmt "<U0025><U0061><U0020><U0025><U002D><U0064><U002E><U0020>/
<U0025><U0062><U0020><U0025><U0048><U003A><U0025><U004D><U003A><U0025><U0053>/
<U0020><U0025><U005A><U0020><U0025><U0059>"
#
#
week 7;19971130;4
first_weekday 2
first_workday 2
Validierung
Internationalisierte Validierung
- Im Model (erzeugt validation.pot)
public $validationDomain = 'validation';
public $validate = array(
'username' => array(
'alphaNumeric' => array(
'rule' => 'alphaNumeric',
'required' => true,
'message' => 'Alphabets and numbers only',
'last' => false
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => 'Username should be between %d and %d characters'
),
'unique' => array(
'rule' => 'isUnique',
'message' => 'This username has already been taken',
'last' => true
)
),
);
- Im Helper (kommt in default.pot)
private static $subSets = array();
public function __construct() {
self::$subSets = array(
'deviceUpload' => array('icon' => 'icon-broadcast', 'color' => 'bg-amber',
'destination' => array('controller' => 'basedatas', 'action' => 'device_config_load'),
'text' => __('Device upload')
),
);
}
public function getSubSet($name) {
return BaseDataHelper::$subSets[$name];
}
- Im Controller
function x() { // Funktionsname ersetzen
$this->Model->set($this->request->data); // Model ersetzen
if ($this->request->is('post')) {
if ($this->Model->validates()) { // ist validiert
$this->Model->create();
if ($this->Model->save($this->request->data)) {
$this->Session->setFlash(__('saved'));
$this->redirect(array('controller' => 'ctrl', 'action' => 'act'));
}
$this->Session->setFlash(__('failed'));
} else { // Validierung fehlgeschlagen
//$errors = $this->User->validationErrors;
}
}
...
}
Links
Validation and exception handling from the ui to the backend
Sprachkonstrukte
- For-Schleifen
foreach ($fields as $i => $field) {
... // $i gibt immer die Bezeichnung oder die Nummer des Array-Elements aus.
... // $field ist das aktuelle Array-Element.
}
- Input-Fields
$param = array(
'autocomplete' => off, // automatisches Ausfüllen durch Browser verhindern
'readonly' => 'readonly',
'disabled' => 'disabled',
'selection' => $selection,
'emptySelectable' => true,
'id' => $id,
'label' => $label,
'type' => $type, // z.B. password, text, file
);
$this->Form->input($field['id'], $param);
- AJAX - auf Server-Seite (Controller)
<?php
App::uses('AppController', 'Controller');
class AjaxController extends AppController {
public $components = array('RequestHandler');
public 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
}
if ($this->request->is('ajax')) {
$this->disableCache();
}
}
public function index() {
$this->set('content', String::uuid());
// render special view for ajax
$this->render('ajax_response', 'ajax');
}
}
?>
- AJAX - auf Server-Seite (View - ajax_response.ctp)
<?php
echo $content;
?>
- AJAX - auf Client-Seite
<div class='result'></div>
// Polling by JavaScript (jQuery)
(function worker() {
$.ajax({
url: '/Ajax/index',
success: function(data) {
$('.result').html(data); // An das Element .result anhängen
},
complete: function() {
// Schedule the next request when the current one's complete
setTimeout(worker, 5000);
}
});
})();
Benutzerdefinierte Sessionmeldungen
<?php
//Standarsverfahren
$this->Session->setFlash('Normal message!'); // --> /View/Elements/flash_error.ctp
// Benutzerdefinierte Klasse
$this->Session->setFlash('Something custom!', 'flash_custom'); // --> /View/Elements/flash_custom.ctp
?>
- Bsp. für flash_custom.ctp
<div id='flash-message' class='input-control text' data-role='input-control'>
<?php echo "<input value='".h($message)."' type='text'>\n"; ?>
</div>
Zugriffskontrolle
Im AppController:
public function beforeFilter() {
// Startseite ohne autentifizierung
$this->Auth->allow('display');
//Configure AuthComponent
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display');
}
Debugging
$foo = array(1,2,3);
Debugger::dump($foo);
Debugger::log('===== JSONData HTML2PDF =======');
$this->log($jsondata, 'debug');
Tests
cake bake test
Tweaks
Benötigt man das letzte SQL z.B. von einem find und man kann nicht das DebugKit benutzen, weil z.B. keine View gerendert wird, dann kann man im Model folgendes verwenden:
function getLastQuery() {
$dbo = $this->getDatasource();
$logs = $dbo->getLog();
$lastLog = end($logs['log']);
return $lastLog['query'];
}