Commit 389ef891 by Qiang Xue

Merge pull request #1743 from Ragazzo/yii2_faker_integration

Yii2 faker integration
parents 4a9b2d2b c5bd2702
......@@ -55,6 +55,7 @@
"yiisoft/yii2-codeception": "self.version",
"yiisoft/yii2-debug": "self.version",
"yiisoft/yii2-elasticsearch": "self.version",
"yiisoft/yii2-faker": "self.version",
"yiisoft/yii2-imagine": "self.version",
"yiisoft/yii2-gii": "self.version",
"yiisoft/yii2-jui": "self.version",
......@@ -97,6 +98,7 @@
"yii\\codeception\\": "extensions/",
"yii\\debug\\": "extensions/",
"yii\\elasticsearch\\": "extensions/",
"yii\\faker\\": "extensions/",
"yii\\gii\\": "extensions/",
"yii\\imagine\\" : "extensions/",
"yii\\jui\\": "extensions/",
......
Yii Framework 2 faker extension Change Log
==============================================
2.0.0 beta under development
----------------------------
- no changes in this release.
2.0.0 alpha, December 1, 2013
-----------------------------
- Initial release.
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\faker;
use Yii;
use yii\console\Exception;
use yii\helpers\FileHelper;
use yii\helpers\Console;
/**
* This command manage fixtures creations based on given template.
*
* Fixtures are one of the important paths in unit testing. To speed up developers
* work these fixtures can be generated automatically, based on prepared template.
* This command is a simple wrapper for the fixtures library [Faker](https://github.com/fzaninotto/Faker).
*
* You should configure this command as follows (you can use any alias, not only "faker:fixture"):
*
* ~~~
* 'controllerMap' => [
* 'fixture' => [
* 'class' => 'yii\faker\FixtureController',
* ],
* ],
* ~~~
*
* To start using this command you need to be familiar (read guide) for the Faker library and
* generate fixtures template files, according to the given format:
*
* ~~~
* #users.php file under $templatePath
*
* return [
* [
* 'table_column0' => 'faker_formatter',
* ...
* 'table_columnN' => 'other_faker_formatter
* 'table_columnN+1' => function ($fixture, $faker, $index) {
* //set needed fixture fields based on different conditions
* return $fixture;
* }
* ],
* ];
* ~~~
*
* If you use callback as a attribute value, then it will be called as shown with three parameters:
* - $fixture - current fixture array.
* - $faker - faker generator instance
* - $index - current fixture index. For example if user need to generate 3 fixtures for tbl_user, it will be 0..2
* After you set all needed fields in callback, you need to return $fixture array back from the callback.
*
* After you prepared needed templates for tables you can simply generate your fixtures via command
*
* ~~~
* php yii fixture/generate users
*
* //also a short version of this command (generate action is default)
* php yii fixture users
*
* //to generate fixtures for several tables, use "," as a separator, for example:
* php yii fixture users,profile
* ~~~
*
* In the code above "users" is template name, after this command run, new file named same as template
* will be created under the $fixturesPath folder.
* You can generate fixtures for all templates by specifying keyword "all_fixtures"
*
* ~~~
* php yii fixture/generate all
* ~~~
*
* This command will generate fixtures for all template files that are stored under $templatePath and
* store fixtures under $fixturesPath with file names same as templates names.
*
* You can specify how many fixtures per file you need by the second parameter. In the code below we generate
* all fixtures and in each file there will be 3 rows (fixtures).
*
* ~~~
* php yii fixture/generate all 3
* ~~~
*
* You can specify different options of this command:
*
* ~~~
* //generate fixtures in russian languge
* php yii fixture/generate users 5 --language='ru_RU'
*
* //read templates from the other path
* php yii fixture/generate all --templatePath='@app/path/to/my/custom/templates'
*
* //generate fixtures into other folders, but be sure that this folders exists or you will get notice about that.
* php yii fixture/generate all --fixturesPath='@tests/unit/fixtures/subfolder1/subfolder2/subfolder3'
* ~~~
*
* You also can create your own data providers for custom tables fields, see Faker library guide for more info (https://github.com/fzaninotto/Faker);
* After you created custom provider, for example:
*
* ~~~
*
* class Book extends \Faker\Provider\Base
* {
* public function title($nbWords = 5)
* {
* $sentence = $this->generator->sentence($nbWords);
* return mb_substr($sentence, 0, mb_strlen($sentence) - 1);
* }
*
* public function ISBN()
* {
* return $this->generator->randomNumber(13);
* }
* }
* ~~~
*
* you can use it by adding it to the $providers property of the current command. In your console.php config:
*
* ~~~
* 'controllerMap' => [
* 'fixture' => [
* 'class' => 'yii\faker\FixtureController',
* 'providers' => [
* 'app\tests\unit\faker\providers\Book',
* ],
* ],
* ],
* ~~~
*
* @property \Faker\Generator $generator
*
* @since 2.0.0
*/
class FixtureController extends \yii\console\controllers\FixtureController
{
/**
* type of fixture generating
*/
const GENERATE_ALL = 'all';
/**
* @var string controller default action ID.
*/
public $defaultAction = 'generate';
/**
* Alias to the template path, where all tables templates are stored.
* @var string
*/
public $templatePath = '@tests/unit/templates/fixtures';
/**
* Language to use when generating fixtures data.
* @var string
*/
public $language;
/**
* Additional data providers that can be created by user and will be added to the Faker generator.
* More info in [Faker](https://github.com/fzaninotto/Faker.) library docs.
* @var array
*/
public $providers = [];
/**
* Faker generator instance
* @var \Faker\Generator
*/
private $_generator;
/**
* Returns the names of the global options for this command.
* @return array the names of the global options for this command.
*/
public function globalOptions()
{
return array_merge(parent::globalOptions(), [
'templatePath','language'
]);
}
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
$this->checkPaths();
$this->addProviders();
return true;
} else {
return false;
}
}
/**
* Generates fixtures and fill them with Faker data.
* @param string $file filename for the table template. You can generate all fixtures for all tables
* by specifiyng keyword "all" as filename.
* @param integer $times how much fixtures do you want per table
*/
public function actionGenerate(array $file, $times = 2)
{
$templatePath = Yii::getAlias($this->templatePath);
$fixturesPath = Yii::getAlias($this->fixturesPath);
if ($this->needToGenerateAll($file[0])) {
$files = FileHelper::findFiles($templatePath, ['only' => ['.php']]);
}
else {
foreach($file as $fileName) {
$filesToSearch[] = $fileName . '.php';
}
$files = FileHelper::findFiles($templatePath, ['only' => $filesToSearch]);
}
if (empty($files)) {
throw new Exception(
"No files were found by name: \"{$file}\". \n"
. "Check that template with these name exists, under template path: \n\"{$templatePath}\"."
);
}
if (!$this->confirmGeneration($files)) {
return;
}
foreach ($files as $templateFile) {
$fixtureFileName = basename($templateFile);
$template = $this->getTemplate($templateFile);
$fixtures = [];
for ($i = 0; $i < $times; $i++) {
$fixtures[$i] = $this->generateFixture($template, $i);
}
$content = $this->exportFixtures($fixtures);
file_put_contents($fixturesPath.'/'.$fixtureFileName, $content);
$this->stdout("Fixture file was generated under: " . realpath($fixturesPath . "/" . $fixtureFileName) . "\n", Console::FG_GREEN);
}
}
/**
* Returns Faker generator instance. Getter for private property.
* @return \Faker\Generator
*/
public function getGenerator()
{
if (is_null($this->_generator)) {
//replacing - on _ because Faker support only en_US format and not intl
$language = is_null($this->language) ? str_replace('-','_', Yii::$app->language) : $this->language;
$this->_generator = \Faker\Factory::create($language);
}
return $this->_generator;
}
/**
* Check if the template path and migrations path exists and writable.
*/
public function checkPaths()
{
$path = Yii::getAlias($this->templatePath);
if (!is_dir($path)) {
throw new Exception("The template path \"{$this->templatePath}\" not exist");
}
}
/**
* Adds users providers to the faker generator.
*/
public function addProviders()
{
foreach($this->providers as $provider) {
$this->generator->addProvider(new $provider($this->generator));
}
}
/**
* Checks if needed to generate all fixtures.
* @param string $file
* @return bool
*/
public function needToGenerateAll($file)
{
return $file == self::GENERATE_ALL;
}
/**
* Returns generator template for the given fixture name
* @param string $file template file
* @return array generator template
* @throws \yii\console\Exception if wrong file format
*/
public function getTemplate($file)
{
$template = require($file);
if (!is_array($template)) {
throw new Exception("The template file \"$file\" has wrong format. It should return valid template array");
}
return $template;
}
/**
* Returns exported to the string representation of given fixtures array.
* @param type $fixtures
* @return string exported fixtures format
*/
public function exportFixtures($fixtures)
{
$content = "<?php\n\nreturn [";
foreach($fixtures as $fixture) {
$content .= "\n\t[";
foreach($fixture as $name=>$value) {
$content .= "\n\t\t'{$name}' => '{$value}',";
}
$content .= "\n\t],";
}
$content .= "\n];\n";
return $content;
}
/**
* Generates fixture from given template
* @param array $template fixture template
* @param integer $index current fixture index
* @return array fixture
*/
public function generateFixture($template, $index)
{
$fixture = [];
foreach($template as $attribute => $fakerProperty) {
if (!is_string($fakerProperty)) {
$fixture = call_user_func_array($fakerProperty,[$fixture,$this->generator, $index]);
} else {
$fixture[$attribute] = $this->generator->$fakerProperty;
}
}
return $fixture;
}
/**
* Prompts user with message if he confirm generation with given fixture templates files.
* @param array $files
* @return boolean
*/
public function confirmGeneration($files)
{
$this->stdout("Fixtures will be generated under the path: \n", Console::FG_YELLOW);
$this->stdout(realpath(Yii::getAlias($this->fixturesPath, false)) ."\n\n", Console::FG_GREEN);
$this->stdout("Templates will be taken from path: \n", Console::FG_YELLOW);
$this->stdout(realpath(Yii::getAlias($this->templatePath, false)) . "\n\n", Console::FG_GREEN);
foreach ($files as $index => $fileName) {
$this->stdout(" " . $index +1 . ". " . basename($fileName) . "\n",Console::FG_GREEN);
}
return $this->confirm('Generate above fixtures?');
}
}
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
Faker Extension for Yii 2
===============================
This extension provides a [`Faker`](https://github.com/fzaninotto/Faker) fixture command for Yii 2.
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-faker "*"
```
or add
```json
"yiisoft/yii2-faker": "*"
```
to the require section of your composer.json.
Usage
-----
To use this extension, simply add the following code in your application configuration (console.php):
```php
'controllerMap' => [
'fixture' => [
'class' => 'yii\faker\FixtureController',
],
],
```
Set valid ```test``` alias in your console config, for example for ```basic``` application template, this should be added
to ```console.php``` config: ```Yii::setAlias('tests', __DIR__ . '/../tests');```
To start using this command you need to be familiar (read guide) for the [Faker](https://github.com/fzaninotto/Faker) library and
generate fixtures template files, according to the given format:
```php
//users.php file under template path (by default @tests/unit/templates/fixtures)
return [
[
'table_column0' => 'faker_formatter',
...
'table_columnN' => 'other_faker_formatter'
'body' => function ($fixture, $faker, $index) {
//set needed fixture fields based on different conditions
$fixture['body'] = $faker->sentence(7,true); //generate sentence exact with 7 words.
return $fixture;
}
],
];
```
If you use callback as a attribute value, then it will be called as shown with three parameters:
* ```$fixture``` - current fixture array.
* ```$faker``` - faker generator instance
* ```$index``` - current fixture index. For example if user need to generate 3 fixtures for tbl_user, it will be 0..2.
After you set all needed fields in callback, you need to return $fixture array back from the callback.
Another example of valid template:
```php
use yii\helpers\Security;
return [
'name' => 'firstName',
'phone' => 'phoneNumber',
'city' => 'city',
'password' => function ($fixture, $faker, $index) {
$fixture['password'] = Security::generatePasswordHash('password_' . $index);
return $fixture;
},
'auth_key' => function ($fixture, $faker, $index) {
$fixture['auth_key'] = Security::generateRandomKey();
return $fixture;
},
];
```
After you prepared needed templates for tables you can simply generate your fixtures via command
```php
//generate fixtures for the users table based on users fixture template
php yii fixture/generate users
//also a short version of this command ("generate" action is default)
php yii fixture users
//to generate fixtures for several tables, use "," as a separator, for example:
php yii fixture users,profile,some_other_table
```
In the code above "users" is template name, after this command run, new file named same as template
will be created under the fixtures path (by default ```@tests/unit/fixtures```) folder.
You can generate fixtures for all templates by specifying keyword ```all```.
```php
php yii fixture/generate all_fixtures
```
This command will generate fixtures for all template files that are stored under template path and
store fixtures under fixtures path with file names same as templates names.
You can specify how many fixtures per file you need by the second parameter. In the code below we generate
all fixtures and in each file there will be 3 rows (fixtures).
```php
php yii fixture/generate all 3
```
You can specify different options of this command:
```php
//generate fixtures in russian language
php yii fixture/generate users 5 --language='ru_RU'
//read templates from the other path
php yii fixture/generate all --templatePath='@app/path/to/my/custom/templates'
//generate fixtures into other folders, but be sure that this folders exists or you will get notice about that.
php yii fixture/generate all --fixturesPath='@tests/unit/fixtures/subfolder1/subfolder2/subfolder3'
```
You also can create your own data providers for custom tables fields, see [Faker]((https://github.com/fzaninotto/Faker)) library guide for more info;
After you created custom provider, for example:
```php
class Book extends \Faker\Provider\Base
{
public function title($nbWords = 5)
{
$sentence = $this->generator->sentence($nbWords);
return mb_substr($sentence, 0, mb_strlen($sentence) - 1);
}
public function ISBN()
{
return $this->generator->randomNumber(13);
}
}
```
You can use it by adding it to the ```$providers``` property of the current command. In your console.php config:
```php
'controllerMap' => [
'fixture' => [
'class' => 'yii\faker\FixtureController',
'providers' => [
'app\tests\unit\faker\providers\Book',
],
],
]
```
\ No newline at end of file
{
"name": "yiisoft/yii2-faker",
"description": "Fixture generator. The Faker integration for the Yii framework.",
"keywords": ["yii", "faker", "fixture"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"authors": [
{
"name": "Mark Jebri",
"email": "mark.github@yandex.ru"
}
],
"require": {
"yiisoft/yii2": "*",
"fzaninotto/faker": "*"
},
"autoload": {
"psr-0": { "yii\\faker\\": "" }
},
"target-dir": "yii/faker"
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\console\controllers;
use Yii;
use yii\console\Controller;
use yii\console\Exception;
/**
* This command manages fixtures load to the database tables.
* You can specify different options of this command to point fixture manager
* to the specific tables of the different database connections.
*
* To use this command simply configure your console.php config like this:
* ~~~
* 'db' => [
* 'class' => 'yii\db\Connection',
* 'dsn' => 'mysql:host=localhost;dbname={your_database}',
* 'username' => '{your_db_user}',
* 'password' => '',
* 'charset' => 'utf8',
* ],
* 'fixture' => [
* 'class' => 'yii\test\DbFixtureManager',
* ],
* ~~~
*
* ~~~
* #load fixtures under $fixturesPath to the "users" table
* php yii fixture/apply users
*
* #also a short version of this command (generate action is default)
* php yii fixture users
*
* #load fixtures under $fixturesPath to the "users" table to the different connection
* php yii fixture/apply users --db='someOtherDbConneciton'
*
* #load fixtures under different $fixturesPath to the "users" table.
* php yii fixture/apply users --fixturesPath='@app/some/other/path/to/fixtures'
* ~~~
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class FixtureController extends Controller
{
use \yii\test\DbTestTrait;
/**
* @var string controller default action ID.
*/
public $defaultAction = 'apply';
/**
* Alias to the path, where all fixtures are stored.
* @var string
*/
public $fixturesPath = '@tests/unit/fixtures';
/**
* Id of the database connection component of the application.
* @var string
*/
public $db = 'db';
/**
* Returns the names of the global options for this command.
* @return array the names of the global options for this command.
*/
public function globalOptions()
{
return array_merge(parent::globalOptions(), [
'db','fixturesPath'
]);
}
/**
* This method is invoked right before an action is to be executed (after all possible filters.)
* It checks that fixtures path and database connection are available.
* @param type $action
* @return boolean
*/
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
$this->checkRequirements();
return true;
} else {
return false;
}
}
/**
* Apply given fixture to the table. Fixture name can be the same as the table name or
* you can specify table name as a second parameter.
* @param string $fixture
*/
public function actionApply($fixture)
{
$this->fixtureManager->basePath = $this->fixturesPath;
$this->fixtureManager->db = $this->db;
$this->loadFixtures([$fixture]);
}
/**
* Truncate given table and clear all fixtures from it.
* @param string $table
*/
public function actionClear($table)
{
$this->dbConnection->createCommand()->truncateTable($table)->execute();
echo "Table \"{$table}\" was successfully cleared. \n";
}
/**
* Checks if the database and fixtures path are available.
* @throws Exception
*/
public function checkRequirements()
{
$path = Yii::getAlias($this->fixturesPath, false);
if (!is_dir($path) || !is_writable($path)) {
throw new Exception("The fixtures path \"{$this->fixturesPath}\" not exist or is not writable");
}
}
/**
* Returns database connection component
* @return yii\db\Connection|null
*/
public function getDbConnection()
{
$db = Yii::$app->getComponent($this->db);
if ($db == null) {
throw new Exception("There is no database connection component with id \"{$this->db}\".");
}
return $db;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment