2016-08-23 4 views
3

В CakePHP 3, предположит, что я хочу, чтобы удалить все отделов, которые имеют некоторые сотрудников назвали «Джон» (отделов имеют много сотрудников, сотрудники принадлежат отделу)CakePHP 3 удаления фильтрации ассоциированных данные

Самый простой подход фильтровать все эти отделы, а также удалять их по одному в цикле Еогеасп:

$departments = $this->Departments->find() 
    ->matching('Employees', function ($q) { 
     return $q->where(['Employees.name' => 'John']); 
    }) 
    ->all(); 

foreach ($departments as $department) { 
    $this->Departments->delete($department); 
} 

Это приведет к одному SELECT, запрос, плюс один запрос на удаление для каждой записи. Я бы предпочел, чтобы только один запрос отправлялся и выполнялся базой данных. Что-то вроде этого:

DELETE Departments 
FROM departments Departments 
INNER JOIN employees Employees ON 
    Employees.department_id = Departments.id 
    AND Employees.name = 'John'; 

Читая документы, которые я нахожу три способа удаления записей:

Есть ли элегантный способ получить мой запрос с помощью CakePHP 3 и ORM?

ответ

0

DeleteAll просто обернуть вокруг

$this->Departments->query() 
    ->delete() 
    ->where([...]) 
    ->execute(); 

Так я хоть что, подобным образом, можно было бы сделать:

$departments = $this->Departments->query() 
    ->delete() 
    ->matching('Employees', function ($q) { 
     return $q->where(['Employees.name' => 'John']); 
    }) 
    ->execute(); 

, но в этом случае вы заметите, что условие соответствия игнорируется. Я вижу, что есть открытая ticket о том, что

Если вы хотите использовать только два запросы, которые могли бы сделать запрос, чтобы получить все идентификаторы и удалить все во втором запросе:

$departments_ids = $this->Departments->find() 
    ->select(['id']) 
    ->matching('Employees', function ($q) { 
     return $q->where(['Employees.name' => 'John']); 
    }) 
    ->toArray(); 

$this->Departments->deleteAll(['id' => $departments_ids ]); 

, что приводит к в чем-то вроде:

DELETE FROM departments WHERE id IN 
(
    // id list here 
) 

Другие решения могут быть преобразованы в ваше внутреннее соединение в подзапросе. Фактически вы хотите удалить все отделы, в которых есть почти сотрудник по имени Джон.

$employees = $this->Departments->Employees->find() 
    ->select(['department_id']) 
    ->distinct() 
    ->where(['Employees.name' => 'John']); 

$departments = $this->Departments->query() 
    ->delete() 
    ->where(['id IN' => $employees]) 
    ->execute(); 
+0

Спасибо @arilia. Собственно, чтобы получить этот запрос, вы должны изменить '' id '=> $ department' '' id IN' => $ department', в противном случае вы получите равное условие «=». С другой стороны, этот запрос возвращает ошибку [1093 mysql] (http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from- пункт). – pperejon

+0

В любом случае полученный SQL-запрос по-прежнему не соответствует требованиям [docs] (http://dev.mysql.com/doc/refman/5.6/en/update.html), где они говорят: «Вы не можете обновить таблицу и выбрать из та же таблица в подзапросе. – pperejon

+0

Я не знал об этом. Я отредактировал или удалил свой ответ – arilia

0

две вещи:

ПЕРВЫЙ: Сценарий я предложил не правильно

Мой реальный случай гораздо сложнее, поэтому я привел пример с известным сотрудника-отдела кейс. Я допустил ошибку: мне нужно удалить всех сотрудников, принадлежащих определенному отделу. Я хочу удалить данные из условий на основе таблицы на связанной таблице.

На самом деле вы не можете удалить отделы, где есть сотрудник по имени Джон, потому что отдел не пуст, есть Джон! : D

Что-то вроде этого:

-- Create structures 
CREATE TABLE IF NOT EXISTS departments (
    id int(11) NOT NULL AUTO_INCREMENT, 
    name varchar(45) NOT NULL, 
    PRIMARY KEY (id) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; 

CREATE TABLE IF NOT EXISTS employees (
    id int(11) NOT NULL AUTO_INCREMENT, 
    department_id int(11) NOT NULL, 
    name varchar(45) NOT NULL, 
    PRIMARY KEY (id), 
    KEY fk_employees_department_idx (department_id) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ; 

ALTER TABLE employees 
    ADD CONSTRAINT fk_employees_departments FOREIGN KEY (department_id) REFERENCES departments (id) ON DELETE NO ACTION ON UPDATE NO ACTION; 


-- Populate some data 
INSERT INTO departments (id, name) VALUES 
(1, 'Sales'), 
(2, 'TI'), 
(3, 'HHRR'); 

INSERT INTO employees (id, department_id, name) VALUES 
(1, 1, 'John'), 
(2, 1, 'Mary'), 
(3, 2, 'Mark'), 
(4, 3, 'Lorenzo'); 


-- Delete all employees that belong to the TI department (Mark should be removed) 
DELETE emp FROM employees emp 
INNER JOIN departments dep ON emp.department_id = dep.id AND dep.name = 'TI'; 

ВТОРОЙ: Вы можете это сделать в CakePHP легко

Вы можете получить тот же результат предыдущего запроса это сделать:

$departments = $this->Employees->Departments->find() 
     ->select(['Departments.id']) 
     ->where(['Departments.name' => 'TI']); 
    $this->Employees->deleteAll(['Employees.department_id IN' => $departments]); 

В результате запрос mysql будет выглядеть следующим образом:

DELETE FROM employees WHERE department_id in 
(SELECT Departments.id AS `Departments__id` 
FROM departments Departments 
WHERE Departments.name = 'TI') 

... который правильный и, кроме того, легко понять. @arilia, ты указал в этом направлении своим первым ответом. Извините за соблазн.

Смежные вопросы