简体   繁体   中英

phalcon automatic select before update

I have a mysql table like this:

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 :

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 ? (i can execute the query directly in Phalcon\\Db\\Adapter\\Pdo\\Mysql , but don't want to.)

(if i ->update a model received from ->findFirst, there wont be any SELECT COUNT(*) "rowcount" ..,it appears only when i make an update directly.)

Using raw SQL queries (as eBrian mentioned) won't help you. 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.

<?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:

No MetaData using 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()

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

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

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.

This is not possible without raw SQL as Phalcon won't update records if it doesn't need to. 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.

This leaves you with the following options:

  • Use raw SQL
  • findFirst() the record, modify the contents, and call update() or 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.

Does this solve your problem?

In the phalcon docs there is a reference to the validation message 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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