1
0

提交代码

This commit is contained in:
2020-08-06 14:50:07 +08:00
parent 9d0d5f4be9
commit d7a848c824
11299 changed files with 1321854 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
/vendor
composer.phar
composer.lock
.DS_Store
.idea

View File

@@ -0,0 +1,14 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- composer self-update
- composer install --prefer-source --no-interaction --dev
script:
- vendor/bin/phpspec run

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Jeffrey Way <jeffrey@jeffrey-way.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,9 @@
default:
paths:
features: tests/features
bootstrap: %behat.paths.features%/bootstrap
extensions:
Behat\MinkExtension\Extension:
base_url: http://localhost:8000
goutte: ~
selenium2: ~

View File

@@ -0,0 +1,29 @@
{
"name": "xethron/laravel-4-generators",
"description": "Rapidly generate resources, migrations, models, and much more.",
"license": "MIT",
"authors": [
{
"name": "Jeffrey Way",
"email": "jeffrey@jeffrey-way.com"
}
],
"require": {
"php": ">=5.4.0",
"illuminate/support": "~5.0"
},
"require-dev": {
"phpspec/phpspec": "~2.0",
"behat/behat": "~2.5.1",
"behat/mink": "~1.5.0",
"behat/mink-extension": "~1.2.0",
"behat/mink-goutte-driver": "~1.0.9",
"behat/mink-selenium2-driver": "~1.1.1",
"phpunit/phpunit": "~3.7"
},
"autoload": {
"psr-0": {
"Way\\Generators": "src/"
}
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,5 @@
{
"providers": [
"Way\\Generators\\GeneratorsServiceProvider"
]
}

View File

@@ -0,0 +1,607 @@
# Fast Workflow in Laravel With Custom Generators
[![Build Status](https://travis-ci.org/JeffreyWay/Laravel-4-Generators.png?branch=master)](https://travis-ci.org/JeffreyWay/Laravel-4-Generators)
This Laravel package provides a variety of generators to speed up your development process. These generators include:
- `generate:model`
- `generate:view`
- `generate:controller`
- `generate:seed`
- `generate:migration`
- `generate:pivot`
- `generate:resource`
- `generate:scaffold`
## Installation
> [Want a 5-minute video overview?](https://dl.dropboxusercontent.com/u/774859/Work/Laravel-4-Generators/Get-Started-With-Laravel-Custom-Generators.mp4)
## Laravel 4.2 and Below
Begin by installing this package through Composer. Edit your project's `composer.json` file to require `way/generators`.
"require-dev": {
"way/generators": "~2.0"
}
> There is no support for Laravel 5, as the framework now includes a number of generators out of the box.
Next, update Composer from the Terminal:
composer update --dev
Once this operation completes, the final step is to add the service provider. Open `app/config/app.php`, and add a new item to the providers array.
'Way\Generators\GeneratorsServiceProvider'
That's it! You're all set to go. Run the `artisan` command from the Terminal to see the new `generate` commands.
php artisan
Next, update Composer from the Terminal:
composer update --dev
Once this operation completes, the final step is to add the service provider. Open `config/app.php`, and add a new item to the providers array.
'Way\Generators\GeneratorsServiceProvider'
That's it! You're all set to go. Run the `artisan` command from the Terminal to see the new `generate` commands.
php artisan
## Usage
Think of generators as an easy way to speed up your workflow. Rather than opening the models directory, creating a new file, saving it, and adding the class, you can simply run a single generate command.
- [Migrations](#migrations)
- [Models](#models)
- [Views](#views)
- [Seeds](#seeds)
- [Pivot](#pivot)
- [Resources](#resources)
- [Scaffolding](#scaffolding)
- [Configuration](#configuration)
### Migrations
Laravel offers a migration generator, but it stops just short of creating the schema (or the fields for the table). Let's review a couple examples, using `generate:migration`.
php artisan generate:migration create_posts_table
If we don't specify the `fields` option, the following file will be created within `app/database/migrations`.
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreatePostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('posts');
}
}
```
Notice that the generator is smart enough to detect that you're trying to create a table. When naming your migrations, make them as descriptive as possible. The migration generator will detect the first word in your migration name and do its best to determine how to proceed. As such, for `create_posts_table`, the keyword is "create," which means that we should prepare the necessary schema to create a table.
If you instead use a migration name along the lines of `add_user_id_to_posts_table`, in that case, the keyword is "add," signaling that we intend to add rows to an existing table. Let's see what that generates.
php artisan generate:migration add_user_id_to_posts_table
This will prepare the following boilerplate:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddUserIdToPostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function(Blueprint $table) {
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function(Blueprint $table) {
});
}
}
```
Notice how, this time, we're not doing `Schema::create`.
#### Keywords
When writing migration names, use the following keywords to provide hints for the generator.
- `create` or `make` (`create_users_table`)
- `add` or `insert` (`add_user_id_to_posts_table`)
- `remove` (`remove_user_id_from_posts_table`)
- `delete` or `drop` (`delete_users_table`)
#### Generating Schema
This is pretty nice, but let's take things a step further and also generate the schema, using the `fields` option.
php artisan generate:migration create_posts_table --fields="title:string, body:text"
Before we decipher this new option, let's see the output:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreatePostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function(Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('posts');
}
}
```
Nice! A few things to notice here:
- The generator will automatically set the `id` as the primary key.
- It parsed the `fields` options, and added those fields.
- The drop method is smart enough to realize that, in reverse, the table should be dropped entirely.
To declare fields, use a comma+space-separated list of key:value:option sets, where `key` is the name of the field, `value` is the [column type](http://laravel.com/docs/schema#adding-columns), and `option` is a way to specify indexes and such, like `unique` or `nullable`. Here are some examples:
- `--fields="first:string, last:string"`
- `--fields="age:integer, yob:date"`
- `--fields="username:string:unique, age:integer:nullable"`
- `--fields="name:string:default('John Doe'), bio:text:nullable"`
- `--fields="username:string(30):unique, age:integer:nullable:default(18)"`
Please make note of the last example, where we specify a character limit: `string(30)`. This will produce `$table->string('username', 30)->unique();`
It is possible to destroy the table by issuing:
php artisan generate:migration delete_posts_table
As a final demonstration, let's run a migration to remove the `completed` field from a `tasks` table.
php artisan generate:migration remove_completed_from_tasks_table --fields="completed:boolean"
This time, as we're using the "remove" keyword, the generator understands that it should drop a column, and add it back in the `down()` method.
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class RemoveCompletedFromTasksTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tasks', function(Blueprint $table) {
$table->dropColumn('completed');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tasks', function(Blueprint $table) {
$table->boolean('completed');
});
}
}
```
### Models
php artisan generate:model Post
This will create the file, `app/models/Post.php` and insert the following boilerplate:
```php
<?php
class Post extends \Eloquent {
}
```
### Views
The view generator is fairly simple.
```bash
php artisan generate:view admin.reports.index
```
This command will create an empty view, `/app/views/admin/reports/index.blade.php`. If the provided directory tree does not exist, it will be created for you.
### Seeds
Laravel provides us with a flexible way to seed new tables.
php artisan generate:seed users
Set the argument to the name of the table that you'd like a seed file for. This will generate `app/database/seeds/UsersTableSeeder.php` and populate it with:
```php
<?php
// Composer: "fzaninotto/faker": "v1.3.0"
use Faker\Factory as Faker;
class UsersTableSeeder extends Seeder {
public function run()
{
$faker = Faker::create();
foreach(range(1, 10) as $index)
{
User::create([
]);
}
}
}
```
This will give you a basic bit of boilerplate, using the popular Faker library. This is a nice way to seed your DB tables. Don't forget to pull in Faker through Composer!
### Pivot
When you require a new pivot table, the `generate:pivot` table expedites the process of creating the appropriate migration.
Simply pass the name of the two tables that require a joining pivot table. For `orders` and `users`, you might do:
```bash
php artisan generate:pivot orders users
```
This will create the following migration:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderUserTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_user', function(Blueprint $table) {
$table->increments('id');
$table->integer('order_id')->unsigned()->index();
$table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_user');
}
}
```
Notice that it correctly sets the table name according to your two provided tables, in alphabetical order. Now, run `php artisan migrate` to create your pivot table!
### Resources
The `generate:resource` command will do a number of things for you:
- Generate a model
- Generate index, show, create, and edit views
- Generate a controller
- Generate a migration with schema
- Generate a table seeder
- Migrate the database
When triggering this command, you'll be asked to confirm each of these actions. That way, you can tailor the generation to what you specifically require.
#### Example
Imagine that you need to build a way to display posts. While you could manually create a controller, create a model, create a migration and populate it with the schema, and then create a table seeder...why not let the generator do that?
```bash
php artisan generate:resource post --fields="title:string, body:text"
```
If you say yes to each confirmation, this single command will give you boilerplate for:
- app/models/Post.php
- app/controllers/PostsController.php
- app/database/migrations/timestamp-create_posts_table.php (including the schema)
- app/database/seeds/PostsTableSeeder.php
### Scaffolding
The scaffolding generator is similar to `generate:resource`, except it will add some beginning boilerplate to these files, as a convenience.
For instance, when running `generate:scaffold post`, your controller boilerplate will be:
```php
<?php
class PostsController extends \BaseController {
/**
* Display a listing of posts
*
* @return Response
*/
public function index()
{
$posts = Post::all();
return View::make('posts.index', compact('posts'));
}
/**
* Show the form for creating a new post
*
* @return Response
*/
public function create()
{
return View::make('posts.create');
}
/**
* Store a newly created post in storage.
*
* @return Response
*/
public function store()
{
$validator = Validator::make($data = Input::all(), Post::$rules);
if ($validator->fails())
{
return Redirect::back()->withErrors($validator)->withInput();
}
Post::create($data);
return Redirect::route('posts.index');
}
/**
* Display the specified post.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$post = Post::findOrFail($id);
return View::make('posts.show', compact('post'));
}
/**
* Show the form for editing the specified post.
*
* @param int $id
* @return Response
*/
public function edit($id)
{
$post = Post::find($id);
return View::make('posts.edit', compact('post'));
}
/**
* Update the specified resource in storage.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$validator = Validator::make($data = Input::all(), Post::$rules);
if ($validator->fails())
{
return Redirect::back()->withErrors($validator)->withInput();
}
$post->update($data);
return Redirect::route('posts.index');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
Post::destroy($id);
return Redirect::route('posts.index');
}
}
```
Please note that you're encouraged to modify this generated controller. It simply provides a starting point.
### Configuration
You may want to modify your templates - how the generated files are formatted. To allow for this, you
need to publish the templates that, behind the scenes, the generators will reference.
```bash
php artisan generate:publish-templates
```
This will copy all templates to your `app/templates` directory. You can modify these however you wish to fit your desired formatting. If you'd prefer a different directory:
```bash
php artisan generate:publish-templates --path=app/foo/bar/templates
```
When you run the `generate:publish-templates` command, it will also publish
the configuration to `app/config/packages/way/generators/config/config.php`. This file will look somewhat like:
```php
<?php
return [
/*
|--------------------------------------------------------------------------
| Where the templates for the generators are stored...
|--------------------------------------------------------------------------
|
*/
'model_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/model.txt',
'scaffold_model_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/scaffolding/model.txt',
'controller_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/controller.txt',
'scaffold_controller_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/scaffolding/controller.txt',
'migration_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/migration.txt',
'seed_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/seed.txt',
'view_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/view.txt',
/*
|--------------------------------------------------------------------------
| Where the generated files will be saved...
|--------------------------------------------------------------------------
|
*/
'model_target_path' => app_path('models'),
'controller_target_path' => app_path('controllers'),
'migration_target_path' => app_path('database/migrations'),
'seed_target_path' => app_path('database/seeds'),
'view_target_path' => app_path('views')
];
```
Also, while you're in this file, note that you can also update the default target directory for each generator.
### Shortcuts
Because you'll likely type these commands over and over, it makes sense to create aliases.
```bash
# Generator Stuff
alias g:m="php artisan generate:model"
alias g:c="php artisan generate:controller"
alias g:v="php artisan generate:view"
alias g:s="php artisan generate:seed"
alias g:mig="php artisan generate:migration"
alias g:r="php artisan generate:resource"
```
These can be stored in, for example, your `~/.bash_profile` or `~/.bashrc` files.

View File

@@ -0,0 +1,632 @@
# 使用自定义代码生成工具快速进行Laravel开发
[![Build Status](https://travis-ci.org/JeffreyWay/Laravel-4-Generators.png?branch=master)](https://travis-ci.org/JeffreyWay/Laravel-4-Generators)
这个Laravle包提供了一种代码生成器使得你可以加速你的开发进程这些生成器包括
- `generate:model` - 模型生成器
- `generate:view` - 视图生成器
- `generate:controller` - 控制器生成器
- `generate:seed` - 数据库填充器
- `generate:migration` - 迁移
- `generate:pivot` - 关联表
- `generate:resource` -资源
- `generate:scaffold` - 脚手架
## 安装
> [需要一个五分钟教程视频吗?](https://dl.dropboxusercontent.com/u/774859/Work/Laravel-4-Generators/Get-Started-With-Laravel-Custom-Generators.mp4)
## Laravel 4.2 或者更低的版本
使用Composer安装这个包编辑你项目的`composer.json`文件在require中添加`way/generators`
"require-dev": {
"way/generators": "~2.0"
}
然后在命令行下执行composer update
composer update --dev
一旦这个操作完成,就只需要最后一步,在配置文件中加入服务提供者。打开`app/config/app.php`文件添加一个新的记录到providers数组中.
'Way\Generators\GeneratorsServiceProvider'
这样就可以了你已经安装完成并可以运行这个包了。运行artisan命令行则可以在终端上看到generate相关命令。
php artisan
## Laravel 5.0 或者更高版本
使用Composer安装这个包编辑你项目的`composer.json`文件在require中添加`way/generators`
"require-dev": {
"way/generators": "~3.0"
}
由于在Laravel高版本中默认文件夹结构需要3.0或者更高的版本才能适应5.0版本以上的Laravel
然后在命令行下执行composer update
composer update --dev
一旦这个操作完成,就只需要最后一步,在配置文件中加入服务提供者。打开`app/config/app.php`文件添加一个新的记录到providers数组中.
'Way\Generators\GeneratorsServiceProvider'
这样就可以了你已经安装完成并可以运行这个包了。运行artisan命令行则可以在终端上看到generate相关命令。
php artisan
## 使用示例
想象一下使用一个生成器加速你的工作流。而不是打开models文件夹创建一个新的文件保存它并且在文件中添加一个class你可以简单的运行一个生成器命令即可完成这一系列动作。
- [Migrations 迁移](#migrations)
- [Models 模型](#models)
- [Views 视图](#views)
- [Seeds 填充](#seeds)
- [Pivot 关联表](#pivot)
- [Resources 资源](#resources)
- [Scaffolding 脚手架](#scaffolding)
- [Configuration 配置](#configuration)
### 迁移
Laravel提供了一个迁移生成器但是它仅仅能够创建数据库结构。让我们再回顾几个例子使用`generate:migration`
php artisan generate:migration create_posts_table
如果我们不指定字段配置项,则下面这个文件将被创建在`app/database/migrations`目录下。
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreatePostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('posts');
}
}
```
注意,生成器能够检测到你正在尝试创建一个表。迁移的名称,尽量应该是可描述的。生成器将扫描你的生成器名字的第一个单词,并尽力确定如何继续。例如,对于迁移`create_posts_table`,关键字"create",意味着我们应该准备必要的架构来创建表。
如果你使用`add_user_id_to_posts_table`代替迁移的名字,在上面的示例中,关键字"add",意味着我们将添加一行到现有的表中,然我们看看这个生成器命令。
php artisan generate:migration add_user_id_to_posts_table
这个命令将会准备一个下面这样的样板:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddUserIdToPostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function(Blueprint $table) {
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function(Blueprint $table) {
});
}
}
```
注意:这一次我们没有做`Schema::create`
#### 关键字
当你在写迁移的名字的时候,使用下面的关键字给生成器提供提示。
- `create` or `make` (`create_users_table`)
- `add` or `insert` (`add_user_id_to_posts_table`)
- `remove` (`remove_user_id_from_posts_table`)
- `delete` or `drop` (`delete_users_table`)
#### 生成数据库模式
这是非常漂亮的,但是让我们更进一步,生成数据库模式的同时,使用`fields`选项。
php artisan generate:migration create_posts_table --fields="title:string, body:text"
在我们解释这个选项之前,让我们先看一下输出:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreatePostsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function(Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('posts');
}
}
```
漂亮!少量的提示在这里:
- 生成器将默认使用自增的`id`字段作为主键
- 它解析`fields`选项,并添加这些字段
- drop方法能够足够聪明的意识到在相反的情况下这个表应该被完全删除
声明字段,使用逗号+空格分隔键值列表[key:value:option sets],其中`key`表示字段的名称,`value`表示[字段的类型](http://laravel.com/docs/schema#adding-columns)`option`表示制定索引或者像是`unique``nullable`这样的属性。
这里是一些示例:
- `--fields="first:string, last:string"`
- `--fields="age:integer, yob:date"`
- `--fields="username:string:unique, age:integer:nullable"`
- `--fields="name:string:default('John Doe'), bio:text:nullable"`
- `--fields="username:string(30):unique, age:integer:nullable:default(18)"`
请注意最后一个示例,这里我们指定了`string(30)`的字符串限制。这将产生`$table->string('username', 30)->unique();`
使用生成器删除表是可能的:
php artisan generate:migration delete_posts_table
作为最后一个示例i让我们运行一个迁移`tasks`表中,删除`completed`字段。
php artisan generate:migration remove_completed_from_tasks_table --fields="completed:boolean"
这一次,我们使用了"remove"关键字,生成器知道它要删除一个字段,并且把它添加到`down()`方法中。
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class RemoveCompletedFromTasksTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tasks', function(Blueprint $table) {
$table->dropColumn('completed');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tasks', function(Blueprint $table) {
$table->boolean('completed');
});
}
}
```
### 模型
php artisan generate:model Post
这将生成一个文件,`app/models/Post.php`并且写入下面的样板
```php
<?php
class Post extends \Eloquent {
}
```
### 视图
视图生成器相当简单。
```bash
php artisan generate:view admin.reports.index
```
这个命令将创建一个空的视图,`/app/views/admin/reports/index.blade.php`。如果提供的文件夹不存在,它会自动帮你创建
### Seeds 生成数据[译注:应该是用来填充测试数据]
Laravel为我们提供了非常灵活的方式来填充表
Laravel provides us with a flexible way to seed new tables.
php artisan generate:seed users
设置你想要生成的生成文件参数。这将生成 `app/database/seeds/UsersTableSeeder.php` 并用一下内容作为填充:
```php
<?php
// Composer: "fzaninotto/faker": "v1.3.0"
use Faker\Factory as Faker;
class UsersTableSeeder extends Seeder {
public function run()
{
$faker = Faker::create();
foreach(range(1, 10) as $index)
{
User::create([
]);
}
}
}
```
这将使用流行的Faker库为你提供一个基本的样板。这将是一个非常漂亮的方式来生成你的数据库表。不要忘记使用Composer来安装Faker
### 关联表[译注pivot 这个词愿意是中心点、中枢的意思这里翻译成关联表比较合适通俗一点就是两个表的mapping关系表带有外键约束]
当你需要一个关联表时,`generate:pivot`可以加速建立相应的表。
简单的传递两个需要关联的表的名字。对于`orders``users`表,你可以:
```bash
php artisan generate:pivot orders users
```
这个命令将创建下面的迁移:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderUserTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_user', function(Blueprint $table) {
$table->increments('id');
$table->integer('order_id')->unsigned()->index();
$table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_user');
}
}
```
注意,它会正确设置你提供的两个表名,排名不分先后。现在,运行`php artisan migrate`来创建你的关联表!
### 资源
`generate:resource`命令将会为你坐一系列的事情:
- 生成一个模型
- 生成index, show, create, edit视图
- 生成一个控制器
- 生成一个数据库结构迁移
- 生成一个数据库填充
- 迁移这个数据库
当你触发这个命令,它将对每个动作进行问询。通过这个方式,你可以告诉生成器,哪些是你确实需要的。
#### 例子
想象如果你需要创建一个方法来显示文章。你需要手动创建一个控制器,一个模型,一个数据库迁移并且填充它,并且创建一个数据库填充...为什么不用生成器来做呢?
```bash
php artisan generate:resource post --fields="title:string, body:text"
```
如果你对每个询问说yes这个命令会给你如下样板
- app/models/Post.php
- app/controllers/PostsController.php
- app/database/migrations/timestamp-create_posts_table.php (including the schema)
- app/database/seeds/PostsTableSeeder.php
### Scaffolding 脚手架
脚手架生成器类似于`generate:resource`,除了创建一些初始化样板外,同时也是为了方便。
例如,在运行`generate:scaffold post`时,你的控制器模板将会将会是:
```php
<?php
class PostsController extends \BaseController {
/**
* Display a listing of posts
*
* @return Response
*/
public function index()
{
$posts = Post::all();
return View::make('posts.index', compact('posts'));
}
/**
* Show the form for creating a new post
*
* @return Response
*/
public function create()
{
return View::make('posts.create');
}
/**
* Store a newly created post in storage.
*
* @return Response
*/
public function store()
{
$validator = Validator::make($data = Input::all(), Post::$rules);
if ($validator->fails())
{
return Redirect::back()->withErrors($validator)->withInput();
}
Post::create($data);
return Redirect::route('posts.index');
}
/**
* Display the specified post.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$post = Post::findOrFail($id);
return View::make('posts.show', compact('post'));
}
/**
* Show the form for editing the specified post.
*
* @param int $id
* @return Response
*/
public function edit($id)
{
$post = Post::find($id);
return View::make('posts.edit', compact('post'));
}
/**
* Update the specified resource in storage.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$validator = Validator::make($data = Input::all(), Post::$rules);
if ($validator->fails())
{
return Redirect::back()->withErrors($validator)->withInput();
}
$post->update($data);
return Redirect::route('posts.index');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
Post::destroy($id);
return Redirect::route('posts.index');
}
}
```
请注意我们鼓励您去修改这些自动生成的控制器。它只是一个简单的开始。
### Configuration 配置
你或许想修改你的模板--自动生成的文件是怎样格式化的。考虑到这一点,你需要发布你的模板,生成器将会使用它们。
```bash
php artisan generate:publish-templates
```
你要复制所有`app/templates`目录下的模板。你可以修改这些只要你满意它的格式。如果你喜欢不同的目录:
```bash
php artisan generate:publish-templates --path=app/foo/bar/templates
```
当你运行`generate:publish-templates` ,它也会将配置发布到`app/config/packages/way/generators/config/config.php`文件。这个文件看起来有点像:
```php
<?php
return [
/*
|--------------------------------------------------------------------------
| Where the templates for the generators are stored...
|--------------------------------------------------------------------------
|
*/
'model_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/model.txt',
'scaffold_model_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/scaffolding/model.txt',
'controller_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/controller.txt',
'scaffold_controller_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/scaffolding/controller.txt',
'migration_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/migration.txt',
'seed_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/seed.txt',
'view_template_path' => '/Users/jeffreyway/Desktop/generators-testing/app/templates/view.txt',
/*
|--------------------------------------------------------------------------
| Where the generated files will be saved...
|--------------------------------------------------------------------------
|
*/
'model_target_path' => app_path('models'),
'controller_target_path' => app_path('controllers'),
'migration_target_path' => app_path('database/migrations'),
'seed_target_path' => app_path('database/seeds'),
'view_target_path' => app_path('views')
];
```
同时,当你修改这个文件的时候,注意你也可以更新每个默认的生成器目标目录。
### Shortcuts 快捷命令
因为你可能会一次又一次的键入如下命令,命令别名显然是有意义的。
```bash
# Generator Stuff
alias g:m="php artisan generate:model"
alias g:c="php artisan generate:controller"
alias g:v="php artisan generate:view"
alias g:s="php artisan generate:seed"
alias g:mig="php artisan generate:migration"
alias g:r="php artisan generate:resource"
```
这些将被保存,例如,你的`~/.bash_profile` 或者 `~/.bashrc` 文件中。

View File

@@ -0,0 +1,26 @@
<?php
namespace spec\Way\Generators\Compilers;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TemplateCompilerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Way\Generators\Compilers\TemplateCompiler');
}
function it_compiles_a_template_with_data()
{
$data = [
'NAME' => 'Foo',
'PARENT' => 'Bar'
];
$this->compile(
'class $NAME$ extends $PARENT$ {}', $data
) ->shouldBe('class Foo extends Bar {}');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace spec\Way\Generators;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Way\Generators\Filesystem\Filesystem;
use Way\Generators\Compilers\TemplateCompiler;
class GeneratorSpec extends ObjectBehavior {
function let(Filesystem $file)
{
// By default, we'll set the file to not exist
// This may be overridden, though
$file->exists('foo.txt')->willReturn(false);
$this->beConstructedWith($file);
}
function it_is_initializable()
{
$this->shouldHaveType('Way\Generators\Generator');
}
function it_compiles_a_template(Filesystem $file, TemplateCompiler $compiler)
{
$template = 'class $NAME$ {}';
$data = ['NAME' => 'Bar'];
$compiledTemplate = 'class Bar {}';
$file->get('template.txt')->shouldBeCalled()->willReturn($template);
$compiler->compile($template, $data)->shouldBeCalled()->willReturn($compiledTemplate);
// When we call compile, we expect the method to
// fetch the given template, compile it down,
// and return the results
$this->compile('template.txt', $data, $compiler)->shouldBe($compiledTemplate);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace spec\Way\Generators\Parsers;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MigrationFieldsParserSpec extends ObjectBehavior {
function it_is_initializable()
{
$this->shouldHaveType('Way\Generators\Parsers\MigrationFieldsParser');
}
function it_parses_a_string_of_fields()
{
$this->parse('name:string')->shouldReturn([
['field' => 'name', 'type' => 'string']
]);
$this->parse('name:string, age:integer')->shouldReturn([
['field' => 'name', 'type' => 'string'],
['field' => 'age', 'type' => 'integer']
]);
$this->parse('name:string:nullable, age:integer')->shouldReturn([
['field' => 'name', 'type' => 'string', 'decorators' => ['nullable']],
['field' => 'age', 'type' => 'integer']
]);
$this->parse('name:string(15):nullable')->shouldReturn([
['field' => 'name', 'type' => 'string', 'args' => '15', 'decorators' => ['nullable']]
]);
$this->parse('name:double(15,8):nullable:default(10), age:integer')->shouldReturn([
['field' => 'name', 'type' => 'double', 'args' => '15,8', 'decorators' => ['nullable', 'default(10)']],
['field' => 'age', 'type' => 'integer']
]);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace spec\Way\Generators\Parsers;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MigrationNameParserSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Way\Generators\Parsers\MigrationNameParser');
}
function it_parses_a_basic_migration_name()
{
$this->parse('create_orders_table')->shouldBe([
'action' => 'create',
'table' => 'orders'
]);
}
function it_parses_a_complex_migration_name()
{
$this->parse('add_first_name_and_last_name_to_recent_orders_table')->shouldBe([
'action' => 'add',
'table' => 'recent_orders'
]);
$this->parse('remove_first_name_from_users_table')->shouldBe([
'action' => 'remove',
'table' => 'users'
]);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace spec\Way\Generators;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Way\Generators\Compilers\TemplateCompiler;
use Way\Generators\Filesystem\Filesystem;
class SchemaCreatorSpec extends ObjectBehavior {
public function let(Filesystem $file, TemplateCompiler $compiler)
{
$this->beConstructedWith($file, $compiler);
}
function it_is_initializable()
{
$this->shouldHaveType('Way\Generators\SchemaCreator');
}
}

View File

@@ -0,0 +1,67 @@
<?php namespace Way\Generators\Commands;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Way\Generators\Templates\Data\Controller as ControllerData;
class ControllerGeneratorCommand extends GeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:controller';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a controller';
/**
* Get the path to the template for the generator.
*
* @return mixed
*/
protected function getTemplatePath()
{
return $this->getPathByOptionOrConfig('templatePath', 'controller_template_path');
}
/**
* Fetch the template data.
*
* @return array
*/
protected function getTemplateData()
{
return (new ControllerData($this->argument('controllerName')))->fetch();
}
/**
* The path to where the file will be created.
*
* @return mixed
*/
protected function getFileGenerationPath()
{
$path = $this->getPathByOptionOrConfig('path', 'controller_target_path');
return $path. '/' . $this->argument('controllerName') . '.php';
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['controllerName', InputArgument::REQUIRED, 'The name of the desired controller.']
];
}
}

View File

@@ -0,0 +1,103 @@
<?php namespace Way\Generators\Commands;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Illuminate\Console\Command;
use Way\Generators\Filesystem\FileAlreadyExists;
use Way\Generators\Generator;
use Config;
abstract class GeneratorCommand extends Command {
/**
* The Generator instance.
*
* @var Generator
*/
protected $generator;
/**
* Create a new GeneratorCommand instance.
*
* @param Generator $generator
*/
public function __construct(Generator $generator)
{
$this->generator = $generator;
parent::__construct();
}
/**
* Fetch the template data.
*
* @return array
*/
protected abstract function getTemplateData();
/**
* The path to where the file will be created.
*
* @return mixed
*/
protected abstract function getFileGenerationPath();
/**
* Get the path to the generator template.
*
* @return mixed
*/
protected abstract function getTemplatePath();
/**
* Compile and generate the file.
*/
public function fire()
{
$filePathToGenerate = $this->getFileGenerationPath();
try
{
$this->generator->make(
$this->getTemplatePath(),
$this->getTemplateData(),
$filePathToGenerate
);
$this->info("Created: {$filePathToGenerate}");
}
catch (FileAlreadyExists $e)
{
$this->error("The file, {$filePathToGenerate}, already exists! I don't want to overwrite it.");
}
}
/**
* Get a directory path through a command option, or from the configuration.
*
* @param $option
* @param $configName
* @return string
*/
protected function getPathByOptionOrConfig($option, $configName)
{
if ($path = $this->option($option)) return $path;
return Config::get("generators.config.{$configName}");
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['path', null, InputOption::VALUE_REQUIRED, 'Where should the file be created?'],
['templatePath', null, InputOption::VALUE_REQUIRED, 'The location of the template for this generator']
];
}
}

View File

@@ -0,0 +1,108 @@
<?php namespace Way\Generators\Commands;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Way\Generators\Templates\Data\Migration as MigrationData;
use Way\Generators\Generator;
class MigrationGeneratorCommand extends GeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:migration';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a new migration';
/**
* Execute the console command.
*/
public function fire()
{
parent::fire();
// We'll run dump-autoload to refresh everything.
if ( ! $this->option('testing'))
{
$this->call('dump-autoload');
}
}
/**
* The path to where the file will be created.
*
* @return mixed
*/
protected function getFileGenerationPath()
{
$path = $this->getPathByOptionOrConfig('path', 'migration_target_path');
$fileName = $this->getDatePrefix() . '_' . $this->argument('migrationName') . '.php';
return "{$path}/{$fileName}";
}
/**
* Get a date prefix for the migration.
*
* @return string
*/
protected function getDatePrefix()
{
return date('Y_m_d_His');
}
/**
* Fetch the template data for the migration generator.
*
* @return array
*/
protected function getTemplateData()
{
return (new MigrationData($this->argument('migrationName'), $this->option('fields')))->fetch();
}
/**
* Get the path to the generator template.
*
* @return mixed
*/
protected function getTemplatePath()
{
return $this->getPathByOptionOrConfig('templatePath', 'migration_template_path');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['migrationName', InputArgument::REQUIRED, 'The migration name']
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['fields', null, InputOption::VALUE_OPTIONAL, 'Fields for the migration'],
['path', null, InputOption::VALUE_OPTIONAL, 'Where should the file be created?'],
['templatePath', null, InputOption::VALUE_OPTIONAL, 'The location of the template for this generator'],
['testing', null, InputOption::VALUE_OPTIONAL, 'For internal use only.']
];
}
}

View File

@@ -0,0 +1,69 @@
<?php namespace Way\Generators\Commands;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ModelGeneratorCommand extends GeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:model';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a model';
/**
* The path to where the file will be created.
*
* @return mixed
*/
protected function getFileGenerationPath()
{
$path = $this->getPathByOptionOrConfig('path', 'model_target_path');
return $path. '/' . ucwords($this->argument('modelName')) . '.php';
}
/**
* Fetch the template data.
*
* @return array
*/
protected function getTemplateData()
{
return [
'NAME' => ucwords($this->argument('modelName')),
'NAMESPACE' => 'App'
];
}
/**
* Get path to the template for the generator.
*
* @return mixed
*/
protected function getTemplatePath()
{
return $this->getPathByOptionOrConfig('templatePath', 'model_template_path');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['modelName', InputArgument::REQUIRED, 'The name of the desired Eloquent model']
];
}
}

View File

@@ -0,0 +1,80 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class PivotGeneratorCommand extends Command {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:pivot';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a pivot table';
/**
* Create a pivot table migration
*/
public function fire()
{
list($tableOne, $tableTwo) = $this->sortDesiredTables();
$this->call('generate:migration', [
'migrationName' => "create_{$tableOne}_{$tableTwo}_table",
'--fields' => $this->getMigrationFields($tableOne, $tableTwo)
]);
}
/**
* Sort the provided pivot tables in alphabetical order
*
* @return array
*/
public function sortDesiredTables()
{
$tables = array_except(array_map('str_singular', $this->argument()), 'command');
sort($tables);
return $tables;
}
/**
* Get the fields for the pivot migration.
*
* @param $tableOne
* @param $tableTwo
* @return array
*/
public function getMigrationFields($tableOne, $tableTwo)
{
return implode(', ', [
"{$tableOne}_id:integer:unsigned:index",
"{$tableOne}_id:foreign:references('id'):on('" . str_plural($tableOne) . "'):onDelete('cascade')",
"{$tableTwo}_id:integer:unsigned:index",
"{$tableTwo}_id:foreign:references('id'):on('" . str_plural($tableTwo) . "'):onDelete('cascade')",
]);
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['tableOne', InputArgument::REQUIRED, 'Name of the first table'],
['tableTwo', InputArgument::REQUIRED, 'Name of the second table']
];
}
}

View File

@@ -0,0 +1,81 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use File, Config;
class PublishTemplatesCommand extends Command {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:publish-templates';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Copy generator templates for user modification';
/**
* Execute the command
*/
public function fire()
{
$this->copyTemplatesDirectoryForEditing();
// We also will publish the configuration
$this->call('config:publish', ['package' => 'way/generators']);
$this->pointConfigFileTemplatesToNewLocation();
$this->info(
"The templates have been copied to '{$this->option('path')}'. " .
"Modify these templates however you wish, and they'll be referenced " .
"when you execute the associated generator command."
);
}
/**
* Copy the default templates, so that the user
* may modify them how they wish.
*/
protected function copyTemplatesDirectoryForEditing()
{
// We'll copy the generator templates
// to a place where the user can edit
// them how they wish.
File::copyDirectory(
__DIR__.'/../templates',
$this->option('path')
);
}
/**
* Update config file to point to the new templates directory
*/
protected function pointConfigFileTemplatesToNewLocation()
{
$configPath = app_path('config/packages/way/generators/config.php');
$updated = str_replace('vendor/way/generators/src/Way/Generators/templates', $this->option('path'), File::get($configPath));
File::put($configPath, $updated);
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['path', null, InputOption::VALUE_OPTIONAL, 'Which directory should the templates be copied to?', 'app/templates']
];
}
}

View File

@@ -0,0 +1,211 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ResourceGeneratorCommand extends Command {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:resource';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a new resource';
/**
* Generate a resource
*
* @return mixed
*/
public function fire()
{
$resource = $this->argument('resource');
$this->callModel($resource);
$this->callView($resource);
$this->callController($resource);
$this->callMigration($resource);
$this->callSeeder($resource);
$this->callMigrate();
// All done!
$this->info(sprintf(
"All done! Don't forget to add `%s` to %s." . PHP_EOL,
"Route::resource('{$this->getTableName($resource)}', '{$this->getControllerName($resource)}');",
"app/routes.php"
));
}
/**
* Get the name for the model
*
* @param $resource
* @return string
*/
protected function getModelName($resource)
{
return ucwords(str_singular(camel_case($resource)));
}
/**
* Get the name for the controller
*
* @param $resource
* @return string
*/
protected function getControllerName($resource)
{
return ucwords(str_plural(camel_case($resource))) . 'Controller';
}
/**
* Get the DB table name
*
* @param $resource
* @return string
*/
protected function getTableName($resource)
{
return str_plural($resource);
}
/**
* Get the name for the migration
*
* @param $resource
* @return string
*/
protected function getMigrationName($resource)
{
return "create_" . str_plural($resource) . "_table";
}
/**
* Call model generator if user confirms
*
* @param $resource
*/
protected function callModel($resource)
{
$modelName = $this->getModelName($resource);
if ($this->confirm("Do you want me to create a $modelName model? [yes|no]"))
{
$this->call('generate:model', compact('modelName'));
}
}
/**
* Call view generator if user confirms
*
* @param $resource
*/
protected function callView($resource)
{
$collection = $this->getTableName($resource);
$modelName = $this->getModelName($resource);
if ($this->confirm("Do you want me to create views for this $modelName resource? [yes|no]"))
{
foreach(['index', 'show', 'create', 'edit'] as $viewName)
{
$viewName = "{$collection}.{$viewName}";
$this->call('generate:view', compact('viewName'));
}
}
}
/**
* Call controller generator if user confirms
*
* @param $resource
*/
protected function callController($resource)
{
$controllerName = $this->getControllerName($resource);
if ($this->confirm("Do you want me to create a $controllerName controller? [yes|no]"))
{
$this->call('generate:controller', compact('controllerName'));
}
}
/**
* Call migration generator if user confirms
*
* @param $resource
*/
protected function callMigration($resource)
{
$migrationName = $this->getMigrationName($resource);
if ($this->confirm("Do you want me to create a '$migrationName' migration and schema for this resource? [yes|no]"))
{
$this->call('generate:migration', [
'migrationName' => $migrationName,
'--fields' => $this->option('fields')
]);
}
}
/**
* Call seeder generator if user confirms
*
* @param $resource
*/
protected function callSeeder($resource)
{
$tableName = str_plural($this->getModelName($resource));
if ($this->confirm("Would you like a '$tableName' table seeder? [yes|no]"))
{
$this->call('generate:seed', compact('tableName'));
}
}
/**
* Migrate database if user confirms
*/
protected function callMigrate()
{
if ($this->confirm('Do you want to go ahead and migrate the database? [yes|no]')) {
$this->call('migrate');
$this->info('Done!');
}
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['resource', InputArgument::REQUIRED, 'Singular resource name']
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['fields', null, InputOption::VALUE_OPTIONAL, 'Fields for the migration']
];
}
}

View File

@@ -0,0 +1,60 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Config;
class ScaffoldGeneratorCommand extends ResourceGeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:scaffold';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Scaffold a new resource (with boilerplate)';
/**
* Call model generator if user confirms
*
* @param $resource
*/
protected function callModel($resource)
{
$modelName = $this->getModelName($resource);
if ($this->confirm("Do you want me to create a $modelName model? [yes|no]"))
{
$this->call('generate:model', [
'modelName' => $modelName,
'--templatePath' => Config::get("generators::config.scaffold_model_template_path")
]);
}
}
/**
* Call controller generator if user confirms
*
* @param $resource
*/
protected function callController($resource)
{
$controllerName = $this->getControllerName($resource);
if ($this->confirm("Do you want me to create a $controllerName controller? [yes|no]"))
{
$this->call('generate:controller', [
'controllerName' => $controllerName,
'--templatePath' => Config::get("generators::config.scaffold_controller_template_path")
]);
}
}
}

View File

@@ -0,0 +1,82 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Config;
class SeederGeneratorCommand extends GeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:seed';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a database table seeder';
/**
* The path where the file will be created
*
* @return mixed
*/
protected function getFileGenerationPath()
{
$path = $this->getPathByOptionOrConfig('path', 'seed_target_path');
$tableName = $this->getTableName();
return "{$path}/{$tableName}TableSeeder.php";
}
/**
* Fetch the template data
*
* @return array
*/
protected function getTemplateData()
{
$tableName = $this->getTableName();
return [
'CLASS' => "{$tableName}TableSeeder",
'MODEL' => str_singular($tableName)
];
}
/**
* Get path to template for generator
*
* @return mixed
*/
protected function getTemplatePath()
{
return $this->getPathByOptionOrConfig('templatePath', 'seed_template_path');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['tableName', InputArgument::REQUIRED, 'The name of the table to seed']
];
}
/**
* Format the table name
*/
protected function getTableName()
{
return Str::studly($this->argument('tableName'));
}
}

View File

@@ -0,0 +1,85 @@
<?php namespace Way\Generators\Commands;
use Illuminate\Support\Facades\File;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class ViewGeneratorCommand extends GeneratorCommand {
/**
* The console command name.
*
* @var string
*/
protected $name = 'generate:view';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a view';
/**
* Create directory tree for views, and fire the generator.
*/
public function fire()
{
$directoryPath = dirname($this->getFileGenerationPath());
if ( ! File::exists($directoryPath))
{
File::makeDirectory($directoryPath, 0777, true);
}
parent::fire();
}
/**
* The path to where the file will be created.
*
* @return mixed
*/
protected function getFileGenerationPath()
{
$path = $this->getPathByOptionOrConfig('path', 'view_target_path');
$viewName = str_replace('.', '/', $this->argument('viewName'));
return sprintf('%s/%s.blade.php', $path, $viewName);
}
/**
* Fetch the template data.
*
* @return array
*/
protected function getTemplateData()
{
return [
'PATH' => $this->getFileGenerationPath()
];
}
/**
* Get the path to the template for the generator.
*
* @return mixed
*/
protected function getTemplatePath()
{
return $this->getPathByOptionOrConfig('templatePath', 'view_template_path');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['viewName', InputArgument::REQUIRED, 'The name of the desired view']
];
}
}

View File

@@ -0,0 +1,13 @@
<?php namespace Way\Generators\Compilers;
interface Compiler {
/**
* Compile the template using
* the given data
*
* @param $template
* @param $data
*/
public function compile($template, $data);
}

View File

@@ -0,0 +1,23 @@
<?php namespace Way\Generators\Compilers;
class TemplateCompiler implements Compiler {
/**
* Compile the template using
* the given data
*
* @param $template
* @param $data
* @return mixed
*/
public function compile($template, $data)
{
foreach($data as $key => $value)
{
$template = preg_replace("/\\$$key\\$/i", $value, $template);
}
return $template;
}
}

View File

@@ -0,0 +1,3 @@
<?php namespace Way\Generators\Filesystem;
class FileAlreadyExists extends \Exception {}

View File

@@ -0,0 +1,3 @@
<?php namespace Way\Generators\Filesystem;
class FileNotFound extends \Exception {}

View File

@@ -0,0 +1,51 @@
<?php namespace Way\Generators\Filesystem;
class Filesystem {
/**
* Make a file
*
* @param $file
* @param $content
* @throws FileAlreadyExists
* @return int
*/
public function make($file, $content)
{
if ( $this->exists($file))
{
throw new FileAlreadyExists;
}
return file_put_contents($file, $content);
}
/**
* Determine if file exists
*
* @param $file
* @return bool
*/
public function exists($file)
{
return file_exists($file);
}
/**
* Fetch the contents of a file
*
* @param $file
* @throws FileNotFound
* @return string
*/
public function get($file)
{
if ( ! $this->exists($file))
{
throw new FileNotFound($file);
}
return file_get_contents($file);
}
}

View File

@@ -0,0 +1,54 @@
<?php namespace Way\Generators;
use Way\Generators\Filesystem\Filesystem;
use Way\Generators\Compilers\TemplateCompiler;
use Way\Generators\UndefinedTemplate;
class Generator {
/**
* @var Filesystem
*/
protected $file;
/**
* @param Filesystem $file
*/
public function __construct(Filesystem $file)
{
$this->file = $file;
}
/**
* Run the generator
*
* @param $templatePath
* @param $templateData
* @param $filePathToGenerate
*/
public function make($templatePath, $templateData, $filePathToGenerate)
{
// We first need to compile the template,
// according to the data that we provide.
$template = $this->compile($templatePath, $templateData, new TemplateCompiler);
// Now that we have the compiled template,
// we can actually generate the file.
$this->file->make($filePathToGenerate, $template);
}
/**
* Compile the file
*
* @param $templatePath
* @param array $data
* @param TemplateCompiler $compiler
* @throws UndefinedTemplate
* @return mixed
*/
public function compile($templatePath, array $data, TemplateCompiler $compiler)
{
return $compiler->compile($this->file->get($templatePath), $data);
}
}

View File

@@ -0,0 +1,214 @@
<?php namespace Way\Generators;
use Illuminate\Support\ServiceProvider;
use Way\Generators\Commands\ControllerGeneratorCommand;
use Way\Generators\Commands\ModelGeneratorCommand;
use Way\Generators\Commands\ResourceGeneratorCommand;
use Way\Generators\Commands\SeederGeneratorCommand;
use Way\Generators\Commands\PublishTemplatesCommand;
use Way\Generators\Commands\ScaffoldGeneratorCommand;
use Way\Generators\Commands\ViewGeneratorCommand;
use Way\Generators\Commands\PivotGeneratorCommand;
class GeneratorsServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Booting
*/
public function boot()
{
// If you need to override the default config, copy config/config.php to /config/generators.config.php and update
$this->publishes([
__DIR__.'/../../config/config.php' => config_path('generators.config.php'),
]);
}
/**
* Register the commands
*
* @return void
*/
public function register()
{
foreach([
'Model',
'View',
'Controller',
'Migration',
'Seeder',
'Pivot',
'Resource',
'Scaffold',
'Publisher'] as $command)
{
$this->{"register$command"}();
}
$this->registerConfig();
}
/**
* Register the model generator
*/
protected function registerModel()
{
$this->app->singleton('generate.model', function($app)
{
$generator = $this->app->make('Way\Generators\Generator');
return new ModelGeneratorCommand($generator);
});
$this->commands('generate.model');
}
/**
* Register the config paths
*/
public function registerConfig()
{
$userConfigFile = $this->app->configPath().'/generators.config.php';
$packageConfigFile = __DIR__.'/../../config/config.php';
$config = $this->app['files']->getRequire($packageConfigFile);
if (file_exists($userConfigFile)) {
$userConfig = $this->app['files']->getRequire($userConfigFile);
$config = array_replace_recursive($config, $userConfig);
}
$this->app['config']->set('generators.config', $config);
}
/**
* Register the view generator
*/
protected function registerView()
{
$this->app->singleton('generate.view', function($app)
{
$generator = $this->app->make('Way\Generators\Generator');
return new ViewGeneratorCommand($generator);
});
$this->commands('generate.view');
}
/**
* Register the controller generator
*/
protected function registerController()
{
$this->app->singleton('generate.controller', function($app)
{
$generator = $this->app->make('Way\Generators\Generator');
return new ControllerGeneratorCommand($generator);
});
$this->commands('generate.controller');
}
/**
* Register the migration generator
*/
protected function registerMigration()
{
$this->app->singleton('generate.migration', function($app)
{
return $this->app->make('Way\Generators\Commands\MigrationGeneratorCommand');
});
$this->commands('generate.migration');
}
/**
* Register the seeder generator
*/
protected function registerSeeder()
{
$this->app->singleton('generate.seeder', function($app)
{
$generator = $this->app->make('Way\Generators\Generator');
return new SeederGeneratorCommand($generator);
});
$this->commands('generate.seeder');
}
/**
* Register the pivot generator
*/
protected function registerPivot()
{
$this->app->singleton('generate.pivot', function($app)
{
return new PivotGeneratorCommand;
});
$this->commands('generate.pivot');
}
/**
* Register the resource generator
*/
protected function registerResource()
{
$this->app->singleton('generate.resource', function($app)
{
$generator = $this->app->make('Way\Generators\Generator');
return new ResourceGeneratorCommand($generator);
});
$this->commands('generate.resource');
}
/**
* register command for publish templates
*/
public function registerpublisher()
{
$this->app->singleton('generate.publish-templates', function($app)
{
return new publishtemplatescommand;
});
$this->commands('generate.publish-templates');
}
/**
* register scaffold command
*/
public function registerScaffold()
{
$this->app->singleton('generate.scaffold', function($app)
{
return new ScaffoldGeneratorCommand;
});
$this->commands('generate.scaffold');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array();
}
}

View File

@@ -0,0 +1,3 @@
<?php namespace Way\Generators;
class InvalidMigrationName extends \Exception {}

View File

@@ -0,0 +1,58 @@
<?php namespace Way\Generators\Parsers;
class MigrationFieldsParser {
/**
* Parse a string of fields, like
* name:string, age:integer
*
* @param string $fields
* @return array
*/
public function parse($fields)
{
if ( ! $fields) return [];
// name:string, age:integer
// name:string(10,2), age:integer
$fields = preg_split('/\s?,\s/', $fields);
$parsed = [];
foreach($fields as $index => $field)
{
// Example:
// name:string:nullable => ['name', 'string', 'nullable']
// name:string(15):nullable
$chunks = preg_split('/\s?:\s?/', $field, null);
// The first item will be our property
$property = array_shift($chunks);
// The next will be the schema type
$type = array_shift($chunks);
$args = null;
// See if args were provided, like:
// name:string(10)
if (preg_match('/(.+?)\(([^)]+)\)/', $type, $matches))
{
$type = $matches[1];
$args = $matches[2];
}
// Finally, anything that remains will
// be our decorators
$decorators = $chunks;
$parsed[$index] = ['field' => $property, 'type' => $type];
if (isset($args)) $parsed[$index]['args'] = $args;
if ($decorators) $parsed[$index]['decorators'] = $decorators;
}
return $parsed;
}
}

View File

@@ -0,0 +1,91 @@
<?php namespace Way\Generators\Parsers;
class MigrationNameParser {
/**
* Parse a migration name, like:
* create_orders_table
* add_last_name_to_recent_orders_table
*
* @param $migrationName
* @throws InvalidActionType
* @return array
*/
public function parse($migrationName)
{
// Split the migration name into pieces
// create_orders_table => ['create', 'orders', 'table']
$pieces = explode('_', $migrationName);
// We'll start by fetching the CRUD action type
$action = $this->normalizeActionName(array_shift($pieces));
// Next, we can remove any "table" string at
// the end of the migration name, like:
// create_orders_table
if (end($pieces) == 'table') array_pop($pieces);
// Now, we need to figure out the table name
$table = $this->getTableName($pieces);
return compact('action', 'table');
}
/**
* Determine what the table name should be
*
* @param array $pieces
* @return string
*/
protected function getTableName(array $pieces)
{
$tableName = [];
// This is deceptively complex, because
// there are a number of ways to write
// these migration names. We'll work backwards
// to figure out the name.
foreach(array_reverse($pieces) as $piece)
{
// Once we get to a connecting word (if any), this
// will signal the end of our search. So, for
// add_name_to_archived_lessons, "archived_lessons"
// would be the table name
if (in_array($piece, ['to', 'for', 'on', 'from', 'into'])) break;
$tableName[] = $piece;
}
// We can't forget to reverse it back again!
return implode('_', array_reverse($tableName));
}
/**
* Try to mold user's input
* to one of the CRUD operations
*
* @param $action
* @return string
*/
protected function normalizeActionName($action)
{
switch ($action)
{
case 'create':
case 'make':
return 'create';
case 'delete':
case 'destroy':
case 'drop':
return 'delete';
case 'add':
case 'append':
case 'update':
case 'insert':
return 'add';
default:
return $action;
}
}
}

View File

@@ -0,0 +1,127 @@
<?php namespace Way\Generators;
use Way\Generators\Filesystem\Filesystem;
use Way\Generators\Compilers\TemplateCompiler;
use Way\Generators\Syntax\AddToTable;
use Way\Generators\Syntax\CreateTable;
use Way\Generators\Syntax\DroppedTable;
use Way\Generators\Syntax\RemoveFromTable;
use Exception;
class SchemaCreator {
/**
* @var Filesystem\Filesystem
*/
private $file;
/**
* @var Compilers\TemplateCompiler
*/
private $compiler;
/**
* @param Filesystem $file
* @param TemplateCompiler $compiler
*/
function __construct(Filesystem $file, TemplateCompiler $compiler)
{
$this->file = $file;
$this->compiler = $compiler;
}
/**
* Build the string for the migration file "up" method
*
* @param array $migrationData
* @param array $fields
* @throws Exception
* @return mixed|string
*/
public function up(array $migrationData, array $fields = [])
{
$this->guardAction($migrationData['action']);
$method = $migrationData['action'] . 'Factory';
return $this->$method($migrationData, $fields);
}
/**
* Build the string for the migration file "down" method
*
* @param array $migrationData
* @param array $fields
* @throws Exception
* @return array|mixed|string
*/
public function down(array $migrationData, $fields = [])
{
$this->guardAction($migrationData['action']);
$opposites = [
'delete' => 'create',
'create' => 'delete',
'remove' => 'add',
'add' => 'remove'
];
$method = $opposites[$migrationData['action']] . 'Factory';
return $this->$method($migrationData, $fields);
}
/**
* @param $action
* @throws Exception
* @internal param array $migrationData
*/
protected function guardAction($action)
{
if (!in_array($action, ['create', 'add', 'remove', 'delete']))
{
throw new InvalidMigrationName('Please rewrite your migration name to begin with "create", "add", "remove", or "delete."');
}
}
/**
* @param $migrationData
* @param $fields
* @return mixed
*/
protected function createFactory($migrationData, $fields)
{
return (new CreateTable($this->file, $this->compiler))->create($migrationData, $fields);
}
/**
* @param $migrationData
* @param $fields
* @return mixed
*/
protected function addFactory($migrationData, $fields)
{
return (new AddToTable($this->file, $this->compiler))->add($migrationData, $fields);
}
/**
* @param $migrationData
* @param $fields
* @return mixed
*/
protected function removeFactory($migrationData, $fields)
{
return (new RemoveFromTable($this->file, $this->compiler))->remove($migrationData, $fields);
}
/**
* @param $migrationData
* @param $fields
* @return string
*/
protected function deleteFactory($migrationData, $fields)
{
return (new DroppedTable)->drop($migrationData['table']);
}
}

View File

@@ -0,0 +1,99 @@
<?php namespace Way\Generators\Syntax;
class AddToTable extends Table {
/**
* Add syntax for table addition
*
* @param $migrationData
* @param array $fields
* @return mixed
*/
public function add($migrationData, array $fields)
{
if ( ! isset($migrationData['method']))
{
$migrationData['method'] = 'table';
}
$compiled = $this->compiler->compile($this->getTemplate(), $migrationData);
return $this->replaceFieldsWith($this->addColumns($fields), $compiled);
}
/**
* Return string for adding all columns
*
* @param $fields
* @return array
*/
protected function addColumns($fields)
{
$schema = [];
foreach($fields as $field)
{
$schema[] = $this->addColumn($field);
}
return $schema;
}
/**
* Return string for adding a column
*
* @param $field
* @return string
*/
private function addColumn($field)
{
$property = $field['field'];
$type = $field['type'];
$output = sprintf(
"\$table->%s(%s)",
$type,
$property ? "'$property'" : null
);
// If we have args, then it needs
// to be formatted a bit differently
if (isset($field['args']))
{
$output = sprintf(
"\$table->%s('%s', %s)",
$type,
$property,
$field['args']
);
}
if (isset($field['decorators']))
{
$output .= $this->addDecorators($field['decorators']);
}
return $output . ';';
}
/**
* @param $decorators
* @return string
*/
protected function addDecorators($decorators)
{
$output = '';
foreach ($decorators as $decorator) {
$output .= sprintf("->%s", $decorator);
// Do we need to tack on the parens?
if (strpos($decorator, '(') === false) {
$output .= '()';
}
}
return $output;
}
}

View File

@@ -0,0 +1,27 @@
<?php namespace Way\Generators\Syntax;
class CreateTable extends Table {
/**
* Build string for creating a
* table and columns
*
* @param $migrationData
* @param $fields
* @return mixed
*/
public function create($migrationData, $fields)
{
$migrationData = ['method' => 'create', 'table' => $migrationData['table']];
// All new tables should have an identifier
// Let's add that for the user automatically
array_unshift($fields, ['field' => 'id', 'type' => 'increments']);
// We'll also add timestamps to new tables for convenience
array_push($fields, ['field' => '', 'type' => 'timestamps']);
return (new AddToTable($this->file, $this->compiler))->add($migrationData, $fields);
}
}

View File

@@ -0,0 +1,16 @@
<?php namespace Way\Generators\Syntax;
class DroppedTable {
/**
* Get string for dropping a table
*
* @param $tableName
* @return string
*/
public function drop($tableName)
{
return "Schema::drop('$tableName');";
}
}

View File

@@ -0,0 +1,50 @@
<?php namespace Way\Generators\Syntax;
class RemoveFromTable extends Table {
/**
* Compile and return string for removing columns
*
* @param $migrationData
* @param array $fields
* @return mixed
*/
public function remove($migrationData, array $fields)
{
$migrationData['method'] = 'table';
$compiled = $this->compiler->compile($this->getTemplate(), $migrationData);
return $this->replaceFieldsWith($this->dropColumns($fields), $compiled);
}
/**
* Return string for dropping all columns
*
* @param array $fields
* @return array
*/
protected function dropColumns(array $fields)
{
$schema = [];
foreach($fields as $field)
{
$schema[] = $this->dropColumn($field);
}
return $schema;
}
/**
* Return string for dropping a column
*
* @param $field
* @return string
*/
private function dropColumn($field)
{
return sprintf("\$table->dropColumn('%s');", $field['field']);
}
}

View File

@@ -0,0 +1,52 @@
<?php namespace Way\Generators\Syntax;
use Way\Generators\Compilers\TemplateCompiler;
use Way\Generators\Filesystem\Filesystem;
abstract class Table {
/**
* @var \Way\Generators\Filesystem\Filesystem
*/
protected $file;
/**
* @var \Way\Generators\Compilers\TemplateCompiler
*/
protected $compiler;
/**
* @param Filesystem $file
* @param TemplateCompiler $compiler
*/
function __construct(Filesystem $file, TemplateCompiler $compiler)
{
$this->compiler = $compiler;
$this->file = $file;
}
/**
* Fetch the template of the schema
*
* @return string
*/
protected function getTemplate()
{
return $this->file->get(__DIR__.'/../templates/schema.txt');
}
/**
* Replace $FIELDS$ in the given template
* with the provided schema
*
* @param $schema
* @param $template
* @return mixed
*/
protected function replaceFieldsWith($schema, $template)
{
return str_replace('$FIELDS$', implode(PHP_EOL."\t\t\t", $schema), $template);
}
}

View File

@@ -0,0 +1,88 @@
<?php namespace Way\Generators\Templates\Data;
class Controller {
/**
* The name of the controller to generate.
*
* @var string
*/
private $controllerName;
/**
* Create a new Controller template data instance.
*
* @param $controllerName
*/
public function __construct($controllerName)
{
$this->controllerName = $controllerName;
}
/**
* Fetch the template data for the controller.
*
* @return array
*/
public function fetch()
{
return [
'name' => $this->getName($this->controllerName),
'collection' => $this->getCollection(),
'resource' => $this->getResource(),
'model' => $this->getModel(),
'namespace' => $this->getNamespace()
];
}
/**
* Format the name of the controller.
*
* @return string
*/
private function getName()
{
return ucwords($this->controllerName); // LessonsController
}
/**
* Format the name of the collection.
*
* @return string
*/
private function getCollection()
{
return strtolower(str_replace('Controller', '', $this->getName())); // lessons
}
/**
* Format the name of the single resource.
*
* @return string
*/
private function getResource()
{
return str_singular($this->getCollection()); // lesson
}
/**
* Format the name of the model.
*
* @return string
*/
private function getModel()
{
return ucwords($this->getResource()); // Lesson
}
/**
* Format the name of the namespace.
*
* @return string
*/
public function getNamespace()
{
return 'App\Http\Controllers';
}
}

View File

@@ -0,0 +1,118 @@
<?php namespace Way\Generators\Templates\Data;
use App;
use Way\Generators\Parsers\MigrationFieldsParser;
use Way\Generators\Parsers\MigrationNameParser;
class Migration {
/**
* The name of the migration.
*
* @var string
*/
private $migrationName;
/**
* A string representation of the migration fields.
*
* @var string
*/
private $fields;
/**
* Create a new Migration template data instance.
*
* @param string $migrationName
* @param string $fields
*/
public function __construct($migrationName, $fields)
{
$this->migrationName = $migrationName;
$this->fields = $fields;
}
/**
* Fetch the template data for a migration generation.
*
* @return array
*/
public function fetch()
{
$parsedName = $this->getParsedMigrationName();
$parsedFields = $this->getParsedMigrationFields();
return [
'class' => $this->getClass(),
'up' => $this->getMigrationUp($parsedName, $parsedFields),
'down' => $this->getMigrationDown($parsedName, $parsedFields)
];
}
/**
* Parse the migration name.
*
* @return array
*/
private function getParsedMigrationName()
{
$nameParser = new MigrationNameParser;
return $nameParser->parse($this->migrationName);
}
/**
* Parse the migration fields.
*
* @return array
*/
private function getParsedMigrationFields()
{
$fieldParser = new MigrationFieldsParser;
return $fieldParser->parse($this->fields);
}
/**
* Get the class name for the migration.
*/
private function getClass()
{
return ucwords(camel_case($this->migrationName));
}
/**
* Get the schema for the up() method.
*
* @param $migrationData
* @param $fields
* @return mixed
*/
private function getMigrationUp($migrationData, $fields)
{
return $this->resolveSchemaCreator()->up($migrationData, $fields);
}
/**
* Get the schema for the down() method.
*
* @param $migrationData
* @param $fields
* @return mixed
*/
private function getMigrationDown($migrationData, $fields)
{
return $this->resolveSchemaCreator()->down($migrationData, $fields);
}
/**
* Get a SchemaCreator instance.
*
* @return mixed
*/
private function resolveSchemaCreator()
{
return App::make('Way\Generators\SchemaCreator');
}
}

View File

@@ -0,0 +1,88 @@
<?php namespace $NAMESPACE$;
use Illuminate\Routing\Controller;
class $NAME$ extends Controller {
/**
* Display a listing of the resource.
* GET /$COLLECTION$
*
* @return Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
* GET /$COLLECTION$/create
*
* @return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
* POST /$COLLECTION$
*
* @return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
* GET /$COLLECTION$/{id}
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
* GET /$COLLECTION$/{id}/edit
*
* @param int $id
* @return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
* PUT /$COLLECTION$/{id}
*
* @param int $id
* @return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
* DELETE /$COLLECTION$/{id}
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
//
}
}

View File

@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class $CLASS$ extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$UP$
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$DOWN$
}
}

View File

@@ -0,0 +1,9 @@
<?php namespace $NAMESPACE$;
use Illuminate\Database\Eloquent\Model;
class $NAME$ extends Model {
protected $fillable = [];
}

View File

@@ -0,0 +1,96 @@
<?php namespace $NAMESPACE$;
use Illuminate\Routing\Controller;
use Redirect, Request;
class $NAME$ extends Controller {
/**
* Display a listing of $COLLECTION$
*
* @return Response
*/
public function index()
{
$$COLLECTION$ = $MODEL$::all();
return view('$COLLECTION$.index', compact('$COLLECTION$'));
}
/**
* Show the form for creating a new $RESOURCE$
*
* @return Response
*/
public function create()
{
return view('$COLLECTION$.create');
}
/**
* Store a newly created $RESOURCE$ in storage.
*
* @return Response
*/
public function store()
{
$MODEL$::create(Request::get());
return Redirect::route('$COLLECTION$.index');
}
/**
* Display the specified $RESOURCE$.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$$RESOURCE$ = $MODEL$::findOrFail($id);
return view('$COLLECTION$.show', compact('$RESOURCE$'));
}
/**
* Show the form for editing the specified $RESOURCE$.
*
* @param int $id
* @return Response
*/
public function edit($id)
{
$$RESOURCE$ = $MODEL$::find($id);
return view('$COLLECTION$.edit', compact('$RESOURCE$'));
}
/**
* Update the specified $RESOURCE$ in storage.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$$RESOURCE$ = $MODEL$::findOrFail($id);
$$RESOURCE$->update(Request::get());
return Redirect::route('$COLLECTION$.index');
}
/**
* Remove the specified $RESOURCE$ from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
$MODEL$::destroy($id);
return Redirect::route('$COLLECTION$.index');
}
}

View File

@@ -0,0 +1,9 @@
<?php namespace $NAMESPACE$;
use Illuminate\Database\Eloquent\Model;
class $NAME$ extends Model {
protected $fillable = [];
}

View File

@@ -0,0 +1,4 @@
Schema::$METHOD$('$TABLE$', function(Blueprint $table)
{
$FIELDS$
});

View File

@@ -0,0 +1,21 @@
<?php
// Composer: "fzaninotto/faker": "v1.4.0"
use Faker\Factory as Faker;
use Illuminate\Database\Seeder;
class $CLASS$ extends Seeder {
public function run()
{
$faker = Faker::create();
foreach(range(1, 10) as $index)
{
$MODEL$::create([
]);
}
}
}

View File

@@ -0,0 +1 @@
$PATH$

View File

@@ -0,0 +1,42 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Where the templates for the generators are stored...
|--------------------------------------------------------------------------
|
*/
'model_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/model.txt'),
'scaffold_model_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/scaffolding/model.txt'),
'controller_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/controller.txt'),
'scaffold_controller_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/scaffolding/controller.txt'),
'migration_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/migration.txt'),
'seed_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/seed.txt'),
'view_template_path' => base_path('vendor/xethron/laravel-4-generators/src/Way/Generators/templates/view.txt'),
/*
|--------------------------------------------------------------------------
| Where the generated files will be saved...
|--------------------------------------------------------------------------
|
*/
'model_target_path' => app_path(),
'controller_target_path' => app_path('Http/Controllers'),
'migration_target_path' => base_path('database/migrations'),
'seed_target_path' => base_path('database/seeds'),
'view_target_path' => base_path('resources/views')
];

View File

View File

@@ -0,0 +1,154 @@
<?php
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Application;
require_once __DIR__.'/../../../vendor/phpunit/phpunit/PHPUnit/Framework/Assert/Functions.php';
/**
* Features context.
*/
class FeatureContext extends BehatContext
{
/**
* The command that we're testing
*
* @var CommandTester
*/
protected $tester;
/**
* @beforeSuite
*/
public static function bootstrapLaravel()
{
require __DIR__.'/../../../../../../vendor/autoload.php';
require __DIR__.'/../../../../../../bootstrap/start.php';
}
/**
* @AfterScenario
*/
public function tearDown()
{
\Illuminate\Support\Facades\File::deleteDirectory(base_path('workbench/way/generators/tests/tmp'), true);
$this->tester = null;
}
/**
* @When /^I generate a migration with name \'([^\']*)\' and fields \'([^\']*)\'$/
*/
public function iGenerateAMigrationWithNameAndFields($migrationName, $fields)
{
$this->tester = new CommandTester(App::make('Way\Generators\Commands\MigrationGeneratorCommand'));
$this->tester->execute([
'migrationName' => $migrationName,
'--fields' => $fields,
'--testing' => true,
'--path' => __DIR__.'/../../tmp',
'--templatePath' => __DIR__.'/../../../src/Way/Generators/templates/migration.txt'
]);
}
/**
* @When /^I generate a model with "([^"]*)"$/
*/
public function iGenerateAModelWith($modelName)
{
$this->tester = new CommandTester(App::make('Way\Generators\Commands\ModelGeneratorCommand'));
$this->tester->execute([
'modelName' => $modelName,
'--path' => __DIR__.'/../../tmp',
'--templatePath' => __DIR__.'/../../../src/Way/Generators/templates/model.txt'
]);
}
/**
* @When /^I generate a controller with "([^"]*)"$/
*/
public function iGenerateAControllerWith($controllerName)
{
$this->tester = new CommandTester(App::make('Way\Generators\Commands\ControllerGeneratorCommand'));
$this->tester->execute([
'controllerName' => $controllerName,
'--path' => __DIR__.'/../../tmp',
'--templatePath' => __DIR__.'/../../../src/Way/Generators/templates/controller.txt'
]);
}
/**
* @When /^I generate a view with "([^"]*)"$/
*/
public function iGenerateAViewWith($viewName)
{
$this->tester = new CommandTester(App::make('Way\Generators\Commands\ViewGeneratorCommand'));
$this->tester->execute([
'viewName' => $viewName,
'--path' => __DIR__.'/../../tmp',
'--templatePath' => __DIR__.'/../../../src/Way/Generators/templates/view.txt'
]);
}
/**
* @When /^I generate a seed with "([^"]*)"$/
*/
public function iGenerateASeedWith($tableName)
{
$this->tester = new CommandTester(App::make('Way\Generators\Commands\SeederGeneratorCommand'));
$this->tester->execute([
'tableName' => $tableName,
'--path' => __DIR__.'/../../tmp',
'--templatePath' => __DIR__.'/../../../src/Way/Generators/templates/seed.txt'
]);
}
/**
* @Given /^the generated migration should match my \'([^\']*)\' stub$/
*/
public function theGeneratedMigrationShouldMatchMyStub($stubName)
{
$expected = file_get_contents(__DIR__."/../../stubs/{$stubName}.txt");
$actual = file_get_contents(glob(__DIR__."/../../tmp/*")[0]);
// Let's compare the stub against what was actually generated.
assertEquals($expected, $actual);
}
/**
* @Then /^I should see "([^"]*)"$/
*/
public function iShouldSee($output)
{
assertContains($output, $this->tester->getDisplay());
}
/**
* @Given /^"([^"]*)" should match my stub$/
*/
public function shouldMatchMyStub($generatedFilePath)
{
// We'll use the name of the generated file as
// the basic for our stub lookup.
$stubName = pathinfo($generatedFilePath)['filename'];
$expected = file_get_contents(__DIR__."/../../stubs/{$stubName}.txt");
$actual = file_get_contents(base_path($generatedFilePath));
// Let's compare the stub against what was actually generated.
assertEquals($expected, $actual);
}
}

View File

@@ -0,0 +1,13 @@
Feature: Generators
Scenario Outline: Generation
When I generate a <command> with "<argument>"
Then I should see "Created:"
And "<generatedFilePath>" should match my stub
Examples:
| command | argument | generatedFilePath |
| model | Order | workbench/way/generators/tests/tmp/Order.php |
| seed | recent_orders | workbench/way/generators/tests/tmp/RecentOrdersTableSeeder.php |
| controller | OrdersController | workbench/way/generators/tests/tmp/OrdersController.php |
| view | orders.bar.index | workbench/way/generators/tests/tmp/orders/bar/index.blade.php |

View File

@@ -0,0 +1,26 @@
Feature: Migrations With Schema
Scenario: Creating a Table
When I generate a migration with name 'create_orders_table' and fields 'name:string'
Then I should see "Created:"
And the generated migration should match my 'CreateOrdersTable' stub
Scenario: Creating a Table With Complex Fields
When I generate a migration with name 'create_orders_table' and fields 'title:string(50):unique, body:text:unique:nullable'
Then I should see "Created:"
And the generated migration should match my 'CreateComplexOrdersTable' stub
Scenario: Dropping a Table
When I generate a migration with name 'drop_orders_table' and fields 'title:string'
Then I should see "Created:"
And the generated migration should match my 'DropOrdersTable' stub
Scenario: Adding to a Table
When I generate a migration with name 'add_title_to_orders_table' and fields 'title:string'
Then I should see "Created:"
And the generated migration should match my 'AddTitleToOrdersTable' stub
Scenario: Removing from a Table
When I generate a migration with name 'remove_title_from_orders_table' and fields 'title:string'
Then I should see "Created:"
And the generated migration should match my 'RemoveTitleFromOrdersTable' stub

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddTitleToOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('orders', function(Blueprint $table)
{
$table->string('title');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('orders', function(Blueprint $table)
{
$table->dropColumn('title');
});
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('orders', function(Blueprint $table)
{
$table->increments('id');
$table->string('title', 50)->unique();
$table->text('body')->unique()->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('orders');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('orders', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('orders');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class DropOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::drop('orders');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::create('orders', function(Blueprint $table)
{
$table->increments('id');
$table->string('title');
$table->timestamps();
});
}
}

View File

@@ -0,0 +1,9 @@
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Order extends Model {
protected $fillable = [];
}

View File

@@ -0,0 +1,88 @@
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
class OrdersController extends Controller {
/**
* Display a listing of the resource.
* GET /orders
*
* @return Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
* GET /orders/create
*
* @return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
* POST /orders
*
* @return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
* GET /orders/{id}
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
* GET /orders/{id}/edit
*
* @param int $id
* @return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
* PUT /orders/{id}
*
* @param int $id
* @return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
* DELETE /orders/{id}
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
// Composer: "fzaninotto/faker": "v1.4.0"
use Faker\Factory as Faker;
use Illuminate\Database\Seeder;
class RecentOrdersTableSeeder extends Seeder {
public function run()
{
$faker = Faker::create();
foreach(range(1, 10) as $index)
{
RecentOrder::create([
]);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class RemoveTitleFromOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('orders', function(Blueprint $table)
{
$table->dropColumn('title');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('orders', function(Blueprint $table)
{
$table->string('title');
});
}
}

View File

@@ -0,0 +1 @@
/Users/jeffreyway/code/open-source/generators/workbench/way/generators/tests/features/bootstrap/../../tmp/orders/bar/index.blade.php