Skip to content

Instantly share code, notes, and snippets.

@Leechael
Forked from clexmond/Entity.php
Created March 11, 2012 12:39
Show Gist options
  • Save Leechael/2016299 to your computer and use it in GitHub Desktop.
Save Leechael/2016299 to your computer and use it in GitHub Desktop.
Implementing custom getters and setters in Lithium
<?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;
}
}
<?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