Monday 21 February 2011

drupal_write_record() and properties

It would be nice if you could use your own classes and objects with drupal_write_record(), but you can't - that's if you want to have private or protected properties.

Let's say you have this class:

class myClass {
  protected $xid, $nid, $mydata;


  public function __construct($nid, $mydata, $xid = 0) {
    $this->xid = $xid;    $this->nid = $nid;
    $this->mydata = $mydata;
  }

  public function __get($property) {
    if (property_exists($this, $property) {
      $value = $this->$property;
    }
    return $value;
  }

  public function __set($property, $value) {
    switch ($property) {
      case 'xid':
         // Don't allow xid to be set, it's read-only
         break;
      default:
        if (property_exists($this, $property) {
          $this->$property = $value;
        }
    }
  }

  public function __isset($property) {
    return isset($this->$property);
  }

  public function save() {
    if ($this->xid) {
      $this->update();
    }
    else {
      $this->insert();
    }
  }

  protected function insert() {
    drupal_write_record('mydata_table', $this);
  }

  protected function update() {
    drupal_write_record('mydata_table', $this, 'xid');
  }
}

This won't work because the drupal_write_record() function uses 'property_exists()' to check for properties and not 'isset()', and that will not return TRUE for private or protected properties.

One of the features of OOP is encapsulation, making data that should not be accessible, not accessible but that means you cannot use drupal_write_record(). If you want to use that function directly with your own classes you have to make the properties public - defeating the point of encapsulation.

However the solution is not too bad, you can do this:

  protected function insert() {
    $object = (object) array();
    foreach ($this as $field => $value) {
      $object->$field = $value;
    }

    drupal_write_record('mydata_table', $object);
    $this->xid = $object->xid;
  }

  protected function update() {
    $object = (object) array();
    foreach ($this as $field => $value) {
      $object->$field = $value;
    }

    drupal_write_record('mydata_table', $object, 'xid');
  }

If truth be told, for the 'insert()' to work you'd need to make 'xid' publicly accessible anyway (since drupal_write_record() wants to write to it), so perhaps this solution is for the best.

No comments: