CakePHP deleteAll для отношения «многие ко многим» не удаляет записи соединения

Я использую CakePHP v3.17 с Postgres 9.4.

Я пытаюсь заставить $this->SomeTable->deleteAll([...]) также удалить записи в таблице соединений.

Изображение шинной системы с таблицей для Stops и таблицей для Routes. Остановки связаны со многими маршрутами (поскольку на каждом из них может останавливаться несколько автобусных маршрутов), а маршруты, очевидно, связаны со многими остановками.

RoutesTable.php:

$this->belongsToMany('Stops');

Таблица остановок.php:

$this->belongsToMany('Routes');

Вот логика удаления, которую я хочу использовать, но НЕ работает, потому что записи в таблице соединений остались:

    $stopsTable = TableRegistry::get('Stops');
    $stopsTable->deleteAll(['agency_id' => $agency->id]);

    $routesTable = TableRegistry::get('Routes');
    $routesTable->deleteAll(['agency_id' => $agency->id]);

Вот логика, которая ДЕЙСТВИТЕЛЬНО работает, но неэффективно, потому что приходится перебирать каждую остановку:

    $stopsTable = TableRegistry::get('Stops');
    foreach ($agency->stops as $stop) {
        $stopsTable->delete($stop);
    }
    $routesTable = TableRegistry::get('Routes');
    $routesTable->deleteAll(['agency_id' => $agency->id]);

Как лучше/правильнее это сделать?

Вот похожий вопрос, но для v2.x, поэтому здесь он не обязательно актуален.


person emersonthis    schedule 19.04.2016    source источник
comment
Есть ли модульный тест в ядре CakePHP, который охватывает ваш случай и обеспечивает удаление записей таблицы соединений? Если нет, сообщите об этом как о проблеме на Github. Вы уверены, что остались только записи таблиц соединений, а не записи таблиц соединений? Попробуйте установить 'dependent' => true, для вашего помощника, я не уверен, что сейчас это верно по умолчанию.   -  person floriank    schedule 20.04.2016
comment
Привет, @burzum. Я пробовал это с зависимым =› true, и да, я уверен, что остались только записи таблицы соединений, потому что я тестирую пустые таблицы... Итак, после теста остановки и маршруты пустой, но route_stops по-прежнему заполнен. Я не уверен в вопросе модульного теста...   -  person emersonthis    schedule 20.04.2016
comment
Вместо этого вам может потребоваться включить каскадное удаление. просто иждивенца.   -  person Greg Schmidt    schedule 20.04.2016
comment
Я не думаю, что массовые удаления работают с каскадными удалениями из-за того, что события до и после не применяются к массовым удалениям. В документации сказано, что нужно загружать записи и удалять их отдельно.   -  person chrisShick    schedule 20.04.2016
comment
@chrisShick Можете ли вы поделиться ссылкой на то, где это говорится в документах?   -  person emersonthis    schedule 20.04.2016
comment
@chrisShick, ты имеешь в виду ссылку Грега? Документ немного сбивает с толку в этом вопросе: по умолчанию объекты в связанных таблицах удаляются с помощью Cake\ORM\Table::deleteAll(). Вроде должно работать...   -  person emersonthis    schedule 20.04.2016
comment
Да, @emersonthis!! Я прошу прощения!! Ссылка Грега относится к документации того, что я упомянул. Да, связанные записи таблицы удаляются с помощью deleteAll. Но deleteAll не запускает события до и после удаления. Поэтому, если вы удалите все на основной модели, связанные не будут удалены. Если вы используете обычное удаление, связанные модели будут удалены с помощью команды deleteAll.   -  person chrisShick    schedule 20.04.2016
comment
@chrisShick Меня смущает это предложение: если вы используете обычное удаление, связанные модели будут удалены с помощью команды deleteAll. Разве delete() и deleteAll() не две разные функции?   -  person emersonthis    schedule 21.04.2016
comment
Да @emersonthis. Давайте используем ваши модели в качестве примера: если я скажу $stopsTable-›delete($stop), то (если зависимый ключ истинен) он удалит ассоциации с помощью функции deleteAll() (внутри обратного вызова beforeDelete), а затем удалит остановка. Если вы используете $stopsTable-›deleteAll(), ассоциации вообще не будут удалены, потому что обратный вызов beforeDelete не сработает.   -  person chrisShick    schedule 21.04.2016
comment
@chrisShick Аааа. delete() вызывает deleteAll() в обратном вызове. Я понимаю, что вы сейчас говорите. Спасибо   -  person emersonthis    schedule 21.04.2016
comment
Точно!! Однако deleteAll не запускает никаких обратных вызовов.   -  person chrisShick    schedule 21.04.2016
comment
^ ...вызывает deleteAll() ассоциации, связанные с этой сущностью, которая delete() обрабатывается   -  person emersonthis    schedule 21.04.2016
comment
@chrisShick Похоже, что на самом деле нет способа удалить все записи соединения без зацикливания и delete()ing каждой сущности.   -  person emersonthis    schedule 21.04.2016


Ответы (1)


Нет способа сделать то, что вам нужно. Но я бы, безусловно, предложил сделать то, что вы уже сказали в своем вступительном сообщении, и обернуть его транзакцией (обратите внимание, что это не проверено, но должно работать):

$stopsTable->connection()->transactional(function () use ($stopsTable, $stops) {
    foreach ($stops as $stop) {
        $stopsTable->delete($stop);
    }
});
person chrisShick    schedule 20.04.2016
comment
Спасибо. Вы уверены, что ->transactional() необходим? Я видел в документах, что все удаления происходят внутри транзакции, но я не был уверен, что это значит. - person emersonthis; 21.04.2016
comment
Документы означают, что удаляемый основной объект и его зависимые ассоциации удаляются в транзакции. - person chrisShick; 21.04.2016