phalcon在更新前自动选择

时间:2014-01-19 13:45:38

标签: php mysql orm phalcon

我有一个像这样的mysql表:

CREATE TABLE testtbl (
id INT(11) NOT NULL AUTO_INCREMENT,
field1 INT(11) NOT NULL DEFAULT '0',
field2 INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (id)
);
INSERT INTO testtbl (field1, field2) VALUES (1, 1);

我的phalcon项目中显示的模型是:

class Testtbl extends \Phalcon\Mvc\Model
{
    public $id;
    public $field1;
    public $field2;

    public function initialize() {
    $this->setConnectionService('db');
    $this->skipAttributes(array('field1'));
    }

    public function columnMap() {
        return array(
            'id' => 'id', 
            'field1' => 'field1', 
            'field2' => 'field2'
        );
    }
}

在我的控制器中我有

$model = new Testtbl();
$model->id=1;
$model->field2 = 2;
if ($model->update() === false) {
    $msg = $model->getMessages();
    print_r($msg);
}

db服务设置为在文件中记录查询。 执行控制器代码后,日志文件包含:

[][INFO] DESCRIBE `testtbl`
[][INFO] SELECT COUNT(*) "rowcount" FROM `testtbl` WHERE `id` = ?
[][INFO] UPDATE `testtbl` SET `field2` = ? WHERE `id` = ?

为什么在更新之前有选择?

如何在没有任何事先选择的情况下进行更新,仍然使用phalcon的模型? (我可以直接在Phalcon \ Db \ Adapter \ Pdo \ Mysql中执行查询,但不想这样做。)

(如果我 - >更新从 - > findFirst收到的模型,则不会有任何SELECT COUNT(*)“rowcount”..,只有当我直接进行更新时才会出现。)

3 个答案:

答案 0 :(得分:2)

使用原始SQL查询(如提到的eBrian)对您没有帮助。您必须在模型中另外提供手动元数据(see here)以防止选择语句。

我使用Testtbl类在metaData()和update()函数中进行注释,进行了以下测试(在postgresql上)。

<?php
namespace Library\Storage;


use Phalcon\Db\Adapter\Pdo\Postgresql;
use Phalcon\Db\Column,
    Phalcon\Mvc\Model\MetaData;

class Testtbl extends \Phalcon\Mvc\Model {

    public $id;
    public $field1;
    public $field2;

    public function metaData()
    {
        return array(

            //Every column in the mapped table
            MetaData::MODELS_ATTRIBUTES => array(
                'id', 'field1', 'field2'
            ),

            //Every column part of the primary key
            MetaData::MODELS_PRIMARY_KEY => array(
                'id'
            ),

            //Every column that isn't part of the primary key
            MetaData::MODELS_NON_PRIMARY_KEY => array(
                'field1', 'field2'
            ),

            //Every column that doesn't allows null values
            MetaData::MODELS_NOT_NULL => array(
                'id', 'field1', 'field2'
            ),

            //Every column and their data types
            MetaData::MODELS_DATA_TYPES => array(
                'id' => Column::TYPE_INTEGER,
                'field2' => Column::TYPE_INTEGER,
                'field2' => Column::TYPE_INTEGER
            ),

            //The columns that have numeric data types
            MetaData::MODELS_DATA_TYPES_NUMERIC => array(
                'id' => true,
                'field1' => true,
                'field2' => true,
            ),

            //The identity column, use boolean false if the model doesn't have
            //an identity column
            MetaData::MODELS_IDENTITY_COLUMN => 'id',

            //How every column must be bound/casted
            MetaData::MODELS_DATA_TYPES_BIND => array(
                'id' => Column::BIND_PARAM_INT,
                'field1' => Column::BIND_PARAM_INT,
                'field2' => Column::BIND_PARAM_INT
            ),

            //Fields that must be ignored from INSERT SQL statements
            MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT => array(
                'id' => true
            ),

            //Fields that must be ignored from UPDATE SQL statements
            MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE => array(
                'id' => true
            )

        );
    }

    public function initialize() {
        $this->setConnectionService('db');
        $this->skipAttributes(array('field1'));
    }

    public function columnMap() {
        return array(
            'id' => 'id',
            'field1' => 'field1',
            'field2' => 'field2'
        );
    }

    public function update($data=null, $whiteList=null) {
        if($this->id > 0) {
            $phql = 'UPDATE testtbl SET field1 = :f1, field2 = :f2 WHERE id = '.$this->id;
            /** @var Postgresql $db */
            $db = $this->getDI()->get('db');
            $db->execute($phql, array('f1' => $this->field1, 'f2' => $this->field2));
        } else {
            throw new \UnexpectedValueException('ID is required for update');
        }
        return TRUE;
    }
}

我观看了我的PostgreSQL日志进行了4次测试:

没有MetaData使用Model-&gt; update()

2014-01-30 20:30:11 CET LOG:  statement: SET search_path TO 'public'
2014-01-30 20:30:11 CET LOG:  execute pdo_stmt_00000001: SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM information_schema.tables WHERE table_schema = 'public' AND table_name='testtbl'
2014-01-30 20:30:11 CET LOG:  statement: DEALLOCATE pdo_stmt_00000001
2014-01-30 20:30:11 CET LOG:  execute pdo_stmt_00000002: SELECT DISTINCT c.column_name AS Field, c.data_type AS Type, c.character_maximum_length AS Size, c.numeric_precision AS NumericSize, c.numeric_scale AS NumericScale, c.is_nullable AS Null, CASE WHEN pkc.column_name NOTNULL THEN 'PRI' ELSE '' END AS Key, CASE WHEN c.data_type LIKE '%int%' AND c.column_default LIKE '%nextval%' THEN 'auto_increment' ELSE '' END AS Extra, c.ordinal_position AS Position FROM information_schema.columns c LEFT JOIN ( SELECT kcu.column_name, kcu.table_name, kcu.table_schema FROM information_schema.table_constraints tc INNER JOIN information_schema.key_column_usage kcu on (kcu.constraint_name = tc.constraint_name and kcu.table_name=tc.table_name and kcu.table_schema=tc.table_schema) WHERE tc.constraint_type='PRIMARY KEY') pkc ON (c.column_name=pkc.column_name AND c.table_schema = pkc.table_schema AND c.table_name=pkc.table_name) WHERE c.table_schema='public' AND c.table_name='testtbl' ORDER BY c.ordinal_position
2014-01-30 20:30:11 CET LOG:  statement: DEALLOCATE pdo_stmt_00000002
2014-01-30 20:30:11 CET LOG:  execute pdo_stmt_00000003: SELECT COUNT(*) "rowcount" FROM "testtbl" WHERE "id" = $1
2014-01-30 20:30:11 CET DETAIL:  parameters: $1 = '1'

使用重写的Model-&gt; update()

没有MetaData
2014-01-30 20:28:38 CET LOG:  statement: SET search_path TO 'public'
2014-01-30 20:28:38 CET LOG:  execute pdo_stmt_00000001: SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM information_schema.tables WHERE table_schema = 'public' AND table_name='testtbl'
2014-01-30 20:28:38 CET LOG:  statement: DEALLOCATE pdo_stmt_00000001
2014-01-30 20:28:38 CET LOG:  execute pdo_stmt_00000002: SELECT DISTINCT c.column_name AS Field, c.data_type AS Type, c.character_maximum_length AS Size, c.numeric_precision AS NumericSize, c.numeric_scale AS NumericScale, c.is_nullable AS Null, CASE WHEN pkc.column_name NOTNULL THEN 'PRI' ELSE '' END AS Key, CASE WHEN c.data_type LIKE '%int%' AND c.column_default LIKE '%nextval%' THEN 'auto_increment' ELSE '' END AS Extra, c.ordinal_position AS Position FROM information_schema.columns c LEFT JOIN ( SELECT kcu.column_name, kcu.table_name, kcu.table_schema FROM information_schema.table_constraints tc INNER JOIN information_schema.key_column_usage kcu on (kcu.constraint_name = tc.constraint_name and kcu.table_name=tc.table_name and kcu.table_schema=tc.table_schema) WHERE tc.constraint_type='PRIMARY KEY') pkc ON (c.column_name=pkc.column_name AND c.table_schema = pkc.table_schema AND c.table_name=pkc.table_name) WHERE c.table_schema='public' AND c.table_name='testtbl' ORDER BY c.ordinal_position
2014-01-30 20:28:38 CET LOG:  statement: DEALLOCATE pdo_stmt_00000002
2014-01-30 20:28:38 CET LOG:  execute pdo_stmt_00000003: UPDATE testtbl SET field1 = $1, field2 = $2 WHERE id = 1
2014-01-30 20:28:38 CET DETAIL:  parameters: $1 = '1', $2 = '2'
2014-01-30 20:28:38 CET LOG:  statement: DEALLOCATE pdo_stmt_00000003

使用Model-&gt; update()

的手动MetaData
2014-01-30 20:26:12 CET LOG:  statement: SET search_path TO 'public'
2014-01-30 20:26:12 CET LOG:  execute pdo_stmt_00000001: SELECT COUNT(*) "rowcount" FROM "testtbl" WHERE "id" = $1
2014-01-30 20:26:12 CET DETAIL:  parameters: $1 = '1'
2014-01-30 20:26:12 CET LOG:  statement: DEALLOCATE pdo_stmt_00000001
2014-01-30 20:26:12 CET LOG:  execute pdo_stmt_00000002: UPDATE "testtbl" SET "field2" = $1 WHERE "id" = $2
2014-01-30 20:26:12 CET DETAIL:  parameters: $1 = '2', $2 = '1'
2014-01-30 20:26:12 CET LOG:  statement: DEALLOCATE pdo_stmt_00000002

使用覆盖模型的手动元数据 - &gt; update()

2014-01-30 20:23:14 CET LOG:  statement: SET search_path TO 'public'
2014-01-30 20:23:14 CET LOG:  execute pdo_stmt_00000001: UPDATE testtbl SET field1 = $1, field2 = $2 WHERE id = 1
2014-01-30 20:23:14 CET DETAIL:  parameters: $1 = '1', $2 = '2'
2014-01-30 20:23:14 CET LOG:  statement: DEALLOCATE pdo_stmt_00000001

如您所见,发出单个更新查询的唯一方法是提供完整的元数据使用带有pdo的原始SQL语句。

答案 1 :(得分:1)

如果没有原始SQL,这是不可能的,因为如果不需要,Phalcon将不会更新记录。由于您正在创建一个新对象并使用主键填充它,因此您要强制Phalcon首先检查是否存在与您设置的ID匹配的实际记录。

这将为您提供以下选项:

  • 使用原始SQL
  • findFirst()记录,修改内容,并调用update()或save()

编辑:

$model = new Testtbl();
$model->setReadConnectionService('master');
$model->id=1;
$model->field2 = 2;
$model->update();

这将确保describe语句(如果您没有模型元数据)和rowcount与具有最新数据的主数据库相对。

这会解决您的问题吗?

答案 2 :(得分:1)

在phalcon文档中,引用了验证消息InvalidUpdateAttempt。 当尝试更新记录但它不存在时产生。 我没有找到任何方法来抑制它的触发器,看起来没有,也许你会有更多的运气。