Skip to content

Commit

Permalink
extract Mutator
Browse files Browse the repository at this point in the history
  • Loading branch information
jarektkaczyk committed Apr 16, 2015
1 parent e702781 commit 108754a
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/Contracts/Metable.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public function getMetaAttributes();
public function getMetaAttributesArray();
public function hasMeta($key);
public function getMeta($key);
public function setMeta($key);
public function setMeta($key, $value);
public function getAllowedMeta();
public function setAllowedMeta(array $attributes);
}
15 changes: 15 additions & 0 deletions src/Contracts/Mutator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php namespace Sofa\Eloquence\Contracts;

interface Mutator
{
/**
* Mutate value using provided methods.
*
* @param mixed $value
* @param string|array $callable
* @return mixed
*
* @throws \LogicException
*/
public function mutate($value, $mutators);
}
51 changes: 51 additions & 0 deletions src/Eloquence.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use Illuminate\Database\Eloquent\Model;
use Sofa\Eloquence\Pipeline\Pipeline;
use Sofa\Eloquence\Pipeline\ArgumentBag;
use Sofa\Eloquence\Contracts\Mutator as MutatorContract;
use Sofa\Eloquence\Mutator\Mutator;

/**
* @version 0.4
Expand All @@ -13,6 +15,13 @@
*/
trait Eloquence
{
/**
* Attribute mutator instance.
*
* @var \Sofa\Eloquence\Contracts\Mutator
*/
protected static $attributeMutator;

/**
* Model's table column listing.
*
Expand All @@ -34,6 +43,24 @@ trait Eloquence
*/
protected $unwrappedHooks = [];

/**
* Boot the trait.
*
* @codeCoverageIgnore
*
* @return void
*/
public static function bootEloquence()
{
if (!isset(static::$attributeMutator)) {
if (function_exists('app') && isset(app()['eloquence.mutator'])) {
static::setAttributeMutator(app('eloquence.mutator'));
} else {
static::setAttributeMutator(new Mutator);
}
}
}

/**
* Register hook on Eloquent method.
*
Expand Down Expand Up @@ -131,6 +158,30 @@ public function newEloquentBuilder($query)
return new Builder($query);
}

/**
* Set attribute mutator instance.
*
* @codeCoverageIgnore
*
* @param \Sofa\Eloquence\Contracts\Mutator $mutator
* @return void
*/
public static function setAttributeMutator(MutatorContract $mutator)
{
static::$attributeMutator = $mutator;
}

/**
* Get attribute mutator instance.
*
* @codeCoverageIgnore
*
* @return \Sofa\Eloquence\Contracts\Mutator
*/
public static function getAttributeMutator()
{
return static::$attributeMutator;
}

/*
|--------------------------------------------------------------------------
Expand Down
14 changes: 11 additions & 3 deletions src/Metable/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,19 @@ class Attribute extends Model implements AttributeContract
*/
protected $visible = ['key', 'value', 'type', 'created_at', 'updated_at'];

public function __construct($key = null, $value = '')
public function __construct($key = null, $value = null)
{
parent::__construct([]);
// default behaviour
if (is_array($key)) {
parent::__construct($key);

$this->set($key, $value);
} else {
parent::__construct();

if (is_string($key)) {
$this->set($key, $value);
}
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/Mutator/InvalidCallableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php namespace Sofa\Eloquence\Mutator;

class InvalidCallableException extends \LogicException
{

}
158 changes: 158 additions & 0 deletions src/Mutator/Mutator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php namespace Sofa\Eloquence\Mutator;

use ReflectionException;
use ReflectionClass;
use ReflectionMethod;
use Illuminate\Support\Traits\Macroable;
use Sofa\Eloquence\Contracts\Mutator as MutatorContract;

class Mutator implements MutatorContract
{
use Macroable;

/**
* @inheritdoc
*/
public function mutate($value, $callables)
{
if (!is_array($callables)) {
$callables = explode('|', $callables);
}

foreach ($callables as $callable) {
list($callable, $args) = $this->parse(trim($callable));

$value = call_user_func_array($callable, array_merge([$value], $args));
}

return $value;
}

/**
* Parse provided mutator functions.
*
* @param string $callable
* @return array
*
* @throws \Sofa\Eloquence\Mutator\InvalidCallableException
*/
protected function parse($callable)
{
list($callable, $args) = $this->parseArgs($callable);

if ($this->isClassMethod($callable)) {
$callable = $this->parseClassMethod($callable);

} elseif ($this->isMutatorMethod($callable)) {
$callable = [$this, $callable];

} elseif (!function_exists($callable)) {
throw new InvalidCallableException("Function [{$callable}] not found.");
}

return [$callable, $args];
}

/**
* Determine whether callable is a class method.
*
* @param string $callable
* @return boolean
*/
protected function isClassMethod($callable)
{
return strpos($callable, '@') !== false;
}

/**
* Determine whether callable is available on this instance.
*
* @param string $callable
* @return boolean
*/
protected function isMutatorMethod($callable)
{
return method_exists($this, $callable) || static::hasMacro($callable);
}

/**
* Split provided string into callable and arguments.
*
* @param string $callable
* @return array
*/
protected function parseArgs($callable)
{
$args = [];

if (strpos($callable, ':') !== false) {
list($callable, $argsString) = explode(':', $callable);

$args = explode(',', $argsString);
}

return [$callable, $args];
}

/**
* Extract and validate class method.
*
* @param string $userCallable
* @return callable
*
* @throws \Sofa\Eloquence\Mutator\InvalidCallableException
*/
protected function parseClassMethod($userCallable)
{
$callable = str_replace('@', '::', $userCallable);

try {
$method = new ReflectionMethod($callable);
} catch (ReflectionException $e) {
throw new InvalidCallableException($e->getMessage());
}

return ($method->isStatic()) ? $callable : $this->getInstanceMethod($method, $userCallable);
}

/**
* Get instance callable.
*
* @param \ReflectionMethod $method
* @param string $userCallable
* @return callable
*
* @throws \Sofa\Eloquence\Mutator\InvalidCallableException
*/
protected function getInstanceMethod(ReflectionMethod $method, $userCallable)
{
$class = $method->getDeclaringClass();

if (!$method->isPublic()) {
throw new InvalidCallableException("Instance method [{$userCallable}] is not public.");
}

if (!$this->canInstantiate($class)) {
throw new InvalidCallableException("Can't instantiate class [{$userCallable}].");
}

return [$class->newInstance(), $method->getName()];
}

/**
* Determine whether instance can be instantiated.
*
* @param \ReflectionClass $class
* @return boolean
*/
protected function canInstantiate(ReflectionClass $class)
{
if (!$class->isInstantiable()) {
return false;
}

$constructor = $class->getConstructor();

return is_null($constructor) || 0 === $constructor->getNumberOfRequiredParameters();
}
}
12 changes: 12 additions & 0 deletions tests/AttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

class AttributeTest extends \PHPUnit_Framework_TestCase {

/**
* @test
*/
public function it_instantiates_as_eloquent_by_default()
{
$emptyModel = new Attribute;
$arrayModel = new Attribute([]);

$this->assertEquals([], $emptyModel->getAttributes());
$this->assertEquals([], $arrayModel->getAttributes());
}

/**
* @test
*/
Expand Down
3 changes: 2 additions & 1 deletion tests/MappableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Illuminate\Database\Eloquent\Model;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Mappable;
use Sofa\Eloquence\Contracts\Mappable as MappableContract;

class MappableTest extends \PHPUnit_Framework_TestCase {

Expand Down Expand Up @@ -158,7 +159,7 @@ public function it_sets_mapped_value()
}
}

class MappableStub {
class MappableStub implements MappableContract {

use Eloquence, Mappable {
hasExplicitMapping as protectedHasExplicitMapping;
Expand Down
3 changes: 2 additions & 1 deletion tests/MetableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Illuminate\Database\Eloquent\Model;
use Sofa\Eloquence\Eloquence;
use Sofa\Eloquence\Metable;
use Sofa\Eloquence\Contracts\Metable as MetableContract;

class MetableTest extends \PHPUnit_Framework_TestCase {

Expand Down Expand Up @@ -203,7 +204,7 @@ public function getBag()
}
}

class MetableStub {
class MetableStub implements MetableContract {
use Eloquence, Metable {
saveMeta as protectedSaveMeta;
whereMeta as protectedWhereMeta;
Expand Down
Loading

0 comments on commit 108754a

Please sign in to comment.