简体   繁体   English

Phalcon更新前自动选择

[英]phalcon automatic select before update

I have a mysql table like this: 我有一个像这样的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);

My Model as it appears in my phalcon project is : 在我的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'
        );
    }
}

In my controller i have 在我的控制器中

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

The db service is set to log the queries in a file. 数据库服务设置为将查询记录在文件中。 After executing the controller's code the log file contains: 执行控制器的代码后,日志文件包含:

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

Why is there a select before the update ? 为什么在更新之前会有一个选择?

How can i make the update without any prior select and still use phalcon's model ? 我如何在没有任何事先选择的情况下进行更新,仍然使用Phalcon的模型? (i can execute the query directly in Phalcon\\Db\\Adapter\\Pdo\\Mysql , but don't want to.) (我可以直接在Phalcon \\ Db \\ Adapter \\ Pdo \\ Mysql中执行查询,但不想这样做。)

(if i ->update a model received from ->findFirst, there wont be any SELECT COUNT(*) "rowcount" ..,it appears only when i make an update directly.) (如果我->更新从-> findFirst收到的模型,则不会有任何SELECT COUNT(*)“ rowcount” ..,它仅在我直接进行更新时出现。)

Using raw SQL queries (as eBrian mentioned) won't help you. 使用原始SQL查询(如eBrian所述)将无济于事。 You have to provide manual metadata ( see here ) additionally in the model to prevent select statements. 您必须在模型中另外提供手动元数据( 请参阅此处 ),以防止选择语句。

I did the following testing (on postgresql) with Testtbl class commenting in/out the metaData() and update() function. 我使用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;
    }
}

I did 4 test runs watching my PostgreSQL log: 我做了4次测试运行,以查看PostgreSQL日志:

No MetaData using Model->update() 没有使用Model-> 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'

No MetaData using overridden Model->update() 使用重写的Model-> update()没有元数据

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

Manual MetaData using Model->update() 使用Model-> update()的手动元数据

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

Manual MetaData using overridden Model->update() 使用重写的Model-> 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

As you can see, the only way to issue one single update query is to provide full metadata and use raw SQL statements with pdo. 如您所见,发出单个更新查询的唯一方法是提供完整的元数据, 并将原始SQL语句与pdo一起使用。

This is not possible without raw SQL as Phalcon won't update records if it doesn't need to. 没有原始SQL,这是不可能的,因为Phalcon不需要更新记录。 Since you are creating a new object and populating it with a primary key, you are forcing Phalcon to first check if there is actual a record that matches the id you set. 由于要创建一个新对象并用主键填充它,因此您迫使Phalcon首先检查是否存在与您设置的ID匹配的记录。

This leaves you with the following options: 这为您提供以下选择:

  • Use raw SQL 使用原始SQL
  • findFirst() the record, modify the contents, and call update() or save() findFirst()记录,修改内容,然后调用update()或save()

Edit: 编辑:

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

This will make sure the describe statements (if you don't have model metadata) and rowcount are against the master db with the most updated data. 这将确保describe语句(如果没有模型元数据)和rowcount与具有最新数据的主数据库相对。

Does this solve your problem? 这样可以解决您的问题吗?

In the phalcon docs there is a reference to the validation message InvalidUpdateAttempt . 在phalcon文档中,有对验证消息InvalidUpdateAttempt的引用。 wich is produced when a record is attempted to be updated but it doesn't exist. 尝试更新记录但不存在该记录时会产生。 I haven't found any way to suppress the trigger for it and it looks like there is none, maybe you will have more luck. 我还没有找到任何抑制触发的方法,而且似乎没有办法,也许您会有更多的运气。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM