<repo_name>atrauzzi/domain-tool-laravel<file_sep>/src/ModelAdapter.php
<?php namespace Atrauzzi\DomainToolLaravel {

	use Atrauzzi\DomainTool\Model\Adapter\Contract as ModelAdapterContract;


	/**
	 * Class ModelAdapter
	 *
	 * Allows Domain Tool to interface nicely with Laravel Eloquent models.
	 *
	 * @package Atrauzzi\DomainToolLaravel
	 */
	class ModelAdapter implements ModelAdapterContract {

		/**
		 * Obtains a value from an instance.
		 *
		 * @param object $instance
		 * @param string $attribute
		 * @return mixed
		 */
		public function getAttribute($instance, $attribute) {
			return $instance->$attribute;
		}

		/**
		 * Assigns a value to an instance.
		 *
		 * @param object $instance
		 * @param string $attribute
		 * @param mixed $value
		 */
		public function setAttribute($instance, $attribute, $value) {
			$instance->$attribute = $value;
		}

		/**
		 * Obtains all attributes that are readable and writable.
		 *
		 * @param object $instance
		 * @return string[]
		 */
		public function getFullAttributes($instance) {
			return array_intersect(
				$this->getReadableAttributes($instance),
				$this->getWritableAttributes($instance)
			);
		}

		/**
		 * Obtains all the attributes of the model that can be set.
		 *
		 * @param object $instance
		 * @return string[]
		 */
		public function getWritableAttributes($instance) {
			return $instance->fillable;
		}

		/**
		 * Obtains all the attributes of the model that can be read.
		 *
		 * @param object $instance
		 * @return string[]
		 */
		public function getReadableAttributes($instance) {
			return $instance->visible;
		}

	}

}<file_sep>/readme.md
# Domain Tool Laravel

Laravel specific bindings to make use of Domain Tool in your projects and packages.<file_sep>/src/ProfileLoader.php
<?php namespace Atrauzzi\DomainToolLaravel {

	use Atrauzzi\DomainTool\ProfileLoader as ProfileLoaderContract;
	//
	use ReflectionClass;


	class ProfileLoader implements ProfileLoaderContract {

		/** @var string */
		protected $configRoot = 'domain-tool';

		/**
		 * @param string $configRoot
		 */
		public function setConfigRoot($configRoot) {
			$this->configRoot = $configRoot;
		}

		//
		//
		//

		/**
		 * Returns whether a type has a base config defined anywhere.
		 *
		 * @param string $type
		 * @param null|string $profileName
		 * @return boolean
		 */
		public function hasProfileForType($type, $profileName = null) {
			return
				$this->config(sprintf('type.%s', $type))
				|| $profileName && $this->config(sprintf('profiles.%s.type.%s', $profileName, $type))
			;
		}

		/**
		 * @param string $type
		 * @param null|string $profileName
		 * @return array
		 */
		public function getProfile($type, $profileName = null) {
			return array_replace_recursive(
				$this->getGlobalProfile($profileName),
				$this->getProfileForType($type, $profileName)
			);
		}

		//
		//
		//

		/**
		 * Returns base config for a type.
		 *
		 * @param string $type
		 * @param null|string $profileName
		 * @return array
		 */
		protected function getProfileForType($type, $profileName = null) {

			$typeReflection = new ReflectionClass($type);

			if($parent = $typeReflection->getParentClass())
				$parentProfile = $this->getProfileForType($parent->getName(), $profileName);
			else
				$parentProfile = [];

			$profile = array_replace_recursive(
				$parentProfile,
				$this->config(sprintf('type.%s', $type), []),
				$this->config(sprintf('profiles.%s.type.%s', $profileName, $type), [])
			);

			return $profile;

		}

		/**
		 * Obtains rules that apply to all types, optionally searching a named profile.
		 *
		 * @param null|string $profileName
		 * @return array
		 */
		protected function getGlobalProfile($profileName = null) {

			$profile = array_replace_recursive(
				[
					'adapter' => 'Atrauzzi\DomainToolLaravel\ModelAdapter',
					'snake_out' => true,
					'snake_in' => true,
					'include_type' => true,
				],
				$this->config('all', []),
				$this->config(sprintf('profiles.%s.all', $profileName), [])
			);

			return $profile;

		}

		/**
		 * A very simple passthrough that applies our config root.
		 *
		 * @param string $key
		 * @param mixed $default
		 * @return mixed
		 */
		protected function config($key, $default = null) {
			return config(sprintf('%s.%s', $this->configRoot, $key), $default);
		}

	}

}<file_sep>/src/ModelWithGetAdapter.php
<?php namespace Atrauzzi\DomainToolLaravel {

	use Atrauzzi\DomainTool\Util;
	use Illuminate\Database\Eloquent\Model;
	use ReflectionMethod;
	use ReflectionClass;


	/**
	 * Class ModelAdapter
	 *
	 * Allows Domain Tool to interface nicely with Laravel Eloquent models.
	 *
	 * @package Atrauzzi\DomainToolLaravel
	 */
	class ModelWithGetAdapter extends ModelAdapter {

		/** @var \ReflectionClass[] */
		protected static $reflectionCache;

		/** @var array */
		protected static $getterAttributeCache;

		/**
		 * Obtains a value from an instance.
		 *
		 * @param object $instance
		 * @param string $attribute
		 * @return mixed
		 */
		public function getAttribute($instance, $attribute) {
			$getter = 'get' . ucfirst($attribute);
			return $instance->$getter();
		}

		/**
		 * Obtains all the attributes of the model that can be set.
		 *
		 * @param object $instance
		 * @return string[]
		 */
		public function getWritableAttributes($instance) {

			if(!$instance instanceof Model)
				$instance = new $instance;

			return $instance->getFillable();

		}

		/**
		 * Assigns a value to an instance.
		 *
		 * Remember that the convention in Laravel is to `snake_case` attribtue names.
		 *
		 * @param object $instance
		 * @param string $attribute
		 * @param mixed $value
		 */
		public function setAttribute($instance, $attribute, $value) {
			$attribute = snake_case($attribute);
			$instance->$attribute = $value;
		}

		/**
		 * Obtains all the attributes of the model that can be read.
		 *
		 * @param object $instanceOrClass
		 * @return string[]
		 */
		public function getReadableAttributes($instanceOrClass) {

			$class = Util::getClass($instanceOrClass);

			if(empty(self::$getterAttributeCache[$class])) {

				$reflection = $this->getReflection($class);
				$attributes = [];

				foreach($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {

					$methodName = $method->getName();

					if(substr($methodName, 0, 3) == 'get')
						$attributes[] = substr($methodName, 3);

				}

				self::$getterAttributeCache[$class] = $attributes;

			}

			return self::$getterAttributeCache[$class];

		}

		//
		//
		//

		/**
		 * Obtains and caches ReflectionClass instances for classes.
		 *
		 * @param string|object $class
		 * @return \ReflectionClass
		 */
		protected function getReflection($class) {

			if(empty($reflectionCache[$class]))
				self::$reflectionCache[$class] = new ReflectionClass($class);

			return self::$reflectionCache[$class];

		}

	}

}<file_sep>/src/ServiceProvider.php
<?php namespace Atrauzzi\DomainToolLaravel {

	use Illuminate\Support\ServiceProvider as Base;
	//
	use Atrauzzi\DomainTool\Config;

	
	class ServiceProvider extends Base {

		public function register() {

			$this->app->singleton('Atrauzzi\DomainTool\Config', function () {
				return new Config(
					[$this->app, 'make'],
					app('Atrauzzi\DomainToolLaravel\ProfileLoader')
				);
			});

		}

		public function boot() {

			$this->publishes([
				__DIR__ . '/../config/domain-tool.php' => config_path('domain-tool.php')
			], 'config');

		}

	}

}<file_sep>/config/domain-tool.php
<?php return [

	// Global profile settings, applies to all types, always (even when no profile is requested).
	'all' => [

		/*
		 * Global default model adapter.
		 *
		 * Can be:
		 * 		- Atrauzzi\DomainTool\Model\Adapter\AutoGetSet
		 * 		- Atrauzzi\DomainTool\Model\Adapter\GetSet
		 * 		- Atrauzzi\DomainTool\Laravel\ModelAdapter
		 * 		- Any other, supplied by another integration library.
		 *
		 */
		'adapter' => 'Atrauzzi\DomainToolLaravel\ModelAdapter',

		/*
		 * Outbound values should be in snake_case.
		 */
		'snake_out' => true,

		/**
		 * Inbound values will be in snake_case.
		 */
		'snake_in' => true,

		/**
		 * Include a type attribute when dumping.
		 */
		'include_type' => true,

	],

	// Default profiles, per type.
	'type' => [
		/*
		 * Type-specific Profiles
		 *
		 * If several of your profiles contain similar instructions for a specific class, you
		 * can avoid that duplication here.  Keep in mind, when no profile is requested, default
		 * does not get applied.
		 *
		'Path\To\Class' => [
		],
		 */
	],


	/*
	 * Sample profile structure
	 *
	'profiles' => [

		'sample-profile' => [

			'all' => [
			],

			'type' => [
				'Path\To\Class' => [

					// When absent/empty, defaults to AutoGetSet adapter with underscore output.
					'adapter' => 'Path\To\AdapterImpl',

					// You can override the PHP fully qualified name that's generated if desired.
					// 'include_type' => 'flat_external_name',

					// Similar to L4's "fillable" array on models, but supports mapping.
					// When absent, all attributes can be set.
					'in' => [
						'id',
						'firstName',
						'tele' => 'phone',
					],

					// Similar to L4's "visible" array on models, but supports mapping.
					// When absent/empty, all attributes are displayed.
					'out' => [
						'id',
						'firstName',
						'phone' => 'tele',
					],

				],
			],


		],

	],
	*/

];