Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
"No changes allowed" when attempting to delete parent model
  • I'm experiencing some strange behaviour when trying to delete a parent model, which has many other models. At first I thought that my relationship wasn't defined correctly, but looks like it is correct as the children reference the parent with a $_belongs_to. This is my current setup:

    class Model_Plan extends \Orm\Model
    {
    protected static $_has_many = array(
    'subscriptions' => array(
    'model_to' => 'Model_Subscription',
    'key_from' => 'id',
    'key_to' => 'plan_id',
    ),
    );
    }

    class Model_Subscription extends \Orm\Model
    {
    protected static $_belongs_to = array(
    'plan' => array(
    'key_from' => 'plan_id',
    'model_to' => 'Model_Plan',
    'key_to' => 'id',
    ),
    );
    }
    When I tried to delete a node from Model_Plan, I got a "No changes allowed" error.

    Digging a little deeper, it appears to let me delete the plan when retrieving related models (which this case was 0):

    $plan = Model_Plan::query()->where("id", 2)->related("subscriptions")->get_one();
    $plan->delete();
    This, however, threw the error:

    $plan = Model_Plan::query()->where("id", 2)->get_one();
    $plan->delete();


    Not sure if there's something obvious that I'm missing or just know little about. Any thoughts are much appreciated!
  • This looks ok, and I can't really explain it.

    An object goes into "frozen" state when it's in the process of being deleted, before updating any related objects. This is done to prevent loops, and should normally be no issue.

    plan_id isn't the primary key of Model_Subscription by any chance?
  • No, id is the primary key in Model_subscription, not plan_id.
  • Then I'm out of idea's. There are two places in the code where this exception can be thrown, but I have never seen it happen.

    Can you post the backtrace, maybe we can deduct from there what is happening.
  • Just got to the bottom of it. So inside Model_Subscription I had a query to get me the active plans and prepopulate the plan_id field (type radio) with key/value pairs and that's what was throwing it off.

    public static function _init()
    {
    if ( $plans = \Model_Plan::query()->where("active", 1)->get() )
    {
    foreach ($plans as $i => $plan) {
    static::$_properties["plan_id"]["form"]["options"][$plan->id] = $plan->name;
    }
    }
    }


    Here's the backtrace (probably unnecessary now);

    Uncaught exception Orm\FrozenObject: No changes allowed.
    Callstack:
    #0 /home/chad/scraper/fuel/packages/orm/classes/query.php(1086): Orm\Model->_relate()
    #1 /home/chad/scraper/fuel/packages/orm/classes/query.php(1174): Orm\Query->hydrate(Array, Array, Array, 'Model_Plan', Array, Array)
    #2 /home/chad/scraper/fuel/app/classes/model/subscription.php(7): Orm\Query->get()
    #3 [internal function]: Model_Subscription::_init()
    #4 /home/chad/scraper/fuel/core/classes/autoloader.php(364): call_user_func('Model_Subscript...')
    #5 /home/chad/scraper/fuel/core/classes/autoloader.php(283): Fuel\Core\Autoloader::init_class('Model_Subscript...')
    #6 [internal function]: Fuel\Core\Autoloader::load('Model_Subscript...')
    #7 [internal function]: spl_autoload_call('Model_Subscript...')
    #8 /home/chad/scraper/fuel/packages/orm/classes/hasmany.php(37): class_exists('Model_Subscript...')
    #9 /home/chad/scraper/fuel/packages/orm/classes/model.php(381): Orm\HasMany->__construct('Model_Plan', 'subscriptions', Array)
    #10 /home/chad/scraper/fuel/packages/orm/classes/model.php(1335): Orm\Model::relations()
    #11 /home/chad/scraper/fuel/app/migrations/028_seed_plans.php(28): Orm\Model->delete()
    #12 [internal function]: Fuel\Migrations\Seed_plans->down()
    #13 /home/chad/scraper/fuel/core/classes/migrate.php(283): call_user_func(Array)
    #14 /home/chad/scraper/fuel/core/classes/migrate.php(254): Fuel\Core\Migrate::run(Array, 'default', 'app', 'down')
    #15 /home/chad/scraper/fuel/core/tasks/migrate.php(315): Fuel\Core\Migrate::down(NULL, 'default', 'app')
    #16 /home/chad/scraper/fuel/core/tasks/migrate.php(150): Fuel\Tasks\Migrate::_down('default', 'app')
    #17 [internal function]: Fuel\Tasks\Migrate->__call('down', Array)
    #18 [internal function]: Fuel\Tasks\Migrate->down()
    #19 /home/chad/scraper/fuel/packages/oil/classes/refine.php(101): call_user_func_array(Array, Array)
    #20 [internal function]: Oil\Refine::run('migrate:down', Array)
    #21 /home/chad/scraper/fuel/packages/oil/classes/command.php(114): call_user_func('Oil\Refine::run', 'migrate:down', Array)
    #22 /home/chad/scraper/oil(57): Oil\Command::init(Array)
    #23 {main}


    I guess I should have paid more attention and I apologise. If I'm following this correctly though, Model_Plan went into frozen state, its related model was initialised, which was fetching results from the parent being deleted... But yet no records were present for Model_Subscription. Kind of lost
  • HarroHarro
    Accepted Answer
    It throws the exception when accessing the relations, before actually running the query. So wether or not there are Model_Subscription records is not relevant, it fails on fetching Model_Plan's relation structure.
  • I understand. I ended up populating the available plans for subscriptions in the controller, even though i was trying to avoid that and place it in the _init() of the model. It appears that the Orm has no issues fetching the relations upon deletion when no queries against it are present in the _init() of its relations but fails when there are.
  • _init is called on first class load.

    The issue you probably have is that when the Model_Subscription is loaded (triggered by activity in the Model_Plan class), Model_Plan is already in a frozen state because the delete is pending. When you run a query which loads it first, you have no problem on the delete, _init has run by then.

    What you can try, in Orm\Model, replace _relate() with

        public function _relate($rels = false)
        {
            if ($rels === false)
            {
                return $this->_data_relations;
            }
            elseif (is_array($rels))
            {
                if ($this->_frozen)
                {
                    throw new FrozenObject('No changes allowed.');
                }
                $this->_data_relations = $rels;
            }
            else
            {
                throw new \FuelException('Invalid input for _relate(), should be an array.');
            }
        }

    which will only throw the exception when it wants to change something, not on retrieval.

    Can you see if this fixes it for you?
  • I'll give it a try
  • Unfortunately, it did not do the trick. I was getting the exact same error with the same backtrace.  Thanks for your help though!
  • Ok, I'll discuss it with Steve, we have to dive into the code then.

    I'm currently traveling, so if I have to do it, it has to wait until I get back...
  • I got the same error on my batch script. The script runs approximately 1 minute while accessing and saving various objects via Models. I use static::$_cached_objects[get_class()] = []; to prevent overloading memory. 

    This particular model is only accessed at the beginning of the script to read status and at the end to save batch results. 

    I get error: ncaught exception Orm\FrozenObject: No changes allowed.

    and fuel/packages/orm/classes/query.php(1205): Orm\Query->hydrate(Array, Array, Array, 'Model_Contacts', Array, Array)

    which Model_Contacts is completely unrelated model to the one that throws frozen error. No relation between those two, but Model_Contacts is being accessed during batch and has implemented said memory cache flush. 

    Hope that helps somehow.
  • You get a FrozenObject Exception when there is an attempt to modify an object that is being saved or deleted (this is to prevent recursion into relations).

    Could you swap your ORM install for the 1.8/develop version (just do a pull and checkout if you have a cloned repo), and see if you still have this problem. We can only fix bugs in the current develop branch.
  • I have the same problem with 1.8 version when trying to remove user with ormauth package. All relation on user model have 'cascade_delete' => false... I can not understand, what a problem?
  • Can you provide the code that leads up to the error, and the backtrace displayed with the exception?
  • This is code:

    public function post_deleteuser()
        {
            if (!\Auth::has_access('admin.users[delete]')) {
                return $this->response(['status' => 'error', 'message' => 'permission denied']);
            }

            // check csrf
            if (!\Security::check_token(\Input::post('fuel_csrf_token'))) {
                return $this->response(array('status' => 'error', 'message' => 'Security token error'));
            }

            $id = \Input::post('id');
            if (!$id) {
                $message = 'Id is required';
                \Log::error($message);
                return $this->response(['status' => 'error', 'message' => $message]);
            }

            $user = Auth_User::find($id);
            if (!$user) {
                $message = 'User is not found, id = '.$id;
                \Log::error($message);
                return $this->response(['status' => 'error', message => $message]);
            }
           
            try {
                $user->delete();
            } catch (Exception $e) {
                \Log::error($e->getCode().': '.$e->getMessage().' in '.$e->getFile().' at '.$e->getLine());
                return $this->response(array('status' => 'error', 'message' => $e->getMessage()));
            }

            return $this->response(['status' => 'ok', 'message' => 'User deleted']);
        }

    Backtrace



    1. PKGPATH/orm/classes/query.php @ line 1249


    2. PKGPATH/orm/classes/query.php @ line 1316


    3. PKGPATH/orm/classes/query.php @ line 1381


    4. PKGPATH/orm/classes/belongsto.php @ line 72


    5. PKGPATH/orm/classes/model.php @ line 1144


    6. PKGPATH/orm/classes/model.php @ line 980


    7. PKGPATH/orm/classes/model.php @ line 1525


    8. APPPATH/modules/admin/classes/controller/usersrest.php @ line 189


    9. COREPATH/base.php @ line 461


    10. COREPATH/classes/controller/rest.php @ line 153


    11. COREPATH/classes/request.php @ line 473


    12. DOCROOT/index.php @ line 71


    13. DOCROOT/index.php @ line 92
  • Before I start chasing ghosts, could you swap your current packages/orm directory for the 1.9/dev version (https://github.com/fuel/orm/archive/1.9/develop.zip) and see if you still have the problem?

    Just rename your current ORM directory and unpack the zip as new packages/orm directory.

    If you still have the issue, please post the backtrace again so I can have a look.
  • I've added some relations to my Auth_User model:

    protected static $_belongs_to = array(
    ...
            'created' => array(
                'model_to'       => 'Model\Auth_User',
                'key_from'       => 'created_by',
                'key_to'         => 'id',
                'cascade_delete' => false
            ),
            'updated' => array(
                'model_to'       => 'Model\Auth_User',
                'key_from'       => 'updated_by',
                'key_to'         => 'id',
                'cascade_delete' => false
            )
        );

        protected static $_has_many = array(
      ...
            'creating'       => array(
                'model_to'       => 'Model\Auth_User',
                'key_from'       => 'id',
                'key_to'         => 'created_by',
                'cascade_delete' => false
            ),
            'updating'       => array(
                'model_to'       => 'Model\Auth_User',
                'key_from'       => 'id',
                'key_to'         => 'updated_by',
                'cascade_delete' => false
            )
        );

    Maybe here is a reason, but I still don't understand how it happens.
  • I can imagine you getting this error if your self-reference points to itself.
  • It's possible, but how to prevent this? That relations (created, updated) is very useful for me.
  • Interesting moment. I changed group of user that I want to delete and try again. I've removed it without any errors. So that relations are not the reason of problem... This problem caused by roles/permissions, which is set for that user. Why?
  • So, after the user was deleted I can not reproduce the problem anymore :-o
    Thank you for you attention to my problem, but it was disappeared as arose... strange.
  • Do you have database profiling active? Can you see what it is trying to do?

    Which Fuel version are you on?
  • No, I did not set database profiling active. I think it was trying to change some many-to-many relation intermediate table, and possibly trying to change linked back source user table, that was frozen at the moment. But I am not sure. I can not reproduce this situation. All work fine for now.

    I used Fuel version 1.8.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion