【レッツ Laravel4!】
2013.5.11 by ToR http://php.s3.to/
Laravel4は洗練されたPHPフレームワークです。
http://laravel.com/
Laravel4マニュアル
http://four.laravel.com/
■ComposerとLaravel4のインストール
Lravel4のインストールにはComposerが必要となります。
ComposerとはPHP関連のパッケージ管理システムです。
・・Linuxの場合
Composerのインストール
#curl -s http://getcomposer.org/installer | php
コマンド化
#mv composer.phar /usr/local/bin/composer
Laravel4のインストール
#composer create-project laravel/laravel l4(プロジェクト名) --prefer-dist
・Windowsの場合
ComposerをWindowsインストーラーでインストール
https://getcomposer.org/Composer-Setup.exe
Laravelをダウンロードして解凍
https://github.com/laravel/laravel/archive/develop.zip
コマンド実行窓を立ち上げて以下を実行
(コマンド実行窓(cmd.exe)は、Win7なら「スタート」→検索窓に「cmd」)
>cd c:
mmp\htdocs\l4(解凍した(composer.jsonのある)場所へ移動)
>composer install
依存関係のあるパッケージがダウンロードされます。
この解説ではWindows上のXAMMPを使用し、Laravel4を「l4」ディレクトリに展開することにします。
■設定
・全般の設定 app/config/app.php
'url' => 'http://localhost/l4/public',
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'key' => '適当なキーを設定',
・データベースの設定 app/config/database.php
データベースはSQLite、MySQL、PostgreSQL,SQLSeverをサポートしています。
'default' => 'mysql',
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'DB名',
'username' => 'DBユーザー名',
'password' => 'DBパスワード名',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
■動作確認
http://localhost/l4/public/
Hello World!と表示されればインストール成功です!
■テーブルの作成と初期データ
・・テーブル作成
・マイグレーション
マイグレーションとは、データベースの管理機能のことで、
テーブルの作成や初期データなどを自動追加・削除することができます。
コマンド実行窓を立ち上げて、Laravel4ディレクトリに移動します
・テーブル用マイグレート作成(テーブル名はmembersとする)
>php artisan migrate:make foo --create=members
この時点ではまだテーブルは作成されない。app/database/migrations/内にPHPファイルが作成される(日付+foo)
up()の部分に、テーブル構造を記載する。書き方はスキーマビルダーhttp://four.laravel.com/docs/schemaを参照
作成例)
Schema::create('members', function(Blueprint $table)
{
$table->increments('id');
$table->string('name',10);
$table->string('email',200);
$table->timestamps();
});
・実行
>php artisan migrate
テーブルが作成されました
・・初期データ追加
・app/databese/seeds/内に次の内容を「MemberTableSeeder.php」として作成
class MemberTableSeeder extends Seeder {
public function run()
{
$posts = [
[ 'name' => 'あああ いいい', 'email' => 'aaa@aaa.com'],
[ 'name' => 'ううう ええ', 'email' => 'bbb@ccc.net']
];
DB::table('members')->insert($posts);
}
}
・app/database/seeds/DataBaseSeedr.phpに以下を追加
$this->call('MemberTableSeeder');
・実行
>php artisan db:seed
初期データが追加されました。
■基本動作コントローラの自動生成
コマンド実行窓から以下を実行
>php artisan controller:make MemberController
app/controllers/内にファイルMemberContoroller.phpが自動作成されます。
・ルートファイルへの追加
./routes.phpに以下を追加
Route::resource('member', 'MemberController');
URLの対応表はこちら
http://four.laravel.com/docs/controllers#resource-controllers
例えば
http://localhost/l4/public/member/2
なら、コントローラのfunction show($id){の部分が実行されます。
■モデルの作成
app/models/内に以下の内容で、Member.phpを作成。たったの一行!
<?php
class Member extends Eloquent {}
Eloquent ORMを使用しています。
■ビューの作成
app/views/内に以下の内容で、index.blade.phpを作成
<table border=1 class="table table-striped">
<tbody>
@foreach($items as $m)
<tr>
<td>{{ $m->id }}</td>
<td>{{ $m->name }}</td>
<td>{{ $m->email }}</td>
<td>{{ $m->created_at }}</td>
</tr>
@endforeach
</tbody>
■データ呼び出し
・コントローラー(app/controllers/MemberController.php)に記述
public function index()
{
return View::make('index', ['items'=>Member::get()]);
}
・確認
http://localhost/l4/public/member
データの呼び出し方は、クエリービルダーhttp://four.laravel.com/docs/queriesに準拠しています。
例)Member::where('id', '>', 10)->order_by('name','desc')->get();
-------------2013.5.13
■テンプレート機能
・拡張子をblade.phpにすることで、Bladeテンプレートが使える
・コントローラでの呼び出しかたは、View::make('test');で、app/views/test.blade.phpが読み込まれる
・・レイアウト機能
・基本
まずはベースのHTMLを作成。app/views/layoutsフォルダを作成して、以下の内容でapp/views/layouts/master.phpとして保存
<html>
<body>
<div class="container">
@yield('content')
</div>
</body>
</html>
差し込むコンテンツを作成。app/views/test.blade.phpとして以下の内容で保存
@extends('layouts.master')
@section('content')
<p>コンテンツ1</p>
<p>コンテンツ2</p>
@stop
読み込むほうには、yield('名前');
差し込む方には、先頭にextends('ベースのファイル名'); セクションの区切りは@section~@stop
・親HTML内の文字を使用する場合
ベースのHTML
<html>
<body>
@section('sidebar')
ここは共通メニュー
@show
</body>
</html>
子のHTML
@extends('layouts.master')
@section('sidebar')
子ヘッダー<br>
@parent
<p>子メニュー</p>
@stop
・・その他変数などの書き方
・変数
{{ $name }}
・if文
@if($a >= 2)
2より大きい
@endif
・foreach文
@foreach($users as $user)
<p>ユーザー名は {{ $user->id }}</p>
@endforeach
■フォームの作成
追加画面を作成してみます。コントローラ(app/controller/MmemberController.php)の
public function create()の部分になります。
・まずはベースのHTMLを作成します。
<!DOCTYPE html>
<html>
<head>
<title>{{ $title }}</title>
</head>
<body>
@yield('content')
</body>
</html>
これをdefault.blade.phpとしてapp/views/layouts/に保存
・追加画面のフォームHTML作成。
@extends('layouts.default')
@section('content')
<h2>新規会員追加</h2>
{{ Form::open(['route' => 'member.store']) }}
{{ Form::token() }}
<p>
{{ Form::label('name', '名前:') }}<br />
{{ Form::text('name', Input::old('name')) }}
</p>
<p>
{{ Form::label('email', 'E-Mail:') }}<br />
{{ Form::text('email', Input::old('email'), ['size'=>'40']) }}
</p>
<p>{{ Form::submit('追加する') }}</p>
{{ Form::close() }}
@stop
これをcreate.blade.phpとしてapp/views/に保存
Form::textは、1つ目がname=""の部分、2つ目がデフォルト値、3つ目がオプションを配列で記述する
・コントローラを修正
public function create()
{
return View::make('create')->with('title','会員追加');
}
タイトルが機能ごとに変わるようにしています。
・動作確認
http://localhost/l4/public/member/create
■Twitter Bootstrapの導入
・Twitter Bootstrapはデザインテンプレートエンジンです。
http://twitter.github.io/bootstrap/
ダウンロードしたjs,css,imgフォルダを、Laravelの「public」に配置
・テンプレートの修正。(app/views/layouts/default.blade.php)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $title }}</title>
{{ HTML::style('css/bootstrap.min.css') }}
</head>
<body>
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="#">メンバー管理</a>
<ul class="nav">
<li>{{ HTML::linkRoute('member.index', '一覧') }}</li>
<li>{{ HTML::linkRoute('member.create', '追加') }}</li>
</ul>
</div>
</div>
<div class="container">
@yield('content')
</div>
{{ HTML::script('http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js') }}
{{ HTML::script('js/bootstrap.min.js') }}
</body>
</html>
HTML::のメソッド名は、vender/laravel/framework/src/Illuminate/Html/HtmlBuilder.phpを参照
・一覧表示画面の作成(app/views/index.blade.php)
@extends('layouts.default')
@section('content')
<table class="table table-striped">
<thead>
<tr><th>ID</th><th>名前</th><th>作成日</th>
<tbody>
@foreach($items as $m)
<tr>
<td>{{ $m->id }}</td>
<td>{{ HTML::linkRoute('member.show', $m->name, [$m->id]) }}</td>
<td>{{ $m->created_at }}</td>
</tr>
@endforeach
</tbody>
</table>
@stop
名前データから個別表示にリンクを張るようにしてみます。
・コントローラーの一覧表示修正(app/contoroller/MemberController.php)
public function index()
{
return View::make('index')
->with('items',Member::orderby('id','desc')->get())
->with('title','会員一覧');
}
IDの大きい順に表示して、タイトル追加。とりあえずは全データを表示しています。
・個別表示画面の作成(app/views/show.blade.php)
@extends('layouts.default')
@section('content')
<p>{{ $member->name }}</p>
<p>{{ $member->email }}</p>
<p><small>{{ $member->updated_at }}</small></p>
{{ HTML::linkRoute('member.index', '戻る') }}
@stop
・コントローラー個別表示修正(app/contoroller/MemberController.php)
public function show($id)
{
return View::make('show')
->with('member',Member::find($id))
->with('title','会員詳細');
}
■バリデーション&保存
バリデーションとはフォームから来たものが条件を満たすかどうかチェックするものです。
・コントローラーのstore()部分を修正
public function store()
{
//ルールの作成
$rules = ['name' => 'required|min:2', 'email' => 'required|min:10'];
//フォームから来るデータ
$input = Input::all();
//データとルールを渡す
$validation = Validator::make($input, $rules);
//OKの場合の処理
if ($validation->passes())
{
// OKの場合はDBにデータ追加して一覧に戻る
Member::create($input);
return Redirect::route('member.index');
}
//NGの場合は戻ってエラー表示
return Redirect::back()->withInput()->withErrors($validation);
}
・テーブルに挿入可能な項目を定義
(app/models/Member.phpを修正します)
class Member extends Eloquent {
protected $fillable = ['name', 'email'];
}
・エラーを表示
(app/views/create.blade.phpに以下を挿入します)
@if ($errors->any())
<ul>
{{ $errors->first('name', '<li>:message</li>') }}
{{ $errors->first('email', '<li>:message</li>') }}
</ul>
@endif
日本語化や項目名の表示についてはまた今度
・追加確認
http://localhost/l4/public/member/create
-------------------------2013.5.16
■バリデーションルールの共通化?
・バリデーションサービス作成
appフォルダに「services」フォルダを作成します。
app/servicesフォルダに「validator」フォルダを作成します。
TOPフォルダにあるcomposer.jsonにオートロードを追加します。
"autoload": [
・・・・
"app/tests/TestCase.php",
"app/services" //←追加
]
・バリデーション抽象化クラスの作成
app/services/validatorに、Validator.phpを作ります。
<?php namespace Services\Validators;
abstract class Validator {
protected $attributes;
public $errors;
function __construct($attributes = null)
{
$this->attributes = $attributes ?: \Input::all();
}
public function passes()
{
$validation = \Validator::make($this->attributes, static::$rules);
if ($validation->passes()) return true;
$this->errors = $validation->messages();
return false;
}
}
・バリデーションルールクラスの作成
app/services/validatorに、Member.phpを作ります。
<?php namespace Services\Validators;
class Member extends Validator {
public static $rules = [
'name' => 'required|min:2',
'email' => 'required|min:10'
];
}
・オートロードの再読込
コマンドラインから以下を実行します。
>composer dumpautoload
(もしくは#php composer.phar dumpautoloadかな)
serviceフォルダ以下を修正した場合は、適宜実行
・コントローラーの修正
チェックの部分
$input = Input::all();
$rules = ['name' => 'required|min:2', 'email' => 'required|min:10'];
$validation = Validator::make($input, $rules);
↓
$validation = new Services\Validator\Member;
エラーを渡す部分
return Redirect::back()->withInput()->withErrors($validation);
↓
return Redirect::back()->withInput()->withErrors($validation->errors);
書き直すとこんな感じです
public function store()
{
$validation = new Services\Validators\Member;
if ($validation->passes())
{
Member::create(Input::all());
return Redirect::route('member.index');
}
return Redirect::back()->withInput()->withErrors($validation->errors);
}
すっきりしました。
■個別画面に編集と削除へのリンク追加
app/views/show.blade.phpの下の方に以下を追加します。
{{ HTML::linkRoute('member.edit', '編集', [$member->id]) }} |
{{ Form::open(['route'=>['member.destroy', $member->id], 'method'=>'delete', 'style'=>'display:inline;']) }}
{{ Form::submit('削除') }}
{{ Form::close() }}
hiddenでIDを渡すのではなくForm::open(['route'=>['member.destroy', $member->id], 'method'=>'delete'という書き方をするみたいです。
■編集画面の作成
・ビューの作成。app/viewsにedit.blade.phpを作ります
@extends('layouts.default')
@section('content')
<h2>会員編集</h2>
@if ($errors->any())
<ul>
{{ $errors->first('name', '<li>:message</li>') }}
{{ $errors->first('email', '<li>:message</li>') }}
</ul>
@endif
{{ Form::open(['route' => ['member.update', $member->id], 'method' => 'put']) }}
<p>
{{ Form::label('name', '名前:') }}<br />
{{ Form::text('name', $member->name) }}
</p>
<p>
{{ Form::label('email', 'E-Mail:') }}<br />
{{ Form::text('email', $member->email, ['size'=>'40']) }}
</p>
<p>{{ Form::submit('更新する') }}</p>
{{ Form::close() }}
@stop
・コントローラーの修正
public function edit($id)
{
return View::make('edit')
->with('member',Member::find($id))
->with('title','会員編集');
}
■更新処理の作成
app/controllers/MemberController.phpを修正します。
バリデーションをクラス化?したので非常にすっきり。モデルもシンプル
public function update($id)
{
$validation = new Services\Validators\Member;
// OKの場合はDB更新して一覧にリダイレクト
if ($validation->passes())
{
Member::find($id)->update(Input::all());
return Redirect::route('member.index');
}
// NGの場合は戻ってエラー表示
return Redirect::back()->withInput()->withErrors($validation->errors);
}
■削除処理の作成
app/controllers/MemberController.phpを修正します。
public function destroy($id)
{
Member::find($id)->delete();
return Redirect::route('member.index');
}
コントローラのソース
http://php.s3.to/tt/MemberController.phps
リソースフルコントローラによるCRUDはとりあえずここまで
------------
■ページネーション
・コントローラの修正
get()をpaginate(1ページ表示数)にするだけ
->with('items',Member::orderby('id','desc')->get())
↓
->with('items',Member::orderby('id','desc')->paginate(5))
・ビューの修正
一覧ページ下部に以下を追加するだけ。
{{ $items->links() }}
めちゃ簡単
■ルートファイルの活用
ルートファイルとはapp/routes.phpのことで、「このURLにはこの処理を行う」という役割があります。
また、GETやPOSTの場合を区別したり、前処理を噛ませたりすることも出来ます。
では、とりあえず一覧表示と個別表示を処理するルートを書いてみます。
まずはURLを決めます。
http://~~~/member → 一覧表示
http://~~~/member/123 → ID=123のデータ表示
書き方ですがおおむね2通りの方法があります。
1.ルートファイル内で完結する
Route::get('member', function()
{
return View::make('index')
->with('items',Member::orderby('id','desc')->paginate(5))
->with('title','会員一覧');
});
Route::get('member/{id}', function($id)
{
return View::make('show')
->with('member',Member::find($id))
->with('title','会員詳細');
});
POSTの場合は、Route::post(~で始まります。
この方法ではコントローラーファイル(app/controllers/???)が不要になりますが、
機能が多い場合は冗長になり見づらくなりそうです。
2.コントローラーで処理させる
Route::get('member', ['as' => 'index', 'uses' => 'MemberController@index']);
Route::get('member/{id}', ['as' => 'show', 'uses' => 'MemberController@show']);
'as'=>には、ルート名(この処理の名前)をつけます。
ビュー内でHTML::linkRoute('index','戻る')と使ったり、
リダイレクト時にRedirect::route('index');と使ったりします。
'uses'=>には、使用するアクションを指定します。
'MemberController@index'だったら、app/controllers/MemberController.phpの
function index()が処理されるという意味になります。
・引数の文字種制限
例えばURLのIDは数字のみに限定したい場合は以下のように書きます。
Route::get('member/{id}', ['as' => 'show', 'uses' => 'MemberController@show'])->where('id', '[0-9]+');
いままでのリソースフルコントローラを書き換えると以下のようになります。
//Route::resource('member', 'MemberController');
Route::get('member', ['as' => 'member.index', 'uses' => 'MemberController@index']);
Route::get('member/create', ['as' => 'member.create', 'uses' => 'MemberController@create']);
Route::post('member', ['as' => 'member.store', 'uses' => 'MemberController@store']);
Route::get('member/{id}', ['as' => 'member.show', 'uses' => 'MemberController@show'])->where('id', '[0-9]+');
Route::get('member/{id}/edit', ['as' => 'member.edit', 'uses' => 'MemberController@edit'])->where('id', '[0-9]+');
Route::put('member/{id}', ['as' => 'member.update','uses' => 'MemberController@update'])->where('id', '[0-9]+');
Route::delete('member/{id}', ['as' => 'member.destroy', 'uses' => 'MemberController@destroy'])->where('id', '[0-9]+');
■CSRF対策
CSRFとは外部からの意図しない投稿攻撃のことです。
フォームにトークンを埋め込むことによって防ぐことができます。
・ビュー側の修正
{{ Form::token() }}
トークンがhiddenで埋め込まれます。
・ルートの修正
Route::post('member', ['before' => 'csrf', 'as' => 'member.store', 'uses' => 'MemberController@store']);
'before' => 'csrf'で前処理にトークンのチェックが行われます。
■ログインページ
認証(Auth)については用意されてるものを使用します。
設定はapp/config/auth.php。
E-mailとパスワードで認証することにします。
・ユーザーテーブル作成(テーブル名users)
>php artisan migrate:make create_users_table --table=users --create
app/database/migrateに日付+???.phpが作成されるので修正
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
・実行
>php artisan migrate
テーブルが作成されました
・初期データの追加
新規ファイルをapp/database/seeds/UserTableSeeder.phpとして作成
<?php
class UserTableSeeder extends Seeder {
public function run()
{
DB::table('users')->delete();
User::create([
'email' => 'aaa@email.com', 'password' => Hash::make('pass')
]);
}
}
}
app/databese/seeds/DatabaseSeeder.phpを修正
public function run()
{
Eloquent::unguard();
$this->call('UserTableSeeder');
}
・実行
>php artisan db:seed
初期データが追加されました。
・コントローラの修正
今回はサンプルとしてmyapgeに行くときに認証を通る設定にしてみます。
app/routes.phpに追加
Route::get('mypage', array('before' => 'auth', function()
{
return '<h2>Hello!!! '.Auth::user()->email.'!</h2>';
}));
'before' => 'auth'が前処理にauthフィルターを通すという意味です。
実際のフィルター等はapp/filters.php内に記述してあります。
Route::filter('auth', function()
{
if (Auth::guest()) return Redirect::to('login');
});
認証が通ってない場合は「login」にリダイレクトされるようになっています。
login時の処理をコントローラに書きます。
// GET時の処理
Route::get('login', function()
{
return View::make('login');//ログインフォーム表示
});
// POST時の処理
Route::post('login', function()
{
// バリデーション等はここに書くといいかも
Auth::attempt( ['email' => Input::get('email'), 'password' => Input::get('password')] );
return Redirect::to('mypage');
});
・ログインフォームの作成
新規ファイルapp/views/login.blade.phpを作成します。
<html>
<body>
<h2>ログイン</h2>
{{ Form::open(['url' => 'login']) }}
<p>
{{ Form::label('name', 'E-mail:') }}<br />
{{ Form::text('email', Input::old('email')) }}
</p>
<p>
{{ Form::label('password', 'Password:') }}<br />
{{ Form::password('password') }}
</p>
<p>{{ Form::submit('Login') }}</p>
{{ Form::close() }}
</body>
</html>