Page Contents
- Табличные объекты
Табличные объекты получают доступ к коллекции сущностей, хранящихся в конкретной
таблице. Для взаимодействия с каждой таблицей, в вашем приложение, необходимо
использовать соответствующий класс Table
. Если вам не нужно настраивать
поведение данной таблицы, можно полностью положиться на CakePHP, который
сгенерирует необходимый класс.
Перед попыткой использовать табличные объекты и ORM, вы должны убедиться в правильной конфигурации соединения с базой данных.
Для начала создайте класс Table
. Подобные классы располагаются в
src/Model/Table. Классы Table
это коллекция типовых моделей реляционной
базы данных, представляющие основной интерфейс для взаимодействия с вашей базой
данных в CakePHP ORM. Самый простой класс таблицы будет выглядеть следующим
образом:
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
}
Обратите внимание, мы не сообщили ORM какую таблицу использует наш класс. При
соблюдение соглашения о базе данных в
этом нет необходимости. В приведённом выше примере будет использоваться таблица
с именем articles
. Если, например, имя таблицы будет blog_posts
, то
класс, отвечающий за взаимодействие с ней, будет именоваться BlogPostsTable
.
Вы можете указать другое имя таблицы, воспользовавшись методом setTable()
:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->setTable('my_table');
// До 3.4.0
$this->table('my_table');
}
}
При явном указание таблицы, ORM будет продолжать следовать соглашениям, ожидая
первичного ключа с именем id
. Если вам нужно изменить и это, можете
воспользоваться методом setPrimaryKey()
:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->setPrimaryKey('my_id');
// До 3.4.0
$this->primaryKey('my_id');
}
}
По умолчанию, табличные объекты используют класс Entity
, основываясь на
соглашение об именование моделей. Например, если ваш класс таблицы называется
ArticlesTable
, то класс Entity
должен называться Article
. Если класс
таблицы с именем PurchaseOrdersTable
, то Entity
класс PurchaseOrder
.
Если, однако, вы хотите использовать имя класса Entity
идущее вразрез с
соглашением, вы можете воспользоваться методом setEntityClass()
для смены
имени:
class PurchaseOrdersTable extends Table
{
public function initialize(array $config)
{
$this->setEntityClass('App\Model\Entity\PO');
// До 3.4.0
$this->entityClass('App\Model\Entity\PO');
}
}
Как видно из приведенных выше примеров, табличные объекты имеют метод
initialize()
, который вызывается после метода конструктора. Рекомендуется
использовать этот метод для выполнения логики инициализации, вместо
переопределения конструктора.
Для взаимодействия с таблицами вашей базы данных, необходимо получить экземпляр
таблицы. Этого можно добиться, используя класс TableRegistry
:
// В контроллере и методе.
use Cake\ORM\TableRegistry;
// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');
Класс TableRegistry
предоставляет различные зависимости для построения
таблицы и ведёт реестр всех созданных экземпляров таблиц, что упрощает
построение отношений и настройку ORM. Смотрите раздел Использование TableRegistry,
для получения дополнительной информации.
Если ваш класс таблицы находится в плагине, необходимо использовать корректное имя класса. Не соблюдение этого правила может привезти к ошибкам. Чтобы этого не допустить, следуйте следующим примерам:
// Таблица плагина
// Prior to 3.6 use TableRegistry::get('PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('PluginName.Articles');
// Таблица плагина с префиксом vendor'а
// Prior to 3.6 use TableRegistry::get('VendorName/PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('VendorName/PluginName.Articles');
Как можно видеть выше, табличные объекты запускают несколько событий. События полезны, если вы хотите подключиться к ORM и добавить логику без подклассов или методов переопределения. Слушатели событий могут быть определены в классах таблиц и поведений. Вы также можете использовать диспетчер событий таблицы для привязки прослушивателей.
При использовании методов обратного вызова в методе initialize()
, они будут
активированы слушателями до срабатывания методов обратного вызова таблицы.
Аналогичная ситуация с контроллерами и компонентами.
Чтобы добавить прослушиватель события в класс Table
или Behavior
, просто
реализуйте сигнатуры метода как описано ниже. Для ознакомления с дополнительной
информацией, по использованию подсистемы событий, см. Система событий.
Model.initialize
Model.beforeMarshal
Model.beforeFind
Model.buildValidator
Model.buildRules
Model.beforeRules
Model.afterRules
Model.beforeSave
Model.afterSave
Model.afterSaveCommit
Model.beforeDelete
Model.afterDelete
Model.afterDeleteCommit
Событие Model.initialize
запускается после вызова методов конструктора и
инициализации. Классы Table
, по умолчанию, не слушают это событие, используя
вместо этого метод initialize
.
Для прослушивания и ответа на событие Model.initialize
, вы можете
создать класс слушателя, реализующий интерфейс EventListenerInterface
:
use Cake\Event\EventListenerInterface;
class ModelInitializeListener implements EventListenerInterface
{
public function implementedEvents()
{
return array(
'Model.initialize' => 'initializeEvent',
);
}
public function initializeEvent($event)
{
$table = $event->getSubject();
// сделаем что-нибудь здесь
}
}
и присоединить слушателя к глобальному диспетчеру EventManager
:
use Cake\Event\EventManager;
$listener = new ModelInitializeListener();
EventManager::instance()->attach($listener);
Это вызовет вызов initializeEvent
любого класса Table
.
Событие Model.beforeMarshal
запускается до преобразования данных запроса в
объекты. Дополнительная информация, об модификации данных запроса перед преобразованием в объекты,
доступна в документации.
Событие Model.beforeFind
запускается перед каждой операцией поиска. Остановив
событие и предоставив возвращаемое значение, вы можете полностью обойти операцию
поиска. Любые изменения, сделанные для экземпляра $query
, будут сохранены для
остальной части поиска. Параметр $primary
служит признаком корневого
запроса, либо связанного с ним запроса. Все ассоциации, участвующие в запросе,
будут запускать событие Model.beforeFind
. Для ассоциаций, которые используют
объединения, будет предоставлен mock-запрос. В вашем слушателе событий вы
можете установить дополнительные поля, условия, или объединения. Эти
параметры/функции будут скопированы в корневой запрос.
Вы можете использовать Model.beforeFind
для ограничения операций поиска, на
основе прав пользователя, или принимать решение о кэширование, на основе текущей
нагрузки.
В предыдущих версиях CakePHP присутствовало событие afterFind
, заменённое с
помощью изменения результатов с помощью Map/Reduce и
конструкторов объекта.
Событие Model.buildValidator
срабатывает после вызова именованных правил
проверки $name
, например, validationDefault
. Можно использовать эту
функцию для добавления методов проверки.
Событие Model.buildRules
срабатывает после создания экземпляра правил
проверки и после вызова табличного метода buildRules()
.
Событие Model.beforeRules
вызывается до применения правил проверки.
Остановив событие, вы можете завершить проверку и вернуть результат применения
правил.
Событие Model.afterRules
вызывается после применения правил проверки.
Остановив это событие, вы можете вернуть итоговый результат проверки правил.
Событие Model.beforeSave
вызывается перед каждым сохранением объекта.
Остановка данной функции прервёт операцию сохранения, вернув результат события.
Остановка событий описано в соответствующем разделе
книги.
Событие Model.afterSave
запускается после сохранения объекта.
Событие Model.afterSaveCommit
запускается после транзакции, в которой прошла
операция сохранения. Оно также срабатывает для неатомарных сохранений, где
операции с базой данных неявно совершены. Событие запускается только для
первичной таблицы, для которой непосредственно вызывается метод save()
.
Событие не запускается, если транзакция началась до вызова сохранения.
Событие запускается перед удалением объекта. Остановка этого события прервёт операцию удаления. Когда событие остановлено, возвратится её результат выполнения. Остановка событий описано в соответствующем разделе книги.
Событие Model.afterDelete
запускается после удаления объекта.
Событие Model.afterDeleteCommit
запускается после транзакции, в которой прошла
операция удаления. Оно также срабатывает для неатомарных удаления, где операции с
базой данных неявно совершены. Событие запускается только для первичной таблицы,
для которой непосредственно вызывается метод delete()
. Событие не
запускается, если транзакция началась до вызова удаления.
Поведения обеспечивают простой способ использования повторяемых фрагментов кода, связанных с табличными классами. Возможно, вам интересно почему поведения это обычные классы, а не трейты. Основная причина в прослушивателях событий. Хотя трейты позволяют использовать повторно фрагменты логики, они усложнят связанные события.
Для добавления поведения в таблицу, необходимо вызвать метод addBehavior()
.
Как правило, наилучшим местом для этого является метод initialize()
:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
}
Как и в случае с ассоциациями, вы можете использовать синтаксис плагина для указания дополнительных параметров конфигурации:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp', [
'events' => [
'Model.beforeSave' => [
'created_at' => 'new',
'modified_at' => 'always'
]
]
]);
}
}
Вы можете узнать больше о поведениях, включая тех, что входят в ядро CakePHP, из соответствующего раздела о поведениях.
По умолчанию, все экземпляры таблицы используют default
соединение с базой
данных. Если ваше приложение использует несколько подключений к базе данных, вам
необходимо указать соответствующее соединение, воспользовавшись методом
defaultConnectionName()
:
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public static function defaultConnectionName() {
return 'replica_db';
}
}
Примечание
Метод defaultConnectionName()
должен быть статическим.
Как мы видели ранее, класс TableRegistry
предоставляет простой способ
использования реестра для доступа к экземплярам таблиц ваших приложений. Он
также предоставляет несколько других полезных функций.
При загрузке таблиц из реестра, вы можете изменить их зависимости или
использовать mock-объекты, предоставляя массив $options
:
$articles = TableRegistry::getTableLocator()->get('Articles', [
'className' => 'App\Custom\ArticlesTable',
'table' => 'my_articles',
'connection' => $connectionObject,
'schema' => $schemaObject,
'entityClass' => 'Custom\EntityClass',
'eventManager' => $eventManager,
'behaviors' => $behaviorRegistry
]);
Обратите внимание на параметры конфигурации connection
и schema
. Они
принимают не строковые значения, а объекты. Соединение будет принимать объект
Cake\Database\Connection
и схему Cake\Database\Schema\Collection
.
Примечание
Если ваша таблица также выполняет дополнительные настройки, в своём методе
initialize()
, то эти значения будут перезаписывать те, что были внесены
в реестр.
Вы также можете предварительно настроить реестр с помощью метода config()
.
Данные конфигурации сохраняются для каждого псевдонима и могут быть
переопределены методом объекта initialize()
:
TableRegistry::config('Users', ['table' => 'my_users']);
Примечание
Настройка таблицы возможна только до или во время первого доступа к псевдониму. Выполнение этого после заполнения реестра не приведёт к желаемому результату.
Примечание
Статический API CakeORMTableRegistry устарел в CakePHP 3.6.0. Вместо этого используйте локатор таблицы.
Во время тестов вам может понадобиться очистить реестр. Это часто полезно, когда вы используете mock-объекты или изменяете зависимости таблиц:
TableRegistry::clear();
Если вы не соблюдали соглашения, вероятней всего CakePHP не сможет найти ваши
классы Table
и Entity
. Чтобы исправить это, вы можете установить
пространство имён с помощью метода Cake\Core\Configure::write
. Например:
/src
/App
/My
/Namespace
/Model
/Entity
/Table
Для схемы выше будет следующая настройка:
Cake\Core\Configure::write('App.namespace', 'App\My\Namespace');