From e6a525fde22eeab4f8a5c1b30bd6538ee3bdeb09 Mon Sep 17 00:00:00 2001 From: Biser Antonov Date: Mon, 5 Dec 2016 15:04:03 +0200 Subject: [PATCH] Added support for callback definitions --- src/Definition.php | 41 +++++++++++++++++++++--- tests/AbstractTestCase.php | 3 ++ tests/DefinitionTest.php | 58 ++++++++++++++++++++++++++++++++++ tests/factories/definition.php | 10 ++++++ 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/Definition.php b/src/Definition.php index 33a57cb..90736f1 100644 --- a/src/Definition.php +++ b/src/Definition.php @@ -12,6 +12,8 @@ namespace League\FactoryMuffin; +use League\FactoryMuffin\Exceptions\DefinitionException; + /** * This is the model definition class. * @@ -50,7 +52,7 @@ final class Definition /** * The attribute definitions. * - * @var array + * @var array|callable */ private $definitions = []; @@ -176,14 +178,24 @@ public function getCallback() * Add an attribute definitions. * * Note that we're appending to the original attribute definitions here. + * Will throw exception if definitions are already defined by a callback. * * @param string $attribute The attribute name. * @param string|callable $definition The attribute definition. * + * @throws DefinitionException + * * @return \League\FactoryMuffin\Definition */ public function addDefinition($attribute, $definition) { + if (is_callable($this->definitions)) { + $message = "Can't add definition for attribute '$attribute'. " + .'Definitions are already defined by a callback.'; + + throw new DefinitionException($this->class, $message); + } + $this->definitions[$attribute] = $definition; return $this; @@ -195,13 +207,21 @@ public function addDefinition($attribute, $definition) * Note that we're appending to the original attribute definitions here * instead of switching them out for the new ones. * - * @param array $definitions The attribute definitions. + * @param array|callable $definitions The attribute definitions. + * + * @throws \InvalidArgumentException * * @return \League\FactoryMuffin\Definition */ - public function setDefinitions(array $definitions = []) + public function setDefinitions($definitions) { - $this->definitions = array_merge($this->definitions, $definitions); + if (is_callable($definitions)) { + $this->definitions = $definitions; + } elseif (is_array($definitions)) { + $this->definitions = array_merge($this->definitions, $definitions); + } else { + throw new \InvalidArgumentException('Definitions must be array or callable.'); + } return $this; } @@ -221,10 +241,21 @@ public function clearDefinitions() /** * Get the attribute definitions. * + * @throws DefinitionException + * * @return array */ public function getDefinitions() { - return $this->definitions; + if (is_callable($this->definitions)) { + $definitions = call_user_func($this->definitions); + if (!is_array($definitions)) { + throw new DefinitionException($this->class, 'Definitions callback must return array.'); + } + + return $definitions; + } else { + return $this->definitions; + } } } diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php index 5c02e85..86aa7e5 100644 --- a/tests/AbstractTestCase.php +++ b/tests/AbstractTestCase.php @@ -22,6 +22,9 @@ */ abstract class AbstractTestCase extends TestCase { + /** + * @var FactoryMuffin + */ protected static $fm; public static function setupBeforeClass() diff --git a/tests/DefinitionTest.php b/tests/DefinitionTest.php index 691e118..0825da1 100644 --- a/tests/DefinitionTest.php +++ b/tests/DefinitionTest.php @@ -378,6 +378,64 @@ public function testNoMakerGroup() $this->assertInstanceOf('CustomMakerStub', $obj); $this->assertSame('bar', $obj->foo); } + + public function testDefineWithCallback() + { + $user = static::$fm->create('definitionscallback:UserModelStub'); + + $this->assertInstanceOf('UserModelStub', $user); + $this->assertInternalType('string', $user->name); + $this->assertInternalType('boolean', $user->active); + $this->assertContains('@', $user->email); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testShouldThrowExceptionWhenDefinitionsAreNeitherArrayNorCallback() + { + try { + static::$fm->define('invaliddefinitions:UserModelStub')->setDefinitions('invalid definitions'); + } catch (\InvalidArgumentException $e) { + $this->assertSame('Definitions must be array or callable.', $e->getMessage()); + + throw $e; + } + } + + /** + * @expectedException \League\FactoryMuffin\Exceptions\DefinitionException + */ + public function testShouldThrowExceptionWhenDefinitionsCallbakcDoesntReturnArray() + { + try { + $model = 'noarraydefinitions:UserModelStub'; + + static::$fm->define($model)->setDefinitions(function () { + return 'not an array'; + }); + static::$fm->getDefinition($model)->getDefinitions(); + } catch (\League\FactoryMuffin\Exceptions\DefinitionException $e) { + $this->assertSame('Definitions callback must return array.', $e->getMessage()); + + throw $e; + } + } + + /** + * @expectedException \League\FactoryMuffin\Exceptions\DefinitionException + */ + public function testShouldThrowExceptionWhenDefinitionsAreSetWithCallbackAndTryToAddDefinition() + { + try { + static::$fm->getDefinition('definitionscallback:UserModelStub')->addDefinition('name', 'foo'); + } catch (\League\FactoryMuffin\Exceptions\DefinitionException $e) { + $message = "Can't add definition for attribute 'name'. Definitions are already defined by a callback."; + $this->assertSame($message, $e->getMessage()); + + throw $e; + } + } } class AttributeDefinitionsStub diff --git a/tests/factories/definition.php b/tests/factories/definition.php index 7dc9b08..6f78195 100644 --- a/tests/factories/definition.php +++ b/tests/factories/definition.php @@ -72,3 +72,13 @@ }); $fm->define('clear:CustomMakerStub')->clearMaker(); + +$fm->define('definitionscallback:UserModelStub')->setDefinitions(function () { + return [ + 'name' => Faker::word(), + 'active' => Faker::boolean(), + 'email' => Faker::email(), + 'age' => Faker::numberBetween(18, 35), + 'profile' => 'factory|ProfileModelStub', + ]; +});