Cakephp如何基于另一个虚拟字段创建虚拟字段

时间:2014-01-14 18:59:32

标签: php mysql sql database cakephp

我有

Contact Table : id, first_name, last_name, company_id
Company Table : id, name

我想在联系表中创建虚拟字段,因此它将显示为“contactInCompany”

"first_name last_name - Company.name" e.g: Andre Robin - Google

我怎样才能实现这一点,我尝试这种方式但它不起作用,它不接受另一个虚拟字段作为输入

public $virtualFields = array(
    'companyName' => 'SELECT name FROM companies where id = Contact.company_id',
    'customerWithCompany' => "CONCAT(Contact.first_name, ' ', 
                              Contact.last_name, ' ', Contact.companyName, '')"     
    );

我也是这样尝试过的,但它不起作用

'customerWithCompany' => "CONCAT(Contact.first_name, ' ', Contact.last_name, '-', 
                         SELECT name FROM companies where id = Contact.company_id)"

我经常需要这个,我会用这个来放入下拉选择框以选择联系人,所以我希望将联系人姓名与公司一起显示

6 个答案:

答案 0 :(得分:8)

您不能对虚拟字段执行此操作,因为虚拟字段中的键只是别名

示例:

class Contact extends AppModel {

  public $virtualFields = array(
      'companyName' => 'SELECT name FROM companies where id = Contact.company_id',
      'customerWithCompany' => "CONCAT(Contact.first_name, ' ', 
                                 Contact.last_name, ' ', Contact.companyName, '')"     
       );

    public function getContacts() {
      return $this->find('all', array(
                   'fields'=>array(
                            'Contact.id',
                            'Contact.first_name', 
                            'Contact.companyName', 
                            'Contact.customerWithCompany'
                           )
                    )
               );
    }

}

如果从ContactsController调用getContacts()方法

以上代码将形成此查询:

SELECT Contact.id, Contact.first_name, 
       (SELECT name FROM companies where id = Contact.company_id) AS Contact__companyName, 
       CONCAT(Contact.first_name, ' ', Contact.last_name, ' ', Contact.companyName, '') AS Contact__customerWithCompany
FROM contacts AS Contact;

此查询不会在mysql上执行,因为无法访问另一列的1列别名。

要实现这一点,你必须使用如下的子查询:Cake php将虚拟字段名称转换为Model__(在下面的例子中是Contact __)

SELECT 
SubQuery.id, 
SubQuery.first_name, 
SubQuery.Contact____companyName,
CONCAT(SubQuery.first_name, ' ', SubQuery.last_name, ' ', SubQuery.Contact____companyName, '') AS Contact__customerWithCompany 

FROM 
(SELECT Contact.id, Contact.first_name, (SELECT name FROM companies where id = Contact.company_id) AS Contact__companyName
FROM contacts AS Contact) AS SubQuery;

如果你想在cake php中构建子查询,请使用 buildStatement DataSource方法

注意:使用联接是上述查询的另一个更好的解决方案,可以在不使用子查询的情况下获得相同的结果。

加入:

public function getContacts() {
    $this->virtualFields['customerWithCompany'] = "CONCAT(Contact.first_name, ' ', Contact.last_name, ' ', Company.name)";
    return $this->find('all', array(
            'fields'=>array(
                'Contact.id',
                'Contact.first_name',
                'Contact.last_name',
                'Company.name',
                'Contact.customerWithCompany'
            ),
            'joins'=>array(
                array(
                    'table'=>'companies',
                    'alias'=>'Company',
                    'type'=>'LEFT',
                    'conditions'=>array(
                        'Contact.company_id = Company.id'
                    )
                )
            )
        )
    );
}

答案 1 :(得分:2)

您不需要virtualField。使用JOIN LEFT进行此简单查询。

$users= $this->Contact->query('SELECT CONCAT(first_name," ",last_name," - ", companies.name) AS customerWithCompany FROM users
                           LEFT JOIN companies on contact.company_id = companies.id');

阅读数据:

            $userList = array(); 
            foreach ($users as $user) {
                $userList[] = $user[0]['customerWithCompany']; 
            }

            var_dump($userList);

输出:

 array(
        (int) 0 =>  'Harts Jackson - Google'
        (int) 1 =>  'Emo Rock - Yahoo'
            ...
)

并且virtualFields的实现也有一些限制。首先,您不能在关联模型上对条件,顺序或字段数组使用virtualField。这样做通常会导致SQL错误,因为字段不会被ORM替换。这是因为很难估计可能找到相关模型的深度。

如果您没有关联的模型(我可以看到您已经拥有并希望分配条件),那么您必须在Contact模型中创建构造函数方法并在构造函数中设置virtualFiled。这是How to create $virtualFields

答案 2 :(得分:1)

在您的联系人模型中写下以下内容:

 public $belongsTo = array(
        'Company' => array(
            'className' => 'Company',
            'foreignKey' => 'company_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );

在公司模型中写道:

 public $hasMany = array(
            'Contact' => array(
                'className' => 'Contact',
                'foreignKey' => 'company_id',
                'dependent' => false,
            ),
  }

现在,当您搜索任何联系人时,它也会包含相关公司的相关信息。打印阵列,您将看到公司索引。

PS:如果我的问题不正确,请告诉我确切的问题,我可以帮助你。

答案 3 :(得分:0)

在您的联系人模型中,请尝试以下操作:

public $belongsTo = array('Company');

public $actsAs = array('Containable');

public $virtualFields = array(
    'customerWithCompany' => "CONCAT(Contact.first_name, ' ', 
                              Contact.last_name, ' ', Company.name, '')"     
    );

并在您的控制器中执行以下操作:

$this->Contact->find(
    'list', 
    array(
        'fields' => array('Contact.id', 'Contact.customerWithCompany'), 
        'contain' => array('Company')
    )
);   

答案 4 :(得分:0)

我不确定,但尝试在各自的模型构造函数中设置虚拟字段。

答案 5 :(得分:0)

这不是主题,但这个带有相关模型的虚拟领域有效:


Orderitem hasMany Necessary
Necessary hasMany Recipe
Necessary hasMany Storage

'inrecipe' =>   'SELECT COUNT(Necessary.id) 
        FROM necessaries AS Necessary 
        INNER JOIN recipes AS Recipe 
        ON Necessary.recipe_id = Recipe.id 
        WHERE Orderitem.id = Necessary.orderitem_id ',

这也有效



'instorage' =>  'SELECT COUNT(Necessary.id) 
        FROM necessaries AS Necessary 
        INNER JOIN recipes AS Recipe 
        ON Recipe.id=Necessary.recipe_id 
        INNER JOIN storages AS Storage 
        ON Storage.id=Necessary.storage_id 
        AND Recipe.quantity = Storage.quantity 
        WHERE Orderitem.id = Necessary.orderitem_id ',

更复杂......



'resolution' => 'SELECT IF(
        (SELECT COUNT(Necessary.id) 
        FROM necessaries AS Necessary 
        INNER JOIN recipes AS Recipe 
        ON Recipe.id=Necessary.recipe_id 
        INNER JOIN storages AS Storage 
        ON Storage.id=Necessary.storage_id AND Recipe.quantity=Storage.quantity 
        WHERE Orderitem.id = Necessary.orderitem_id )
         = 
        (SELECT COUNT(Necessary.id) 
        FROM necessaries AS Necessary 
        INNER JOIN recipes AS Recipe 
        ON Necessary.recipe_id = Recipe.id 
        WHERE Orderitem.id = Necessary.orderitem_id )
        ,"YES", "NO") ',