Conditions and Master Fields

The key difference between DSQL and Model is that Model has a strictly defined set of fields. Fields can be of a few types such as regular SQL fields, calculated fields or fields from joined table. All three cases will have different format when in a query. Developer who uses Model_Table, however, has a luxury of ignoring field type and placing a condition just by using field name.

class Model_Ojiisan extends Model_User {
  
// Ojiisan = Old Man
  
function init(){
    
parent::init();

    
$this->addField('gender')->enum(array('M','F'));
    
$this->addExpression('age','year(now())-year(birthdate)');  

    
$this->addCondition('gender','M');
    
$this->addCondition('age','>',65);
    
  }
}

Above code will approach queries for both fields differently, but the syntax is consistent. This allows for UI to ignore nature of the fields and generically add conditions as needed.

Setting conditions will modify master query and it will have effect on selects, updates and deletes. It will not have any effect on insert() and therefore model will allow you to insert a record which you won't be able to load with the same model afterwards.

Master Fields

Master fields are similar to conditions, but model will also use a default value for when inserting records. masterField() does not support operation.

class Model_Ojiisan extends Model_User {
  
// Ojiisan = Old Man
  
function init(){
    
parent::init();

    
$this->addField('gender')->enum(array('M','F'));
    
$this->addExpression('age','year(now())-year(birthdate)');  

    
$this->setMasterField('gender','M');
    
$this->addCondition('age','>',65);
  }
}

$model=$page->add('Model_Ojiisan');
$model->set('age',20);
$model->save();

In the example above, model attempt to insert record with gender=M and age=20. save() method will attempt to load model after it is successfully saved, but the condition will prevent model from loading. This will cause exception and the transaction will be rolled back.

setMasterField() will also mark field as system to avoid accidental change. Be aware that setMasterField() should happen before model is binded with view, otherwise system field flag will have no effect:

// WRONG
$page->add('CRUD')
  ->
setModel('Book')
  ->
setMasterField('published','Y');
// RIGHT
$m=$page->add('Model_Book')
  ->
setMasterField('published','Y');
$page->add('CRUD')->setModel($m);

Conditions on Calculated Fields

Let's assume that we have a model called "Model_Member" and we are looking to extend it into "Model_Member_Active". Active members are those whoose "expiry_date" is in the future.

Let's look at two ways how to achieve this.

Adding Conditions on Expression

Expressions are treated like a normal fields by a model. You can define an expression, then add condition on it.

class Model_Member_Active extends Model_Member {
  function 
init(){
    
parent::init();
    
$this->addExpression('is_member')->set('if(expiry_date>now(),"Y","N")');
    
$this->addCondition('is_member','Y');
  }
}

Modifying master DSQL

The other option is to simply modify master DSQL and have the condition embedded into it.

class Model_Member_Active extends Model_Member {
  function 
init(){
    
parent::init();
    
$this->dsql->where($this->dsql->expr('if(expiry_date>now())'));
  }
}

Conditions on relative fields or joined models

Suppose you want to list all books by female authors. You would already have a book model, but it does not contain field for author. The following code accomplishes the task:

$book_by_f=$this->add('Model_Book');
$book_by_f->join('author')->addField('gender')->enum(array('M','F'));
$book_by_f->addCondition('gender','F');

If you wish to have a standalone model, then you need to copy this code inside a model's init:

class Model_Book_ByF extends Model_Book {
  function 
init(){
    
parent::init();
    
$book_by_f=$this;
    
$book_by_f->join('author')->addField('gender')->enum(array('M','F'));
    
$book_by_f->addCondition('gender','F');
  }
}

Note that adding to this model would also create a related author record. To prevent accidental adding and avoid accidental change of the gender in a form, you can do this:

$book_by_f->getField('gender');
$book_by_f->addHook('beforeInsert',function($t){throw $t->exception('Do not add records!');});