Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support in Document magic methods, for an event to trigger hand…

…lers when magic attributes are not resolved via normal methods.

Added RelationPk behavior, which will return the primary key for relation(s) when calling a non-existent (magic) attribute of {relation}Pk.
Created folder for events
Removed support for MetaData magic attribute detection, due to issues that this causes along with incompleteness of the feature.

--HG--
rename : Event.php => events/Event.php
rename : Event.php => events/Magic.php
  • Loading branch information...
commit b49c73b5d62aa5584e6cb97bb73028ce0cbbfa9f 1 parent 6470ba8
intel352 authored
View
10 Behavior.php
@@ -2,6 +2,8 @@
namespace ext\activedocument;
+use \ext\activedocument\events\Event;
+
/**
* Behavior
*
@@ -36,8 +38,8 @@ public function events() {
/**
* Responds to {@link \ext\activedocument\Document::onBeforeSave} event.
* Override this method if you want to handle the corresponding event of the {@link \CBehavior::owner owner}.
- * You may set {@link \ext\activedocument\Event::isValid} to be false to quit the saving process.
- * @param \ext\activedocument\Event $event event parameter
+ * You may set {@link \ext\activedocument\events\Event::isValid} to be false to quit the saving process.
+ * @param \ext\activedocument\events\Event $event event parameter
*/
public function beforeSave(Event $event) {
@@ -55,8 +57,8 @@ public function afterSave(\CEvent $event) {
/**
* Responds to {@link \ext\activedocument\Document::onBeforeDelete} event.
* Override this method if you want to handle the corresponding event of the {@link \CBehavior::owner owner}.
- * You may set {@link \ext\activedocument\Event::isValid} to be false to quit the deletion process.
- * @param \ext\activedocument\Event $event event parameter
+ * You may set {@link \ext\activedocument\events\Event::isValid} to be false to quit the deletion process.
+ * @param \ext\activedocument\events\Event $event event parameter
*/
public function beforeDelete(Event $event) {
View
92 Document.php
@@ -4,7 +4,9 @@
use \Yii,
\CModel,
-\CEvent;
+\CEvent,
+\ext\activedocument\events\Event
+;
Yii::import('ext.activedocument.Relation', true);
@@ -273,6 +275,13 @@ public function &__get($name) {
else if (isset($this->getMetaData()->relations->$name))
return $this->getRelated($name);
else {
+ if ((strncasecmp($name,'on',2)!==0 || !$this->hasEvent($name)) && !method_exists($this, 'get'.$name) &&
+ $this->hasEvent('onGetMissingAttribute') && $this->getEventHandlers('onGetMissingAttribute')->getCount()>0) {
+ $this->onGetMissingAttribute($event=new events\Magic($this, events\Magic::GET, $name));
+ if ($event->handled)
+ return $event->result;
+ }
+
$return = parent::__get($name);
return $return;
}
@@ -283,8 +292,15 @@ public function __set($name, $value) {
if (isset($this->getMetaData()->relations->$name)) {
$this->_related[$name] = $value;
$this->_modifiedAttributes[] = $name;
- } else
+ } else {
+ if ((strncasecmp($name,'on',2)!==0 || !$this->hasEvent($name)) && !method_exists($this, 'set'.$name) &&
+ $this->hasEvent('onSetMissingAttribute') && $this->getEventHandlers('onSetMissingAttribute')->getCount()>0) {
+ $this->onSetMissingAttribute($event=new events\Magic($this, events\Magic::SET, $name, $value));
+ if ($event->handled)
+ return $event->result;
+ }
parent::__set($name, $value);
+ }
}
}
@@ -297,8 +313,15 @@ public function __isset($name) {
return true;
else if (isset($this->getMetaData()->relations->$name))
return $this->getRelated($name) !== null;
- else
+ else {
+ if ((strncasecmp($name,'on',2)!==0 || !$this->hasEvent($name)) && !method_exists($this, 'get'.$name) &&
+ $this->hasEvent('onIssetMissingAttribute') && $this->getEventHandlers('onIssetMissingAttribute')->getCount()>0) {
+ $this->onIssetMissingAttribute($event=new events\Magic($this, events\Magic::SETIS, $name));
+ if ($event->handled)
+ return $event->result;
+ }
return parent::__isset($name);
+ }
}
public function __unset($name) {
@@ -308,8 +331,15 @@ public function __unset($name) {
} else if (isset($this->getMetaData()->relations->$name)) {
unset($this->_related[$name]);
$this->_modifiedAttributes[] = $name;
- } else
+ } else {
+ if ((strncasecmp($name,'on',2)!==0 || !$this->hasEvent($name)) && !method_exists($this, 'set'.$name) &&
+ $this->hasEvent('onUnsetMissingAttribute') && $this->getEventHandlers('onUnsetMissingAttribute')->getCount()>0) {
+ $this->onUnsetMissingAttribute($event=new events\Magic($this, events\Magic::SETUN, $name));
+ if ($event->handled)
+ return $event->result;
+ }
parent::__unset($name);
+ }
}
public function __call($name, $parameters) {
@@ -326,9 +356,36 @@ public function __call($name, $parameters) {
return $this;
}
+ if ((strncasecmp($name,'on',2)!==0 || !$this->hasEvent($name)) &&
+ $this->hasEvent('onCallMissingMethod') && $this->getEventHandlers('onCallMissingMethod')->getCount()>0) {
+ $this->onCallMissingMethod($event=new events\Magic($this, events\Magic::CALL, $name, $parameters));
+ if ($event->handled)
+ return $event->result;
+ }
+
return parent::__call($name, $parameters);
}
+ public function onGetMissingAttribute(events\Magic $event) {
+ $this->raiseEvent('onGetMissingAttribute', $event);
+ }
+
+ public function onSetMissingAttribute(events\Magic $event) {
+ $this->raiseEvent('onSetMissingAttribute', $event);
+ }
+
+ public function onIssetMissingAttribute(events\Magic $event) {
+ $this->raiseEvent('onIssetMissingAttribute', $event);
+ }
+
+ public function onUnsetMissingAttribute(events\Magic $event) {
+ $this->raiseEvent('onUnsetMissingAttribute', $event);
+ }
+
+ public function onCallMissingMethod(events\Magic $event) {
+ $this->raiseEvent('onCallMissingMethod', $event);
+ }
+
/**
* Returns arrays of indexed keys, only applicable to HasMany or ManyMany relations
*
@@ -376,7 +433,7 @@ public function getRelatedKeysByIndex($name, array $indexes, array $keys = array
public function getRelatedKeys($name) {
$relation = $this->getMetaData()->relations[$name];
- if (!isset($this->getObject()->data[$name]))
+ if (!isset($this->getObject()->data[$name]) || !($relation instanceof Relation))
return null;
if ($relation instanceof HasManyRelation) {
@@ -385,7 +442,11 @@ public function getRelatedKeys($name) {
$pks = array_keys($pks);
return $pks;
} else {
- return self::stringify($this->getObject()->data[$name]);
+ if ($relation->nested === true) {
+ if ($obj = $this->getRelated($name))
+ return $obj->getEncodedPk();
+ }else
+ return self::stringify($this->getObject()->data[$name]);
}
return null;
}
@@ -431,7 +492,7 @@ public function &getRelatedByIndex($name, array $indexes, $refresh = false, arra
public function &getRelated($name, $refresh = false, array $params = array(), array $keys = array(), $criteria = array()) {
if ($keys !== array())
$keys = array_map(array('self', 'stringify'), $keys);
- if (!$refresh && $params === array() && $criteria === array() && (isset($this->_related[$name]) || array_key_exists($name, $this->_related)))
+ if (!$refresh && $params === array() && $criteria === array() && (isset($this->_related[$name]) || array_key_exists($name, $this->_related))) {
if ($keys !== array() && is_array($this->_related[$name])) {
$related = array_filter($this->_related[$name], function(Document $document) use($keys) {
return in_array($document->getEncodedPk(), $keys);
@@ -440,6 +501,7 @@ public function &getRelated($name, $refresh = false, array $params = array(), ar
} else {
return $this->_related[$name];
}
+ }
$md = $this->getMetaData();
if (!isset($md->relations[$name]))
@@ -508,8 +570,20 @@ public function &getRelated($name, $refresh = false, array $params = array(), ar
$this->_related[$name] = $finder->findAllByPk($pks, $_criteria, $params);
} elseif ($relation instanceof StatRelation) {
$this->_related[$name] = $finder->count($_criteria, $params);
- } else
- $this->_related[$name] = $finder->findByPk($data[$name], $_criteria, $params);
+ } else {
+ if ($relation->nested === true) {
+ /**
+ * @todo Need to verify that this solution works consistently...
+ */
+ $obj = $finder->populateDocument(
+ $finder->getContainer()->getObject(null, $data[$name], true)
+ );
+ if ($obj!==null)
+ $this->_related[$name] = $finder->findByPk($obj->getEncodedPk(), $_criteria, $params);
+ } else {
+ $this->_related[$name] = $finder->findByPk($data[$name], $_criteria, $params);
+ }
+ }
}
}
View
87 DocumentBehavior.php
@@ -1,87 +0,0 @@
-<?php
-
-namespace ext\activedocument;
-
-/**
- * DocumentBehavior
- *
- * @version $Version$
- * @author $Author$
- */
-class DocumentBehavior extends \CModelBehavior {
-
- /**
- * Declares events and the corresponding event handler methods.
- * If you override this method, make sure you merge the parent result to the return value.
- * @return array events (array keys) and the corresponding event handler methods (array values).
- * @see CBehavior::events
- */
- public function events() {
- return array_merge(parent::events(), array(
- 'onBeforeSave' => 'beforeSave',
- 'onAfterSave' => 'afterSave',
- 'onBeforeDelete' => 'beforeDelete',
- 'onAfterDelete' => 'afterDelete',
- 'onBeforeFind' => 'beforeFind',
- 'onAfterFind' => 'afterFind',
- ));
- }
-
- /**
- * Responds to {@link CActiveRecord::onBeforeSave} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * You may set {@link CModelEvent::isValid} to be false to quit the saving process.
- * @param \ext\activedocument\Event $event event parameter
- */
- public function beforeSave(Event $event) {
-
- }
-
- /**
- * Responds to {@link CActiveRecord::onAfterSave} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * @param \ext\activedocument\Event $event event parameter
- */
- public function afterSave(Event $event) {
-
- }
-
- /**
- * Responds to {@link CActiveRecord::onBeforeDelete} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * You may set {@link CModelEvent::isValid} to be false to quit the deletion process.
- * @param CEvent $event event parameter
- */
- public function beforeDelete(\CEvent $event) {
-
- }
-
- /**
- * Responds to {@link CActiveRecord::onAfterDelete} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * @param CEvent $event event parameter
- */
- public function afterDelete(\CEvent $event) {
-
- }
-
- /**
- * Responds to {@link CActiveRecord::onBeforeFind} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * @param CEvent $event event parameter
- * @since 1.0.9
- */
- public function beforeFind(\CEvent $event) {
-
- }
-
- /**
- * Responds to {@link CActiveRecord::onAfterFind} event.
- * Overrides this method if you want to handle the corresponding event of the {@link CBehavior::owner owner}.
- * @param CEvent $event event parameter
- */
- public function afterFind(\CEvent $event) {
-
- }
-
-}
View
13 Event.php
@@ -1,13 +0,0 @@
-<?php
-
-namespace ext\activedocument;
-
-/**
- * Event
- *
- * @version $Version$
- * @author $Author$
- */
-class Event extends \CModelEvent {
-
-}
View
29 MetaData.php
@@ -36,7 +36,6 @@ class MetaData extends CComponent {
* @var \ArrayObject
*/
protected $_classMeta;
- protected $_docAttributeRegex = '/\@(?<attribute>\w+)(?<!property|property-read|property-write|var)\s+(?:(\$)\w+\:\s)?\s*(?<value>[^\s]+)\s+\2?\s*(?<comment>.*)?/';
/**
* @var \ArrayObject
@@ -84,22 +83,13 @@ public function getClassMeta() {
return $this->_classMeta;
$reflectionClass = $this->getReflectionClass();
- /**
- * @todo Temporarily disabling, need logic so this only executes when required
- $properties = $relations = array();
- $parentClass = $reflectionClass->getParentClass();
- if($parentClass->getNamespaceName()!='ext\activedocument') {
- $properties = Document::model($parentClass->getName())->getMetaData()->getProperties();
- $relations = Document::model($parentClass->getName())->getMetaData()->getRelations();
- }
- */
$this->_classMeta = new \ArrayObject(array(
'properties' => new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS),
'relations' => new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS),
), \ArrayObject::ARRAY_AS_PROPS);
/**
- * Parse class phpdoc (also detects magic properties)
+ * Parse class phpdoc
*/
$this->parsePhpDoc($reflectionClass->getDocComment());
@@ -165,8 +155,6 @@ protected function parsePhpDoc($phpdoc, \ReflectionProperty $property = null) {
/**
* Parse class meta
*/
- if ($property === null)
- array_walk($phpdoc, array($this, 'parsePhpDocAttributes'));
array_walk($phpdoc, array($this, 'parsePhpDocProperties'), $property);
}
}
@@ -199,21 +187,6 @@ protected function cleanPhpDoc($phpdoc) {
}
/**
- * Parses phpdoc text to find general pattern of "attribute value comment"
- *
- * @param string $string Line of phpdoc
- * @param int $index
- */
- protected function parsePhpDocAttributes($string, $index) {
- if (preg_match($this->_docAttributeRegex, $string, $matches)) {
- $matches = array_intersect_key($matches, array('attribute' => null, 'value' => null, 'comment' => null));
-
- if (!array_key_exists($matches['attribute'], $this->_classMeta))
- $this->_classMeta->{$matches['attribute']} = new \ArrayObject($matches, \ArrayObject::ARRAY_AS_PROPS);
- }
- }
-
- /**
* Parses phpdoc text to identify class property definitions
*
* @param string $string Line of phpdoc
View
52 behaviors/RelationPk.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace ext\activedocument\behaviors;
+
+use \ext\activedocument\Document,
+\ext\activedocument\events\Magic,
+\ext\activedocument\BaseRelation,
+\ext\activedocument\Relation;
+/**
+ * RelationPk answers a model call to non-existing {relation}Pk attribute, and returns the pk(s) of the related model(s)
+ * Acts as a proxy to Document::getRelatedKeys
+ */
+class RelationPk extends \ext\activedocument\Behavior {
+
+ public $relations = array();
+
+ /**
+ * @param \ext\activedocument\Document $owner
+ */
+ public function attach($owner) {
+ parent::attach($owner);
+ $owner->attachEventHandler('onGetMissingAttribute',array($this,'fetchRelationPk'));
+ $this->relations = array_keys(array_filter($owner->getMetaData()->getRelations(true),
+ function(BaseRelation $relation){
+ return ($relation instanceof Relation);
+ }
+ ));
+ }
+
+ /**
+ * @param \ext\activedocument\Document $owner
+ */
+ public function detach($owner) {
+ $owner->detachEventHandler('onGetMissingAttribute',array($this,'fetchRelationPk'));
+ parent::detach($owner);
+ }
+
+ /**
+ * @param \ext\activedocument\events\Magic $event
+ * @return void
+ */
+ public function fetchRelationPk(Magic $event) {
+ if(strcasecmp($event->name, 'encodedpk')===0 || strlen($event->name)<=2
+ || substr_compare($event->name, 'pk', -2, 2, true)!==0 || $this->relations === array())
+ return;
+ elseif(($name=substr($event->name, 0, -2)) && in_array($name, $this->relations)) {
+ $event->result = $this->getOwner()->getRelatedKeys($name);
+ $event->handled = true;
+ }
+ }
+
+}
View
10 events/Event.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace ext\activedocument\events;
+
+/**
+ * Event
+ */
+class Event extends \CModelEvent {
+
+}
View
38 events/Magic.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace ext\activedocument\events;
+
+/**
+ * Magic event
+ * Used when handling non-existent variable|method events
+ */
+class Magic extends \CEvent {
+
+ const GET='__get';
+ const SET='__set';
+ const SETIS='__isset';
+ const SETUN='__unset';
+ const CALL='__call';
+
+ /**
+ * @var string One of the above constants
+ */
+ public $method;
+
+ /**
+ * @var string Request variable|method name
+ */
+ public $name;
+
+ /**
+ * @var mixed Result to return, if any
+ */
+ public $result;
+
+ public function __construct($sender, $method, $name, $params=null) {
+ parent::__construct($sender,$params);
+ $this->method = $method;
+ $this->name = $name;
+ }
+
+}

1 comment on commit b49c73b

@intel352
Owner

An important note, as ActiveDocument is still not considered Stable, an important change occurred in this commit, with the removal of support for magic attribute detection from a model's class phpdoc.

The phpdoc magic attribute detection support was limited in terms of what could be done with it in the future, and impeded IDE hinting for magic variables that are fulfilled by a getter/setter method or a behavior.

Phpdoc detection for real class properties still exists, and going forward it will be expanded to proper notation detection for property type definition, validation, and more.

Please sign in to comment.
Something went wrong with that request. Please try again.