Browse Source

Add ability to define model factories statically

Marlin Cremers 10 years ago
parent
commit
fccfc6ec39

+ 1 - 0
config/bootstrap.php

@@ -13,6 +13,7 @@
  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  */
  */
 
 
+use Cake\Datasource\FactoryLocator;
 use Cake\Routing\Router;
 use Cake\Routing\Router;
 
 
 define('TIME_START', microtime(true));
 define('TIME_START', microtime(true));

+ 74 - 0
src/Datasource/FactoryLocator.php

@@ -0,0 +1,74 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Datasource;
+
+use Cake\ORM\TableRegistry;
+use InvalidArgumentException;
+
+class FactoryLocator
+{
+    /**
+     * A list of model factory functions.
+     *
+     * @var callable[]
+     */
+    protected static $_modelFactories = [];
+
+    /**
+     * Register a callable to generate repositories of a given type.
+     *
+     * @param string $type The name of the repository type the factory function is for.
+     * @param callable $factory The factory function used to create instances.
+     * @return void
+     */
+    public static function add($type, callable $factory)
+    {
+        static::$_modelFactories[$type] = $factory;
+    }
+
+    /**
+     * Drop a model factory.
+     *
+     * @param string $type The name of the repository type to drop the factory for.
+     * @return void
+     */
+    public static function drop($type)
+    {
+        unset(static::$_modelFactories[$type]);
+    }
+
+    /**
+     * Get the factory for the specified repository type.
+     *
+     * @param string $type The repository type to get the factory for.
+     * @throws InvalidArgumentException If the specified repository type has no factory.
+     * @return callable The factory for the repository type.
+     */
+    public static function get($type)
+    {
+        if (!isset(static::$_modelFactories['Table'])) {
+            static::$_modelFactories['Table'] = [TableRegistry::locator(), 'get'];
+        }
+
+        if (!isset(static::$_modelFactories[$type])) {
+            throw new InvalidArgumentException(sprintf(
+                'Unknown repository type "%s". Make sure you register a type before trying to use it.',
+                $type
+            ));
+        }
+
+        return static::$_modelFactories[$type];
+    }
+}

+ 7 - 8
src/Datasource/ModelAwareTrait.php

@@ -41,7 +41,7 @@ trait ModelAwareTrait
     public $modelClass;
     public $modelClass;
 
 
     /**
     /**
-     * A list of model factory functions.
+     * A list of overridden model factory functions.
      *
      *
      * @var array
      * @var array
      */
      */
@@ -104,13 +104,12 @@ trait ModelAwareTrait
             return $this->{$alias};
             return $this->{$alias};
         }
         }
 
 
-        if (!isset($this->_modelFactories[$modelType])) {
-            throw new InvalidArgumentException(sprintf(
-                'Unknown repository type "%s". Make sure you register a type before trying to use it.',
-                $modelType
-            ));
+        if (isset($this->_modelFactories[$modelType])) {
+            $factory = $this->_modelFactories[$modelType];
+        }
+        if (!isset($factory)) {
+            $factory = FactoryLocator::get($modelType);
         }
         }
-        $factory = $this->_modelFactories[$modelType];
         $this->{$alias} = $factory($modelClass);
         $this->{$alias} = $factory($modelClass);
         if (!$this->{$alias}) {
         if (!$this->{$alias}) {
             throw new MissingModelException([$modelClass, $modelType]);
             throw new MissingModelException([$modelClass, $modelType]);
@@ -119,7 +118,7 @@ trait ModelAwareTrait
     }
     }
 
 
     /**
     /**
-     * Register a callable to generate repositories of a given type.
+     * Override a existing callable to generate repositories of a given type.
      *
      *
      * @param string $type The name of the repository type the factory function is for.
      * @param string $type The name of the repository type the factory function is for.
      * @param callable $factory The factory function used to create instances.
      * @param callable $factory The factory function used to create instances.

+ 168 - 0
tests/TestCase/Datasource/FactoryLocatorTest.php

@@ -0,0 +1,168 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.0
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\Datasource;
+
+use Cake\Datasource\FactoryLocator;
+use Cake\TestSuite\TestCase;
+
+/**
+ * FactoryLocatorTest test case
+ */
+class FactoryLocatorTest extends TestCase
+{
+    /**
+     * Test get factory
+     *
+     * @return void
+     */
+    public function testGet()
+    {
+        $this->assertInternalType('callable', FactoryLocator::get('Table'));
+    }
+
+    /**
+     * Test get non existing factory
+     *
+     * @return void
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Unknown repository type "Test". Make sure you register a type before trying to use it.
+     */
+    public function testGetNonExisting()
+    {
+        FactoryLocator::get('Test');
+    }
+
+    /**
+     * test add()
+     *
+     * @return void
+     */
+    public function testAdd()
+    {
+        FactoryLocator::add('Test', function ($name) {
+            $mock = new \StdClass();
+            $mock->name = $name;
+            return $mock;
+        });
+
+        $this->assertInternalType('callable', FactoryLocator::get('Test'));
+    }
+
+    /**
+     * test drop()
+     *
+     * @return void
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Unknown repository type "Test". Make sure you register a type before trying to use it.
+     */
+    public function testDrop()
+    {
+        FactoryLocator::drop('Test');
+
+        FactoryLocator::get('Test');
+    }
+
+    /**
+     * test loadModel() with plugin prefixed models
+     *
+     * Load model should not be called with Foo.Model Bar.Model Model
+     * But if it is, the first call wins.
+     *
+     * @return void
+     */
+    public function testLoadModelPlugin()
+    {
+        $stub = new Stub();
+        $stub->setProps('Articles');
+        $stub->modelType('Table');
+
+        $result = $stub->loadModel('TestPlugin.Comments');
+        $this->assertInstanceOf('TestPlugin\Model\Table\CommentsTable', $result);
+        $this->assertInstanceOf('TestPlugin\Model\Table\CommentsTable', $stub->Comments);
+
+        $result = $stub->loadModel('Comments');
+        $this->assertInstanceOf('TestPlugin\Model\Table\CommentsTable', $result);
+        $this->assertInstanceOf('TestPlugin\Model\Table\CommentsTable', $stub->Comments);
+    }
+
+    /**
+     * test alternate model factories.
+     *
+     * @return void
+     */
+    public function testModelFactory()
+    {
+        $stub = new Stub();
+        $stub->setProps('Articles');
+
+        $stub->modelFactory('Table', function ($name) {
+            $mock = new \StdClass();
+            $mock->name = $name;
+            return $mock;
+        });
+
+        $result = $stub->loadModel('Magic', 'Table');
+        $this->assertInstanceOf('\StdClass', $result);
+        $this->assertInstanceOf('\StdClass', $stub->Magic);
+        $this->assertEquals('Magic', $stub->Magic->name);
+    }
+
+    /**
+     * test alternate default model type.
+     *
+     * @return void
+     */
+    public function testModelType()
+    {
+        $stub = new Stub();
+        $stub->setProps('Articles');
+
+        FactoryLocator::add('Test', function ($name) {
+            $mock = new \StdClass();
+            $mock->name = $name;
+            return $mock;
+        });
+        $stub->modelType('Test');
+
+        $result = $stub->loadModel('Magic');
+        $this->assertInstanceOf('\StdClass', $result);
+        $this->assertInstanceOf('\StdClass', $stub->Magic);
+        $this->assertEquals('Magic', $stub->Magic->name);
+    }
+
+    /**
+     * test MissingModelException being thrown
+     *
+     * @return void
+     * @expectedException \Cake\Datasource\Exception\MissingModelException
+     * @expectedExceptionMessage Model class "Magic" of type "Test" could not be found.
+     */
+    public function testMissingModelException()
+    {
+        $stub = new Stub();
+
+        FactoryLocator::add('Test', function ($name) {
+            return false;
+        });
+
+        $stub->loadModel('Magic', 'Test');
+    }
+
+    public function tearDown()
+    {
+        FactoryLocator::drop('Test');
+
+        parent::tearDown();
+    }
+}

+ 12 - 6
tests/TestCase/Datasource/ModelAwareTraitTest.php

@@ -13,6 +13,7 @@
  */
  */
 namespace Cake\Test\TestCase\Datasource;
 namespace Cake\Test\TestCase\Datasource;
 
 
+use Cake\Datasource\FactoryLocator;
 use Cake\Datasource\ModelAwareTrait;
 use Cake\Datasource\ModelAwareTrait;
 use Cake\TestSuite\TestCase;
 use Cake\TestSuite\TestCase;
 
 
@@ -59,7 +60,6 @@ class ModelAwareTraitTest extends TestCase
     {
     {
         $stub = new Stub();
         $stub = new Stub();
         $stub->setProps('Articles');
         $stub->setProps('Articles');
-        $stub->modelFactory('Table', ['\Cake\ORM\TableRegistry', 'get']);
         $stub->modelType('Table');
         $stub->modelType('Table');
 
 
         $result = $stub->loadModel();
         $result = $stub->loadModel();
@@ -83,7 +83,6 @@ class ModelAwareTraitTest extends TestCase
     {
     {
         $stub = new Stub();
         $stub = new Stub();
         $stub->setProps('Articles');
         $stub->setProps('Articles');
-        $stub->modelFactory('Table', ['\Cake\ORM\TableRegistry', 'get']);
         $stub->modelType('Table');
         $stub->modelType('Table');
 
 
         $result = $stub->loadModel('TestPlugin.Comments');
         $result = $stub->loadModel('TestPlugin.Comments');
@@ -105,13 +104,13 @@ class ModelAwareTraitTest extends TestCase
         $stub = new Stub();
         $stub = new Stub();
         $stub->setProps('Articles');
         $stub->setProps('Articles');
 
 
-        $stub->modelFactory('Test', function ($name) {
+        $stub->modelFactory('Table', function ($name) {
             $mock = new \StdClass();
             $mock = new \StdClass();
             $mock->name = $name;
             $mock->name = $name;
             return $mock;
             return $mock;
         });
         });
 
 
-        $result = $stub->loadModel('Magic', 'Test');
+        $result = $stub->loadModel('Magic', 'Table');
         $this->assertInstanceOf('\StdClass', $result);
         $this->assertInstanceOf('\StdClass', $result);
         $this->assertInstanceOf('\StdClass', $stub->Magic);
         $this->assertInstanceOf('\StdClass', $stub->Magic);
         $this->assertEquals('Magic', $stub->Magic->name);
         $this->assertEquals('Magic', $stub->Magic->name);
@@ -127,7 +126,7 @@ class ModelAwareTraitTest extends TestCase
         $stub = new Stub();
         $stub = new Stub();
         $stub->setProps('Articles');
         $stub->setProps('Articles');
 
 
-        $stub->modelFactory('Test', function ($name) {
+        FactoryLocator::add('Test', function ($name) {
             $mock = new \StdClass();
             $mock = new \StdClass();
             $mock->name = $name;
             $mock->name = $name;
             return $mock;
             return $mock;
@@ -151,10 +150,17 @@ class ModelAwareTraitTest extends TestCase
     {
     {
         $stub = new Stub();
         $stub = new Stub();
 
 
-        $stub->modelFactory('Test', function ($name) {
+        FactoryLocator::add('Test', function ($name) {
             return false;
             return false;
         });
         });
 
 
         $stub->loadModel('Magic', 'Test');
         $stub->loadModel('Magic', 'Test');
     }
     }
+
+    public function tearDown()
+    {
+        FactoryLocator::drop('Test');
+
+        parent::tearDown();
+    }
 }
 }