Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Trouble with eager loading singular relations
  • Hi there!

    I have model with some has_one relations:
    protected static $_has_one = array('i18uk', 'i18ru');


    Now i fetch it from controller, make some changes and then attempt to save model:
    $pages = Model_Page::find('all', array('related' => array('i18uk', 'i18ru')));
    foreach ($pages as $p)
    {
    $pages->arrange = (string) array_search($c->id, \Input::post('arrange'), true);
    $p->is_changed('arrange') and $p->save();
    }


    Works good, except when one of the related model does not exist:
    $p->i18ru = Model_Pages_i18ru::forge();
    $p->i18uk = null;
    $p->save();
    // thrown exception "Call to a member function is_changed() on null"
    // fuel/packages/orm/classes/observer/updatedat.php @ line 128


    It would be a solution if i could unload some cached relations before save model, is it possible?
  • unset($p->i18uk) would be the documented way of doing it.

    I've pushed an update to 1.9/dev to make assigning null or array() functionally equivalent to unset().
  • `unset($p->i18uk)` is not working, because it assign `$p->_data_relations['i18uk'] = null`, but `$p->is_fetched('i18uk')` still `true`
  • HarroHarro
    Accepted Answer
    is_fetched() should still return true, as it was fetched. If you reset that, and you would try to access the relation after the reset, it would fetch it again, and undo your removal of the related object.

    I think the problem is in the observer, I checked the rest of the ORM code, and that explicitly checks for reset relation properties (null or array()), but the update observer doesn't.

    So that code should be:

        // If the relation is not loaded or reset then ignore it.
        if ( ! $obj->is_fetched($relation) or empty($obj->{$relation}))
        {
            return false;
        }

    weird though that after all these years this suddenly pops up. All our apps use the createdat and updatedat observers, and I've never seen this error.
  • I don't think this is a solution.

    I have been testing this, but I can't reproduce your problem, with the stock 1.9/dev orm code. This suggests your problem is elsewhere.

    This is my test code:

    // truncate test table
    DBUtil::truncate_table('parent');
    DBUtil::truncate_table('child');

    // create test data
    $query = DB::insert('parent', array('name', 'child_id'))->values(array('Record 1', 1))->execute();
    $query = DB::insert('child', array('name'))->values(array('Record 1', 1))->execute();

    // get parent 1
    $parent = Model_Parent::find(1);
    Debug::dump($parent);

    // fetch its child
    $parent->child;
    Debug::dump($parent);

    // uncouple the child from the parent
    $parent->child = null;

    // save the parent
    $parent->save();
    Debug::dump($parent);

    It works without problems, and without ORM code changes. It doesn't call the UpdateAt observer on the relation that has been reset.
  • Sorry, I forgot to mention that I'm using UpdatedAt observer:
    'Orm\\Observer_UpdatedAt' => array(
    'events' => array('before_save'),
    'relations' => array(
    'i18ru', 'i18uk'
    ),
    ),

    I already found another solution, but it seemed to me that the situation was abnormal.
  • Thanks, I'll update my test code accordingly.
  • Doesn't make any difference, still works without problems.

    You haven't defined the relations the other way (from Model_Pages_i18ru to Model_Page) as has_one instead of belongs_to, by any chance?
  • I see. 
    Here is my Model_Page class. 
    Maybe my mistake is multiple relations to Model_Pages_i18n, but I don't understand how to do in another way. 'i18n' relation is for frontend, depends on active lang, but 'i18ru', 'i18uk' is for backend controller.
    Hmm, but maybe some brainfuck tricks here in model getter?
     
    class Model_Page extends \Orm\Model
    {
    protected static $_properties = array(
    'id',
    'slug',
    'arrange',
    );

    protected static $_has_one = array(
    'i18n' => array(
    'key_from' => 'id',
    'model_to' => 'Model_Pages_i18n',
    'key_to' => 'page_id',
    'conditions' => array(), // look at static::_init()
    ),
    'i18ru' => array(
    'key_from' => 'id',
    'model_to' => 'Model_Pages_i18n',
    'key_to' => 'page_id',
    'conditions' => array(
    'where' => array( array( 'lang', '=', 'ru' ) ),
    ),
    ),
    'i18uk' => array(
    'key_from' => 'id',
    'model_to' => 'Model_Pages_i18n',
    'key_to' => 'page_id',
    'conditions' => array(
    'where' => array( array( 'lang', '=', 'uk' ) ),
    ),
    ),
    );

    protected static $_observers = array(
    'Orm\\Observer_UpdatedAt' => array(
    'events' => array('before_save'),
    'relations' => array(
    'i18ru', 'i18uk'
    ),
    ),
    );

    public static function _init()
    {
    static::$_has_one['i18n']['conditions']['where'] = array(
    array( 'lang', '=', \Lang::get_lang() )
    );
    }

    public function & __get($property)
    {
    if (in_array($property, array('title', 'menu', 'body', 'meta_title', 'meta_keywords', 'meta_description')))
    {
    $result = ($this->i18n) ? $this->i18n->$property : \Lang::get_lang().' '.$property;
    $result =& $result;
    return $result;
    }
    else
    {
    return $this->get($property);
    }
    }
    }


    And my backend controller:

    function action_index()
    {
    $pages = Model_Page::find('all', array('related' => array('i18n', 'i18ru', 'i18uk')));
    if ($arrange = \Input::post('arrange'))
    {
    foreach ($pages as $p)
    {
    unset($p->i18n, $p->i18ru, $p->i18uk);
    $p->arrange = (string) array_search($p->id, $arrange, true);
    $p->is_changed('arrange') and $p->save();
    }
    \Response::redirect_back();
    }
    $this->template->title = __('global.text_pages');
    $this->template->content = \View::forge('admin/page/index')->set('pages', $pages);
    }
  • And your Model_Pages_i18ru and Model_Pages_i18uk ?

    Are the relations back to Model_Page defined as "belongs_to"? 

Howdy, Stranger!

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

In this Discussion