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

Changing Model source table dynamically

I want to dynamically change the underlying source table used for a model. Can't seem to get this to work.

As an example, I've two tables in my database:

  • 'source1' has 1 record
  • 'source2' has 2 records

This following code does report a changed source, but the count of records doesn't change:

    $test = new TestModel();

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo '<br/>';

    $mgr = $test->getModelsManager();
    $mgr->setModelSource($test, 'source2');

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo '<br/>';

It outputs this:

Source: source1
Count: 1

Source: source2
Count: 1

Checking through the cphalcon source code, I can see that the Models Manager is called to initialize the Model. It also keeps a track of the initialized modules. The Model constructor is declared as final meaning I can't overload it. I've run out of ideas, short of dynamically having my code modify it's own files, which would be really bad practice.

Have I missed something? Any help much appreciated.



98.9k

You can use onConstruct(), it's an event fired after the object is constructed:

class TestModel extends \Phalcon\Mvc\Model
{
    public function onConstruct()
    {
        //...
    }
}

Hi,

I'm not sure if you've understood my question. I spotted the call to onConstruct in the source code, but I'm seeking to change the model source after the object has been created as I show in my code above. I'm trying to avoid creating about 30 models which are exactly the same but with different table names. I'd like to use just one model.

My code above appears to work and as you can see, the model correctly reports the source via getSource() after it's changed. The problem is that the result of a data query doesn't reset after I change the source in this way.

Here's another test I've done that maybe shows more clearly the issue (same tables as above):

    $test = new TestModel();

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo '<br/>';

    $mgr = $test->getModelsManager();
    $mgr->setModelSource($test, 'source2');

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo 'Recount: '.$test->count().'<br/>';
    echo '<br/>';

It outputs this:

Source: source1
Count: 1

Source: source2
Count: 1
Recount: 2

This is not good. The same Model returns different data for the count and the recount queries. I suspect it may be inbuilt caching somewhere in the Query / Criteria / Resultset. It's at least inconsistent and maybe a bug?

Any further ideas / advice ?

We have the same problem, please help us to resolve this problem, it feels like phalcon caches sql query ResultSet even if the src table was changed, it is a bug!!! We really love phalcon, but because of this problem we can not do anything with it... We use it in our background tasks(cron) and there we have to change tables using prefixes and run needed tasks for each table... Please help us as soon as possible



3.9k
Accepted
answer

Okay, I found a workaround, same tables as above, note the 'destruct' call:

    $test = new TestModel();

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo '<br/>';

    $mgr = $test->getModelsManager();
    $mgr->__destruct();
    $mgr->setModelSource($test, 'source2');

    echo 'Source: '.$test->getSource().'<br/>';
    echo 'Count: '.count($test->find()).'<br/>';
    echo '<br/>';

It outputs this:

Source: source1
Count: 1

Source: source2
Count: 2

Success!!

According to the API docs here https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_Model_Manager.html the destruct method 'Destroys the PHQL cache'. This should really be done automatically on source change. It appears to be this 'PHQL Planning' that is causing the issue: https://docs.phalcon.io/en/latest/reference/models-cache.html#caching-of-phql-planning

Here is a behavior to handle this in case it helps anyone:

use Phalcon\Mvc\Model\Behavior;
use Phalcon\Mvc\Model\BehaviorInterface;

class ResetSource extends Behavior implements BehaviorInterface
{

    public function missingMethod($model, $method, $arguments=array())
    {

        switch ($method) {

            case 'resetSource':

                $mgr = $model->getModelsManager();
                $mgr->__destruct();
                $mgr->setModelSource($model, $arguments[0]);
                return TRUE;
                break;

        }       

    }    

}
edited Nov '14

Hello,

I had the same problem (want to use only one model for many tables in different schemas) and I used an equivalent solution to sove it, but I created the follow static method in my BaseModel class:

class BaseModel extends \Phalcon\Mvc\Model
{
   static public function resetPHQLCache()
   {
      $o = \Phalcon\Di::getDefault()->getModelsManager();
      $o->__destruct();
   }
}

In this way, I can reset PHQL cache without instance an object.

@phalcon: please, I'd like to reopen this discussion, because I think it is not the better way to do this. I mean, it is not a good idea delete all cache (cache of all models) just to change the schema and source of only one model. I have more than 100 models in my application and, in the solution above, I would reset the cache several times. It seems slow (or am I wrong?).

Is there some better way to change the source and/or the schema of a single model?

Thank you very much.



181

@phalcon: on Phacon 2.0, ModelsManager does not provides a __destruct() method anymore. It's possible to reopen this discussion? How can PHQL cache be reseted from now on?



43.9k

@Ivan, github phalcon repo is a better place for feature request.



3.2k
edited Nov '15

So at the risk of bumping. I find my self in the situation that we need to upgrade to Phalcon 2. We have a data isolation system in our Phalcon 1 app, that requires to change the model source dynamically. This was acheived by calling __destruct() on the models manager before reseting the table.

I now cannot upgrade to Phalcon 2.

Did anyone find a soluton that did not involve modifying and rebuilding Phalcon?

Many thanks



1.0k

i have same problem with __destruct(). somebody found solution?



9.5k

Is there a way to make this work with Collection? There is a collection manager but it does not support changing of source, not even collection itself. If there is, I might have missed it, let me know. If not, what's the workaround for this? Thanks.



9.5k
edited Mar '16

Thanks for the response Ie51.

Yes, I have checked that one but it is about setting up a connection. What I was looking for is how to change the source of an already initialised collection-extended (more like a model but for MongoDB) class dynamically. I have already found a workaround although it does not use such classes anymore. Instead, I used MongoDB::execute() to run MongoDB commands. It is a bit hardcore but it provided the solution I was looking for.



43.9k

<?php

use Phalcon\Mvc\Collection;

class Robots extends Collection
{
    public function getSource()
    {
        return "the_robots";
    }
}

this is how to set the source table.



9.5k
edited Mar '16

Thanks for the reply. This enables you to set a collection manually, although this approach cannot be used dynamically. You have to specify a known collection beforehand to set it. If the collection is something to be identified dynamically (e.g., a collection created dynamically), this won't work.



43.9k

ok, maybe you can use setSource($myDynamicCollection) in initialze() method



9.5k

No, that won't work either. I have already found an alternative though, as I have mentioned in my earlier response. Hope that would help others as well who happens to read this thread. Thanks.