-
-
Save EmanueleMinotto/151c5610a58ace895b3d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Doctrine\ORM\EntityManager, | |
Doctrine\ORM\Configuration, | |
Doctrine\ORM\Mapping\ClassMetadata; | |
/** | |
* Active Entity trait | |
* | |
* Limitations: a class can only ever be assocaited with ONE active entity manager. Multiple entity managers | |
* per entity class are not supported. | |
*/ | |
trait ActiveEntity | |
{ | |
private $doctrineEntityManager; | |
private $doctrineClassMetadata; | |
public function setDoctrine($em, $classMetadata) | |
{ | |
$this->doctrineEntityManager = $em; | |
$this->doctrineClassMetadata = $classMetadata; | |
} | |
private function set($field, $args) | |
{ | |
if (isset($this->doctrineClassMetadata->fieldMappings[$field])) { | |
$this->$field = $args[0]; | |
} else if (isset($this->doctrineClassMetadata->associationMappings[$field]) && | |
$this->doctrineClassMetadata->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) { | |
$assoc = $this->doctrineClassMetadata->associationMappings[$field]; | |
if (!($args[0] instanceof $assoc['targetEntity'])) { | |
throw new \InvalidArgumentException( | |
"Expected entity of type '".$assoc['targetEntity']."'" | |
); | |
} | |
if ($assoc['type'] & ClassMetadata::ONE_TO_ONE && !$assoc['isOwning']) { | |
$setter = "set".$assoc['mappedBy']; | |
$args[0]->$setter($this); | |
} | |
$this->$field = $args[0]; | |
} else { | |
throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->doctrineClassMetadata->name."'"); | |
} | |
} | |
private function get($field) | |
{ | |
if ( (isset($this->doctrineClassMetadata->fieldMappings[$field]) && $this->doctrineClassMetadata->fieldMappings[$field]['type'] != "boolean") || | |
isset($this->doctrineClassMetadata->associationMappings[$field])) { | |
return $this->$field; | |
} else { | |
throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->doctrineClassMetadata->name."'"); | |
} | |
} | |
private function add($field, $args) | |
{ | |
if (isset($this->doctrineClassMetadata->associationMappings[$field]) && | |
$this->doctrineClassMetadata->associationMappings[$field]['type'] & ClassMetadata::TO_MANY) { | |
$assoc = $this->doctrineClassMetadata->associationMappings[$field]; | |
if (!($args[0] instanceof $assoc['targetEntity'])) { | |
throw new \InvalidArgumentException( | |
"Expected entity of type '".$assoc['targetEntity']."'" | |
); | |
} | |
// add this object on the owning side aswell, for obvious infinite recursion | |
// reasons this is only done when called on the inverse side. | |
if (!$assoc['isOwning']) { | |
$setter = (($assoc['type'] & ClassMetadata::MANY_TO_MANY) ? "add" : "set").$assoc['mappedBy']; | |
$args[0]->$setter($this); | |
} | |
$this->$field->add($args[0]); | |
} else { | |
throw new \BadMethodCallException("There is no method ".$method." on ".$this->doctrineClassMetadata->name); | |
} | |
} | |
private function is($field) | |
{ | |
if ( isset($this->doctrineClassMetadata->fieldMappings[$field]) && $this->doctrineClassMetadata->fieldMappings[$field]['type'] == "boolean") { | |
return $this->$field; | |
} else { | |
throw new \BadMethodCallException("There is no method ".$method." on ".$this->doctrineClassMetadata->name); | |
} | |
} | |
private function initializeDoctrine() | |
{ | |
$this->doctrineEntityManager = ActiveEntityRegistry::getClassManager($className = get_class($this)); | |
$this->doctrineClassMetadata = $this->doctrineEntityManager->getClassMetadata($className); | |
} | |
/** | |
* @param string $method | |
* @param array $args | |
* @return mixed | |
*/ | |
public function __call($method, $args) | |
{ | |
// this happens if you call new on the entity. | |
if ($this->doctrineClassMetadata === null) { | |
$this->initializeDoctrine(); | |
} | |
$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 if (substr($command, 0, 2) == "is") { | |
$this->is($field, $args); | |
} else { | |
throw new \BadMethodCallException("There is no method ".$method." on ".$this->doctrineClassMetadata->name); | |
} | |
} | |
public function persist() | |
{ | |
$this->doctrineEntityManager->persist($this); | |
} | |
public function remove() | |
{ | |
$this->doctrineEntityManager->remove($this); | |
} | |
static public function create(array $data = array()) | |
{ | |
$instance = new static(); | |
$instance->initializeDoctrine(); | |
foreach ($data AS $k => $v) { | |
$instance->set($k, array($v)); | |
} | |
return $instance; | |
} | |
static public function createQueryBuilder($rootAlias = 'r') | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->createQueryBuilder($rootAlias); | |
} | |
static public function find($id) | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->find($class, $id); | |
} | |
static public function findOneBy(array $criteria = array()) | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->getRepository($class)->findOneBy($criteria); | |
} | |
static public function findBy(array $criteria = array(), $orderBy = null, $limit = null, $offset = null) | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->getRepository($class)->findBy($criteria, $orderBy, $limit, $offset); | |
} | |
static public function findAll() | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->getRepository($class)->findAll($criteria); | |
} | |
static public function expr() | |
{ | |
$class = get_called_class(); | |
return ActiveEntityRegistry::getClassManager($class)->getExpressionBuilder(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class ActiveEntityListener | |
{ | |
public function postLoad($args) | |
{ | |
$entity = $args->getEntity(); | |
$em = $args->getEntityManager(); | |
$metadata = $em->getClassMetadata(get_class($entity)); | |
if (in_array("ActiveEntity", $metadata->reflClass->getTraitNames())) { | |
$entity->setDoctrine($em, $metadata); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Doctrine\ORM\EntityManager; | |
class ActiveEntityRegistry | |
{ | |
/** | |
* @var array | |
*/ | |
private static $managers = array(); | |
private static $defaultManager = array(); | |
static public function setClassManager($class, EntityManager $manager) | |
{ | |
self::$managers[$class] = $manager; | |
} | |
static public function setDefaultManager(EntityManager $manager) | |
{ | |
self::$defaultManager = $manager; | |
} | |
static public function getClassManager($class) | |
{ | |
if (isset(self::$managers[$class])) { | |
return self::$managers[$class]; | |
} else if (self::$defaultManager) { | |
return self::$defaultManager; | |
} else { | |
throw new \BadMethodCallException("ActiveEntity is not yet connected to an EntityManager."); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @Entity | |
*/ | |
class Article | |
{ | |
use ActiveEntity,Timestampable; | |
/** @Id @Column(type="integer") @GeneratedValue */ | |
private $id; | |
/** @Column */ | |
private $headline; | |
/** @Column(type="text") */ | |
private $body; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$lib = 'path/to/doctrine2/'; | |
require $lib . 'lib/Doctrine/ORM/Tools/Setup.php'; | |
Setup::registerAutoloadGit($lib); | |
$cache = new \Doctrine\Common\Cache\ArrayCache; | |
$config = Setup::createAnnotationMetadataConfiguration(array(), true); | |
$config->setSQLLogger(new Doctrine\DBAL\Logging\EchoSQLLogger()); | |
$connectionOptions = array( | |
'driver' => 'pdo_sqlite', | |
'memory' => true, | |
); | |
$evm = new \Doctrine\Common\EventManager(); | |
$evm->addEventListener(array('postLoad'), new ActiveEntityListener); | |
$em = EntityManager::create($connectionOptions, $config, $evm); | |
ActiveEntityRegistry::setDefaultManager($em); | |
$schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em); | |
$schemaTool->createSchema(array( | |
$em->getClassMetadata("Article") | |
)); | |
$article = new Article(); | |
$article->setHeadline("foo"); | |
$article->setBody("barz!"); | |
$other = Article::create(array('headline' => 'foo', 'body' => 'omg!?')); | |
$article->persist(); | |
$other->persist(); | |
$em->flush(); | |
$em->clear(); | |
$article = Article::find(1); | |
$article->remove(); | |
$em->flush(); | |
$articles = Article::findBy(array('headline' => 'foo')); | |
echo count($articles) . " articles\n"; | |
$articles = Article::createQueryBuilder('r')->where( | |
Article::expr()->like("r.body", '%omg%') | |
); | |
echo count($articles) . " articles\n"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Doctrine\Common\Util\Inflector; | |
trait SerializableEntity | |
{ | |
static private function serializeEntity($entity) | |
{ | |
$className = get_class($entity); | |
$em = ActiveEntityRegistry::getClassManager($className); | |
$class = $em->getClassMetadata($className); | |
$data = array(); | |
foreach ($class->fieldMappings as $field => $mapping) { | |
$value = $class->reflFields[$field]->getValue($entity); | |
$field = Inflector::tableize($field); | |
if ($value instanceof \DateTime) { | |
$data[$field] = $value->format(\DateTime::ATOM); | |
} else if (is_object($value)) { | |
$data[$field] = (string)$value; | |
} else { | |
$data[$field] = $value; | |
} | |
} | |
foreach ($class->associationMappings as $field => $mapping) { | |
$key = Inflector::tableize($field); | |
if ($mapping['isCascadeDetach']) { | |
$data[$key] = self::serializeEntity( $class->reflFields[$field]->getValue($entity) ); | |
} else if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadata::TO_ONE) { | |
// if its not detached to but there is an owning side to one entity at least reflect the identifier. | |
$data[$key] = $em->getUnitOfWork()->getEntityIdentifier( $class->reflFields[$field]->getValue($entity) ); | |
} | |
} | |
return $data; | |
} | |
public function toArray() | |
{ | |
return self::serializeEntity($this); | |
} | |
public function toJson() | |
{ | |
return json_encode($this->toArray()); | |
} | |
public function toDOMDocument() | |
{ | |
$arrToXml = function($node, $data) use (&$arrToXml) { | |
foreach ($data AS $k => $v) { | |
$child = $node->ownerDocument->createElement($k); | |
$node->appendChild($child); | |
if (is_array($v)) { | |
$arrToXml($child, $v); | |
} else { | |
$child->appendChild($node->ownerDocument->createTextNode($v)); | |
} | |
} | |
}; | |
$className = get_class($this); | |
$em = ActiveEntityRegistry::getClassManager($className); | |
$class = $em->getClassMetadata($className); | |
$dom = new \DOMDocument('1.0', 'UTF-8'); | |
$root = $dom->createElement(Inflector::tableize($class->reflClass->getShortName())); | |
$dom->appendChild($root); | |
$arrToXml($root, $this->toArray()); | |
return $dom; | |
} | |
public function toXml($formatOutput = false) | |
{ | |
$dom = $this->toDOMDocument(); | |
$dom->formatOutput = $formatOutput; | |
return $dom->saveXML(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
trait Timestampable | |
{ | |
/** @Column(type="datetime") */ | |
private $created; | |
/** @Column(type="datetime") */ | |
private $updated; | |
/** @PrePersist */ | |
public function onPrePersist() | |
{ | |
$this->created = new \DateTime("now"); | |
$this->updated = new \DateTime("now"); | |
} | |
/** @PreUpdate */ | |
public function onPreUpdate() | |
{ | |
$this->updated = new \DateTime("now"); | |
} | |
public function getCreated() | |
{ | |
return $this->created; | |
} | |
public function getUpdated() | |
{ | |
return $this->updated; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment