Как создать отношение "Принадлежит ко многим" в Cake PHP 3 - PullRequest
1 голос
/ 05 мая 2020

У меня есть база данных с таблицей projekte (немецкое слово для проектов). Некоторые проекты имеют отношение друг к другу. Итак, я хотел бы иметь отношение BTM.

Я создал joinTable «projektverbindungen» со следующими полями:

projekt_id
nebenprojekt_id

Я нашел здесь похожий вопрос: Связь между таблица и сама , и я попробовал ответить ndm, но безуспешно.

Вот моя ProjekteTable. php

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

    $this->setTable('projekte');
    $this->setDisplayField('name');
    $this->setPrimaryKey('id');

    $this->hasOne('Projekteigenschaften', [
      'foreignKey' => 'projekt_id',
      'dependent' => true,
    ]);
    $this->belongsToMany('Projekte', [
      'foreignKey' => 'projekt_id',
      'targetForeignKey' => 'nebenprojekt_id',
      'joinTable' => 'projektverbindungen',
    ]);
  }
}

Вот мой шаблон (add.ctp )

<?php
echo $this->Form->create($projekt);
echo $this->Form->control('name', ['class' => 'form-control']);
echo $this->Form->control('projekteigenschaft.projektverantwortlich');
echo $this->Form->control('projekteigenschaft.beschreibung');
echo $this->Form->control('projekte._ids', ['options' => $projekte, 'multiple' => true]);
echo $this->Form->button(__('Submit'));
echo $this->Form->end();
?>

Первый шаг, сохранение проекта со связанным проектом, работает должным образом. id созданного проекта был сохранен как projektverbindungen.projekt_id, а id связанного проекта как projektverbindungen.nebenprojekt_id.

Когда я запрашиваю проект без связи с другими проектами, например:

$projekt = $this->Projekte->get($id, [
  'contain' => ['Projekteigenschaften']
]);

запрос выглядит так:

SELECT Projekte.id AS `Projekte__id`, Projekte.name AS `Projekte__name`, Projekteigenschaften.id AS `Projekteigenschaften__id`, Projekteigenschaften.projektverantwortlich AS `Projekteigenschaften__projektverantwortlich`, Projekteigenschaften.beschreibung AS `Projekteigenschaften__beschreibung`, Projekteigenschaften.projekt_id AS `Projekteigenschaften__projekt_id` FROM projekte Projekte LEFT JOIN projekteigenschaften Projekteigenschaften ON Projekte.id = (Projekteigenschaften.projekt_id) WHERE (Projekte.id = :c0 AND (Projekte.deleted) IS NULL)

А результат отладки выглядит так:

"id": "6862279f-8134-401f-86ff-9278a3bfa5c3",
"name": "My Project",
"projekteigenschaft": {
    "id": "89d9e241-e700-4c31-9266-ee5717f2a0aa",
    "projektverantwortlich": "Blisginnis, Ralf",
    "beschreibung": ""
}

Все работает нормально.

Но когда я добавляю проекты, которые будут содержать вот так:

$projekt = $this->Projekte->get($id, [
  'contain' => ['Projekteigenschaften', 'Projekte']
]);

Запрос выглядит так же, как указано выше, но сущность выглядит немного иначе:

"Projekteigenschaften": {
    "id": "89d9e241-e700-4c31-9266-ee5717f2a0aa",
    "projektverantwortlich": "Blisginnis, Ralf",
    "beschreibung": ""
}

Projekteigenschaften, похоже, больше не быть отношением hasOne, и "Projekte" полностью игнорируется.

Кто-нибудь знает, что я сделал не так? Или я должен предпочесть другой способ сделать это?

редактировать после комментария ndm

Я попытался определить отношения следующим образом:

$this->belongsToMany('Projektverbindungen', [
  'class' => 'Projekte',
  'foreignKey' => 'projekt_id',
  'targetForeignKey' => 'nebenprojekt_id',
  'joinTable' => 'projektverbindungen',
]);

и изменил шаблон add.ctp следующим образом:

echo $this->Form->control('projektverbindungen._ids', ['options' => $projekte, 'multiple' => true]);

Но тогда связь не сохраняется.

Я также попытался переименовать joinTable в projekte_projekte. Похоже, это не имело никакого значения.

Затем я попытался использовать сквозную опцию, но результаты были еще хуже. Поэтому я продолжил попытки найти решение с помощью описанного выше метода.

2-е редактирование

projekverbindungen ist доступно в Projekt. php:

protected $_accessible = [
  'name' => true,
  'projekteigenschaft' => true,
  'projekte' => true,
  'projektverbindungen' => true,
];

Отладка requestData:

[
  'name' => 'My Project',
  'projekteigenschaft' => [
    'projektverantwortlich' => 'John Doe',
    'beschreibung' => '',
  'projektverbindungen' => [
    '_ids' => [
      (int) 0 => '809031f2-4ecd-4dfb-82d5-2c911286dd21'
    ]
  ]
]

Отладка объекта после исправления:

object(App\Model\Entity\Projekt) {

  'name' => 'My Project',
  'projekteigenschaft' => object(App\Model\Entity\Projekteigenschaft) {

    'projektverantwortlich' => 'John Doe',
    'beschreibung' => '',
    '[new]' => true,
    '[accessible]' => [
        'projektverantwortlich' => true,
        'beschreibung' => true,
        'projekt_id' => true,
        'projekt' => true
    ],
    '[dirty]' => [
        'projektverantwortlich' => true,
        'beschreibung' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[hasErrors]' => false,
    '[errors]' => [],
    '[invalid]' => [],
    '[repository]' => 'Projekteigenschaften'

  },
  'projektverbindungen' => [],
  '[new]' => true,
  '[accessible]' => [
    'name' => true,
    'projekteigenschaft' => true,
    'projekte' => true,
    'projektverbindungen' => true
  ],
  '[dirty]' => [
    'name' => true,
    'projekteigenschaft' => true,
    'projektverbindungen' => true
  ],
  '[original]' => [],
  '[virtual]' => [],
  '[hasErrors]' => false,
  '[errors]' => [],
  '[invalid]' => [],
  '[repository]' => 'Projekte'
}

3-е изменение

В моем bootstrap . php у меня это:

Inflector::rules('plural', [
  '/^(projekt)$/i' => '\1e',
  '/^(projekteigenschaft|projektverbindung)$/i' => '\1en',
]);
Inflector::rules('singular', [
  '/^(projekt)e$/i' => '\1',
  '/^(projekteigenschaft|projektverbindung)en$/i' => '\1',
]);

После вашей рекомендации я дополнительно добавил propertyName к определению ассоциации:

$this->belongsToMany('Projektverbindungen', [
  'class' => 'Projekte',
  'propertyName' => 'Projektverbindungen',
  'foreignKey' => 'projekt_id',
  'targetForeignKey' => 'nebenprojekt_id',
  'joinTable' => 'projektverbindungen',
]);

После этого исправленная сущность выглядит как это:

object(App\Model\Entity\Projekt) {

  'name' => 'My Project',
  'projekteigenschaft' => object(App\Model\Entity\Projekteigenschaft) {

    'projektverantwortlich' => 'John Doe',
    'beschreibung' => '',
    '[new]' => true,
    '[accessible]' => [
        'projektverantwortlich' => true,
        'beschreibung' => true,
        'projekt_id' => true,
        'projekt' => true
    ],
    '[dirty]' => [
        'projektverantwortlich' => true,
        'beschreibung' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[hasErrors]' => false,
    '[errors]' => [],
    '[invalid]' => [],
    '[repository]' => 'Projekteigenschaften'

  },
  'projektverbindungen' => [
    '_ids' => [
        (int) 0 => '1e28a3d1-c914-44be-b821-0e87d69cd95f'
    ]
  ],
  '[new]' => true,
  '[accessible]' => [
    'name' => true,
    'projekteigenschaft' => true,
    'projekte' => true,
    'projektverbindungen' => true
  ],
  '[dirty]' => [
    'name' => true,
    'projekteigenschaft' => true,
    'projektverbindungen' => true
  ],
  '[original]' => [],
  '[virtual]' => [],
  '[hasErrors]' => false,
  '[errors]' => [],
  '[invalid]' => [],
  '[repository]' => 'Projekte'
}

Но все еще нет новой записи в таблице "projektverbindungen"

1 Ответ

1 голос
/ 05 мая 2020

Последнее предложение ndm помогло. Теперь все работает как положено. Большое спасибо!

Вот правильная настройка:

ProjekteTable. php

  $this->belongsToMany('Nebenprojekte', [
    'className' => 'Projekte',
    'foreignKey' => 'projekt_id',
    'targetForeignKey' => 'nebenprojekt_id',
    'joinTable' => 'projektverbindungen',
  ]);

Здесь я использовал свойство class вместо className , это была самая большая проблема. Это действительно смущает, потому что в Поваренной книге правильное название этого свойства: https://book.cakephp.org/3/en/orm/associations.html#belongstomany -associations

Тем не менее, возможно, кто-то другой совершает ту же ошибку, и эта цепочка может помочь.

Во-вторых, нельзя использовать имя объединяемого объекта в качестве имени ассоциации.

Остальное просто ...

Делаем ассоциацию доступной в Класс сущности (Projekt. php):

protected $_accessible = [
  'name' => true,
  'projekteigenschaft' => true,
  'nebenprojekte' => true,
];

ProjekteController. php («добавить» и «изменить»):

public function add()
{
    $projekt = $this->Projekte->newEntity();
    if ($this->request->is('post')) {
        $projekt = $this->Projekte->patchEntity($projekt, $this->request->getData());
        if ($this->Projekte->save($projekt)) {
            $this->Flash->success(__('flash message'));

            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('error message'));
    }

    $projekte = $this->Projekte->find('list');
    $this->set(compact('projekt', 'projekte'));
}

public function edit($id = null)
{
    $projekt = $this->Projekte->get($id, [
      'contain' => ['Projekteigenschaften', 'Nebenprojekte']
    ]);

    if ($this->request->is(['patch', 'post', 'put'])) {
        $projekt = $this->Projekte->patchEntity($projekt, $this->request->getData());
        if ($this->Projekte->save($projekt)) {
            $this->Flash->success(__('flash message'));

            return $this->redirect(['action' => 'index']);
        }
        $this->Flash->error(__('error message'));
    }

    $projekte = $this->Projekte->find('list')->where(['id !=' => $id]);
    $this->set(compact('projekt', 'projekte'));
}

В таких шаблонах, как add.ctp или edit.ctp:

echo $this->Form->control('nebenprojekte._ids', ['options' => $projekte, 'multiple' => true]);

Если вы используете другой язык, кроме английского sh, не забудьте установить правильные правила перегиба. bootstrap. php:

Inflector::rules('plural', [
  '/^(projekt|nebenprojekt)$/i' => '\1e',
  '/^(projekteigenschaft)$/i' => '\1en',
]);
Inflector::rules('singular', [
  '/^(projekt|nebenprojekt)e$/i' => '\1',
  '/^(projekteigenschaft)en$/i' => '\1',
]);
...