Итак, у меня есть следующая (упрощенная) модель
+------------------+
| project_statuses |
+------------------+
+---------------------+ +----| id |
| projects | | | name |
+---------------------+ | +------------------+
| id | | +---------+
| name | | | clients |
| project_statuses_id |----+ +---------+
| client_id |---------| id |
+---------------------+ | name |
| +---------+
+------------------+ |
| clients_projects |------+
+------------------+
| id |
| client_id |
| project_id |
+------------------+
где проект принадлежит многим клиентам, и у клиента может быть много проектов, но только один клиент (Projects.client_id
) может взять на себя ответственность за проект. Статус проекта здесь для сравнения.
Итак, ассоциации в моих ProjectsTable.php
, ClientsTabe.php
и ProjectStatusesTable.php
выглядят так
// In ProjectsTable.php
$this->belongsTo('ProjectStatuses', [
'foreignKey' => 'project_status_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Clients', [
'foreignKey' => 'client_id',
'joinType' => 'INNER'
]);
$this->belongsToMany('Clients', [
'foreignKey' => 'project_id',
'targetForeignKey' => 'client_id',
'joinTable' => 'clients_projects'
]);
// In ClientsTabe.php
$this->hasMany('Projects', [
'foreignKey' => 'client_id'
]);
$this->belongsToMany('Projects', [
'foreignKey' => 'client_id',
'targetForeignKey' => 'project_id',
'joinTable' => 'clients_projects'
]);
// In ProjectStatusesTable.php
$this->hasMany('Projects', [
'foreignKey' => 'project_status_id'
]);
Теперь на моей странице projects/index
я хотел бы иметь таблицу, показывающую идентификатор проекта, имя, статус и ответственных. Итак, я подумал о чем-то вроде этого
// In ProjectsController.php
$this->Projects->find()->select(['Projects.id','Projects.name'])
->contain(['ProjectStatuses' => [
'fields' => [
'ProjectStatuses.name',
]]])
->contain(['Clients' => [
'fields' => [
'Clients.name',
]]]);
Но выбирается только ProjectStatuses.name
, для Clients.name
он выдает ошибку You are required to select the "ClientsProjects.project_id" field(s)
, сообщающую мне, что просматривает ассоциацию belongsToMany
, а не ассоциацию belongsTo
.
Фактически, если я просто напишу ->contain('Clients')
вместо указания поля Clients.name
, он отправит следующие запросы
SELECT
Projects.id AS `Projects__id`,
Projects.name AS `Projects__name`,
ProjectStatuses.name AS `ProjectStatuses__name`
FROM
projects Projects
INNER JOIN project_statuses ProjectStatuses ON ProjectStatuses.id = (Projects.project_status_id)
SELECT
ClientsProjects.id AS `Clients_CJoin__id`,
ClientsProjects.client_id AS `Clients_CJoin__client_id`,
ClientsProjects.project_id AS `Clients_CJoin__project_id`,
Clients.id AS `Clients__id`,
Clients.name AS `Clients__name`,
FROM
clients Clients
INNER JOIN clients_projects ClientsProjects ON Clients.id = (ClientsProjects.client_id)
WHERE
ClientsProjects.project_id in (4)
Как я могу указать объекту запроса получить Clients.name
через Projects.client_id
так же, как я получаю ProjectStatuses.name
через Projects.project_statuses_id
, вместо того, чтобы проходить мимо таблицы ClientProjects
?
В таблице "Проекты" вы создали две ассоциации для клиентов, и наоборот. Это не работает (как сказал Горец: «Может быть только один») и, вероятно, является источником всех ваших проблем.
Вам нужно будет изменить одну из этих ассоциаций в каждой таблице. Может быть, проект должен belongsToMany
Clients, но только belongTo
один ResponsibleClient, а клиент должен belongsToMany
Projects и hasMany
ReponsibleProjects?
Это сработало как шарм :) Думаю, теперь я понял важность псевдонимов, большое вам спасибо!