Create relation based on GET param

I’m building an application where users can create/update documents (texts), everytime the document is saved, there is a new revision being made. The document entity has a foreign key pointing to the active revision. This works fine using a BELONGS_TO relationship like this


    public function relations()

    {

        return array(

            'rev' => array(self::BELONGS_TO, 'Revision', 'current_revision'),

        );

    }

But here is the problem: users should also be able to view earlier revisions. So I’m getting the revision ID in my controller, and I need to return the data, which is the document AND the related revision.

So I need something like this:


    public function relations()

    {

        return array(

            'rev' => array(self::BELONGS_TO, 'Revision', $_GET['rev_id']),

        );

    }

This will obviously not work, but what IS the most elegant way of doing this? One option that has crossed my mind is to make an extra column in the document table, and set the ID of the revision that the users wants to see, and then use that column in the relation. But updating a row just to view data seems ugly.

try


$data = ModelName::model()->with('rev')->find('rev_id=:id', array('id'=>$_GET['rev_id']));

and use the first rev definition

This doesn’t work since it will also use the existing ‘rev’ relation, so it searches for the ‘current_revision’ value AND the supplied revision ID, so it will never return anything. (unless the values are the same)

I’ve tried adding a new relation using HAS_ONE relation (matching ALL revisions to a document) and then using find() to get the right ID. This works but the new relation messes up my structure…

Are there any other ways of doing this? I was thinking about dynamically overwriting the relation in the AR class, but this probably isn’t the right way. Another option is to change the current_revision ID just before loading the data, and after that, returning it to the previous ID. But this requires two saves for every view so that’s pretty ugly also…

I tried this, and it worked… but it hurt my eyes ;)


public function relations()

{

    $rev_rel = array(self::BELONGS_TO, 'Revision', 'current_rev');

    if ((int)$_GET['rev'] > 0) {

        $rev_rel = array(self::HAS_ONE, 'Revision', 'pid', 'on' => 'rev.nr=' . $_GET['rev']);

    }


    return array(

        'rev' => $rev_rel,

    );

}

Is there a way to dynamically set a property to the AR class, instead of using $_GET vars?

In order to be ‘MVC correct’ I suppose you could create a controller property based on GET value and refer to $this->property in the model. Models aren’t supposed to deal with POST, GET directly.

You’d probably want to ‘sanitize’ that GET before assigning it.

In the model then you could just check if $this->rev is !empty.

I know models shouldn’t be doing controller-logic (and relations shouldn’t contain business logic either) but this was more like a proof of concept.

I think I’ll just have to create a new attribute and assign that, that would make it a little bit less ugly. The ideal way would be to change the existing ‘current_relation’ attribute so that I could use my existing relation instead of hacking in a new one. But I’m not sure if this is possible without actually saving the model before fetching the relation.