by 尤川豪
/views
/controllers
/models
    /Article.php
    /User.php
    /Order.php
    /Product.php
    /Category.php
    /Coupon.phpmodel
entity
class Article extends Eloquent
{
    public function getDate(){/*...*/}
    // 呈現給台灣地區的時間格式
    public function getTaiwaneseDateTime(){/*...*/}
    // 呈現給歐美地區的時間格式
    public function getWesternDateTime(){/*...*/}
    public function getTaiwaneseDate(){/*...*/}
    public function getWesternDate(){/*...*/}
}class ArticlePresenter
{
    protected $article;
    public function __construct(Article $article)
    {
        $this->article = $article;
    }
    // 呈現給台灣地區的時間格式
    public function getTaiwaneseDateTime(){
        return date('Y-m-d', $this->article->created_at);
    }
    // 呈現給歐美地區的時間格式
    public function getWesternDateTime(){/*...*/}
    public function getTaiwaneseDate(){/*...*/}
    public function getWesternDate(){/*...*/}
}@foreach($articles as $article)
    <?php $presenter = new ArticlePresenter($article); ?>
    發文日期:{{ $presenter->getTaiwaneseDate() }}
@endforeachclass Article extends Eloquent
{
    public function present()
    {
        return new ArticlePresenter($this);
    }
}
@foreach($articles as $article)
    發文日期:{{ $article->present()->getTaiwaneseDate() }}
@endforeachclass Article extends Eloquent
{
	protected $presenterInstance;
	public function present()
	{
		if ( ! $this->presenterInstance)
		{
			$this->presenterInstance = new ArticlePresenter($this);
		}
		return $this->presenterInstance;
	}
} trait PresentableTrait {
	protected $presenterInstance;
	public function present()
	{
                // ...
		if ( ! $this->presenterInstance)
		{
			$this->presenterInstance = new $this->presenter($this);
		}
		return $this->presenterInstance;
	}
} use Laracasts\Presenter\PresentableTrait;
class Article extends \Eloquent {
    use PresentableTrait;
    protected $presenter = 'ArticlePresenter';
}use Laracasts\Presenter\Presenter;
class ArticlePresenter extends Presenter {
    // 呈現給台灣地區的時間格式
    public function getTaiwaneseDateTime(){/*...*/}
    // 呈現給歐美地區的時間格式
    public function getWesternDateTime(){/*...*/}
    public function getTaiwaneseDate(){/*...*/}
    public function getWesternDate(){/*...*/}
}@foreach($articles as $article)
    發文日期:{{ $article->present()->getTaiwaneseDate() }}
@endforeach$users = User::where('votes', '>', 100)
                        ->whereGender('W')
                        ->orderBy('created_at')
                        ->get();還容易duplicate
$users = User::popular()->women()->orderBy('created_at')->get();$users = User::where('votes', '>', 100)->whereGender('W')->orderBy('created_at')->get();用了Query Scope
沒用Query Scope
class User extends Eloquent {
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }
    public function scopeWomen($query)
    {
        return $query->whereGender('W');
    }
}Laravel官方提供的Query Scopes...
class UserRepository
{
    public function getPopularWomen()
    {
        return User::where('votes', '>', 100)->whereGender('W')->orderBy('created_at')->get();
    }
}$repository = new UserRepository();
$users = $repository->getPopularWomen();class UserController extends BaseController
{
    protected $users;
    
    public function __construct(UserRepository $repository)
    {
        parent::__construct();
        
        $this->users = $repository;
    }
    
    public function getIndex()
    {
        $women = $this->users->getPopularWomen();
    }
}$users = User::where('votes', '>', 100)->whereGender('W')->orderBy('created_at')->get();
$users = User::popular()->women()->orderBy('created_at')->get();
$users = $this->users->getPopularWomen();<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
abstract class EloquentRepository
{
    protected $model;
    
    public function __construct($model = null)
    {
        $this->model = $model;
    }
    
    public function getById($id)
    {
        return $this->model->find($id);
    }
    
    public function getAll()
    {
        return $this->model->all();
    }
    public function save($data)
    {
        if ($data instanceOf Model) {
            return $this->storeEloquentModel($data);
        }
    }
    
    public function saveMany($collection)
    {
        foreach($collection as $model)
        {
            $this->storeEloquentModel($model);
        }
    }
    public function delete($model)
    {
        return $model->delete();
    }
    protected function storeEloquentModel($model)
    {        
        if ($model->getDirty()) {
            return $model->save();
        } else {
            return $model->touch();
        }
    }
}
都用Active Record了,
testing避開資料庫可不容易...
$validation = Validator::make(
    array(
        'name' => Input::get( 'name' ),
        'email' => Input::get( 'email' ),
    ),
    array(
        'name' => array( 'required', 'alpha_dash' ),
        'email' => array( 'required', 'email' ),
    )
);
 
if ( $validation->fails() ) {
    $errors = $validation->messages();
}
// ...還容易duplicate
class Ball extends Eloquent
{
    private $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );
    public function validate($data)
    {
        // make a new validator object
        $v = Validator::make($data, $this->rules);
        // return the result
        return $v->passes();
    }
}$b = new Ball();
if ($b->validate(Input::all())){
    // success code
}else{
    // failure code
}class ArticleForm
{
    protected $validationRules = [
        'title' => 'required',
        'content' => 'required',
    ];
    protected $inputData;
    protected $validator;
    public function __construct($input)
    {
        $this->inputData = $input;
    }
    public function isValid()
    {
        $this->validator = Validator::make($this->input, $this->validationRules);
        return $this->validator->passes();
    }
    
    public function getErrors()
    {
        return $this->validator->errors();
    }
}$form = new ArticleForm( Input::all() );
 
if ( ! $form->isValid() ){
    return Redirect::back()->with( [ 'errors' => $form->getErrors() ] );    
}
 
$article = new Article( Input::only('title', 'content', 'status') );
 
$article->save();class ArticleForm
{
    protected $validationRules = [
        'title' => 'required',
        'content' => 'required',
    ];
    protected $inputData;
    protected $validator;
    public function __construct($input)
    {
        $this->inputData = $input;
    }
    public function isValid()
    {
        $this->validator = Validator::make($this->input, $this->validationRules);
        return $this->validator->passes();
    }
    
    public function getErrors()
    {
        return $this->validator->errors();
    }
}class FormModel
{
    protected $validationRules;
    protected $inputData;
    protected $validator;
    public function __construct($input)
    {
        $this->inputData = $input;
    }
    public function isValid()
    {
        $this->validator = Validator::make($this->input, $this->validationRules);
        return $this->validator->passes();
    }
    
    public function getErrors()
    {
        return $this->validator->errors();
    }
}class ArticleForm extends FormModel
{
    protected $validationRules = [
        'title' => 'required',
        'content' => 'required',
    ];
}    protected $happyArticleValidationRules;
    protected $angryArticleValidationRules;
    protected $funnyArticleValidationRules;
    public function isValidHappy()
    {
        $this->validator = Validator::make($this->input, $this->happyArticleValidationRules);
        return $this->validator->passes();
    }
    
    public function isValidAngry()
    {
        $this->validator = Validator::make($this->input, $this->angryArticleValidationRules);
        return $this->validator->passes();
    } 
   
    public function isValidFunny()
    {
        $this->validator = Validator::make($this->input, $this->funnyArticleValidationRules);
        return $this->validator->passes();
    }月租費就收400元好了
class CheckoutController extends Controller
{
    public function postSubmitOrder()
    {
        // 1. 利用Stripe從信用卡扣款        
        \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
        \Stripe\Charge::create(array(
            "amount" => 400,
            "currency" => "usd",
            "source" => array(
                // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
                "number" => $user->creditCard->number,
                "exp_month" => $user->creditCard->expMonth,
                "exp_year" => $user->creditCard->expYear,
                "cvc" => $user->creditCard->cvc
            ),
        ));    
        
        // 2. 建立訂單
        $order = new Order();
        
        $order->price = 400;
        $order->user_id = Auth::user()->id;
        
        $order->save();
        
        // 3. 發送確認Email
        Mail::send('emails.confirmation', [], function($message) use ($user)
        {
            $message->to( $user->email )
                              ->subject( '謝謝您的訂購。' );
        });
                
    }
}// 1. 利用Stripe從信用卡扣款        
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
\Stripe\Charge::create(array(
    "amount" => 400,
    "currency" => "usd",
    "source" => array(
        // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
        "number" => $user->creditCard->number,
        "exp_month" => $user->creditCard->expMonth,
        "exp_year" => $user->creditCard->expYear,
        "cvc" => $user->creditCard->cvc
    ),
));    
// 2. 建立訂單
$order = new Order();
$order->price = 400;
$order->user_id = Auth::user()->id;
$order->save();
// 3. 發送確認Email
Mail::send('emails.confirmation', [], function($message) use ($user)
{
    $message->to( $user->email )
                      ->subject( '謝謝您的訂購。' );
});// 1. 利用Stripe從信用卡扣款        
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
\Stripe\Charge::create(array(
    "amount" => 400,
    "currency" => "usd",
    "source" => array(
        // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
        "number" => $user->creditCard->number,
        "exp_month" => $user->creditCard->expMonth,
        "exp_year" => $user->creditCard->expYear,
        "cvc" => $user->creditCard->cvc
    ),
));    
// 2. 建立訂單
$order = new Order();
$order->price = 400;
$order->user_id = Auth::user()->id;
$order->save();
// 3. 發送確認Email
Mail::send('emails.confirmation', [], function($message) use ($user)
{
    $message->to( $user->email )
                      ->subject( '謝謝您的訂購。' );
});// 1. 利用Stripe從信用卡扣款        
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
\Stripe\Charge::create(array(
    "amount" => 400,
    "currency" => "usd",
    "source" => array(
        // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
        "number" => $user->creditCard->number,
        "exp_month" => $user->creditCard->expMonth,
        "exp_year" => $user->creditCard->expYear,
        "cvc" => $user->creditCard->cvc
    ),
));    
// 2. 建立訂單
$order = new Order();
$order->price = 400;
$order->user_id = Auth::user()->id;
$order->save();
// 3. 發送確認Email
Mail::send('emails.confirmation', [], function($message) use ($user)
{
    $message->to( $user->email )
                      ->subject( '謝謝您的訂購。' );
});// 1. 利用Stripe從信用卡扣款        
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
\Stripe\Charge::create(array(
    "amount" => 400,
    "currency" => "usd",
    "source" => array(
        // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
        "number" => $user->creditCard->number,
        "exp_month" => $user->creditCard->expMonth,
        "exp_year" => $user->creditCard->expYear,
        "cvc" => $user->creditCard->cvc
    ),
));    
// 2. 建立訂單
$order = new Order();
$order->price = 400;
$order->user_id = Auth::user()->id;
$order->save();
// 3. 發送確認Email
Mail::send('emails.confirmation', [], function($message) use ($user)
{
    $message->to( $user->email )
                      ->subject( '謝謝您的訂購。' );
});class User extends Eloquent
{
    public function checkout()
    {
        // 1. 利用Stripe從信用卡扣款        
        \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
        
        \Stripe\Charge::create(array(
            "amount" => 400,
            "currency" => "usd",
            "source" => array(
                // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
                "number" => $this->creditCard->number,
                "exp_month" => $this->creditCard->expMonth,
                "exp_year" => $this->creditCard->expYear,
                "cvc" => $this->creditCard->cvc
            ),
        ));    
        
        // 2. 建立訂單
        $order = new Order();
        
        $order->price = 400;
        
        $order->user_id = $this->id;
        
        $order->save();
        
        // 3. 發送確認Email
        Mail::send('emails.confirmation', [], function($message) use ($user)
        {
            $message->to( $user->email )
                              ->subject( '謝謝您的訂購。' );
        });
    }
}// 原本的Checkout controller
$user->checkout();// 瀏覽專輯頁面一鍵AJAX付款的對應controller
$user->checkout();// 用iframe做plugin讓藝人能放進官網的controller
$user->checkout();// 處理手機App結帳的controller
$user->checkout();工程師A:是否變得優雅許多!
class Order extends Eloquent
{
    public function checkout()
    {
        // 1. 利用Stripe從信用卡扣款        
        \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
        $user = User::find($this->user_id);
        
        \Stripe\Charge::create(array(
            "amount" => 400,
            "currency" => "usd",
            "source" => array(
                // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
                "number" => $user->creditCard->number,
                "exp_month" => $user->creditCard->expMonth,
                "exp_year" => $user->creditCard->expYear,
                "cvc" => $user->creditCard->cvc
            ),
        ));    
        
        // 2. 發送確認Email
        Mail::send('emails.confirmation', [], function($message) use ($user)
        {
            $message->to( $user->email )
                              ->subject( '謝謝您的訂購。' );
        });
    }
}// 原本的Checkout controller
$order = new Order(
    Auth::user()->id, 
    400
);
$order->checkout();// 瀏覽專輯頁面一鍵AJAX付款的對應controller
$order = new Order(
    Auth::user()->id, 
    400
);
$order->checkout();// 用iframe做plugin讓藝人能放進官網的controller
$order = new Order(
    Auth::user()->id, 
    400
);
$order->checkout();// 處理手機App結帳的controller
$order = new Order(
    Auth::user()->id, 
    400
);
$order->checkout();工程師B:這才叫做優雅!
class BillService
{
    public function checkout(User $user)
    {
        // 1. 利用Stripe從信用卡扣款        
        \Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");
        
        \Stripe\Charge::create(array(
            "amount" => 400,
            "currency" => "usd",
            "source" => array(
                // 這是一個簡化的範例。請別把信用卡資料存在資料庫。
                "number" => $user->creditCard->number,
                "exp_month" => $user->creditCard->expMonth,
                "exp_year" => $user->creditCard->expYear,
                "cvc" => $user->creditCard->cvc
            ),
        ));    
        
        // 2. 建立訂單
        $order = new Order();
        
        $order->price = 400;
        
        $order->user_id = $user->id;
        
        $order->save();
        
        // 3. 發送確認Email
        Mail::send('emails.confirmation', [], function($message) use ($user)
        {
            $message->to( $user->email )
                              ->subject( '謝謝您的訂購。' );
        });   
    }
}// 原本的Checkout controller
$service = new BillService();
$service->checkout(
    Auth::user()
);// 瀏覽專輯頁面一鍵AJAX付款的對應controller
$service = new BillService();
$service->checkout(
    Auth::user()
);// 用iframe做plugin讓藝人能放進官網的controller
$service = new BillService();
$service->checkout(
    Auth::user()
);// 處理手機App結帳的controller
$service = new BillService();
$service->checkout(
    Auth::user()
);工程師C:兩位別吵了!都沒擁有它!
多個entity爭執不下、
行為本身又很複雜的時候,
獨立成service。
class Document extends Eloquent
{
    public function addPermission($value, $type, $role, $withLink = false) 
    {
        $googleClient = new Google_Client();
        
        $googleClient->setClientId(Config::get('google_drive.clientId'));
        
        $googleClient->setClientSecret(Config::get('google_drive.clientSecret'));
        $googleClient->setRedirectUri(Config::get('google_drive.redirectUri'));
        $googleClient->setAccessType('offline');
        $googleClient->setScopes(array('https://www.googleapis.com/auth/drive'));            
        $googleClient->setApprovalPrompt('force');
        
        $googleService = new Google_Service_Drive($this->googleClient);
        $googleClient->setAccessToken(/*...*/);        
        
        $newPermission = new Google_Service_Drive_Permission();
        $newPermission->setValue($value);
        $newPermission->setType($type);
        $newPermission->setRole($role);
        $newPermission->setWithLink($withLink);
        $googleService->permissions->insert($this->fileId, $newPermission);        
    }
}<?php
class GoogleDriveService{
    
    protected $googleClient;
    protected $googleService;
    
    public function __construct(){
        $this->googleClient = new Google_Client();
        $this->googleClient->setClientId(Config::get('google_drive.clientId'));
        
        $this->googleClient->setClientSecret(Config::get('google_drive.clientSecret'));
        $this->googleClient->setRedirectUri(Config::get('google_drive.redirectUri'));
        $this->googleClient->setAccessType('offline');
        $this->googleClient->setScopes(array('https://www.googleapis.com/auth/drive'));            
        $this->googleClient->setApprovalPrompt('force');
        
        $this->googleService = new Google_Service_Drive($this->googleClient);
        $this->googleClient->setAccessToken(TokenServiceProider::getAccessToken());        
    }
    public function insertPermission( $fileId, $value, $type, $role, $withLink = false) {
        $newPermission = new Google_Service_Drive_Permission();
        $newPermission->setValue($value);
        $newPermission->setType($type);
        $newPermission->setRole($role);
        $newPermission->setWithLink($withLink);
        $this->googleService->permissions->insert($fileId, $newPermission);
    }
}class Document extends Eloquent
{
    public function addPermission($value, $type, $role, $withLink = false) 
    {
        $service = new GoogleDriveService();
        $service->insertPermission($this->fileId, $value, $type, $role, $withLink = false) ;
    }
}<?php
class GoogleDriveService{
    
    protected $googleClient;
    protected $googleService;
    
    public function __construct(){
        $this->googleClient = new Google_Client();
        $this->googleClient->setClientId(Config::get('google_drive.clientId'));
        
        $this->googleClient->setClientSecret(Config::get('google_drive.clientSecret'));
        $this->googleClient->setRedirectUri(Config::get('google_drive.redirectUri'));
        $this->googleClient->setAccessType('offline');
        $this->googleClient->setScopes(array('https://www.googleapis.com/auth/drive'));            
        $this->googleClient->setApprovalPrompt('force');
        
        $this->googleService = new Google_Service_Drive($this->googleClient);
        $this->googleClient->setAccessToken(TokenServiceProider::getAccessToken());        
    }
    public function insertPermission( $fileId, $value, $type, $role, $withLink = false) {
        $newPermission = new Google_Service_Drive_Permission();
        $newPermission->setValue($value);
        $newPermission->setType($type);
        $newPermission->setRole($role);
        $newPermission->setWithLink($withLink);
        $this->googleService->permissions->insert($fileId, $newPermission);
    }
}<?php namespace Howtomakeaturn\GoogleDriveKing;
use Google_Clinet;
use Google_Service_Drive;
class GoogleDriveKing{
    protected $googleClient;
    protected $googleService;
    
    public function __construct(){
        $this->googleClient = new Google_Client();
        $this->googleClient->setClientId(Config::get('google_drive.clientId'));
        
        $this->googleClient->setClientSecret(Config::get('google_drive.clientSecret'));
        $this->googleClient->setRedirectUri(Config::get('google_drive.redirectUri'));
        $this->googleClient->setAccessType('offline');
        $this->googleClient->setScopes(array('https://www.googleapis.com/auth/drive'));            
        $this->googleClient->setApprovalPrompt('force');
        
        $this->googleService = new Google_Service_Drive($this->googleClient);
        $this->googleClient->setAccessToken(TokenServiceProider::getAccessToken());        
    }
    // ...
}<?php
class BackupController extends Controller
{
    public function postDownload()
    {
        $results = DB::select('select * from users');
        // ...
    }
}<?php
namespace Howtomakeaturn\CSVDumper;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
class CSVDumper{
    
    protected $tableName;
    protected $doctrineSchemaManager;
    protected $queryBuilder;
    
    public function __construct($tableName)
    {
        $this->tableName = $tableName;
        $this->doctrineSchemaManager = Schema::getConnection()->getDoctrineSchemaManager();
        $this->queryBuilder = DB::table($tableName);
    }
    // ...
}<?php
class BackupController extends Controller
{
    public function postDownload()
    {
        // ...
        foreach ($tableNames as $name) {
            $dumper = new Howtomakeaturn\CSVDumper\CSVDumper($name);
            $result = $dumper->dumpAndStoreTable(storage_path() . '/database-backup');
        }
        // ...
    }
}/views
/controllers
/models
    /Article.php
    /User.php
    /Order.php
    /Product.php
    /Category.php
    /Coupon.php/views
/controllers
/MyApp
    /Article
        /Article.php
        /Presenter.php
        /Repository.php
        /Form.php
    /User
        /User.php
        /Presenter.php
        /Repository.php
        /Form.php
    /Order
    // ...
    /Service
        /FirstService.php
        /SecondService.php
/GithubName
    /CoolPackageOne
    /CoolPackageTwo
    /CoolPackageThree/views
/controllers
/MyApp
    /Entities
        /Article.php
        /User.php
    /Presenters
        /Article.php
        /User.php
    /Repositories
        /Article.php
        /User.php
    /Forms
        /Article.php
        /User.php
    // ...
    /Service
        /FirstService.php
        /SecondService.php
/GithubName
    /CoolPackageOne
    /CoolPackageTwo
    /CoolPackageThreeFactory, Search, Utility, Observer, Listener...etc