Check if an object has changed

前端 未结 5 2322
闹比i
闹比i 2021-02-19 21:31

Is there a more native way (e.x. a built-in function) with less userland code to check if an objects property values have changed instead of using one of those methods:

相关标签:
5条回答
  • 2021-02-19 22:07

    I can offer you another solution to the problem, In fact to detect "if an object has changed" we can use observer pattern design principles. May that way should be better for some people who want to get notify about changes in object.

    Contracts/ISubject.php

    <?php
    
    namespace Contracts;
    
    interface ISubject
    {
        public function attach($observer): void;
        public function detach($observer): void;
        public function notify(): void;
    }
    

    Contracts/IObserver.php

    <?php
    
    namespace Contracts;
    
    interface IObserver
    {
        public function update($subject);
    }
    

    Subject.php

    class Subject implements ISubject
    {
        public $state; // That is detector 
    
        private $observers;
    
        public function __construct()
        {
            $this->observers = new \SplObjectStorage(); // That is php built in object for testing purpose I use SplObjectStorage() to store attach()'ed objects.
        }
    
        public function attach($observer): void
        {
            echo "Subject: Attached an observer.\n";
            $this->observers->attach($observer);
        }
    
        public function detach($observer): void
        {
            $this->observers->detach($observer);
            echo "Subject: Detached an observer.\n";
        }
    
        public function notify(): void
        {
            echo "Subject: Notifying observers...\n";
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    
        public function someYourLogic()
        {
            $this->state = rand(0, 10);
            echo "Subject: My state has just changed to: {$this->state}\n";
            $this->notify();
        }
    }
    

    Observer1.php | Plus you are able to have as many ConcreteObserver as you want

    class Observer1 implements IObserver
    {
        public function update($subject): void
        {
            if ($subject->state < 5) {
                echo "Observer1: Reacted to the event.\n";
            }
        }
    }
    

    Clinet.php

    $subject = new Subject();
    
    $o1 = new Observer1();
    $subject->attach($o1);
    $subject->someYourLogic();
    
    0 讨论(0)
  • 2021-02-19 22:16

    We can implement it without observer.

    For pure php, we can use $attributes & $original to check what has been modified check this explanation if needed.

    $modifiedValues = [];
    foreach($obj->attributes as $column=>$value) {
        if(!array_key_exists($column, $obj->original) || $obj->original[$column] != $value) {
            $modifiedValues[$column] = $value;
        }
    }
    // then check $modifiedValues if it contains values
    

    For Laravel user, we can use the isDirty() method. Its usage:

    $user = App\User::first();
    $user->isDirty();          //false
    $user->name = "Peter";
    $user->isDirty();          //true
    
    0 讨论(0)
  • 2021-02-19 22:17

    There is no built-in method, I'm afraid. The shadow copy approach is the best way.

    A simpler way, if you have control over the class, is to add a modified variable:

    $this->modified = false;
    

    When I modify the object in any way, I simply use

    $obj->modified = true;
    

    This way I can later check

    if($obj->modified){ // Do Something
    

    to check if it was modified. Just remember to unset($obj->modified) before saving content in a database.

    0 讨论(0)
  • 2021-02-19 22:22

    What you are trying to do?

    You are trying to compare object with itself, after some chain of "unknown" operations to check if the object has changed. If this is true, there are some logical points to observe. At first, if you want to compare object with itself, you've got only two options:

    1. Remember the whole object state (for example hash, or just copy whole object)
    2. Track changes over time

    There is no other logical approach. Comparing memory allocations, real objects, copying objects, comparing hashes, is all in point one. Tracking changes, saving changes inside object, remembering meantime operations, inside point 2.

    So in my opinion this question is sort of backing up data questions. In that case there are many, many solutions but none of them are hardcoded inside php as far as I'm concerned. Why?


    The answer is simple. PHP guys have got the same problems you've got :). Because if this would be hardocded inside php, then php should run / use one of those mechanisms (1) or (2).

    In that case every object that you create, and every operation you made should be written somewhere to remember every state / object / something and use them for comparison in the future.

    While you need this solution, almost ~100% of websites don't. So hardcoding this inside php would made ~100% of websites work slower and your work faster ;).


    PHP hypothetical solution?

    The only solution (maybe built in php in the future) I can think of is making some kind of php config flag: track objects, and only if this flag is true, then run all the php mechanisms of tracking objects states. But this also mean a huge performance gap. As all the ifs (if tracking, if tracking, if tracking) are also procesor and memory time consuming.

    There is also a problem, what to compare? You need to compare object with same object, but... Few minutes ago? Few operations ago? No... You must point exactly one place in code, and then point second place in code and compare object in those two places. So hypothetical auto tracking is... Kind of powerless, as there is no "key" in the object state ofer time array. I mean, even if you got magic_object_comparer function, what it should look like?

    <?php
    
        function magic_object_comparer() {} // Arguments??
        function magic_object_comparer($object_before, $object_after) {} // you must save object_before somewhere...??
        function magic_object_comparer($object, $miliseconds) {} // How many miliseconds?
        function magic_object_comparer($object, $operations) {} // How many operations? Which operations?
    
    
        magic_comparer_start($object);
        // ... Few operations...
        $boolean = magic_comparer_compare_from start($object);
        // Same as own implementation...
    ?>
    

    Sadly, you are left with own implementation...

    After all, I would propose to implement some kind of own mechanism for that, and remember to use it only there, where you need it. As this mechanism will for sure be time and memory consuming. So think carefully:

    1. Which objects you want to compare. Why?
    2. When you want to compare them?
    3. Does all changes need to be compared?
    4. What is the easiest way of saving those states changes?

    And after all of that, try to implement it. I see that you've got a huge php knowledge, so I'm pretty sure that you will figure out something. There are also many comments, and possible ideas in this question and discussion.


    But after all maybe I explained a little why, there is no build in solution, and why there should not be one in the future... :).


    UPDATE

    Take a look here: http://www.fluffycat.com/PHP-Design-Patterns/. This is a great resource about php patterns. You should take a look at adapter, decorator and observer patterns, for possible elegant object oriented solutions.

    0 讨论(0)
  • 2021-02-19 22:23

    While I too am looking for a very fast/faster approach, a variant of method 2 is effectively what I use. The advantage of this method is that it is (pretty) fast (in comparison to an isset()), depending on object size. And you don't have to remember to set a ->modified property each time you change the object.

    global $shadowcopy; // just a single copy in this simple example.
    $thiscopy = (array) $obj; // don't use clone.
    if ($thiscopy !== $shadowcopy) {
    // it has been modified
    // if you want to know if a field has been added use array_diff_key($thiscopy,$shadowcopy);
    
    }
    $shadowcopy = $thiscopy; // if you don't modify thiscopy or shadowcopy, it will be a reference, so an array copy won't be triggered.
    

    This is basically method 2, but without the clone. If your property value is another object (vobj), then clone may be necessary (otherwise both references will point to the same object), but then it is worth noting that it is that object vobj you want to see if has changed with the above code. The thing about clone is that it is constructing a second object (similar performance), but if you want to see what values changed, you don't care about the object itself, only the values. And array casting of an object is very fast (~2x the speed of a boolean cast of a bool) .. well, up until large objects. Also direct array comparison === is very fast, for arrays under say 100 vals.

    I'm pretty sure an even faster method exists...

    0 讨论(0)
提交回复
热议问题