problem with CActiveRecord::$scenario

Why? Most of the time, the AR state is completely irrelevant - most validations do not depend on the state of AR at all. And as a workaround, you propose this statement:


$model->scenario='being '.($model->isNewRecord ? 'inserted':'updated').' by an administrator'

Look at the complexity of this statement - think of how many times you would need to write that same statement over again, and think about what it expresses: “we don’t care about AR state in this case”. That is probably the case for most validation rules.

I think the most common situation should require the most simple expression - but this achieves the opposite.

But who says you only need to validate once?

And how can you explain this clearly in documentation? "If you need to use the scenario for other aspects, besides validation as provided by the framework, please track your own scenarios using some other variable".

Even if you could explain it like this (or better), that just raises more questions - if I’m tracking my scenario using a different variable, how can I add validation rules that apply to my scenario?

And even if you end up writing your own validator, perhaps extending a standard validator, to take your scenario into account, you would still run into edge cases, for example when validation is performed more than once, where your scenario is out of sync with $scenario - something that passed validation the first time might not pass the second time, and not because the scenario changed, but because the condition of AR changed internally, something your application has no control of.

But read it again:

"Scenario affects how validation is performed and which attributes can be massively assigned"

How can the circumstances of AR’s internal statemachine management be described as what is performed and what can be done to a model?

Remember, this an attribute that applies to any model, not just to persistent ones - but you think it’s acceptable that, under some condition, in this case inserting, the meaning of this property suddenly changes to describe something that is unrelated to models in general, and specific to AR?

I disagree. You have this one case, inserting, where suddenly the meaning of an attribute changes - I think that’s messy, confusing, and hard to explain… in my opinion, anything that’s too hard to explain, or contains too many “what if’s”, just isn’t good enough.

I hope you don’t think I’m just trying to be argumentative here :wink:

I think there’s a real general issue here, and the current approach is not clean and causes edge cases.

Citing Ruby on Rails, it completely separates the condition of the associated database record from anything else - the “on” attribute, for validations, is used only to track the condition of the underlying record, e.g. “update” or “insert”, nothing else, thus avoiding the domain overlap that occurs in Yii’s AR.

To describe scenarios or cases where a particular rule may or may not apply, Rails uses another attribute named "if", and you can see an example of how that works here.

An example of how that might translate to Yii’s AR could be something along the lines of this:




class User extends CModel

{

  public $registering=false;

  

  public function rules()

  {

    return array(

        array('username', 'required'),

        array('username', 'length', 'min'=>3, 'max'=>12),

        array('password', 'compare', 'compareAttribute'=>'password2', 'if'=>'registering'),

        array('password', 'authenticate', 'if'=>'loggingIn'),

    );

  }

  

  public function getLoggingIn()

  {

    return isset($_POST['LoginForm']) ? true : false;

  }

}


...


class UserController extends CController

{

  public function actionRegister()

  {

    $user = new User;

    $user->registering = true;

    

    if (isset($_POST['User']))

    {

      $user->attributes = $_POST['User'];

      if ($user->validate())

      {

        // etc.

      }

    }

  }

}



It would seem that maybe what we need is an untanglement of this domain overlap, but in the opposite direction?

Maybe the answer is not to add a separate property, e.g. $condition, for exclusive use by AR, but rather to reserve the existing $scenario for exclusive use by AR?

I guess the problem with that idea, is that $scenario is already present in the general-purpose CModel - there is probably already myriad code using the $scenario attribute for conditional validation of form models, for example.

I don’t have an answer to that right now, but let’s try to think in another direction and see where that takes us. Ideas, anyone? :slight_smile:

Thinking about this, I still think "scenario" is the wrong word to describe this - if the meaning of this property is changed to imply something about the condition of the record stored by AR (or another third-party persistence engine), the definition of the word "scenario" is even less applicable than before… "a narrative describing foreseeable interactions of types of users (characters) and the system" - the $scenario property really describes interaction between an application component and a framework component, there is no user involved (not necessarily).

On the other hand, the $scenario property is probably being used extensively on form-models, and possibly other throw-away/abstract models, to actually describe a scenario in which a user is working with the model.

In my opinion, the word “scenario” is just too misleading - with a name like that, you have to expect this kind of usage - even the meaning of this property was changed, just by means of documentation, you have to expect it’s going to cause confusion.

Besides, actual use-case scenarios could be more accurately described by adding transient state variables, and/or methods that calculate state, to the model, as in the code example I posted above - much more accurately than trying to collapse the conditions into one dimension.

Some validation rules may apply to certain types of users, with a certain status, on certain days - so you could very well have validation rules that rely on more than "one dimension" to describe a scenario.

Note that some validation rules may apply to certain conditions AND/OR other conditions - but we don’t need to attempt to support ways to express this with some sort of complex syntax, like ‘if’=>’(this || that) && theOther’, which would probably involve parsing or eval() or worse…

All that’s required is a simple attribute, and that can be implemented by a get*() accessor, which can then implement any kind of logic required to determine if a validation rule applies, so ‘if’=>‘thisOrThatAndTheOther’ would just invoke getThisOrThatAndTheOther() which would contain that logic.

The solution is pretty simple, I think? Probably just a few lines of code.

The real challenge is how to deal with the existing misnomer ($scenario) and how to make the best possible recommendation for when to use these features…

Found that on wiki Scenario_(computing) too.

So the scenario by definition is not a AR’s attribute that can be changed by changing its states. It is as far as I understand the definition something that is completely outside of the AR, different category, different story.

Another quote:

"Scenarios are neither predictions nor forecasts, but rather attempts to reflect on or portray the way in which a system is used in the context of daily activity."

and "scenarios are used directly to define the wanted behaviour of software".

Let’s see at UML.

Scenarios are described in sequence diagrams for example.

States of objects are described in state diagrams.

And for me it was really confusing to understand that the scenario reflects statemachine of AR.

Current state of AR depends on many factors, on of them may be the current "we are here" of the scenario. So the state depends on scenario. The state is not the scenario.

I still might be wrong, I’m still learning!

Mindplay, don’t give up, I tell ya, this will end up like: move to 1.1.x release :lol:

@veris: do not attempt to look for an existing interpretation of “scenario”. This is a term coined in Yii only. It originates from RoR’s ‘on’ parameter and later was extended based on Jonah’s proposal.

@mindplay: before worrying about how to implement it, I think we need to clarify the issue and reach agreement first. In your "User" example, why would scenario be inappropriate? Using the current implementation, you could do:




class User extends CModel

{

  public $registering=false;

  

  public function rules()

  {

        ....

        array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),

        array('password', 'authenticate', 'on'=>'login'),

  }

}


...


class UserController extends CController

{

  public function actionRegister()

  {

    $user = new User('register');

    .....

  }

  

  public function actionLogin()  

  {

    $user = new User('login');

    .....

  }

}



Maybe I still don’t quite understand the issue. If you have another example, could you enlighten me? Thanks.

BTW, my proposed solution below is only for explanation purpose. As I said, this is rarely needed in practice (or maybe you can convince me it happens frequently?)




$model->scenario='being '.($model->isNewRecord ? 'inserted':'updated').' by an administrator



In your example, suppose there’s a third scenario.

Now suppose you have a whole bunch of rules that apply to 2 of your 3 scenarios.

You can’t express that without repeating every rule for the 2 scenarios that apply.

You can write a rule like:

array(‘username’, ‘required’, ‘on’=>‘register, login’)

Maybe I still don’t understand you properly?