Posts Tagged ‘propel’

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

Criteria::CUSTOM – the bane of my existence, until now…

Wednesday, April 22nd, 2009

A couple weeks ago I came across a query that I needed to write that would only retrieve the records that had the same date  as another table’s record.  Since I am using propel, I looked for a simple way to format the datetime field to just match the dates in the query so that only the appropriate records would be returned.  I googled for a long time, not reaching any solution.

I ended up with a stopgap measure to get me by until I could focus more time on the problem.  I iterated through the results and used php to compare the dates and drop the nonequal date records.  Today the problem came to a head where I could not move forward with my work until I solved the issue.  Since I’m using mysql and propel, I ended up creating a custom criteria using the mysql ‘DATE’ function.  I know this is suboptimal in that my query is now tied to mysql, but I really don’t expect to be changing databases soon, or ever.

Most of the solutions that I found online would not work with the DATE function for one reason or another (another = I couldn’t figure it out).  I finally found my solution in the symfony forums (can you believe it!?!?!) here.  For some reason google failed me on this one.  So out of the billions of pages online I had to find the one (1) that had my solution.  Luckily time_to_find_solution < losing_my_mind.  So the very simple solution is this:

$c->add(ActivityInfoPeer::ACTIVITY_EVENT_TIME, 'DATE('.ActivityInfoPeer::ACTIVITY_EVENT_TIME.')= DATE('.ActivitySeismoInfoPeer::ASI_EVENT_TIME.')', Criteria::CUSTOM);

Till Next Time

adding OR conditional to a symfony query

Thursday, April 9th, 2009

I had to do some looking to find how to do an ‘or’ statement in symfony with propel.  I found out at a google’s group here and the api documentation from propel is here.  Once I found the information it was surprisingly easy.  Below is the code I ended up using to select records either updated in the last two weeks OR activities that are delayed AND activities that the posting times are NULL (implying they are not posted yet).

$c = new Criteria();
$c->AddDescendingOrderByColumn(ActivityInfoPeer::ACTIVITY_EVENT_TIME);
 
$criterion = $c->getNewCriterion(
ActivityInfoPeer::UPDATED_AT, date("Y-m-d H:i:s", time() - 1209600), Criteria::GREATER_THAN)->addOr(
     $c->getNewCriterion(ActivityInfoPeer::ACTIVITY_DELAYED, 1, Criteria::EQUAL)
     )->addAnd($c->getNewCriterion(ActivityInfoPeer::ACTIVITY_POST_TIME, null, Criteria::ISNULL)
     );
$c->add($criterion);
 
$this->pending_activities = ActivityInfoPeer::doSelect($c);

Till Next Time

setting memory_limit for symfony propel:build-model

Thursday, March 12th, 2009

A few days ago my company got our production server for the upcoming website up and running so that I can start working with it.  Since this server is being hosted at another office out of state I am learning how to deploy symfony on a server that I don’t have physical access to.

When doing all my previous work (all 3 months!) with symfony, I have either worked on it on my local machine or on a small dev server in our server closet.  The main difference on this new server is that I cannot update the php.ini file whenever I choose.  I am able to have it modified when there is a long-term need (ie- setting magic_quotes_gpc & short_open_tag to Off).  But when running the build commands for the new platform I need a good bit of memory.  The propel:build-model action takes more than 32 MB of memory and since I couldn’t change the php.ini file I had to find a way around it.

I discovered that the symfony .bat file that is in the symfony distro has php in it and I was able to simply use the ini_set function to set the memory limit to 64MB while executing any CLI symfony scripts.

Till Next Time