293 lines
17 KiB
Markdown
293 lines
17 KiB
Markdown
|
# Переход с 2.3.x
|
|||
|
|
|||
|
Многое в Kohana v3 работает совсем по другому, нежели в Kohana 2.3, вот список самых популярных советов для обновляющихся.
|
|||
|
|
|||
|
## Правила именования
|
|||
|
|
|||
|
Ветка 2.x выделяет различные 'типы' классов (т.е. контроллер, модель и т.д.), используя для этого суффиксы. Директории внутри папок model / controller не влияют на имя класса.
|
|||
|
|
|||
|
В 3.0 от данного подхода отказались в пользу файловых соглашений Zend framework , в котором имя класса является путем к файлу, разделенному знаками подчеркивания вместо слэщей (т.е. `/some/class/file.php` становится `Some_Class_File`)
|
|||
|
|
|||
|
Смотри [описание соглашений](start.conventions) для получения подробной информации.
|
|||
|
|
|||
|
## Библиотека Input
|
|||
|
|
|||
|
Библиотека Input была исключена из 3.0, просто используйте `$_GET` и `$_POST`.
|
|||
|
|
|||
|
### Защита от XSS
|
|||
|
|
|||
|
Если Вам нужно обезопасить от XSS введенные пользователем данные, можете использовать [Security::xss_clean] :
|
|||
|
|
|||
|
$_POST['description'] = security::xss_clean($_POST['description']);
|
|||
|
|
|||
|
Также можно вызывать [Security::xss_clean] в качестве фильтра для объекта [Validate]:
|
|||
|
|
|||
|
$validation = new Validate($_POST);
|
|||
|
|
|||
|
$validate->filter('description', 'Security::xss_clean');
|
|||
|
|
|||
|
### POST & GET
|
|||
|
|
|||
|
Одной из важнейших возможностей библиотеки Input было то, что если Вы попытаетесь обратиться к значению одного из суперглобальных массивов и оно не задано, библиотека Input возвратит значение по умолчанию, которое Вы зададите, например:
|
|||
|
|
|||
|
$_GET = array();
|
|||
|
|
|||
|
// $id установлено в 1
|
|||
|
$id = Input::instance()->get('id', 1);
|
|||
|
|
|||
|
$_GET['id'] = 25;
|
|||
|
|
|||
|
// $id установлено в 25
|
|||
|
$id = Input::instance()->get('id', 1);
|
|||
|
|
|||
|
В 3.0 Вы можете сделать то же самое с помощью [Arr::get]:
|
|||
|
|
|||
|
$_GET = array();
|
|||
|
|
|||
|
// $id установлено в 1
|
|||
|
$id = Arr::get($_GET, 'id', 1);
|
|||
|
|
|||
|
$_GET['id'] = 42;
|
|||
|
|
|||
|
// $id установлено в 42
|
|||
|
$id = Arr::get($_GET, 'id', 1);
|
|||
|
|
|||
|
## Библиотека ORM
|
|||
|
|
|||
|
Произошло немало серьезных изменений в ORM после версии 2.3, вот список наиболее известных проблем после обновления.
|
|||
|
|
|||
|
### Свойства
|
|||
|
|
|||
|
Все свойства модели теперь начинаются со знака подчеркивания (_) и больше не доступны через метод `__get()`. Вместо этого Вы должны вызвать метод с именем свойства, но без подчеркивания.
|
|||
|
|
|||
|
Например, свойство, бывшее в 2.3 `loaded`, теперь называется `_loaded` и извне класса доступно через `$model->loaded()`.
|
|||
|
|
|||
|
### Связи
|
|||
|
|
|||
|
В 2.3, если Вы хотели пройтись по связанным с моделью объектам, надо было использовать:
|
|||
|
|
|||
|
foreach($model->{relation_name} as $relation)
|
|||
|
|
|||
|
Однако в новой системе это не будет работать. В версии 2.3 любой запрос с использованием библиотеки Database, был сгенерирован глобально, т.е. у Вас не получится создать одновременно два запроса. Вот пример:
|
|||
|
|
|||
|
# TODO: Нужен достойный пример!!!!
|
|||
|
|
|||
|
Этот запрос приведет к ошибке, т.к. второй запрос будет 'наследовать' условия первого, тем самым вызывая чертовщину.
|
|||
|
В v3.0 это было исправлено выделением каждого запроса в свою собственную 'песочницу', это приводит к тому, что некоторые вещи работают не так, как ожидается. Пример:
|
|||
|
|
|||
|
foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
|
|||
|
{
|
|||
|
echo $post->title;
|
|||
|
}
|
|||
|
|
|||
|
[!!] (Смотри описание нового синтаксиса запросов в [руководстве по Database](tutorials.databases))
|
|||
|
|
|||
|
В 2.3 данный запрос вернет объект-итератор всех записей пользователя с id=3, где поле `post_date` лежит в диапазоне последних 24 часов. Однако новая версия применит условия where к пользовательской модели и вернет объединенную через join модель `Model_Post`.
|
|||
|
|
|||
|
Для достижения нужного эффекта необходимо перераспределить порядок вызовов:
|
|||
|
|
|||
|
foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
|
|||
|
{
|
|||
|
echo $post->title;
|
|||
|
}
|
|||
|
|
|||
|
Аналогично со связями `has_one`:
|
|||
|
|
|||
|
// Неправильно
|
|||
|
$user = ORM::factory('post', 42)->author;
|
|||
|
// Правильно
|
|||
|
$user = ORM::factory('post', 42)->author->find();
|
|||
|
|
|||
|
### Связи много-ко-многим
|
|||
|
|
|||
|
В 2.3 Вы можете указать `has_and_belongs_to_many` тип связи. В 3.0 эта функциональность была переработана в связь *сквозное* `has_many`.
|
|||
|
|
|||
|
В моделях определяется связь `has_many` с другой моделью, но добавляется атрибут `'through' => 'table'` , где `'table'` - имя промежуточной таблицы. Например (в контексте области записи<>категории):
|
|||
|
|
|||
|
$_has_many = array
|
|||
|
(
|
|||
|
'categories' => array
|
|||
|
(
|
|||
|
'model' => 'category', // внешняя модель
|
|||
|
'through' => 'post_categories' // промежуточная модель
|
|||
|
),
|
|||
|
);
|
|||
|
|
|||
|
Если Вы настроили kohana с использованием табличных префиксов, то не стоит беспокоиться о явном указании префиксов в модели.
|
|||
|
|
|||
|
### Внешние ключи
|
|||
|
|
|||
|
Если Вы хотели переопределить внешний ключ в ORM ветки 2.x, надо было указать в принадлежащей (слабой) модели свойство `$foreign_keys` со значением имени внешнего ключа.
|
|||
|
|
|||
|
В 3.0 Вы определяется ключ `foreign_key` в самом объявлении связи, вот так:
|
|||
|
|
|||
|
Class Model_Post extends ORM
|
|||
|
{
|
|||
|
$_belongs_to = array
|
|||
|
(
|
|||
|
'author' => array
|
|||
|
(
|
|||
|
'model' => 'user',
|
|||
|
'foreign_key' => 'user_id',
|
|||
|
),
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
В данном примере мы настроили поле `user_id` для таблицы записей.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
В связях has_many атрибут `far_key` (дальний ключ) является полем в промежуточной таблице, которое связано с внешней (для текущей) таблицей, а внешний ключ является полем в промежуточной таблице, которое связано с "этой" таблицей.
|
|||
|
|
|||
|
Следуя вышесказанному, "posts" связаны много-ко-многим с "categories" через `posts_sections`.
|
|||
|
|
|||
|
| categories | posts_sections | posts |
|
|||
|
|------------|------------------|---------|
|
|||
|
| id | section_id | id |
|
|||
|
| name | post_id | title |
|
|||
|
| | | content |
|
|||
|
|
|||
|
Class Model_Post extends ORM
|
|||
|
{
|
|||
|
protected $_has_many = array(
|
|||
|
'sections' => array(
|
|||
|
'model' => 'category',
|
|||
|
'through' => 'posts_sections',
|
|||
|
'far_key' => 'section_id',
|
|||
|
),
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
Class Model_Category extends ORM
|
|||
|
{
|
|||
|
protected $_has_many = array (
|
|||
|
'posts' => array(
|
|||
|
'model' => 'post',
|
|||
|
'through' => 'posts_sections',
|
|||
|
'foreign_key' => 'section_id',
|
|||
|
),
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Очевидно, что настройки псевдонимов выглядят несколько неадекватными, но это хороший пример того, как работает схема внешний/дальний ключ.
|
|||
|
|
|||
|
### ORM Iterator
|
|||
|
|
|||
|
Стоит также отметить, что от класса `ORM_Iterator` отказались в пользу `Database_Result`.
|
|||
|
|
|||
|
Если надо получить массив объектов ORM с ключами из первичных ключей, надо вызвать [Database_Result::as_array], вот так:
|
|||
|
|
|||
|
$objects = ORM::factory('user')->find_all()->as_array('id');
|
|||
|
|
|||
|
Здесь `id` является первичным ключом таблицы.
|
|||
|
|
|||
|
## Библиотека Router
|
|||
|
|
|||
|
В версии 2 была библиотека Router, которая обрабатывала основной запрос приложения. Она позволяла определить базовые маршруты в файле `config/routes.php` и использовать собственные регулярные выражения для них, однако этого было недостаточно для описания чего-нибудь нестандартного.
|
|||
|
|
|||
|
## Маршруты
|
|||
|
|
|||
|
Система маршрутизации (теперь логичнее называть ее системой запросов) в версии 3.0 стала намного более гибкой. Маршруты объявляются в загрузочном файле (`application/bootstrap.php`) и скриптах init.php модулей (`modules/module/init.php`). (Стоит также отметить, что маршруты рассматриваются в том же порядке, в каком были определены).
|
|||
|
|
|||
|
Вместо объявления массива маршрутов теперь необходимо создавать новый объект [Route] на каждый маршрут. В отличие от ветки 2.x нет необходимости отделять один URI от другого. Вместо этого Вы определяете шаблон с использованием переменных для обозначения секций (таких как контроллер, метод, id).
|
|||
|
|
|||
|
К примеру, в предыдущей системе данное выражение:
|
|||
|
|
|||
|
$config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
|
|||
|
|
|||
|
подменит URI `controller/id/method` на `controller/method/id`. В 3.0 следует использовать:
|
|||
|
|
|||
|
Route::set('reversed','(<controller>(/<id>(/<action>)))')
|
|||
|
->defaults(array('controller' => 'posts', 'action' => 'index'));
|
|||
|
|
|||
|
[!!] Каждому маршруту назначается уникальное имя (в данном случае он называется `reversed`), причины описаны в [учебнике по URL](tutorials.urls).
|
|||
|
|
|||
|
Угловые скобки являются признаком динамических секций, которые будут сохранены в виде переменных. Круглые скобки обозначают необязательные участки. Если Вы хотите обрабатывать только адреса, начинающиеся с `admin`, используйте:
|
|||
|
|
|||
|
Rouse::set('admin', 'admin(/<controller>(/<id>(/<action>)))');
|
|||
|
|
|||
|
А если надо заставить пользователя указать контроллер:
|
|||
|
|
|||
|
Route::set('admin', 'admin/<controller>(/<id>(/<action>))');
|
|||
|
|
|||
|
Также Kohana не устанавливает сама значений по умолчанию. Если Вы хотите, чтобы Kohana установила метод 'index' как дефолтный, необходимо указать это в явном виде! Сделайте это с помощью [Route::defaults]. Когда надо указать регулярное выражение для отдельных сегментов uri, добавьте параметр - массив вида `segment => regex`. Например:
|
|||
|
|
|||
|
Route::set('reversed', '(<controller>(/<id>(/<action>)))', array('id' => '[a-z_]+'))
|
|||
|
->defaults(array('controller' => 'posts', 'action' => 'index'))
|
|||
|
|
|||
|
Значение сегмента `id` теперь должно состоять только из маленьких латинских букв и знака подчеркивания.
|
|||
|
|
|||
|
### Экшены
|
|||
|
|
|||
|
Еще один момент, который необходимо отметить - методы контроллера, которые должны быть доступны через url теперь называются "экшены" ("actions"), и начинаются с префикса 'action_'. В вышеуказанном примере, если пользователь введет `admin/posts/1/edit`, то экшен будет называться `edit`, но вызываемый метод контроллера получится `action_edit`. Подробности в [учебнике url](tutorials.urls).
|
|||
|
|
|||
|
## Сессии
|
|||
|
|
|||
|
Больше нет методов Session::set_flash(), Session::keep_flash() и Session::expire_flash(), вместо них используйте [Session::get_once].
|
|||
|
|
|||
|
## Хэлпер URL
|
|||
|
|
|||
|
Изменилось немногое - `url::redirect()` перемещен в `$this->request->redirect()` (при вызове из контроллера) / `Request::instance()->redirect()`
|
|||
|
|
|||
|
`url::current` заменен на `$this->request->uri()`
|
|||
|
|
|||
|
## Valid / Validation
|
|||
|
|
|||
|
Эти два класса были объединены в единый класс `Validate`.
|
|||
|
|
|||
|
Немного изменился синтаксис для валидации массивов:
|
|||
|
|
|||
|
$validate = new Validate($_POST);
|
|||
|
|
|||
|
// Применяем фильтр на все элементы массива
|
|||
|
$validate->filter(TRUE, 'trim');
|
|||
|
|
|||
|
// Для указания специфических правил используйте rule()
|
|||
|
$validate
|
|||
|
->rule('field', 'not_empty')
|
|||
|
->rule('field', 'matches', array('another_field'));
|
|||
|
|
|||
|
// Устанавливайте множество правил для одного поля через rules(), передавая массив вида rules => params в качестве второго параметра
|
|||
|
$validate->rules('field', array(
|
|||
|
'not_empty' => NULL,
|
|||
|
'matches' => array('another_field')
|
|||
|
));
|
|||
|
|
|||
|
Стандартное правило 'required' было переименовано в 'not_empty' для ясности.
|
|||
|
|
|||
|
## Библиотека View
|
|||
|
|
|||
|
Сделано несколько незначительных изменений, которые стоит отметить.
|
|||
|
|
|||
|
В 2.3 представления формировались в зоне видимости контроллера, что позволяло в представлении использовать `$this` как ссылку на контроллер, и в 3.0 теперь по-другому. Представления теперь генерируются в пустом окружении. Если необходимо использовать `$this` в шаблоне, создайте ссылку с помощью [View::bind]: `$view->bind('this', $this)`.
|
|||
|
|
|||
|
Тем не менее, стоит отметить, что это *очень* плохая привычка, т.к. повышает сцепление представления с контроллером и усложняет повторное использование. Рекомендуется подключать необходимые переменные таким образом:
|
|||
|
|
|||
|
$view = View::factory('my/view');
|
|||
|
|
|||
|
$view->variable = $this->property;
|
|||
|
|
|||
|
// ИЛИ если нравится запись по цепочке
|
|||
|
|
|||
|
$view
|
|||
|
->set('variable', $this->property)
|
|||
|
->set('another_variable', 42);
|
|||
|
|
|||
|
// НЕ рекомендуется
|
|||
|
$view->bind('this', $this);
|
|||
|
|
|||
|
Так как представление формируется в пустом окружении, `Controller::_kohana_load_view` теперь избыточно. Если надо изменить представление до его генерации (например, добавить меню сайта), используйте [Controller::after].
|
|||
|
|
|||
|
<?php
|
|||
|
|
|||
|
Class Controller_Hello extends Controller_Template
|
|||
|
{
|
|||
|
function after()
|
|||
|
{
|
|||
|
$this->template->menu = '...';
|
|||
|
|
|||
|
return parent::after();
|
|||
|
}
|
|||
|
}
|