Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: fix-models
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 350 lines (304 sloc) 9.451 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
<?php
/**
* Models
* A bunch of classes that standardize access to WordPress data. Model based, similar
* to Django, Backbone or Rails. Heavily inspired by Backbone's models and collections.
* TODO: Use more references in order to save memory
**/

namespace mtv\models;

use Iterator,
    ArrayAccess,
    Countable,
    Exception,
    LogicException,
    BadMethodCallException,
    JsonableException,
    NotImplementedException;
use mtv\shortcuts;

/**
* Model class
*
* You can access the fields on a model object in the following ways:
* $post->field_name;
* $post->attributes['field_name'];
*
* If you need an array of the fields on a model object, do this:
* $data = $post->attributes;
**/
class Model {
    // Where the model's data is stored
    public $attributes = array();
    // Default model data used when a new model is created
    public $defaults = array();
    // Attributes that are OK to send over the wire
    public $json_fields = array();
    // Attributes that can be updated over the wire
    public $editable_json_fields = array();

    public static $collection = 'mtv\models\Collection';

    // Is this model's data sync'd with the DB?
    protected $_synchronized = false;
    // Attributes that have been changed since the last save or fetch
    protected $_previous_attributes = array();

    public function __construct( $args=array() ) {
        $this->initialize( $args );
        if ( empty($this->defaults) )
            $this->set( $args );
        else
            $this->set( array_merge($this->defaults, $args) );
    }

    /**
* Write the data in this model to permanent storage
**/
    public function save() { throw new NotImplementedException(); }

    /**
* Validate the data in this model
**/
    public function validate() {
        throw new NotImplementedException();
    }

    /**
* Call initialize when the model is created
**/
    public function initialize( $args ) {}

    public function __toString() {
        return get_called_class();
    }

    public function __get($name) {
        return $this->attributes[$name];
    }

    public function __set( $name, $val ) {
        $this->set( array( $name => $val ) );
    }

    public function __unset( $name ) {
        $this->clear( $name );
    }

    public function __isset( $name ) {
        return isset($this->attributes[$name]);
    }

    /**
* Delete all the attributes in this model
**/
    public function clear() {
        // TODO: update $this->_previous_attributes with removed items
        foreach ( func_get_args() as $arg ) {
            $this->_previous_attributes[$arg] = $this->attributes[$arg];
            unset( $this->attributes[$arg] );
        }
    }

    /**
* Set a bunch of attributes at once
**/
    public function set( $args, $fetching=false ) {
        $this->attributes = array_merge( $this->attributes, (array) $args );
    }

    /**
* Populate this model from permanent storage
**/
    public function fetch() {
        // get my attributes from the db
        // $from_db = new Object;
        // pass the results to reload
        // $this->reload( $from_db );
        throw new NotImplementedException();
    }

    /**
* Process the raw data from permanent storage
**/
    public function parse( &$data ) {
        // Make sure we have an array and not an object
        if ( is_object($data) )
            return (array) $data;
        else
            return $data;
    }

    /**
* Update this model with data from permanent storage
**/
    public function reload( &$data ) {
        // Parse raw data from the DB
        $tmp =& $this->parse($data);

        // Reset any change tracking
        $this->_previous_attributes = array();
        $this->_synchronized = true;

        // Set the attributes
        $this->set( $tmp, true );
    }

    /**
* Returns an array of selected values
* TODO: Return all values if no params are given
**/
    public function values(/* $key [,$key [,$key ...] ] */) {
        $keys = func_get_args();
        $ret = array();
        foreach ( $keys as $key )
            $ret[] = $this->$key;
        return $ret;
    }

    /**
* Returns an assoc array of this model's data to send over the wire
**/
    public function to_json() {
        // decide which fields to send over the wire
        if ( empty( $this->json_fields ) )
            return $this->attributes;
        else {
            $ret = array();
            foreach ( $this->json_fields as $k )
                $ret[$k] = $this->attributes[$k];

            return $ret;
        }
    }

    /**
* Create a new model from json data sent over the wire
**/
    public static function from_json( &$data ) {
        $class = get_called_class();
        $obj = new $class;
        $obj->set_from_json($data);
        return $obj;
    }

    /**
* Update this model from json data sent over the wire
**/
    public function set_from_json( &$data ) {
        // decide which fields to use from the wire
        $new_data = (array) json_decode( stripslashes( $data ), true );
        if ( empty( $this->editable_json_fields ) )
            $this->set( $new_data );
        else {
            foreach ( $new_data as $k=>$v )
                if ( in_array($k, $this->editable_json_fields) )
                    $this->$k = $v;
        }
    }

}

class Collection implements Iterator, ArrayAccess, Countable {
    public $models = array();
    public static $model = 'mtv\models\Model';
    public static $default_filter = array();

    public function __construct( $array=array() ) {
        foreach ( $array as $args ) {
            array_push( $this->models, new static::$model( $args ) );
        }
    }

    /**
* Makes a collection iterable like an array
**/
    public function current() {
        return current( $this->models );
    }
    public function next() {
        return next( $this->models );
    }
    public function key() {
        return key( $this->models);
    }
    public function valid() {
        $key = key( $this->models );
        return ($key !== NULL && $key !== FALSE);
    }
    public function rewind() {
        reset( $this->models );
    }

    /**
* ArrayAccess interface
* Makes a collection object addressable like an array
**/
    public function offsetExists( $offset ) {
        return isset( $this->models[$offset] );
    }
    public function offsetGet( $offset ) {
        return isset( $this->models[$offset] ) ? $this->models[$offset] : null;
    }
    public function offsetSet( $offset, $value ) {
        if (is_null($offset)) $this->models[] = $value;
        else $this->models[$offset] = $value;
    }
    public function offsetUnset( $offset ) {
        unset($this->models[$offset]);
    }

    /**
* Countable interface
* Makes `count($collection)` work
**/
    public function count() {
        return count($this->models);
    }

    /**
* Add a model instance to this collection
**/
    public function add( $model ) {
        $this->models[] =& $model;
    }

    /**
* Remove all model instances from this collection
**/
    public function clear() {
        $this->models = array();
    }

    /**
* Return specific values from all the model instances in this collection
* TODO: Return all values if no params are given
**/
    public function values(/* $key [,$key [,$key ...] ] */) {
        $keys = func_get_args();
        $ret = array();
        foreach ( $this->models as $model ) {
            $row = array();
            foreach ( $keys as $key )
                $row[] = $model->$key;
            $ret[] = $row;
        }
        return $ret;
    }

    /**
* Return the results of calling `to_json` on all the model instances in this collection
**/
    public function to_json() {
        $tmp_array = array();
        foreach ($this->models as $model) {
            array_push($tmp_array, $model->to_json());
        }
        return $tmp_array;
    }

    /**
* Create, fetch and return a single model instance with primary keys
**/
    public static function get( $args ) {
        $model = new static::$model( $args );
        $model->fetch();
        return $model;
    }

    /**
* Create, fetch and return a single model instances based on some unique keys
**/
    public static function get_by( $args ) { throw new NotImplementedException(); }

    /**
* Return a collection containing all model instances
**/
    public static function all() {
        return static::filter(array());
    }

    /**
* Return a collection containing model instances matching certian criteria
**/
    public static function filter( $args ) { throw new NotImplementedException(); }
}

/**
* Model exceptions
**/
class ModelParseException extends JsonableException {}

class ModelNotFound extends JsonableException {
    public $class_name;

    public function __construct( $class_name, $message = "", $code = 0, $previous = NULL ) {
        parent::__construct( $message, $code, $previous );
        $this->class_name = $class_name;
    }

    public function to_json() {
        return array(
            'message' => $this->message,
            'code' => $this->code,
            'class' => $this->class
        );
    }
}

Something went wrong with that request. Please try again.