-
-
Save Leechael/2016299 to your computer and use it in GitHub Desktop.
Implementing custom getters and setters in Lithium
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 | |
namespace path\to\lib; | |
use BadMethodCallException; | |
use lithium\core\Libraries; | |
use lithium\util\Inflector; | |
/** | |
* This class can extend either \lithium\data\entity\Record or \lithium\data\entity\Document | |
* and takes care of automatically calling getters and setters in the format getProperty and | |
* setProperty. | |
* | |
* Implement by adding the following to the connection in your connections.php bootstrap: | |
* 'classes' => array('entity' => 'path\to\lib\Entity') | |
* | |
* @see lithium\data\Entity | |
*/ | |
class Entity extends \lithium\data\Entity { | |
protected static $_magicMethodsCache = array(); | |
/** | |
* Added ability to change based model dynamically. | |
*/ | |
public function to ($format, array $options = array()) { | |
$class = Libraries::locate('models', $format); | |
if (class_exists($class)) { | |
return $this->_model = $class; | |
} | |
return parent::to($format, $options); | |
} | |
/** | |
* Implementing custom getters and setters in Lithium | |
* | |
* @see https://gist.github.com/1946493 | |
*/ | |
/** | |
* Extends custom getter to allow for custom getters | |
* | |
* @see \lithium\data\Entity::__get | |
*/ | |
public function &__get ($name) { | |
$type = $this->_getMagicMethodType('__get', $name); | |
if (!$type) { | |
if (isset($this->_relationships[$name])) { | |
$type = 'relationship'; | |
} elseif (isset($this->_updated[$name])) { | |
$type = 'field'; | |
$model = $this->_model; | |
$method = 'get' . Inflector::camelize($name); | |
if ($this->__methodExists($method)) { | |
$type = $method; | |
} | |
} | |
if ($type) { | |
$this->_setMagicMethodType('__get', $name, $type); | |
} | |
} | |
if ($type) { | |
switch ($type) { | |
case 'relationship': | |
return $this->_relationships[$type]; | |
case 'field': | |
return $this->_updated[$name]; | |
default: | |
return $this->$type(); | |
} | |
} | |
$null = null; | |
return $null; | |
} | |
/** | |
* Extends magic setter to allow for custom setters | |
* | |
* @see \lithium\data\Entity::__set | |
*/ | |
public function __set ($name, $value = null) { | |
if (is_array($name) && !$value) { | |
return array_map(array(&$this, '__set'), array_keys($name), array_values($name)); | |
} | |
$type = $this->_getMagicMethodType('__set', $name); | |
if (!$type) { | |
$type = 'property'; | |
$method = 'set' . Inflector::camelize($name); | |
if ($this->__methodExists($method)) { | |
$type = $method; | |
} | |
$this->_setMagicMethodType('__set', $name, $type); | |
} | |
if ($type !== 'property') { | |
$value = $this->$method($value); | |
} | |
$this->_updated[$name] = $value; | |
} | |
/** | |
* Extends magic detector to allow for custom detectors. | |
*/ | |
public function __isset ($name) { | |
$type = $this->_getMagicMethodType('__isset', $name); | |
if (!$type) { | |
$type = 'property'; | |
$method = 'has' . Inflector::camelize($name); | |
if ($this->__methodExists($method)) { | |
$type = $method; | |
} | |
$this->_setMagicMethodType('__isset', $name, $type); | |
} | |
if ($type !== 'property') { | |
return $this->$method(); | |
} | |
return isset($this->_updated[$name]); | |
} | |
/** | |
* Refactor magic method `__call` beyond the basic one. | |
* | |
* @see \lithium\data\Entity::__call | |
*/ | |
public function __call ($method, $params) { | |
$type = $this->_getMagicMethodType('__call', $method); | |
if (!$type) { | |
$type = $this->__methodExists($method); | |
if ($type) { | |
$this->_setMagicMethodType('__call', $method, $type); | |
} | |
} | |
$model = $this->_model; | |
array_unshift($params, $this); | |
if ($type === 'model') { | |
$class = $model::invokeMethod('_object'); | |
return call_user_func_array(array(&$class, $method), $params); | |
} elseif ($type === 'extends') { | |
$methods = $model::instanceMethods(); | |
$method = $methods[$method]; | |
return call_user_func_array($method, $params); | |
} | |
$message = "No model bound or unhandled method call `{$method}`."; | |
throw new BadMethodCallException($message); | |
} | |
/** | |
* Detects method exists or not with searching in instanceMethods. | |
* | |
* AS current not magic method names `__methodExists`, but implemented | |
* in this way may be helpful. | |
* | |
* @params string $method | |
* @return boolean | |
*/ | |
public function __methodExists ($method) { | |
if ($model = $this->_model) { | |
if (method_exists($model, $method)) { | |
return 'model'; | |
} | |
$methods = $model::instanceMethods(); | |
if (isset($methods[$method]) && is_callable($methods[$method])) { | |
return 'extends'; | |
} | |
} | |
return false; | |
} | |
/** | |
* As custom getters is high priority than real data, and this method should | |
* return raw value to a specified field, so override it with handwrite direct | |
* access logic and no longer access via `__get`. | |
* | |
* @see \lithium\data\Entity::__call | |
*/ | |
public function data ($name = null) { | |
if ($name) { | |
if (isset($this->_relationships[$name])) { | |
return $this->_relationships[$name]; | |
} | |
if (isset($this->_updated[$name])) { | |
return $this->_updated[$name]; | |
} | |
return null; | |
} | |
return $this->to('array'); | |
} | |
/** | |
* Accessor for whole magic methods determine cache. | |
* As cache is available on runtime, this method alongs you made cache persistently, | |
* or sharing via memcached. | |
* | |
* @param array $data | |
* @return array | |
*/ | |
public static function magicMethodType (array $data = array()) { | |
if ($data) { | |
return static::$_magicMethodsCache = $data; | |
} | |
return static::$_magicMethodsCache; | |
} | |
protected function _getMagicMethodType ($method, $field) { | |
$model = $this->_model; | |
$key = "{$model}::{$method}::{$field}"; | |
return isset(static::$_magicMethodsCache[$key]) ? static::$_magicMethodsCache[$key] : null; | |
} | |
protected function _setMagicMethodType ($method, $field, $type) { | |
$model = $this->_model; | |
$key = "{$model}::{$method}::{$field}"; | |
return static::$_magicMethodsCache[$key] = $type; | |
} | |
} |
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 | |
namespace path\to\lib; | |
class Model extends \lithium\data\Model { | |
/** | |
* Over-ridden create method to prevent passing data. This enforces all data applied | |
* to a model being fed through the magic setter. | |
* | |
* @see \lithium\data\Model::create | |
*/ | |
public static function create(array $data = array(), array $options = array()) { | |
$new = parent::create(array(), $options); | |
$new->set($data); | |
return $new; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment