【问题标题】:Cakephp 3 delete filtering by associated dataCakephp 3 按关联数据删除过滤
【发布时间】:2016-08-23 16:39:05
【问题描述】:

在 CakePHP 3 中,假设我要删除所有 Departments 有一些名为“John”的 EmployeeDepartments 有很多 Employees,Employees 属于 Department)

最简单的方法是过滤所有这些部门,并在foreach循环中一个一个地删除它们:

$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 查询。我希望只有一个查询由数据库发送和执行。像这样的:

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

阅读文档我发现了三种删除记录的方法:

有没有一种优雅的方式来使用 CakePHP 3 和 ORM 获取我的查询?

【问题讨论】:

    标签: mysql cakephp cakephp-3.0


    【解决方案1】:

    两件事:

    第一:我建议的场景不正确

    我的真实案例要复杂得多,所以我以众所周知的 employee-department 案例为例。我犯了一个错误:我需要删除属于某个部门的所有员工。我想从基于相关表的 con 条件的表中删除数据。

    实际上,你不能删除有员工叫约翰的部门,因为部门不是空的,有约翰! :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,您的第一个答案指向了这个方向。很抱歉造成混乱。

    【讨论】:

      【解决方案2】:

      deleteAll 只是一个环绕

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

      所以我认为,以类似的方式,可以这样做:

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

      但在这种情况下,您会注意到匹配条件被忽略了。我看到有一个开放的ticket 关于那个

      如果您只想使用两个查询,您可以在第二个查询中执行一个查询来检索所有 id 并删除所有:

      $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
      )
      

      另一种解决方案是在子查询中转换您的内部联接。实际上,您想删除几乎拥有名为 John 的 Employee 的所有部门。

      $employees = $this->Departments->Employees->find()
          ->select(['department_id'])
          ->distinct()
          ->where(['Employees.name' => 'John']);
      
      $departments = $this->Departments->query()
          ->delete()
          ->where(['id IN' => $employees])
          ->execute();
      

      【讨论】:

      • 谢谢@arilia。实际上,要获得该查询,您应该将'id' => $departments 更改为'id IN ' => $departments,否则您会得到一个相等的条件“=”。另一方面,该查询返回1093 mysql error
      • 无论如何,根据the docs 他们说,生成的 SQL 查询仍然是错误的 您不能更新表并在子查询中从同一个表中进行选择。
      • 我不知道。我将编辑或删除我的答案
      • 再次感谢@arilia。我还尝试了第一种方法,使用deletematching,但实际上匹配标准被忽略了。您使用 IN 数组 的其他解决方案将起作用。问题是我的实际问题与部门和员工无关(这只是一个例子),我的部门列表可能有数千条记录,所以我想它会在某个时候破坏查询。
      • 我开始认为,除了硬编码之外,没有办法用 Cake 启动我的查询。我将在您指出的问题中引用这个问题。谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-15
      相关资源
      最近更新 更多