Skip to content

Instantly share code, notes, and snippets.

@davekiss
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davekiss/e5b16006fd98dfde1e15 to your computer and use it in GitHub Desktop.
Save davekiss/e5b16006fd98dfde1e15 to your computer and use it in GitHub Desktop.
Low Level ORM
<?php
class API {
/**
* Create a new section for a given course.
*
* @return mixed
*/
public function create() {
$course = WP_Coach_Course::find( 1 );
$section = $course->sections->create();
}
}
<?php
class WP_Coach_Collection implements IteratorAggregate, JsonSerializable {
private $collection = array();
private $owner;
private $target_class;
private $target_post_type;
private static $class_dict = array(
'course' => 'WP_Coach_Course',
'sections' => 'WP_Coach_Section',
'lessons' => 'WP_Coach_Lesson',
);
/**
* Set the collection that should be iterated
* @param mixed $collection empty, single WP_Coach_{model}, or array of WP_Coach_{model}
*/
public function __construct( $owner, $prop_name ) {
$this->owner = $owner;
$this->target_class = self::_get_class_from_prop_name( $prop_name );
$this->target_post_type = strtolower( $this->target_class );
$this->collection = self::_get_associated($this->owner, $this->target_class, $this->target_post_type);
}
/**
* [create description]
* @return [type] [description]
*/
public function create( $args = array() ) {
$target = $this->target_class;
$owner = $this->owner;
$result = $target::create( $args );
update_post_meta($result->ID, '_' . $owner::POST_TYPE . '_id', $owner->ID );
return $result;
}
/**
* Tells the iterator which array to use
* during iterations.
*
* @return [type] [description]
*/
public function getIterator() {
return new ArrayIterator($this->collection);
}
/**
* Tells the serializer to use the collection when encoding to JSON
*
* @return array
*/
public function jsonSerialize() {
return $this->collection;
}
/**
* [_get_class_from_prop_name description]
* @param [type] $property [description]
* @return [type] [description]
*/
private static function _get_class_from_prop_name($property) {
return self::$class_dict[$property];
}
/**
* [_get_associated description]
* @return [type] [description]
*/
private static function _get_associated($owner, $target_class, $target_post_type) {
// Run an association query with meta key
$query = array(
'posts_per_page' => -1,
'post_type' => $target_post_type,
'perm' => 'readable',
'post_status'=> 'any',
'meta_query' => array(
array(
'key' => '_' . $owner::POST_TYPE . '_id',
'value' => $owner->ID,
'compare' => '=',
),
),
);
$posts = get_posts( $query );
$collection = call_user_func_array( array( 'self', 'init_instance' ) , array($posts, $target_class) );
return $collection;
}
/**
* Creates a new model instance for each of the given
* WP_Post objects
*
* @param mixed $posts empty, single or array of WP_Post
* @param string $klass Model name
* @return mixed empty, single or array of Model instances
*/
private static function init_instance($posts, $klass) {
if ( empty( $posts ) ) {
return array();
}
if ( is_array($posts) ) {
$result = array();
foreach( $posts as $post) {
$result[] = new $klass($post);
}
} else {
$result = new $klass($post);
}
return $result;
}
}
<?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class WP_Coach_Course extends WP_Coach_Model {
const POST_TYPE = 'wp_coach_course';
const HAS_MANY = 'sections';
}
<?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
abstract class WP_Coach_Model {
public function __construct( $WP_Post ) {
$post_vars = get_object_vars( $WP_Post );
if ( is_array( $post_vars) ) {
foreach ($post_vars as $key => $value) {
$this->{$key} = $value;
}
}
}
/**
* Returns a model containing the post
*
* @param int $id Post ID
* @return object
*/
public static function find( $id ) {
$klass = get_called_class();
$post = get_post( $id );
return new $klass( $post );
}
/**
* Create a new record for the calling model
*
* @return object New record
*/
public static function create( $args = array() ) {
$class_name = get_called_class();
$post_type = strtolower( $class_name );
$id = wp_insert_post( array(
'post_status' => 'draft',
'post_type' => $post_type,
)
);
if ( $id !== 0 ) {
return get_post( $id );
} else {
throw new WP_Coach_Exception( __( sprintf('Failed to insert new record for $1%s', $post_type), 'wp-coach') );
}
}
/**
* Check the property being called to determine if we're
* trying to return an associated object
*
* @param string $name Property name
* @param mixed $arguments passed arguments
* @return [type] [description]
*/
public function __get( $prop_name ) {
// If it is a normal or cached property, just return it
if ( property_exists($this, $prop_name) ) {
return;
}
$class_name = get_class($this);
if ( defined($class_name . '::HAS_MANY') && $this::HAS_MANY === $prop_name ) {
# We don't want to set the prop name directly on the calling class due
# to some nasty recursion that would occur in the collection ($owner property)
#
# Figure out where we can cache this instead of just returning it.
# $this->{$prop_name} = new WP_Coach_Collection($this, $prop_name);
return new WP_Coach_Collection($this, $prop_name);
}
throw new Exception( __('Property does not exist on model', 'wp-coach') );
}
}
<?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class WP_Coach_Section extends WP_Coach_Model {
const POST_TYPE = 'wp_coach_section';
const BELONGS_TO = 'course';
const HAS_MANY = 'lessons';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment