Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cakephp 3 delete filtering by associated data

In CakePHP 3, suppose I want to delete all Departments that have some Employee named "John" (Departments have many Employees, Employees belongs to Department)

The easiest approach is to filter all those Departments, and delete them one by one in a foreach loop:

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

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

This will result in one SELECT query plus one DELETE query for each record. I'd prefer one only query to be sent and executed by the database. Something like this:

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

Reading the docs I find three ways to delete records:

  • Using the Table object one by one, get the object, then delete it.
  • Using the Table with deleteAll, which takes an array of conditions over the target table, not the associated ones.
  • Using the ORM, again with only an array of conditions.

Is there an elegant way to obtain my query using CakePHP 3 and the ORM?

like image 942
pperejon Avatar asked Mar 02 '26 14:03

pperejon


1 Answers

Two things:

FIRST: The scenario I suggested is not correct

My real case is much more complicated, so I made an example with the well known employee-department case. I made a mistake: what I need is to delete all employees that belong to a certain department. I want to remove data from a table based con conditions on a related table.

Actually, you can't delete departments where there is an employee named John, 'cause the department is not empty, there's John!! :D

Something like this:

-- 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';

SECOND: You can do this in CakePHP easily

You can get the same result of the previous query doing this:

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

The resulting mysql query will be:

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

...which is correct and moreover easy to understand. @arilia, you pointed in this direction with your first answer. Sorry for the confussion.

like image 124
pperejon Avatar answered Mar 04 '26 06:03

pperejon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!