Created
October 3, 2013 20:44
-
-
Save n00ge/6816816 to your computer and use it in GitHub Desktop.
Dynamic relationships in Eloquent with Laravel 4
This file contains hidden or 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 | |
use Illuminate\Database\Eloquent\Relations\BelongsToMany; | |
use Illuminate\Database\Eloquent\Relations\BelongsTo; | |
use Illuminate\Database\Eloquent\Model as Eloquent; | |
abstract class BaseEloquent extends Eloquent | |
{ | |
protected $associations = array(); | |
protected static $SINGULAR_COLLECTION_TYPES = array( | |
'belongsTo', | |
'hasOne', | |
'morphTo', | |
); | |
// monkey-patched but a pull request has been submitted to update this: | |
// https://github.com/laravel/framework/pull/2378 | |
public function belongsTo($related, $foreignKey = null, $relation = null) | |
{ | |
if (is_null($relation)) | |
{ | |
list(, $caller) = debug_backtrace(false); | |
$relation = $caller['function']; | |
} | |
if (is_null($foreignKey)) | |
{ | |
$foreignKey = snake_case($relation).'_id'; | |
} | |
$instance = new $related; | |
$query = $instance->newQuery(); | |
return new BelongsTo($query, $this, $foreignKey, $relation); | |
} | |
// monkey-patched but a pull request has been submitted to update this: | |
// https://github.com/laravel/framework/pull/2378 | |
public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $name = null) | |
{ | |
if (is_null($name)) { | |
$caller = $this->getBelongsToManyCaller(); | |
$name = $caller['function']; | |
} | |
$foreignKey = $foreignKey ?: $this->getForeignKey(); | |
$instance = new $related; | |
$otherKey = $otherKey ?: $instance->getForeignKey(); | |
if (is_null($table)) | |
{ | |
$table = $this->joiningTable($related); | |
} | |
$query = $instance->newQuery(); | |
return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $name); | |
} | |
public function getAssociations() | |
{ | |
return $this->associations; | |
} | |
public function getLinksAttribute() | |
{ | |
$links = array(); | |
foreach ($this->associations as $association => $details) { | |
$snakeCase = snake_case($association); | |
$links[$snakeCase] = $this->getLinkValue($association, $details); | |
} | |
return $links; | |
} | |
public function __call($method, $args) | |
{ | |
if ($this->isAssociationMethod($method)) { | |
return $this->getAssociation($method); | |
} | |
return parent::__call($method, $args); | |
} | |
public function __get($key) | |
{ | |
$association = $this->getAssociationFromKey($key); | |
if (isset($association)) return $association; | |
return parent::__get($key); | |
} | |
private function getValidator() | |
{ | |
return Validator::make($this->attributes, $this->getRules()); | |
} | |
private function getRules() | |
{ | |
$replace = ($this->getKey() > 0) ? $this->getKey() : ''; | |
$rules = $this->rules; | |
foreach ($rules as $key => $rule) { | |
$rules[$key] = str_replace(':id', $replace, $rule); | |
} | |
return $rules; | |
} | |
private function getLinkValue($association, $details) | |
{ | |
$details = $this->getAssociationDetails($association); | |
$associationType = $this->getAssociationType($details); | |
if ($associationType == 'belongsTo') { | |
$id = $this->getBelongsToProperty($association, $details); | |
if (isset($id)) return $this->getAttribute($id); | |
} | |
$isSingular = $this->isSingularCollection($association, $associationType); | |
$results = $this->$association()->getResults(array('id')); | |
if ($isSingular) { | |
return empty($results) ? null : $results->getKey(); | |
} | |
return $this->getIdsFromResults($results); | |
} | |
private function getBelongsToProperty($association, $details) | |
{ | |
$belongsToDetails = $this->getBelongsToDetails($association, $details); | |
return $belongsToDetails[1]; | |
} | |
private function isSingularCollection($association, $associationType) | |
{ | |
return in_array($associationType, self::$SINGULAR_COLLECTION_TYPES); | |
} | |
private function getIdsFromResults($results) | |
{ | |
$ids = array(); | |
foreach ($results as $result) { | |
$ids[] = $result->id; | |
} | |
return $ids; | |
} | |
private function isAssociationMethod($method) | |
{ | |
$hasAssociations = is_array($this->associations); | |
return $hasAssociations && array_key_exists($method, $this->associations); | |
} | |
private function getAssociation($association) | |
{ | |
$associationDetails = $this->getAssociationDetails($association); | |
$associationType = $this->getAssociationType($associationDetails); | |
$typeDetails = $this->getAssociationTypeDetails( | |
$association, | |
$associationType, | |
$associationDetails[$associationType] | |
); | |
$association = $this->getAssociationWithDetails( | |
$associationType, | |
$typeDetails | |
); | |
return $association; | |
} | |
private function getAssociationDetails($association) | |
{ | |
return $this->associations[$association]; | |
} | |
private function getAssociationType($details) | |
{ | |
$keys = array_keys($details); | |
return $keys[0]; | |
} | |
private function getAssociationTypeDetails($association, $type, $details) | |
{ | |
switch ($type) { | |
case 'belongsTo': | |
return $this->getBelongsToDetails($association, $details); | |
break; | |
case 'belongsToMany': | |
return $this->getBelongsToManyDetails($association, $details); | |
break; | |
case 'morphTo': | |
return $this->getMorphToDetails($association, $details); | |
break; | |
default: | |
return (array) $details; | |
break; | |
} | |
} | |
private function getBelongsToDetails($association, $details) | |
{ | |
if (is_array($details) && count($details) > 2) return $details; | |
$foreignKey = snake_case($association) . '_id'; | |
$related = $this->fetchKeyValue(0, (array) $details); | |
return array($related, $foreignKey, $association); | |
} | |
private function getBelongsToManyDetails($association, $details) | |
{ | |
$details = (array) $details; | |
$related = $this->fetchKeyValue(0, $details); | |
$table = $this->fetchKeyValue(1, $details); | |
$foreignKey = $this->fetchKeyValue(2, $details); | |
$otherKey = $this->fetchKeyValue(3, $details); | |
return array( | |
$related, | |
$table, | |
$foreignKey, | |
$otherKey, | |
$association | |
); | |
} | |
private function getMorphToDetails($association, $details) | |
{ | |
if (is_array($details) && count($details) >= 1) return $details; | |
$details = (array) $details; | |
$name = snake_case($association); | |
$type = $this->fetchKeyValue(1, $details); | |
$id = $this->fetchKeyValue(2, $details); | |
return array($name, $type, $id); | |
} | |
private function fetchKeyValue($key, $array) | |
{ | |
return array_key_exists($key, $array) ? $array[$key] : null; | |
} | |
private function getAssociationWithDetails($type, $details) | |
{ | |
return call_user_func_array( | |
array($this, $type), | |
$details | |
); | |
} | |
private function getAssociationFromKey($key) | |
{ | |
if ( ! array_key_exists($key, $this->associations)) return; | |
$camelKey = camel_case($key); | |
$relations = $this->$camelKey()->getResults(); | |
return $this->relations[$key] = $relations; | |
} | |
} |
This file contains hidden or 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 | |
class PreviewImage extends BaseEloquent | |
{ | |
protected $table = 'preview_images'; | |
protected $appends = array('links'); | |
protected $associations = array( | |
'sampleModel' => array( | |
'hasOne' => 'SampleModel' | |
), | |
); | |
} |
This file contains hidden or 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 | |
class SampleModel extends BaseEloquent | |
{ | |
protected $table = 'sample_models'; | |
protected $appends = array('links'); | |
protected $associations = array( | |
'previewImage' => array( | |
'belongsTo' => 'PreviewImage' | |
), | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment