abstract class PersistentObject

PersistentObject base class that implements getter/setter methods for all mapped fields and associations by overriding __call.

This class is a forward compatible implementation of the PersistentObject trait.

Limitations:

1. All persistent objects have to be associated with a single ObjectManager, multiple ObjectManagers are not supported. You can set the ObjectManager with `PersistentObject#setObjectManager()`. 2. Setters and getters only work if a ClassMetadata instance was injected into the PersistentObject. This is either done on `postLoad` of an object or by accessing the global object manager. 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. 5. Only the inverse side associations get autoset on the owning side aswell. Setting objects on the owning side will not set the inverse side associations.

@example

PersistentObject::setObjectManager($em);

class Foo extends PersistentObject { private $id; }

$foo = new Foo(); $foo->getId(); // method exists through __call

@author Benjamin Eberlei <kontakt@beberlei.de>

Hierarchy

Expanded class hierarchy of PersistentObject

1 file declares its use of PersistentObject
PersistentObjectTest.php in drupal/core/vendor/doctrine/common/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php

File

drupal/core/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php, line 58

Namespace

Doctrine\Common\Persistence
View source
abstract class PersistentObject implements ObjectManagerAware {

  /**
   * @var ObjectManager
   */
  private static $objectManager;

  /**
   * @var ClassMetadata
   */
  private $cm;

  /**
   * Set the object manager responsible for all persistent object base classes.
   *
   * @param ObjectManager $objectManager
   */
  public static function setObjectManager(ObjectManager $objectManager = null) {
    self::$objectManager = $objectManager;
  }

  /**
   * @return ObjectManager
   */
  public static function getObjectManager() {
    return self::$objectManager;
  }

  /**
   * Inject Doctrine Object Manager
   *
   * @param ObjectManager $objectManager
   * @param ClassMetadata $classMetadata
   *
   * @throws \RuntimeException
   */
  public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) {
    if ($objectManager !== self::$objectManager) {
      throw new \RuntimeException("Trying to use PersistentObject with different ObjectManager instances. " . "Was PersistentObject::setObjectManager() called?");
    }
    $this->cm = $classMetadata;
  }

  /**
   * Sets a persistent fields value.
   *
   * @param string $field
   * @param array $args
   *
   * @throws \BadMethodCallException - When no persistent field exists by that name.
   * @throws \InvalidArgumentException - When the wrong target object type is passed to an association
   * @return void
   */
  private function set($field, $args) {
    $this
      ->initializeDoctrine();
    if ($this->cm
      ->hasField($field) && !$this->cm
      ->isIdentifier($field)) {
      $this->{$field} = $args[0];
    }
    else {
      if ($this->cm
        ->hasAssociation($field) && $this->cm
        ->isSingleValuedAssociation($field)) {
        $targetClass = $this->cm
          ->getAssociationTargetClass($field);
        if (!$args[0] instanceof $targetClass && $args[0] !== null) {
          throw new \InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'");
        }
        $this->{$field} = $args[0];
        $this
          ->completeOwningSide($field, $targetClass, $args[0]);
      }
      else {
        throw new \BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm
          ->getName() . "'");
      }
    }
  }

  /**
   * Get persistent field value.
   *
   *
   * @param string $field
   *
   * @throws \BadMethodCallException - When no persistent field exists by that name.
   * @return mixed
   */
  private function get($field) {
    $this
      ->initializeDoctrine();
    if ($this->cm
      ->hasField($field) || $this->cm
      ->hasAssociation($field)) {
      return $this->{$field};
    }
    else {
      throw new \BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm
        ->getName() . "'");
    }
  }

  /**
   * If this is an inverse side association complete the owning side.
   *
   * @param string $field
   * @param ClassMetadata $targetClass
   * @param object $targetObject
   */
  private function completeOwningSide($field, $targetClass, $targetObject) {

    // add this object on the owning side aswell, for obvious infinite recursion
    // reasons this is only done when called on the inverse side.
    if ($this->cm
      ->isAssociationInverseSide($field)) {
      $mappedByField = $this->cm
        ->getAssociationMappedByTargetField($field);
      $targetMetadata = self::$objectManager
        ->getClassMetadata($targetClass);
      $setter = ($targetMetadata
        ->isCollectionValuedAssociation($mappedByField) ? "add" : "set") . $mappedByField;
      $targetObject
        ->{$setter}($this);
    }
  }

  /**
   * Add an object to a collection
   *
   * @param string $field
   * @param array $args
   *
   * @throws \BadMethodCallException
   * @throws \InvalidArgumentException
   */
  private function add($field, $args) {
    $this
      ->initializeDoctrine();
    if ($this->cm
      ->hasAssociation($field) && $this->cm
      ->isCollectionValuedAssociation($field)) {
      $targetClass = $this->cm
        ->getAssociationTargetClass($field);
      if (!$args[0] instanceof $targetClass) {
        throw new \InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'");
      }
      if (!$this->{$field} instanceof Collection) {
        $this->{$field} = new ArrayCollection($this->{$field} ?: array());
      }
      $this->{$field}
        ->add($args[0]);
      $this
        ->completeOwningSide($field, $targetClass, $args[0]);
    }
    else {
      throw new \BadMethodCallException("There is no method add" . $field . "() on " . $this->cm
        ->getName());
    }
  }

  /**
   * Initialize Doctrine Metadata for this class.
   *
   * @throws \RuntimeException
   * @return void
   */
  private function initializeDoctrine() {
    if ($this->cm !== null) {
      return;
    }
    if (!self::$objectManager) {
      throw new \RuntimeException("No runtime object manager set. Call PersistentObject#setObjectManager().");
    }
    $this->cm = self::$objectManager
      ->getClassMetadata(get_class($this));
  }

  /**
   * Magic method that implements
   *
   * @param string $method
   * @param array $args
   *
   * @throws \BadMethodCallException
   * @return mixed
   */
  public function __call($method, $args) {
    $command = substr($method, 0, 3);
    $field = lcfirst(substr($method, 3));
    if ($command == "set") {
      $this
        ->set($field, $args);
    }
    else {
      if ($command == "get") {
        return $this
          ->get($field);
      }
      else {
        if ($command == "add") {
          $this
            ->add($field, $args);
        }
        else {
          throw new \BadMethodCallException("There is no method " . $method . " on " . $this->cm
            ->getName());
        }
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PersistentObject::$cm private property
PersistentObject::$objectManager private static property
PersistentObject::add private function Add an object to a collection
PersistentObject::completeOwningSide private function If this is an inverse side association complete the owning side.
PersistentObject::get private function Get persistent field value.
PersistentObject::getObjectManager public static function
PersistentObject::initializeDoctrine private function Initialize Doctrine Metadata for this class.
PersistentObject::injectObjectManager public function Inject Doctrine Object Manager Overrides ObjectManagerAware::injectObjectManager
PersistentObject::set private function Sets a persistent fields value.
PersistentObject::setObjectManager public static function Set the object manager responsible for all persistent object base classes.
PersistentObject::__call public function Magic method that implements