Posts Tagged ‘manual database field changes’

modifying symfony database fields

Friday, May 22nd, 2009

I am making progress on my company’s new website.  I’m about 3 months in and will be launching the first version of the site in a few weeks.  So it was dismaying, to say the least, when I found that when I created the database schema for the symfony project I used the wrong data type for a few fields.

In the current platform these particular fields are stored in MySQL ‘text’ fields.  Simple, expands as needed, no problem.  I accidentally set these fields in the new schema as varchars.  My issue is that for most users, my varchar(32) field would be fine, but for that occasional user who uses lots of explosives & different delays, this would mean a loss of data.  Not something I am very keen on.

I googled a bit and could not find any information on doing a reconfiguration of the database fields in symfony and I figured it wouldn’t be too difficult to manually convert the field to a blob and modify all the accessing functions.  In the end, I was right!  Assuming you don’t make any mistakes it is remarkably simple to change a database field from a varchar to a blob.  You only need to modify two files and four functions.  So, without further adieu, I give to you the code to make a blob field accessible instead of a varchar field for symfony through propel.

(Original code is commented out for reference)

// lib/model/om/BaseActivityInfo.php
	public function getActivityExplosiveType()
	{
//		return $this->activity_explosive_type;
		$content = "";
		if(is_resource($this->activity_explosive_type))
		{
			while(!feof($this->activity_explosive_type))
			{
				$content .= fread($this->activity_explosive_type, 4096);
			}
			rewind($this->activity_explosive_type);
			return $content;
		}
		else
			return $this->activity_explosive_type;
	}
 
	public function setActivityExplosiveType($v)
	{
// this is the new code (copied & modified from setActivityExtra1) to set BLOB type
 
		// Because BLOB columns are streams in PDO we have to assume that they are
		// always modified when a new value is passed in.  For example, the contents
		// of the stream itself may have changed externally.
		if (!is_resource($v)) {
			$this->activity_explosive_type = fopen('php://memory', 'r+');
			fwrite($this->activity_explosive_type, $v);
			rewind($this->activity_explosive_type);
		} else { // it's already a stream
			$this->activity_explosive_type = $v;
		}
		$this->modifiedColumns[] = ActivityInfoPeer::ACTIVITY_EXPLOSIVE_TYPE;
 
		return $this;
/*
//	this is the original code for when the field was a simple varchar
		if ($v !== null) {
			$v = (string) $v;
		}
*/
	}
 
	public function hydrate($row, $startcol = 0, $rehydrate = false)
	{
		try {
//   ... other field's code ...
 
//		this is the original hydration code for the varchar type activity_explosive_type
//			$this->activity_explosive_type = ($row[$startcol + 36] !== null) ? (string) $row[$startcol + 36] : null;
//		this is the new hydration code for the blob type activity_explosive_type
			if ($row[$startcol + 36] !== null) {
				$this->activity_explosive_type = fopen('php://memory', 'r+');
				fwrite($this->activity_explosive_type, $row[$startcol + 36]);
				rewind($this->activity_explosive_type);
			} else {
				$this->activity_explosive_type = null;
			}
//  ... other field's code ...
			$this->resetModified();
 
			$this->setNew(false);
 
			if ($rehydrate) {
				$this->ensureConsistency();
			}
 
			// FIXME - using NUM_COLUMNS may be clearer.
			return $startcol + 92; // 92 = ActivityInfoPeer::NUM_COLUMNS - ActivityInfoPeer::NUM_LAZY_LOAD_COLUMNS).
 
		} catch (Exception $e) {
			throw new PropelException("Error populating ActivityInfo object", $e);
		}
	}
 
		if ($this->activity_explosive_type !== $v) {
			$this->activity_explosive_type = $v;
			$this->modifiedColumns[] = ActivityInfoPeer::ACTIVITY_EXPLOSIVE_TYPE;
		}
 
		return $this;
 
	} // setActivityExplosiveType()
 
// lib/model/map/ActivityInfoMapBuilder.php
 
	public function doBuild()
	{
		$this->dbMap = Propel::getDatabaseMap(ActivityInfoPeer::DATABASE_NAME);
 
		$tMap = $this->dbMap->addTable(ActivityInfoPeer::TABLE_NAME);
		$tMap->setPhpName('ActivityInfo');
		$tMap->setClassname('ActivityInfo');
 
		$tMap->setUseIdGenerator(true);
 
		$tMap->addPrimaryKey('ID', 'Id', 'INTEGER', true, null);
// ... other field's code ...
//	this is the original code for the varchar type activity_explosive_type field
//		$tMap->addColumn('ACTIVITY_EXPLOSIVE_TYPE', 'ActivityExplosiveType', 'VARCHAR', false, 32);
// 	this is the new code for the blob type activity_explosive_type field
		$tMap->addColumn('ACTIVITY_EXPLOSIVE_TYPE', 'ActivityExplosiveType', 'BLOB', false, null);
// ... other field's code ...
	} // doBuild()
 
} // ActivityInfoMapBuilder

UPDATE:
I was doing some work with these form elements and realized that the Validators need to be modified as well. It’s rather simple, all you need to do is change the validator to remove any size limitations and convert the Validator to a sfValidatorString from whatever type of sfValidator it was. Code below:

// lib/form/base/BaseActivityInfoForm.class.php
...other code...
// original code
//      'activity_explosive_type'          => new sfValidatorString(array('max_length' => 32, 'required' => false)),
      'activity_explosive_type'          => new sfValidatorString(array('required' => false)),
// original code
//      'activity_explosive_make'          => new sfValidatorString(array('max_length' => 32, 'required' => false)),
      'activity_explosive_make'          => new sfValidatorString(array('required' => false)),
// original code
//      'activity_explosive_quantity'      => new sfValidatorNumber(array('required' => false)),
      'activity_explosive_quantity'      => new sfValidatorString(array('required' => false)),
...other code...

Till Next Time