Difference between #4 and #2 of Using the jamselect widget with min and max values

unchanged
Title
Using the jamselect widget with min and max values
unchanged
Category
How-tos
unchanged
Tags
Form validation, Widgets
changed
Content
The problem
-----------
I needed to have users select from a list of keywords, in this example they must
select at least two keywords but they cannot select more than six.

The keywords will be stored in a text field in a comma delimited format.  The
keywords will be created by the site administrator.

In a very timely fashion the
[jamselect](http://www.yiiframework.com/extension/jamselect/
"jamselect") widget showed up, thanks volkmar!

The widget does not provide min/max filtering so I will implement a custom rule.


### FirstFirst put the widget in a form
------------------------------
This is my implementation.

~~~
[php]
<?php	

$this->widget(
    'application.extensions.asmselect.JAMSelect',
    array(
        'selected'=>$this->getKeywords($model->keywords),
		'data'=>CHtml::listData(Keywords::model()->findAll(),'word', 'word'),
                'htmlOptions'=>array(
            'title'=>'Select up to six keywords',
            'name'=>'keywordArray'
        ),
        'animate'=>true
    ));

?>
~~~
#### A few things to notice
The widget is located in /protected/extensions not
/protected/extensions/widgets, I only point this out for people who aren't sure
where to put things.  You have quite a bit of flexibility on where you locate
widgets and components. It doesn't makes sense to me to create another folder in
extensions but perhaps on a very large project it would.

 The dropdown list dataA few things to notice
----------------------
The widget is being pulled from a table that has only two fields,
'id' and 'word', sincelocated in `/protected/extensions not
/protected/extensions/widgets`, I wantonly point this out
for people who aren't sure where to select the word from the
listput things.  You have quite a bit of flexibility on where you
locate widgets and store exactly the same valuecomponents.
It doesn't makes sense to me to create another folder in the database
I needextensions but perhaps on a key=>value array that
looks like 'word'=>'word'.very large project it would.

 The $_POSTThe dropdown list data will contain an
array of selected values, this will beis being pulled from a table
that has only two fields, `id` and `word`, since I want to select the word from
the list and store exactly the same value in $_POST['keywordArray']

#### Lets look at the controller (create)database I need a
key=>value array that looks like `word'=>'word`.

The `$_POST` data will contain an array of selected values, this will be in
`$_POST['keywordArray']`

Lets look at the controller (create)
------------------------------------
~~~
[php]
	publicpublic function actionCreate()
	{
		$model=new
{
    $model=new EventAbstract;
		
		$this->layout = '//layouts/column1';

		// Uncomment the following line if AJAX validation is needed
		// $this->performAjaxValidation($model);    $this->layout =
'//layouts/column1';

		if(isset($_POST['EventAbstract']))
		{
			$model->attributes=$_POST['EventAbstract'];    // Uncomment the
following line if AJAX validation is needed
    // $this->performAjaxValidation($model);

     // I better do a check in case no keywords were selected
    $keywordArray = (!isset($_POST['keywordArray']) ? array() :
$_POST['keywordArray']); if(isset($_POST['EventAbstract']))
    {
        $model->attributes=$_POST['EventAbstract'];

    // now    // I either have an array of keywords
or an empty array
    // send the array tobetter do a function to format
thecheck in case no keywords in a 
    // comma delimited format
    $this->setKeywords($model,'keywords',$keywordArray);were
selected
        $keywordArray = (!isset($_POST['keywordArray']) ? array() :
$_POST['keywordArray']); 

    // it's all done but not yet validated
			if($model->save())
				$this->redirect(array('view','id'=>$model->id));
		}    // now I either have an array of keywords or an empty array
        // send the array to a function to format the keywords in a 
        // comma delimited format
        $this->setKeywords($model,'keywords',$keywordArray);

		$this->render('create',array(
			'model'=>$model,
		));
	}        // it's all done but not yet validated
        if($model->save())
            $this->redirect(array('view','id'=>$model->id));
    }

    $this->render('create',array('model'=>$model));
}
~~~
#### Here
Here is the function that formats the keywords
----------------------------------------------
Remember the keywords are suppled by the admin so I am not doing any checking
here for capitals or illegal characters.


~~~
[php]
	publicpublic function
setKeywords($model,$field,$keys)
	{
		$model->$field
{
    $model->$field = (empty($keys) ? null : implode(',',$keys));
	}
}
~~~

~~~
#### UpdateUpdate record
-------------
And when we perform an update on the record we have to turn our comma delimited
field back into an array, so similarly.

~~~
[php]

	publicpublic function getKeywords($delimited)
	{
		$keys
{
    $keys = explode(',',$delimited);
		return
    return $keys;
	}
}
~~~

#### RulesRules
-----
And now for the rules (in the model file)


~~~
[php]
	publicpublic function rules()
	{
		//
{
    // NOTE: you should only define rules for those attributes that
		//
    // will receive user inputs.
return
    return array(
//
        // other rules are here
			   
array('keywords','keywordCount','skipOnError'=>true,'maxKeys'=>6,'minKeys'=>2),
//
       
array('keywords','keywordCount','skipOnError'=>true,'maxKeys'=>6,'minKeys'=>2),
        // keywords is the field in the table currently holding the
submitted value
//
        // $model->keywords
//
        // keywordCount is the function (in the model) that will do the
validation
//
//skipOnError,
        //
        //skipOnError, maxKeys and minKeys are all parameters that will be
sent
        //sent to the validation function

// other rules

);
	
	}

    );
}
~~~

#### TheThe validation function


-----------------------
~~~
[php]
public function keywordCount($attribute,$params)
{
	 $keys    $keys = explode(',',$this->$attribute);
//
    // check if there are keywords exist
	if(!$this->$attribute)
	{
	    $this->addError($attribute,'<strong>If
    if(!$this->$attribute)
    {
        $this->addError($attribute,'<strong>If you need to add a
keyword please contact the administrator</strong>');
	} 
//
    } 
    // check if there are enough keywords
	if(count($keys)
    if(count($keys) < $params['minKeys'])
	{
	    $this->addError($attribute,'Please
    {
        $this->addError($attribute,'Please choose at least
'.$params['minKeys'].($params['minKeys'] > 1 ? ' keywords' :'
keyword'));
	} 
//
    } 
    // check if there are too many keywords
	if(count($keys)
    if(count($keys) > $params['maxKeys'])
	{
	    $this->addError($attribute,'You
    {
        $this->addError($attribute,'You have chosen too many keywords
max = '.$params['maxKeys']);
	}	 
}
	
    }	 
}	
~~~

Now the user can select between 2 and 6 keywords.

doodle