Is it possible to perform a "soft update" using the model save() function

Hello,

I have been developing an application using Phalcon models and it works great.

However there are a few situations where I need to use the save() function without overwriting certain fields on update operations. For example if I have a table with the columns:

id, name, age

1, bob, 20

2, anna, 28,

3, tracey, 37

Now if I perform an update, without specifing the name anna:

2, , 29

It overwrites anna with nothing and I've destroyed the record...

Obviously this is an extremely simple example. I have real tables with >25 fields which I need to update maybe only half of them in certain controllers.

Making an initial database call to retrieve the record is too expensive in my opinion and therefore not an option.

I hope I'm missing something obvious.

Tim



19.0k

I hope I'm missing something obvious.

Yeah, I think you're right. In your model initialize() function, code this:

$this->useDynamicUpdate(true);

This will only update your record fields that had changed (I think this is what you want). Then, when you change the value of $record->property, it will only update that field.



4.8k

Thanks for the suggestion.

I forgot to mention that I had already tried $this->useDynamicUpdate(), but unfortunately it makes no difference.

I just tested again to be sure but when I set $this->useDynamicUpdate(true); in my model, then comment out some $record->property fields in my save function, the fields I just commented out still get blasted away.

I'm using Phalcon 1.3.2... Could that be the problem?



1.9k
edited Oct '14

You're looking to hook in to the 'beforeUpdate' event on a model.

<?php

use Phalcon\Mvc\Model,
    Phalcon\Events\Manager as EventsManager;

class Robots extends Model
{
    public function initialize()
    {
        $eventsManager = new EventsManager();

        //Attach an anonymous function as a listener for "model" events
        $eventsManager->attach('model', function($event, $robot) {
            if ($event->getType() == 'beforeUpdate') {
                if (!$robot->someProperty) {
                    // skip property update;
                }
            }
            return true;
        });

        //Attach the events manager to the event
        $this->setEventsManager($eventsManager);
    }
}

Check the docs at http://docs.phalconphp.com/en/latest/reference/models.html#events-and-events-manager



4.8k

Thanks for the suggestion!

I have thought about doing this as well, but doesn't this restrict my options for updating the model globally??

I still need to update all the fields, just not all in the same action.



1.9k

I don't see how? You could just loop over the fields in a key=>value construct, and only update those fields that have a value. Or you could implement "dirty" flags on your models to flag which properties have been modified, and then only update the values for those properties & persist them to your DB.



4.8k

Ok I think I understand what you mean although I'm still not sure it is going to work because...

When I look at the $robot object in the 'beforeSave' I only see the fields that I've specified in the save() call within the controller... However ALL the fields are still being fried in the table. So even if I override the fields in a key => value construct I think it is still going to overwrite the other values that I can't catch.

Is there any other documentation or examples I could look at?

Thanks for your help either way... I'm surprised this isn't a bit more built in to Phalcon.



1.9k

I'm not quite sure I'm still following you... Are you trying to save an update to an object with unknown (runtime) properties in your controller? If not, then what is the reason that you can't "catch" the values for your object's properties?

Maybe it would help if you posted a code example of what you're trying to accomplish.

On 14/10/2014 5:36, Tim Mohr wrote:



4.8k

Hi Sventunus,

Sorry, I'm not being very clear because I've spent so long on this problem, I think I've confused myself.

So I want to only update the fields in the DB that I present to the ORM in the save() function.

In my controller I'm testing for user variable values like this:

if(isset($progress)) {
            $task->setProgress($progress);
        }
$result = $task->save();

Then in the model I have your example code:

        $eventsManager = new EventsManager();

        //Attach an anonymous function as a listener for "model" events
        $eventsManager->attach('model', function($event, $object) {
                if ($event->getType() == 'beforeSave') {
                    foreach($object as $key => $value) {
                        // Update database here???
                    }
                }
                return true;
            });

        //Attach the events manager to the event
        $this->setEventsManager($eventsManager);

I don't understand how to push all the keys/values to the DB. Seeing as only the keys and values that I set in the save() appear in the beforeSave function I want to update everything in this object.

Parallel to that I still don't understand why I even have to do this in the first place. I'm only giving the save() function the "fields" I want to update. Why does it overwrite everything else in the process?

Does this make any sense?



1.9k

Hi Tim,

It makes alot of sense, yes! I'll see if I can make some time this week to set up a test case for your use case (shouldn't take me more than 10 minutes or so, but even those or hard to find :-/). I think your case is a valid one, because I recon having seen other similar questions here on the board that suggest the same problem. I'll get back on this one, promised!



4.8k
edited Oct '14

Hi Sventunus,

Thank-you very much for getting back to me.

I really appreciate any time you can put into this!

Tim



4.8k

Hi Sventunus,

Do you have any idea when you might get a chance to look at this?

Tim