We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

[BUG] Model on update bug

Hello, how to solve it, when i try to save, parent model for example i have a car and car have a user ( owner ) and when i try to change user ( owner ) to another one, phalcon php trows error about duplicate entry email.

But why ? Phalcon execute update or insert on update car ???? What's that ? Can you explain me that ? Here is the peace of my code:

    $car->user_id = $user->id;
    if($car->save()){
        $this->response->redirect('/auto/fotogalerie/'.$car->car->id.'/');
    }

Inside of car model ( initialize):

    public function initialize(){
          $this->hasOne('model_id','\Model\Car\Model','id',array(
              'alias' => 'model'
          ));

          $this->hasOne('manufacturer_id','\Model\Car\Manufacturer','id',array(
              'alias' => 'manufacturer'
          ));

          $this->hasOne("id",'\Model\Car\Top20','car_id',array(
              'alias' => 'top20'
          ));

          $this->hasMany('id','\Model\Photo','car_id',array(
              'alias' => 'photos'
          ));

          $this->hasOne("user_id",'\Model\User','id',array(
              'alias' => 'user'
          ));

          $this->hasMany("id", "\Model\Car\ParamAssign","car_id",array(
              'alias'     => "params"
          ));        
    }


16.4k

Yeah phalcon update / save with the methOd save if the primary key iS present

Then, how to fix it, it's very important to me, because i need to fix this to finish up burned project. :)



16.4k

Can you show the Method wheRe you use then save function? And the model validation

The request, $this->car is a fa

    if($this->request->isPost()){
            $data['contact'] = $this->request->getPost();
            $car->car->fn_data = base64_encode(serialize($data));
            if($form->isValid($data['contact'])){
                //check if user is exist
                $user = \Model\User::findFirst(array(
                    'email = :email:',
                    'bind' => array('email' => $data['contact']['email'])
                ));                

                //when the user doesn't exists create new one
                if(!$user){
                    //then create user and activate user account when is user created by operator
                    $u = new \User();
                    $user = $u->createFromContact($data['contact'], $car->car);
                }

                if(!$user){
                    $this->flash->notice('Omlouváme se, při pokusu vytvořit Váš uživatelský účet došlo k chybě, opakujte akci prosím později.');
                }
                else {
                    //do other stuff
                    $car->car->user_id = $user->id;
                    if($car->car->save()){
                        $this->response->redirect('/auto/fotogalerie/'.$car->car->id.'/');
                    }

                    $this->flash->notice('Při ukládání došlo k chybě, opakujte pokus prosím později.');
                }

            }
            else {
                \Helper\Flash::warning($form->getMessages(),$this->flash);
            }
    //            $this->response->redirect('auto/kontakt/'.intval($this->request->getParam('id')).'/');
        }

And the model:


    namespace Model;

    use Phalcon\Exception;

    /**
     * Description of Car
     *
     * @author Webvizitky, Softdream <[email protected]>,<[email protected]>
     * @copyright (c) 2013, Softdream, Webvizitky
     * @package name
     * @category name
     * 
     */

    class Car extends \Core\Model {

        public $id;
        public $user_id;
        public $manufacturer_id;

        public $model_id;
        public $title;
        public $price;
        public $description;
        public $color;
        public $bulk;
        public $fuel;
        public $doors;
        public $places;
        public $state;
        public $performance;
        public $body;
        public $vintage;
        public $milage;
        public $owners;
        public $vin;
        public $mark;
        public $service_list;
        public $type;
        public $drive;
        public $stk;
        public $main_image;
        public $top;
        public $highlight;
        public $expiration;
        public $visits;
        public $url;
        public $created_at;
        public $updated_at;
        public $is_leasing;
        public $is_dph;
        public $is_prooved;
        public $is_active;
        public $fn_data;
        public $source_link;
        public $hash;

        public $paramsMap = array(
            1 => 'color',
            2 => 'bulk',
            3 => 'fuel',
            4 => 'car.doors',
            5 => 'places',
            6 => 'state',
            7 => 'security',
            8 => 'exterior',
            9 => 'interior',
            10 => 'performance',
            15 => 'body',
            18 => 'vintage',
            19 => 'price',
            20 => 'security',
            21 => 'manufacturer_id',
            22 => 'model_id',
            23 => 'milage',
            24 => 'mark',
            25 => 'vin',
            26 => 'owners',
            27 => 'pack_size',
            28 => 'stk',
            29 => 'type',
            30 => 'is_leasing',
            31 => 'is_dph',
            32 => 'is_prooved',
            33 => 'drive',
            34 => 'service_list'

        );

        public function initialize(){
            $this->hasOne('model_id','\Model\Car\Model','id',array(
                'alias' => 'model'
            ));

            $this->hasOne('manufacturer_id','\Model\Car\Manufacturer','id',array(
                'alias' => 'manufacturer'
            ));

            $this->hasOne("id",'\Model\Car\Top20','car_id',array(
                'alias' => 'top20'
            ));

            $this->hasMany('id','\Model\Photo','car_id',array(
                'alias' => 'photos'
            ));

            $this->belongsTo("user_id",'\Model\User','id',array(
                'alias' => 'user'
            ));

            $this->hasMany("id", "\Model\Car\ParamAssign","car_id",array(
                'alias'     => "params"
            ));

        }

        public function getParam($group,$countInfo = false){
            $param = new Car\ParamAssign();
            $param->setFilter('car_id', $this->id);
            $param->setFilter('group_id', $group);
            $params = $param->load();
            $c = count($params);
            if($c === 1 && $countInfo === false){
                $params = isset($params{0}) ?  $params{0}->value : null;
            }

            if($countInfo){
                $half = floor($c/2);
                $half = (($c-$half) > $half) ? $c-$half : $half;
                $p = array(
                    'items' => $params,
                    'count' => $c,
                    'half'  => $half
                );

                return $p;            
            }
            return $params;
        }

    //    public function setFilter($name, $value, $operator = '=', $group = null, $glues = array()) {
    //        if(is_numeric($name)){
    //            $name = $this->paramsMap[$name];
    //        }
    //        parent::setFilter($name, $value, $operator, $group, $glues);
    //    }

        public function setFilters(array $filters){
            foreach($filters as $column => $value){
                if(is_array($value)){
                    if(isset($value['from']) || isset($value['to'])){
                        if(isset($value['from']) && !empty($value['from'])){
                            $this->setFilter($column, $value['from'],'>',$column.'range');
                        }

                        if(isset($value['to']) && !empty($value['to'])){
                            $this->setFilter($column, $value['to'],'<',$column.'range');
                        }
                    }
                    else if(!empty($value)){
                        $this->setFilter($column, $value);
                    }
                }
                else if(!empty($value)){
                    $this->setFilter($column, $value);                    
                }
            }
        }

        /**
         * Prepare and calculate vote of quality 
         * @return array
         */
        public function getVoting(){
            $count = $this->getStateVote()
                    +$this->getFuelVote()
                    +$this->getMilageVote()
                    +$this->getOldVote()
                    +$this->getServiceListVote()
                    +$this->getOwnersVote();

            return $count;  
        }

        protected function getStateVote(){
            $s = 0;
            switch($this->state){
                case 'Dobrý':
                    $s = 15;
                break;
                case 'Odpovídající stáří':
                    $s = 10;
                break;
                case 'Velmi dobrý':
                    $s = 22;
                break;
                case "Pefrektní":
                    $s = 30;
                break;            
            }

            return ($s*1);
        }

        protected function getMilageVote(){
            $s  = 0;
            if($this->milage < 50000){
                $s = 20;
            }
            else if($this->milage > 50000 && $this->milage < 100000){
                $s = 16;
            }
            else if($this->milage > 100000 && $this->milage < 150000){
                $s = 13;
            }
            else if($this->milage > 150000 && $this->milage < 200000){
                $s = 7;
            }
            else if($this->milage > 200000 && $this->milage < 250000){
                $s = 5;
            }
            else if($this->milage > 250000 && $this->milage < 300000){
                $s = 3;
            }
            else {
                $s = 0;
            }

            return $s;
        }

        protected function getOldVote(){
            $age = date("Y",time())-$this->vintage;
            if($age < 5){
                $age = 30;
            }
            else if($age > 5 && $age < 10){
                $age = 20;
            }
            else if($age > 10 && $age < 15){
                $age = 10;
            }
            else {
                $age = 0;
            }

            return $age;
        }

        protected function getServiceListVote(){
            return ($this->service_list) ? 10 : 0;
        }

        protected function getOwnersVote(){
            $s = 0;
            if($this->owners < 2 ){
                $s = 5;
            }
            else if($this->owners == 3){
                $s = 3;
            }
            else if($this->owners == 4){
                $s = 1;
            }

            return $s;
        }

        protected function getFuelVote(){
            $s = 0;
            //calculating fuels
            if($this->fuel == 'Benzín'){
                if($this->bulk < 1800){
                    $s = 5;
                }
                else if($this->bulk > 1800 && $this->bulk < 2500){
                    $s = 3;
                }
                else {
                    $s = 1;
                }
            }
            else if($this->fuel == 'Nafta'){
                if($this->bulk < 2500){
                    $s = 5;
                }
                else if($this->bulk > 2500 && $this->bulk < 3500){
                    $s = 3;
                }
                else {
                    $s = 1;
                }
            }
            else if($this->fuel == 'LPG'){
                $s = 5;
            }
            else if($this->fuel == 'CNG'){
                $s = 5;
            }
            else if($this->fuel == 'Elektro'){
                $s = 5;
            }
            else if($this->fuel =='Hybridní'){
                if($this->bulk < 3000){
                    $s = 5;
                }
                else if($this->bulk > 3000 && $this->bulk < 4000){
                    $s = 3;
                }
                else {
                    $s = 1;
                }
            }

            return $s;
        }

        public function getStateData(){
            return array(
                'state'     => $this->state,
                'model_id'  => $this->model_id,
                'manufacturer_id' => $this->manufacturer_id,
                'mark'      => $this->mark,
                'vin'       => $this->vin,
                21          => $this->manufacturer_id,
                22          => $this->model_id
            );
        }

         public function getFnData(){
            $data = array(
                'state'     => array(),
                'desc'      => array(),
                'outfit'    => array(),
                'price'     => array(),
                'contact'   => array()   
            );

            $data['state'][6] = $this->state;
            $data['state'][25] = $this->vin;
            $data['state'][21] = $this->manufacturer_id;
            $data['state']['model_id'] = $this->model_id;
            $data['state'][24] = $this->mark;

            $data['desc'][18] = $this->vintage;
            $data['desc'][26] = $this->owners;
            $data['desc'][32] = $this->is_prooved;
            $data['desc'][4] = $this->doors;
            $data['desc'][5] = $this->places;
            $data['desc'][1] = $this->color;
            $data['desc'][2] = $this->bulk;
            $data['desc'][3] = $this->fuel;
            $data['desc'][10] = $this->performance;
            $data['desc'][23] = $this->milage;
            $data['desc'][28] = $this->stk;
            $data['desc'][15] = $this->body;

            $params = $this->params;
            foreach($params as $param){
                if(!isset($data['outfit'][$param->group_id])){
                    $data['outfit'][$param->group_id] = array();
                }

                $data['outfit'][$param->group_id][] = $param->value;
            }

            $data['price'][29] = $this->type;
            $data['price'][31] = $this->is_dph;
            $data['price'][19] = $this->price;
            $data['price']['description'] = $this->description;
            if($this->user){
                $user = $this->user;
                $data['contact']['forename'] = $user->profile->forename;
                $data['contact']['surname'] = $user->profile->surname;
                $data['contact']['phone'] = $user->phone;
                $data['contact']['email'] = $user->email;
                $data['contact']['city'] = $user->profile->city;
                $data['contact']['street'] = $user->profile->street;
                $data['contact']['postal'] = $user->profile->postal;
            }
            return $data;

        }

    }


16.4k

Can you try something simple, create another instance of car and set the primary key and the user id, try updating it that way

edited Mar '15

It happens when i load model at the start:

    public function updateCarAction(){
        $car = \Model\Car::findFirst(123);
        //other stuff validaton etc

        $car->user_id = 12;//new user
        $car->save();
    }

So, after save the information is not updated, but when i add loading car model just above save and passing user_id like that:

    public function updateCarAction(){
        $car = \Model\Car::findFirst(123);
        //other stuff validaton etc

        $car = \Model\Car::findFirst(123);
        $car->user_id = 12;;//new user
        $car->save();
    }

Everything works like charm, can you someone explain me what the fu... is that ? Because i don't understand why it happen.... it refuses all normal coding practics....

It happens when i load model at the start:

  public function updateCarAction(){
      $car = \Model\Car::findFirst(123);
      //other stuff validaton etc

      $car->user_id = 12;//new user
      $car->save();
  }

Can you please copy-paste phalcon error you become after $car->save()?

If you are sure, that $car exists in database, maybe you can use $car->update() instead of $car->save(). When you use save() metod phalcon check if record exist in DB (select to DB) and then execute update or insert. When you use update() metod, phalcon execute update. So you can save one select operation and you can be sure, you dont create new record instead of update existing record.

So, after save the information is not updated, but when i add loading car model just above save and passing user_id like that:

  public function updateCarAction(){
      $car = \Model\Car::findFirst(123);
      //other stuff validaton etc

      $car = \Model\Car::findFirst(123);
      $car->user_id = 12;;//new user
      $car->save();
  }

Check if instance of Model\Car you select first time is the same as the one you select second time. Is it possible that you change instance of \Model\Car (selected to variable $car) in "doing other stuff".

edited Mar '15

Hi, Pete.

The stuff what are you talking about is here and i describe that below,

    if($this->request->isPost()){
            $data['contact'] = $this->request->getPost();
            $car->car->fn_data = base64_encode(serialize($data));
            if($form->isValid($data['contact'])){
                //check if user is exist
                $user = \Model\User::findFirst(array(
                    'email = :email:',
                    'bind' => array('email' => $data['contact']['email'])
                ));                

                //when the user doesn't exists create new one
                if(!$user){
                    //then create user and activate user account when is user created by operator
                    $u = new \User();
                    $user = $u->createFromContact($data['contact'], $car->car);
                }

                if(!$user){
                    $this->flash->notice('Omlouváme se, při pokusu vytvořit Váš uživatelský účet došlo k chybě, opakujte akci prosím později.');
                }
                else {
                    //do other stuff
                    $car->car->user_id = $user->id;
                    if($car->car->save()){
                        $this->response->redirect('/auto/fotogalerie/'.$car->car->id.'/');
                    }

                    $this->flash->notice('Při ukládání došlo k chybě, opakujte pokus prosím později.');
                }

            }
            else {
                \Helper\Flash::warning($form->getMessages(),$this->flash);
            }
    //            $this->response->redirect('auto/kontakt/'.intval($this->request->getParam('id')).'/');
        }

But here is nothing to change, or nothing happen to change the model. The car Facade is here:


    /*
     * To change this license header, choose License Headers in Project Properties.
     * To change this template file, choose Tools | Templates
     * and open the template in the editor.
     */

    /**
     * Description of Car
     *
     * @author flipixo
     */
    class Car extends \Phalcon\Mvc\User\Component {

    /**
     * @var \Model\Car
     */
    public $car;

    protected $hasChange = 0;

    /**
     * @var \Model\Car\ParamAssign
     */
    public $params;

    protected $data;
    public function __construct($id = null) {
        if($id){
            $car = \Model\Car::findFirst($id);
            if(!$car){
                $car = new \Model\Car();
            }
        }
        else {
            $car = new \Model\Car();
        }

        $this->car = $car;
    }

    public function save(){        
        $data = $this->request->getPost();
        if(!isset($data[22]) && isset($data['model_id'])){
            $data[22] = $data['model_id'];
        }

        $this->hasChange = 0;
        foreach($data as $paramGroup => $value){
            if(!is_array($value)){
                $this->addCarItem($paramGroup, $value);
            }
        }
        $ok = true;
        if(!$this->car->id){
            $this->car->created_at = date("Y-m-d H:i:s",time());
            $this->car->expiration = date("Y-m-d H:i:s",strtotime("+30 days",time()));
        }

        $this->car->updated_at = date("Y-m-d H:i:s",time());
        $ok = $this->car->save();

        if($this->car->id){
            $ok = $this->addParams($data);
        }

        return $ok;
    }

    protected function addCarItem($group_id, $value){
        if(is_numeric($group_id)){
            $variable = $this->car->paramsMap[$group_id];
        }
        else {
            $variable = $group_id;
        }

        if($this->car->{$variable} !== $value){
            $this->car->$variable = $value;
            $this->hasChange = 1;
        }
    }

    protected function addParams(array $data){
        $found = 0;
        $saved = 0;
        foreach($data as $group => $value){
            $group = intval($group);
            if(in_array($group,array(7,8,9,20))){
                $found++;
                if($this->saveParam($group, $value)){
                    $saved++;
                }
            }
        }

        if($found === $saved){
            return true;
        }

        return false;
    }

    protected function saveParam($group, $value){
        if(is_array($value)){
            //reset params
            $params = new \Model\Car\ParamAssign();
            $params->setFilter('car_id', $this->car->id);
            $params->setFilter('group_id', $group);
            $paramsToDelete = $params->load(1,9999);
            $params->delete($paramsToDelete);

            $saved = true;
            foreach($value as $section => $val){
                if($val){
                    $param = new \Model\Car\ParamAssign();
                    $param->group_id = $group;
                    $param->value = $val;
                    $param->car_id = $this->car->id;
                    if(!$param->save()){
                        $saved = false;
                    }
                }
            }
            return $saved;
        }
        else {
            if($value){
                $param = \Model\Car\ParamAssign::findFirst(array(
                    'conditions'    => 'car_id = :car_id: AND group_id = :group:',
                    'bind'          => array('car_id' => intval($this->car->id), 'group' => $group)
                ));   

                if(!$param){
                    $param = new \Model\Car\ParamAssign();
                }

                $param->group_id = $group;
                $param->value = $value;
                $param->car_id = $this->car->id;

                return $param->save();
            }            
        }

        return true;
    }

    public function applyPaidProduct(\Model\Order $order){
        $items = json_decode($order->items,1);
        $carInfo = $items['item'];
        $productInfo = $items['products'];
        $user = \Model\User::findFirst($order->user_id);

        $car = \Model\Car::findFirst(intval($carInfo['id']));
        switch($productInfo['id']){//todo
            case 1:
                $car->expiration = date("Y-m-d H:i:s",strtotime('+60 days', strtotime($car->expiration)));
                $timeHighlight = ($car->highlight > date("Y-m-d H:i:s",time())) ? strtotime($car->highlight) : time();
                $timeTop = ($car->top > date("Y-m-d H:i:s",time())) ? strtotime($car->top) : time();
                $car->highlight = date("Y-m-d H:i:s",strtotime("+30 days",$timeHighlight));
                $car->top = date("Y-m-d H:i:s",strtotime("+30 days",$timeTop));
            break;
            case 2: 
                $car->expiration = date("Y-m-d H:i:s",strtotime('+60 days', strtotime($car->expiration)));
                $timeHighlight = ($car->highlight > date("Y-m-d H:i:s",time())) ? strtotime($car->highlight) : time();
                $timeTop = ($car->top > date("Y-m-d H:i:s",time())) ? strtotime($car->top) : time();
                $car->highlight = date("Y-m-d H:i:s",strtotime("+30 days",$timeHighlight));
                $car->top = date("Y-m-d H:i:s",strtotime("+30 days",$timeTop));

                //top 20
                $countTop20 = \Model\Car\Top20::find(array(
                    'from_date < :now: AND to_date > :now:',
                    'order' => 'to_date DESC',
                    'bind'  => array('now' => date("Y-m-d H:i:s",time())),
                    'limit' => 20
                ));
                $top20 = \Model\Car\Top20::findFirst(array(
                    'car_id = :car:',
                    'bind' => array('car' => $car->id)
                ));                
                if(!$top20){
                    $top20 = new \Model\Car\Top20();
                }

                $count = count($countTop20);
                if($count == 20){
                    $lastItem = $countTop20->getFirst();  
                    $top20->from_date = $lastItem->to_date;
                }
                else {
                    $top20->from_date = date("Y-m-d H:i:s",time());
                }

                $top20->to_date = date("Y-m-d H:i:s",strtotime("+7 days",strtotime($top20->from_date)));
                $top20->car_id = $car->id;
                $top20->created_at = date("Y-m-d H:i:s",time());
                if(!$top20->save()){
                    return false;
                }

            break;
            case 4: 
                $timeTop = ($car->top > date("Y-m-d H:i:s",time())) ? strtotime($car->top) : time();
                $car->top = date("Y-m-d H:i:s",strtotime("+30 days",$timeTop));
                //when car have expiration less than top expiraiton update the expiration to top expiraiton
                if($car->expiration < $car->top){
                    $car->expiration = $car->top;
                }
            break;
            case 7:
                $highlightTop = ($car->highlight > date("Y-m-d H:i:s",time())) ? strtotime($car->highlight) : time();
                $car->highlight = date("Y-m-d H:i:s",strtotime("+30 days",$highlightTop));
                //when car have expiration less than top expiraiton update the expiration to top expiraiton
                if($car->expiration < $car->highlight){
                    $car->expiration = $car->highlight;
                }
            break;
            case 8:
                $countTop20 = \Model\Car\Top20::find(array(
                    'from_date < :now: AND to_date > :now:',
                    'order' => 'to_date DESC',
                    'bind'  => array('now' => date("Y-m-d H:i:s",time())),
                    'limit' => 20
                ));
                $top20 = \Model\Car\Top20::findFirst(array(
                    'car_id = :car:',
                    'bind' => array('car' => $car->id)
                ));                
                if(!$top20){
                    $top20 = new \Model\Car\Top20();
                }

                $count = count($countTop20);
                if($count == 20){
                    $lastItem = $countTop20->getFirst();  
                    $top20->from_date = $lastItem->to_date;
                }
                else {
                    $top20->from_date = date("Y-m-d H:i:s",time());
                }

                $top20->to_date = date("Y-m-d H:i:s",strtotime("+7 days",strtotime($top20->from_date)));
                $top20->car_id = $car->id;
                $top20->created_at = date("Y-m-d H:i:s",time());
                if(!$top20->save()){
                    return false;
                }

            break;
            default:
                return false;
            break;                
        }

        if($car->save()){

            return true;
        }

        return false;
    }

}

And in both cases, car is founded.

My test cases are:

  1. First test with the facade and dump info about car.
    • Result same thing
  2. Second test with Phalcon ORM model so just a cal Car::findFirst() and apply change
    • Result same thing
  3. Third test with cal the car again close to before call save
    • Result all works.

It happens when i load model at the start:

 public function updateCarAction(){
     $car = \Model\Car::findFirst(123);
     //other stuff validaton etc

     $car->user_id = 12;//new user
     $car->save();
 }

Can you please copy-paste phalcon error you become after $car->save()?

If you are sure, that $car exists in database, maybe you can use $car->update() instead of $car->save(). When you use save() metod phalcon check if record exist in DB (select to DB) and then execute update or insert. When you use update() metod, phalcon execute update. So you can save one select operation and you can be sure, you dont create new record instead of update existing record.

So, after save the information is not updated, but when i add loading car model just above save and passing user_id like that:

 public function updateCarAction(){
     $car = \Model\Car::findFirst(123);
     //other stuff validaton etc

     $car = \Model\Car::findFirst(123);
     $car->user_id = 12;;//new user
     $car->save();
 }

Check if instance of Model\Car you select first time is the same as the one you select second time. Is it possible that you change instance of \Model\Car (selected to variable $car) in "doing other stuff".

Try catch sql statement witch Phalcon build and send to database and then run this statement directly from your database IDE (sql developer, mysql workbench, or whatever you use)

Log sgl is great doing in DBListener::beforeQuery(). Take a look here https://docs.phalcon.io/en/latest/reference/events.html



16.4k
Accepted
answer

Kamil did you find the solution?

Hmm i make a bad click ...

No i'm not find a solution, i has do it what i have to write below.

Kamil did you find the solution?