Doctrine Collection -> ActionScript ArrayCollection

Doctrine Collection -> ActionScript ArrayCollection

One of the difficulties of using Doctrine with AMF (Zend_AMF in my case) is that the Doctrine_Collection class does not have a method to convert it to an ActionScript ArrayCollection so that it can play nice with AMF/ActionScript. You could use the toArray() method on a Doctrine collection but then you would have to cast the arrays back to objects when it gets hydrated on the ActionScript side. Kinda defeats the purpose of using an ORM

So I decided to create a new method, toAmf(), in Doctrine as a work around. Ideally, creating a new AMF hydration method in Doctrine would be better and save some processing but I’ll save that for another day. For the record, I’ve tested and been using this hack in Doctrine 1.1 and 1.2 w/o any problems.

We’re going to be basing this off of the toArray() method so you’ll see that the toAmf() method created is very similar. First thing you need to do is add the following ArrayCollection class to the Doctrine_Collection class in Doctrine/Collection.php.

class ArrayCollection{
	public $_explicitType = "flex.messaging.io.ArrayCollection";
	public $source = array();
 
	function ArrayCollection(){
		$this->source = array();
	}
}

I put this above the Doctrine_Collection class decleration. You could also probably just put it in a separate php file and include it or add it to a bootstrap file (I just do it the ugly way). This ArrayCollection class will be what we convert the Doctrine collections to.

Next, you’ll need to create the following new method in the Doctrine_Collection class:

    public function toAmf($deep = false, $prefixKey = false)
    {
        $data = new ArrayCollection();
        foreach ($this as $key => $record) {          
 
        	if($prefixKey) $key = get_class($record) . '_' .$key;
 
			$data->source[$key] = $record->toAmf($deep, $prefixKey);
        }
 
        return $data;
    }

Now we’ll need to create a toAmf() method in the Doctrine_Record class in Doctrine/Record.php. This method will convert any nested collections using the toAmf() method from the Doctrine_Collection class above.

    public function toAmf($deep = true, $prefixKey = false)
    {
        if ($this->_state == self::STATE_LOCKED || $this->_state == self::STATE_TLOCKED) {
            return false;
        }
 
        $stateBeforeLock = $this->_state;
        $this->_state = $this->exists() ? self::STATE_LOCKED : self::STATE_TLOCKED;
 
        $a = array();
 
        foreach ($this as $column => $value) {
            if ($value === self::$_null || is_object($value)) {
                $value = null;
            }
 
            $columnValue = $this->get($column);
 
            if ($columnValue instanceof Doctrine_Record) {
                $a[$column] = $columnValue->getIncremented();
            } else {
                $a[$column] = $columnValue;
            }
        }
 
        if ($this->_table->getIdentifierType() ==  Doctrine::IDENTIFIER_AUTOINC) {
            $i      = $this->_table->getIdentifier();
            $a[$i]  = $this->getIncremented();
        }
 
        if ($deep) {
            foreach ($this->_references as $key => $relation) {
                if ( ! $relation instanceof Doctrine_Null) {
                    $a[$key] = $relation->toAmf($deep, $prefixKey);
                }
            }
        }
 
        // [FIX] Prevent mapped Doctrine_Records from being displayed fully
        foreach ($this->_values as $key => $value) {
            $a[$key] = ($value instanceof Doctrine_Record)
                ? $value->toAmf($deep, $prefixKey) : $value;
        }
 
        $this->_state = $stateBeforeLock;
 
        return (object)$a;
    }

That’s it. Here’s a simple example of it in use:

	$q = Doctrine_Query::create()->from('Users u');
	$result = $q->execute();
	$amf_compatible = $result ->toAmf(true);

Don’t forget to set your _explictType on your classes as I explained here so that your ActionScript knows what type to hydrate it to. Thanks to Jonathan Wage for pointing me in the right direction.