diff --git a/README.md b/README.md index d4ad5e6..1896e2e 100644 --- a/README.md +++ b/README.md @@ -2149,9 +2149,10 @@ foreach ($connections as $id => $connection) -### The models provider +### Model collection -The models provider manages models. +Models are managed using a model collection that resolves model attributes (such as database +connections) and instantiate them. @@ -2314,6 +2315,38 @@ var_dump($models->is_installed()); // [ "nodes" => false, "contents" => false ] +#### Model provider + +The `get_model()` helper retrieves models using their identifier. It is used by active records to +retrieve their model when required, and by queries during joins. Models are retrieved using the +model collection returned by [ModelProvider][]. + +The following example demonstrates how to define a model provider: + +```php + **Note:** A `LogicException` is thrown if no provider is defined when `get_model()` require it. + + + + + ## Records caching By default, each model uses an instance of [RuntimeActiveRecordCache][] to cache its records. @@ -2397,34 +2430,7 @@ already instantiated model. -## Patching - -### Retrieving models from a provider - -The `get_model()` helper retrieves models using their identifier. It is used by active -records to retrieve their model when required, and by queries during joins. You need to patch -this helper according to your application logic because the default implementation only throws -`\RuntimeException`. - -In the following example, the `get_model()` helper is patched to retrieve models from a provider -similar to the one we've seen in previous examples. - -```php - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ICanBoogie\ActiveRecord; - -/** - * Patchable helpers of the ActiveRecord package. - * - * @method static Model get_model(string $id) Returns the model with the corresponding identifier. - */ -class Helpers -{ - static private $mapping = [ - - 'get_model' => [ __CLASS__, 'default_get_model' ] - - ]; - - /** - * Calls the callback of a patchable function. - * - * @param string $name Name of the function. - * @param array $arguments Arguments. - * - * @return mixed - */ - static public function __callStatic($name, array $arguments) - { - $method = self::$mapping[$name]; - - return $method(...$arguments); - } - - /** - * Patches a patchable function. - * - * @param string $name Name of the function. - * @param callable $callback Callback. - * - * @throws \RuntimeException is attempt to patch an undefined function. - * - * @return callable - */ - static public function patch($name, $callback) - { - if (empty(self::$mapping[$name])) - { - throw new \LogicException("Undefined patchable: $name."); - } - - $previous = self::$mapping[$name]; - self::$mapping[$name] = $callback; - - return $previous; - } - - /* - * Default implementations - */ - - /** - * @throws \LogicException - */ - static protected function default_get_model() - { - throw new \LogicException("The function `ICanBoogie\\ActiveRecord\\get_model()` needs to be patched."); - } -} diff --git a/lib/ActiveRecord/ModelProvider.php b/lib/ActiveRecord/ModelProvider.php new file mode 100644 index 0000000..6b196a3 --- /dev/null +++ b/lib/ActiveRecord/ModelProvider.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\ActiveRecord; + +/** + * Provides a {@link Model} instance. + */ +class ModelProvider +{ + /** + * @var callable {@link Model} provider + */ + static private $provider; + + /** + * Defines the {@link Model} provider. + * + * @param callable $provider + * + * @return callable The previous provider, or `null` if none was defined. + */ + static public function define(callable $provider) + { + $previous = self::$provider; + + self::$provider = $provider; + + return $previous; + } + + /** + * Returns the current provider. + * + * @return callable|null + */ + static public function defined() + { + return self::$provider; + } + + /** + * Undefine the provider. + */ + static public function undefine() + { + self::$provider = null; + } + + /** + * Returns a {@link Model} instance using the provider. + * + * @param string $id Model identifier. + * + * @return Model + * + * @throws ModelNotDefined if the model cannot be provided. + */ + static public function provide($id) + { + $provider = self::$provider; + + if (!$provider) + { + throw new \LogicException("No provider is defined yet. Please define one with `ModelProvider::define(\$provider)`."); + } + + $model = $provider($id); + + if (!$model) + { + throw new ModelNotDefined($id); + } + + return $model; + } +} diff --git a/tests/ActiveRecordTest.php b/tests/ActiveRecordTest.php index d265604..e09619b 100644 --- a/tests/ActiveRecordTest.php +++ b/tests/ActiveRecordTest.php @@ -1,17 +1,27 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace ICanBoogie; -use ICanBoogie\ActiveRecord\Helpers; use ICanBoogie\ActiveRecord\Model; +use ICanBoogie\ActiveRecord\ModelCollection; use ICanBoogie\ActiveRecord\ModelNotDefined; +use ICanBoogie\ActiveRecord\ModelProvider; use ICanBoogie\ActiveRecord\RecordNotValid; use ICanBoogie\ActiveRecord\Schema; use ICanBoogie\ActiveRecordTest\Sample; use ICanBoogie\ActiveRecordTest\ValidateCase; /** - * @covers \ICanBoogie\ActiveRecord + * @group record */ class ActiveRecordTest extends \PHPUnit_Framework_TestCase { @@ -19,20 +29,25 @@ class ActiveRecordTest extends \PHPUnit_Framework_TestCase public function setUp() { + $sample_model = &$this->sample_model; + + if ($sample_model) + { + return; + } + $sample_model = $this->mockModel(); - Helpers::patch('get_model', function($model_id) use ($sample_model) { + ModelProvider::define(function($model_id) use ($sample_model) { if ($model_id === 'sample') { return $sample_model; } - throw new ModelNotDefined($sample_model); + return null; }); - - $this->sample_model = $sample_model; } public function test_should_resolve_model_id_from_const()