Custom Validator Example

OK, this may be completely obvious to everyone, but I’m a newbie and didn’t see how to do it, so I thought I’d share. I needed to validate that a registering user entered city, state, and zip code data that was internally consistent. I have a table populated with data from zipcodes.com which provides the data for that validation.

My validation looks like:




class CityStateZipValidator extends CValidator

{

	public $skipOnError=true;

	protected function validateAttribute($object,$attribute)

	{

		$value=$object->$attribute;

		

		if ($object->CountryCode != 'US')

			return;

		if ($this->isEmpty($object->Zip))

			return;

		if ($this->isEmpty($object->City))

			return;

		if ($this->isEmpty($object->State))

			return;

			

		$zips = ZipCode::model()->findAll('ZIPCODE=?',array($object->Zip));

		

		if (count($zips) == 0)

			{

			$this->addError($object,'Zip','Invalid zip/postal code specified.',array('{value}'=>$object->Zip));

			return;

			}

		

		$citymatch = false;

		$statematch = false;

		$city_to_match = strtolower($object->City);

		foreach ($zips as $zip)

				{

				if (strtolower($zip->GetAttribute('CITY')) == $city_to_match ||

					strtolower($zip->GetAttribute('CITYALIASNAME')) == $city_to_match ||

					strtolower($zip->GetAttribute('CITYALIASABBREVIATION')) == $city_to_match)

					{

					$citymatch = true;

					}

				if ($zip->GetAttribute('STATE') == $object->State)

					{

					$statematch = true;

					}

				}

		

		if(! $statematch)

		{

			$this->addError($object,'State','The state selected doesn\'t match the zip/postal code specified. Did you mean '.$zips[0]->GetAttribute('STATE').'?',array('{value}'=>$object->State));

		}

		if(! $citymatch)

		{

			$this->addError($object,'City','The city specified doesn\'t match the zip/postal code. Did you mean '.$zips[0]->GetAttribute('CITY').'?',array('{value}'=>$object->Zip));

		}

	}

}



Obviously, this code should be made more flexible and accept a lot of the hard-coded model fields as parameters, make the error messages translateable, etc, but for a quick and dirty approach, it will work.

That’s why I wouldn’t make it a class-based validator. You can make it a part of your model class (method-based validator). Everything else looks good to me :)