Skip to content

Commit

Permalink
WIP - Progress to an extendable TypeCaster and Factory
Browse files Browse the repository at this point in the history
  • Loading branch information
Aden Fraser committed Apr 29, 2016
1 parent 6ee8c3a commit 3d891e7
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/Illuminate/Contracts/Database/TypeCaster/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Illuminate\Contracts\Database\TypeCaster;

interface TypeCaster
{
/**
* Register a custom Type Caster extension.
*
* @param string $rule
* @param \Closure|string $fromDatabase
* @param \Closure|string|null $toDatabase
* @return void
*/
public function extend($rule, $fromDatabase, $toDatabase = null);
}
28 changes: 28 additions & 0 deletions src/Illuminate/Database/DatabaseServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Database\Eloquent\QueueEntityResolver;
use Illuminate\Database\Connectors\ConnectionFactory;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Database\Eloquent\TypeCaster as TypeCasterFactory;

class DatabaseServiceProvider extends ServiceProvider
{
Expand Down Expand Up @@ -37,6 +38,8 @@ public function register()

$this->registerQueueableEntityResolver();

$this->registerTypeCasterFactory();

// The connection factory is used to create the actual connection instances on
// the database. We will inject the factory into the manager so that it may
// make the connections while they are actually needed and not of before.
Expand Down Expand Up @@ -85,4 +88,29 @@ protected function registerQueueableEntityResolver()
return new QueueEntityResolver;
});
}

/**
* Register the Type Caster factory.
*
* @return void
*/
protected function registerTypeCasterFactory()
{
$this->app->singleton('typecaster', function ($app) {
return new TypeCasterFactory($app);
});
}

/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [
'typecaster',
];
}
}

74 changes: 74 additions & 0 deletions src/Illuminate/Database/Eloquent/TypeCaster/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Illuminate\Database\Eloquent\TypeCaster;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Database\TypeCaster\Factory as FactoryContract;

class Factory implements FactoryContract
{
/**
* The IoC container instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $container;

/**
* All of the custom Type Caster extensions.
*
* @var array
*/
protected $extensions = [];

/**
* Create a new Type Caster factory instance.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return void
*/
public function __construct(Container $container = null)
{
$this->container = $container;
}

/**
* Create a new Type Caster instance.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\TypeCaster\TypeCaster
*/
public function make(Model $model)
{
$typecaster = new TypeCaster($model);

// Next we'll set the IoC container instance of the type caster, which is used
// to resolve out class based type casting extensions. If it is not set then
// these extension types wont be possible on these type caster instances.
if (! is_null($this->container)) {
$typecaster->setContainer($this->container);
}

$typecaster->addExtensions($this->extensions);

return $typecaster;
}

/**
* Register a custom Type Caster extension.
*
* @param string $rule
* @param \Closure|string $fromDatabase
* @param \Closure|string|null $toDatabase
* @return void
*/
public function extend($rule, $fromDatabase, $toDatabase = null)
{
$this->extensions[$rule] = [
'from' => $fromDatabase,
'to' => $toDatabase,
];
}
}
178 changes: 178 additions & 0 deletions src/Illuminate/Database/Eloquent/TypeCaster/TypeCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

namespace Illuminate\Database\Eloquent\TypeCaster;

use Str;
use Closure;
use BadMethodCallException;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Container\Container;

class TypeCaster
{
/**
* The container instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $container;

/**
* The Model to perform Type Casting on.
*
* @var array
*/
protected $model;

/**
* All of the custom Type Casting extensions.
*
* @var array
*/
protected $extensions = [];

/**
* Create a new Type Caster instance.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function __construct(Model $data)
{
$this->model = $model;
}

/**
* Run the Type Caster's rule against an attribute.
*
* @return void
*/
public function cast()
{

}

/**
* Determine if an cast has been set on an attribute.
*
* @param string $attribute
* @return bool
*/
public function hasCast($attribute)
{
return ! is_null($this->getCast($attribute));
}

/**
* Get a cast type and its parameters for a given attribute.
*
* @param string $attribute
* @return array|null
*/
protected function getCast($attribute)
{

}

/**
* Get the array of the custom Type Caster extensions.
*
* @return array
*/
public function getExtensions()
{
return $this->extensions;
}

/**
* Register an array of custom Type Caster extensions.
*
* @param array $extensions
* @return void
*/
public function addExtensions(array $extensions)
{
if ($extensions) {
$keys = array_map('\Illuminate\Support\Str::snake', array_keys($extensions));

$extensions = array_combine($keys, array_values($extensions));
}

$this->extensions = array_merge($this->extensions, $extensions);
}

/**
* Register a custom Type Caster extension.
*
* @param string $rule
* @param \Closure|string $extension
* @return void
*/
public function addExtension($rule, $extension)
{
$this->extensions[Str::snake($rule)] = $extension;
}

/**
* Set the IoC container instance.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return void
*/
public function setContainer(Container $container)
{
$this->container = $container;
}

/**
* Call a custom validator extension.
*
* @param string $rule
* @param array $parameters
* @return bool|null
*/
protected function callExtension($rule, $parameters)
{
$callback = $this->extensions[$rule];

if ($callback instanceof Closure) {
return call_user_func_array($callback, $parameters);
} elseif (is_string($callback)) {
return $this->callClassBasedExtension($callback, $parameters);
}
}

/**
* Call a class based validator extension.
*
* @param string $callback
* @param array $parameters
* @return bool
*/
protected function callClassBasedExtension($callback, $parameters)
{
list($class, $method) = explode('@', $callback);

return call_user_func_array([$this->container->make($class), $method], $parameters);
}

/**
* Handle dynamic calls to class methods.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
$rule = Str::snake(substr($method, 8));

if (isset($this->extensions[$rule])) {
return $this->callExtension($rule, $parameters);
}

throw new BadMethodCallException("Method [$method] does not exist.");
}
}
19 changes: 19 additions & 0 deletions src/Illuminate/Support/Facades/TypeCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Illuminate\Support\Facades;

/**
* @see \Illuminate\Database\Eloquent\TypeCaster\Factory
*/
class TypeCaster extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'typecaster';
}
}

0 comments on commit 3d891e7

Please sign in to comment.