CakePHP 3 baza danych i model

W tej części zaczniemy tworzyć prosty interfejs do zarządzania użytkownikami. W tym celu utworzymy tabelę users w bazie danych. Pola w tabeli są w większości oczywiste, ale pola passkey i timeout są częścią funkcjonalności resetowania hasła, którą dodamy później.

CREATE TABLE `users` (
  `id` bigint(11) NOT NULL,
  `username` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` varchar(61) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
  `first_name` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
  `last_name` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
  `role` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL,
  `passkey` varchar(13) COLLATE utf8mb4_unicode_ci NOT NULL,
  `timeout` timestamp NULL DEFAULT NULL,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Po utworzeniu tabeli możesz użyć wbudowanego skryptu wiersza poleceń (shell scriptu) aby wypiec (bake) model, kontroler i widok. Więcej na ten temat w dokumentacji. Model CakePHP określa interakcję aplikacji z tabelą bazy danych. Powinien być jeden model na tabelę. Dla naszej tabeli users należy utworzyć 2 pliki: Jeden o nazwie UsersTable.php, który znajduje się w katalogu src/Model/Table/ i jeden o nazwie User.php znajdujący się w katalogu src/Model/Entity/. Obiekty tabel służą do łączenia się z kolekcjami obiektów (tj. tabelami bazy danych). Chociaż wiele z poniższej klasy nie wymaga wyjaśnień, dodałam kilka komentarzy, aby wyjaśnić niektóre z poniższych linii.
Zawartość pliku UsersTable.php:
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class UsersTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('users'); // nazwa tabeli w bazie danych
        $this->displayField('full_name'); // pole lub pole wirtualne (virtual field) używane do wyświetlania domyślnego w powiązanych modelach, jeśli brak będzie to "id"
        $this->primaryKey('id'); // Primary key tabeli, jeśli brak będzie to "id"

        $this->addBehavior('Timestamp'); // Pozwala modelowi na automatyczne datowanie tworzenia/modyfikacji
    }
}
Encje (Entity) reprezentują poszczególne obiekty np rekordy.
Zawartość pliku User.php:
<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;

class User extends Entity
{
    // Pola które mogą być zmieniane za pomocą newEntity() lub patchEntity()
    protected $_accessible = [
        '*' => true,
        'id' => false
    ];
    protected $_hidden = [ 'password' ]; // Pole wyłączone z wyświetlania
    // Hashowanie hasła przed zapisem i walidacją
    protected function _setPassword($value)
    {
        $hasher = new DefaultPasswordHasher();
        return $hasher->hash($value);
    }
}
Walidacja
W ramach interfejsu bazy danych model wykonuje również walidację danych. Istnieje wiele podstawowych reguł zaimplementowanych w CakePHP domyślnie, ale możesz również dodać własną regułę niestandardową. Fragment pliku src/Model/Table/UsersTable.php odpowiadający za walidację;
public function validationDefault(Validator $validator)
{
	$validator
		->integer('id')
		->allowEmpty('id', 'create'); // id nie jest obowiązkowe
	$validator
		->requirePresence('username', 'create')
		->notEmpty('username')
		->add('username', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);
	$validator
		->requirePresence('password', 'create')
		->notEmpty('password', 'You must enter a password', 'create')
			->add('password', [
				'length' => [
				'rule' => ['minLength', 8],
				'message' => 'Passwords must be at least 8 characters long.',
			]
		]);
	$validator
		->requirePresence('first_name', 'create')
		->notEmpty('first_name');
	$validator
		->requirePresence('last_name', 'create')
		->notEmpty('last_name');
	$validator
		->email('email')
		->requirePresence('email', 'create')
		->notEmpty('email');
	$validator
		->requirePresence('role', 'create')
		->notEmpty('role');
	$validator
		->dateTime('timeout')
		->allowEmpty('timeout');
	return $validator;
}
public function buildRules(RulesChecker $rules)
{
	$rules->add($rules->isUnique(['username']));
	$rules->add($rules->isUnique(['email']));
	return $rules;
}
Teraz zajmiemy się bardziej skomplikowaną walidacją. Powszechnym sprawdzaniem poprawności przy tworzeniu użytkowników jest dwukrotne wpisanie hasła w celu potwierdzenia potwierdzenia wprowadzenia poprawnej wartości. Z CakePHP można to łatwo osiągnąć za pomocą tymczasowego pola wirtualnego ("confirm_password") i reguły compareWith. Dodaj następujący walidator do powyższej funkcji validationDefault:
$validator
	->requirePresence('confirm_password', 'create')
	->notEmpty('confirm_password')
	->allowEmpty('confirm_password', 'update')
	->add('confirm_password', 'compareWith', [
		'rule' => ['compareWith', 'password'],
		'message' => 'Passwords do not match.'
	]);
Dodaj poniższą funkcję i zmień walidator.
/**
 * Checks password for a single instance of each:
 * number, uppercase, lowercase, and special character
 *
 * @param type $password
 * @param array $context
 * @return boolean
 */
public function checkCharacters($password, array $context)
{
	// number
	if (!preg_match("#[0-9]#", $password)) {
		return false;
	}
	// Uppercase
	if (!preg_match("#[A-Z]#", $password)) {
		return false;
	}
	// lowercase
	if (!preg_match("#[a-z]#", $password)) {
		return false;
	}
	// special characters
	if (!preg_match("#\W+#", $password) ) {
		return false;
	}
	return true;
}
Innym powszechną walidacją jest wymaganie bezpiecznych haseł.
$validator
	->requirePresence('password', 'create')
	->notEmpty('password', 'You must enter a password', 'create')
->add('password', [
	'length' => [
	'rule' => ['minLength', 8],
	'message' => 'Passwords must be at least 8 characters long.',
]
])
	->add('password', 'custom', [
		'rule' => [$this, 'checkCharacters'],
		'message' => 'The password must contain 1 number, 1 uppercase, 1 lowercase, and 1 special character'
	]);