アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Laravel 5.4でWeb APIを作る

前回の記事では、Laravelでフロントエンド開発を行うための開発環境の作り方を解説しました。今回は、LaravelでWeb APIを作る方法を解説します。

データベースの準備



アプリケーションのデータはデータベースに保存するようにしたいので、セットアップを行います。
Laravelアプリケーションを作成すると、以下の値でデフォルトの接続情報が作成されます。


  • データベース: MySQL

  • データベース名: homestead

  • ユーザ名: homestead

  • パスワード: secret



ローカル開発環境にMySQLがインストール済みなら、上と同じ条件で新しいデータベースを作成するのが手軽です。
手元の環境にMySQLをインストールしたくない、といった場合には、開発用VMLaravel Homesteadを利用するのが良いでしょう。

DBへの接続情報は、アプリケーションのルートディレクトリの「.env」という隠しファイルに記述します。



$ cat .env
APP_ENV=local
APP_KEY=base64:sWS+TR6ESaDXCXGJKtk7twOtsE2lHVUqJdLUula6t3I=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=


データベース名等が異なる場合は、.envを適宜編集してください。

セットアップができたら、DBに接続できるか確認するため、「php artisan migrate:status」を実行します。なお、Homestead等のVMを使用している場合は、VM内で実行してください。
成功すれば、以下のような出力が得られるはずです。



$ php artisan migrate:status
+------+------------------------------------------------+
| Ran? | Migration                                      |
+------+------------------------------------------------+
| N    | 2014_10_12_000000_create_users_table           |
| N    | 2014_10_12_100000_create_password_resets_table |
+------+------------------------------------------------+


もしもPDOException等のエラーが出てしまった場合は、.envに記述した接続情報が正しいか見直してください。

新しいテーブルの追加



はじめに、「artisan make:migration」を使って、マイグレーションファイルの雛形を作成します(ファイル名は実行した日時によって異なります)。



$ php artisan make:migration create_items_table
Created Migration: 2017_03_16_002633_create_items_table


作成できたら、以下の内容で置き換えます。



<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->text('content');
            $table->boolean('checked')->default(false);
            $table->timestamps();
            $table->foreign('user_id')
                ->references('id')
                ->on('users');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('items');
    }
}


簡単なToDoリストのテーブルで、user_idが持ち主、contentがタスクの内容、checkedが完了済みか否かを示します。
作成できたら「php artisan migrate」コマンドでテーブルを作成します。



$ php artisan migrate
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2017_03_16_002633_create_items_table


もし失敗してしまった場合は、「php artisan migrate:reset」コマンドで、全てのマイグレーションが実行される前の状態に戻しましょう。
また、リセットが上手くいっていない場合には、テーブルが消えずに残ってしまうことがあります。
その場合、データベースにログインしてDROP TABLEでテーブルを削除しましょう。

マイグレーションに成功すると、以下のテーブルが作成されます。



mysql> desc items;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | int(10) unsigned | NO   | MUL | NULL    |                |
| content    | text             | NO   |     | NULL    |                |
| checked    | tinyint(1)       | NO   |     | 0       |                |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+


雛形の作成



APIを作成するためには、(1) itemsテーブルに対応するモデルクラスの作成 (2) Itemの操作を行うためのコントローラーの作成 が必要です。
以下のコマンドを実行すると、上記作業をコマンド一発で行なえます。



$ php artisan make:model Item --controller --resource


make:modelコマンドに「--controller(または-c)」オプションを渡すと、モデルクラスに対応したコントローラーが作成されます。さらに、「--resource(または-r)」オプションを追加すると、「リソースコントローラー」が作成されます。リソースコントローラーでは、show()、edit()等のメソッドの引数にあらかじめモデルクラスが定義されています。また、後述するresourceルートと組み合わせると便利です。



<?php
// (省略)
    /**
     * Display the specified resource.
     *
     * @param  \App\Item  $item
     * @return \Illuminate\Http\Response
     */
    public function show(Item $item)
    {
        //
    }


初期データの登録



データベースへの初期データの登録には、Seederという仕組みを使うと便利です。
MySQLにログインしてINSERT文を発行してもいいのですが、ユーザを登録する際にpasswordをbcryptでハッシュ化する作業が必要だったりして、かえって面倒だったりします。Seederでデータを登録できるようにしておくと、テストの際にも利用しやすいので、オススメです。

database/seeds/DatabaseSeeder.phpを以下の内容に書き換えます。



<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $faker = \Faker\Factory::create();
        
        $user = new \App\User();
        $user->name = $faker->name;
        $user->email = $faker->unique()->safeEmail;
        $user->password = bcrypt('password');
        $user->remember_token = str_random(10);
        $user->save();
        
        $item = new \App\Item();
        $item->user_id = $user->id;
        $item->content = $faker->text();
        $item->save();
    }
}


本格的なデータ登録処理を行うならテーブルごとにSeederを作ると良いですが、ここではUserとItemを1つずつ登録したいだけなので、処理をベタ書きしています。
また、Fakerを使用して適当なダミーデータを生成しています。
DatabaseSeederは「php artisan db:seed」コマンドで実行できます。成功したら、以下のようなデータが登録されます(内容はランダムで変わります)。



mysql> select * from users\G
*************************** 1. row ***************************
            id: 1
          name: Prof. Macy Stanton
         email: emarquardt@example.com
      password: $2y$10$fTiwsn9d8VPL81XrTslB4OqT1qv5Si8qYoADECnmFC04AbxVfcEVO
remember_token: ckzIlT20s1
    created_at: 2017-03-16 01:48:48
    updated_at: 2017-03-16 01:48:48
1 row in set (0.00 sec)

mysql> select * from items\G
*************************** 1. row ***************************
        id: 1
   user_id: 1
   content: Qui voluptatem ea qui in. Alias incidunt ullam rem. Et sequi et et atque sequi sunt modi alias. Odit aut sed fugiat natus. Adipisci eum et omnis debitis.
   checked: 0
created_at: 2017-03-16 01:48:48
updated_at: 2017-03-16 01:48:48
1 row in set (0.00 sec)


モデルの関連付け



テーブル同士の関係性(Relationships)は、「artisan make model」コマンドでは生成されないため、手書きする必要があります。

UserはItemを0個以上もつので、app/User.phpにitems()メソッドを追加して、hasMany()メソッドを呼び出します。



<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function items()
    {
        return $this->hasMany(Item::class);
    }
}


同様に、Itemは必ずいずれかのUserに属するので、app/Item.phpを以下のように編集します。



<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}


ルーティング



コントローラーを呼び出すためのルーティングを定義します。
routes/api.php を以下のように変更します。



<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

// 認証は面倒なので一旦省略
Route::resource('/items', 'ItemController', ['except' => ['create', 'edit']]);


APIにも認証が必要ですが、ここでは一旦省略しています(次回の記事で、OAuth 2.0を使った認証方法を紹介します)。
Route::resource()メソッドを使うと、先ほど作成したリソースコントローラーに対応するルートが定義されます。
ただし、新規作成画面(GET /RESOURCE/create)と編集画面(GET /RESOURCE/ID/edit)のルートは、APIには必要ありません。そのため、「 ['except' => ['create', 'edit']]」でcreateとeditのルートは除外しています。
ルートを定義したら、「php artisan route:list」で確認しておきましょう。



コントローラーの実装



ルーティングができたので、コントローラーを実装します。
まずは動作確認のため、app/Http/Controllers/ItemController.phpのindex()メソッドを以下の内容に書き換えます。



<?php
// (省略)
    public function index()
    {
        return response(Item::all());
    }


この状態で、 http://localhost:8000/api/items にアクセスすると、以下のようなJSONが返ってくるはずです。



機能テスト



コントローラーが動くようになったので、コントローラーの機能テストを作成しましょう。
php artisan make:test ItemTest」で機能テスト(Feature Test)を作成できます。
作成したtests/Feature/ItemTest.phpを以下のように置き換えます。



<?php

namespace Tests\Feature;

use App\Item;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ItemTest extends TestCase
{
    use DatabaseMigrations;

    protected function setUp()
    {
        parent::setUp();
        (new \DatabaseSeeder())->run(); // テストデータ登録
    }

    public function testIndex()
    {
        $response = $this->get('/api/items');

        $response->assertStatus(200);
        $this->assertCount(1, $response->json());
    }

    public function testShow()
    {
        $response = $this->get('/api/items/1');

        $response->assertStatus(200);
    }

    public function testStore()
    {
        $data = ['content' => 'ブログを書く'];
        $response = $this->post('/api/items', $data);

        $response->assertStatus(201);
        $response->assertJson($data);
        $item = Item::query()->find($response->json()['id']);
        $this->assertInstanceOf(Item::class, $item);
    }

    public function testUpdateContent()
    {
        $data = ['content' => 'ブログを書く'];
        $response = $this->patch('/api/items/1', $data);
        
        $response->assertStatus(200);
        $response->assertJson($data);
        $item = Item::query()->find(1);
        $this->assertSame('ブログを書く', $item->content);
    }

    public function testUpdateChecked()
    {
        $data = ['checked' => 1];
        $response = $this->patch('/api/items/1', $data);

        $response->assertStatus(200);
        $response->assertJson($data);
        $item = Item::query()->find(1);
        $this->assertEquals(true, $item->checked);
    }

    public function testDelete()
    {
        $response = $this->delete('/api/items/1');

        $response->assertStatus(200);
        $this->assertNull(Item::query()->find(1));
    }
}


はじめに、「use DatabaseMigrations;」することで、それぞれのテストの実行前にmigrate、実行後にmigrate:rollbackが実行されるようにしています。
これによって、あるテストで作成したデータが別のテストに影響することを避けられます。

テストデータの登録は手抜きをして、先ほど作成したSeederを使ってます。
テストケースはとりあえず正常系だけ書いてます。
やや特殊な点として、更新はcontentまたはcheckedのいずれか単独でも可能な仕様としています。

この状態でテストを実行してもいいのですが、接続先のデータベースが「homestead(デフォルトの場合)」となっているため、できればテスト用のデータベースを分けたいところです。

テスト環境のデータベースの切り換えは、.envなどでもできますが、phpunit.xmlに書くのが手軽でしょう。phpunit.xmlの<php>~</php>で囲まれた部分を、以下のように書き換えます(DB_DATABASEの定義を追加)。



        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="DB_DATABASE" value="test_homestead"/>


また、データベースは自動作成されないので、MySQLにログインして「CREATE DATABASE test_homestead」でテスト用のデータベースを作成しておきます。

用意ができたら、以下のように実行します(まだ実装が完了していないので、エラーになります)。



$ ./vendor/bin/phpunit tests/Feature/ItemTest.php


バリデーション



ItemControllerで新しいItemを登録する機能は以下のように実装できます。
※ログインユーザーの取得処理については、ログイン機能実装後に作成します。



<?php
// 省略
    public function store(Request $request)
    {
        $item = new Item();
        // todo: ログインユーザのidが入るようにする
        $item->user_id = \App\User::query()->first()->id;
        $item->content = $request->input('content');
        $item->save();
        return response($item, 201);
    }


現状では、入力値のバリデーションが全くありません。
contentにすごく長い文字列が入っていると、DB保存時にエラーが発生します。

Laravelでバリデーションを実装する方法はいくつかありますが、個人的にはFormRequestを使った方法がオススメです。
FormRequestは以下のコマンドで作成できます。



$ php artisan make:request ItemStoreFormRequest


実行すると、app/Request/ItemFormStoreRequest.phpが追加されます。この中身を以下のように書き換えましょう。



<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ItemStoreFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true; // todo: 認証実装
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'content' => 'required|string|max:255',
        ];
    }
}


contentというフィールドの値が、(1) nullまたは空文字列の場合 (2) 文字列ではない場合 (3) 255文字異常の長さの場合 にエラーが返るように設定しています。

次に、ItemControllerのstore()メソッドで使用するRequestクラスを置き換えます。



<?php
// 省略
use App\Http\Requests\ItemStoreFormRequest;
// 省略
    public function store(ItemStoreFormRequest $request)


このようにすると、バリデーションはFormRequest、保存処理はコントローラー、という風に責務を分担できます。

同じように、ItemUpdateFormRequestも作成します。



<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ItemUpdateFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true; // todo: 認証実装
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'content' => 'string|max:255',
            'checked' => 'boolean'
        ];
    }
}


登録と更新では、バリデーションの条件が違うので、FormRequestクラスも別にしています。
具体的には、(1) contentは登録では必須だが、更新では必須ではない (2) checkedを変更できるのは更新の場合のみ という違いがあります。

最後に、コントローラーの全体像を掲載します。



<?php

namespace App\Http\Controllers;

use App\Http\Requests\ItemStoreFormRequest;
use App\Http\Requests\ItemUpdateFormRequest;
use App\Item;

class ItemController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return response(Item::all());
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \App\Http\Requests\ItemStoreFormRequest $request
     * @return \Illuminate\Http\Response
     */
    public function store(ItemStoreFormRequest $request)
    {
        $item = new Item();
        // todo: ログインユーザのidが入るようにする
        $item->user_id = \App\User::query()->first()->id;
        $item->content = $request->input('content');
        $item->save();
        return response($item, 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Item $item
     * @return \Illuminate\Http\Response
     */
    public function show(Item $item)
    {
        return response($item);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \App\Http\Requests\ItemUpdateFormRequest $request
     * @param  \App\Item                                $item
     * @return \Illuminate\Http\Response
     */
    public function update(ItemUpdateFormRequest $request, Item $item)
    {
        if ($request->input('content')) {
            $item->content = $request->input('content');
        }
        if ($request->input('checked')) {
            $item->checked = $request->input('checked');
        }

        $item->save();
        return response($item);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Item $item
     * @return \Illuminate\Http\Response
     */
    public function destroy(Item $item)
    {
        $item->delete();
        return response('{}'); // 返すものがないので空のJSONを返す
    }
}


これで一通りの実装ができたので、テストがすべて通るはずです。



$ ./vendor/bin/phpunit tests/Feature/ItemTest.php
PHPUnit 5.7.15 by Sebastian Bergmann and contributors.

......                                                              6 / 6 (100%)

Time: 3.19 seconds, Memory: 8.00MB

OK (6 tests, 14 assertions)


ソースコードの全体はGitHubでも公開しているので、参考にしてください。

ここまでで、Itemの取得・作成・更新・削除が行えるWeb APIができました。
次回は、このAPIを使うVue.jsアプリケーションを作っていきます。

参考



Laravel Homestead
Database: Migrations
Database: Seeding
Eloquent: Relationships
Routing
Validation

Laravel 5.4で Vue.js開発環境を手軽に作る

こんにちは。宇都宮です。
最近はアシアル社内でもLaravelを使うことが増えてきました。また、フロントエンドも、ReactやVue.jsを使ったプロジェクトをちらほら見かけるようになってきました。
今回は、Laravelのインストール方法と、フロントエンド開発環境のセットアップ、簡単なVueコンポーネントの作り方を解説します。

Laravelとフロントエンド



Laravelでは、5.3から、Vue.jsが標準のJavaScriptフレームワークになりました。
さらに、5.4では、フロントエンドのビルドツールが、gulpベースのElixirから、WebpackベースのMixに変わりました。
Laravelをインストールすれば、Web APIはLaravelで作って、SPA(Single Page Application)をVue.jsで組む、といったことが簡単にできるようになっています。
なお、LaravelとVue.jsが密結合しているわけではないので、Vue.jsではなくReactやAngularを使うこともできます。

インストール



本記事では、Laravel 5.4.15を使用します。
また、Laravel 5.4の動作には、PHP 5.6.4以上と、いくつかのPHP拡張モジュールが必要です。

インストールにはいくつかの方法がありますが、以下ではcomposerを使用します。



composer create-project --prefer-dist laravel/laravel sample


上記コマンドを使用すると、現在のディレクトリに「sample」という名前のディレクトリが追加され、その中にLaravelアプリケーションが作成されます。
作成直後のディレクトリ構造は以下のようになります。



$ tree -L 1
.
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── package.json
├── phpunit.xml
├── public
├── readme.md
├── resources
├── routes
├── server.php
├── storage
├── tests
├── vendor
└── webpack.mix.js

10 directories, 8 files


まずは、Laravelアプリケーションが正常に動くか確認するため、開発用Webサーバを起動してみましょう。



$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>


次に、ブラウザで「 http://127.0.0.1:8000 」にアクセスしてみましょう。
以下のような画面が表示されれば正常に動作しています。



フロントエンドのセットアップ



Laravel 5.4をインストールすると、以下の内容のpackage.jsonが付いてきます。



{
  "private": true,
  "scripts": {
  // 省略
  },
  "devDependencies": {
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "jquery": "^3.1.1",
    "laravel-mix": "^0.8.1",
    "lodash": "^4.17.4",
    "vue": "^2.1.10"
  }
}


Laravelをインストールしたディレクトリで「npm install」を実行すれば、必要なライブラリが入ります。
npmのために、事前にNode.jsをインストールしておきましょう。

インストールが完了すると、以下の環境が作成されます。



さきほど省略したpackage.jsonには、npmで実行できるスクリプトが定義されています。たとえば、上記コンパイル処理を一括で実行する際は「npm run dev」を使用します。

注意点として、2017年3月15日現在、「npm run dev」はcross-env.jsのパスの問題で失敗する可能性があります。
GitHubIssueが挙がっているので、将来的には直っているはずです。
取り急ぎの修正方法としては、cross-env.jsのパスを修正して、以下のようにしましょう。



  "scripts": {
    "dev": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --watch-poll --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "hot": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "production": "node node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },


scriptを修正して「npm run dev」を実行するとコンパイルが成功するはずです。

これで、public/js/app.jsにコンパイル済みのJavaScript、public/css/app.cssコンパイル済みのCSSが出力されるようになります。
また、「npm run watch」を走らせておくと、JavaScriptCSSを追加・変更した際に自動的にコンパイルが行われるようになります。

まずはVue.jsが使えるようになっているか確認しましょう。
resources/views/welcome.blade.php を以下の内容で置き換えます。



<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello Vue</title>
    <link rel="stylesheet" href="css/app.css"/>
    <script type="text/javascript">
        window.Laravel = window.Laravel || {};
        window.Laravel.csrfToken = "{{csrf_token()}}";
    </script>
</head>
<body>
<div id="app">
    <example></example>
</div>
<script src="js/app.js"></script>
</body>
</html>


以下のように表示されればOKです。



ここでは、Laravelに付属するExampleという名前のVueコンポーネントを使用しています。
また、resources/assets/js/bootstrap.jsで参照している「window.Laravel.csrfToken」という変数の定義も行っています。この変数の使いみちについては後で説明します。

JavaScriptアプリケーションのブートストラップ



resources/assets/js/bootstrap.js に、JavaScriptアプリケーションを動作させるのに必要な初期化処理を記述します。




window._ = require('lodash');

/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

window.$ = window.jQuery = require('jquery');

require('bootstrap-sass');

/**
 * Vue is a modern JavaScript library for building interactive web interfaces
 * using reactive data binding and reusable components. Vue's API is clean
 * and simple, leaving you to focus on building your next great project.
 */

window.Vue = require('vue');

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

window.axios = require('axios');

window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': window.Laravel.csrfToken,
    'X-Requested-With': 'XMLHttpRequest'
};

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from "laravel-echo"

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: 'your-pusher-key'
// });



ほとんどライブラリを読み込んでいるだけですが、1つ興味深い処理が行われています。



window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': window.Laravel.csrfToken,
    'X-Requested-With': 'XMLHttpRequest'
};


axiosは、HTTPクライアントのライブラリです。
ここでは、axoisの設定を変更し、リクエストヘッダにCSRFトークンと、XMLHttpRequestの目印を付けるようにしています。
このようにすることで、axiosを使って送られたリクエストに自動的にCSRFトークンが付与されるようになります。

Vue.jsアプリケーションの起動



アプリケーションの起動処理等は resources/assets/js/app.js に記述します。
ここは以下のようになっています。




/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */

require('./bootstrap');

/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */

Vue.component('example', require('./components/Example.vue'));

const app = new Vue({
    el: '#app'
});


(1) bootstrap.jsを読み込んで初期化処理を実行
(2) Example.vueというVueコンポーネントを読み込み
(3) Vueアプリケーションを起動

という流れです。

Vue.jsアプリケーションを起動する際には、どの要素をVue.jsアプリケーションのルートとするかを指定します。ここでは、idが"app"である要素がVue.jsアプリケーションのルートになるように指定しています。

Vue.jsでは、「コンポーネント」という単位で、再利用可能なパーツを定義することができます。
resources/assets/js/components/Example.vueは以下の内容になっています。



<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>

                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>


templateタグの中には、コンポーネントのHTMLを記述します。同様に、scriptタグの中には、コンポーネントの動作を定義します。ここでは使用されていませんが、styleタグを使用して、コンポーネントに適用するCSSを定義することもできます。

独自コンポーネントの定義



次に、独自のVueコンポーネントを定義してみましょう。
resources/assets/js/components/Hello.vueというファイルを、以下の内容で追加します。



<template>
    <h2>Hello <span class="name">{{name}}</h2>!</div>
</template>

<script>
    export default {
        props: ['name']
    }
</script>

<style scoped>
    .name {
        font-weight: bold;
    }
</style>


ここでは、Vue.jsのコンポーネントに、外部から値を渡せるように実装しています。

はじめに、コンポーネントがもつプロパティを、propsプロパティの中に配列で定義します。
次に、コンポーネントを利用する箇所で「<hello name="Laravel"></hello>」のように、コンポーネントに渡したい値を記述します。
propsに定義したプロパティはコンポーネント内から参照できるので、{{name}} のように記述すると文字列としてプロパティの値を出力することができます。

※{{}}という記法はLaravelのテンプレートエンジン(Blade)でも値を出力する際に使用します。Vueコンポーネント内で{{}}を使用する際は気にする必要はありませんが、Bladeテンプレートの中でVue.jsの{{}}を使いたい場合「@{{}}」という風に先頭に@をつけると、Bladeはこの部分を無視します。

style要素にscoped属性をつけている点にも注目です。scoped属性を使うと、style要素内で定義されたスタイルが適用される範囲を限定することができます。ここでは、Vueコンポーネントのテンプレート内でのみ適用可能な「name」クラスを定義しています。テンプレートの外部で「name」クラスを使った場合には、このスタイルは適用されません。

次に、app.jsにHelloコンポーネントを登録します。



Vue.component('hello', require('./components/Hello.vue'));


最後に、welcome.blade.phpのbodyの中を以下のように書き換えます。



<div id="app">
    <hello></hello>
    <hello name="Laravel"></hello>
    <hello name="Vue.js"></hello>
</div>


以下のように表示されればOKです。



参考



Symfonyの歩き方/5分でわかるSymfony Best Practices

この記事はSymfony Advent Calendar 2015 10日目の記事です。前日の記事はtsuboさんの「簡単デバッグ」でした。




こんにちは。宇都宮です。今日は、社内勉強会で使用した、Symfony入門者向けの資料を共有します。

Symfonyの歩き方





Symfonyの学習方法について、特に公式ドキュメントの読み方に焦点を当てています。

Symfonyの公式ドキュメントは、Symfonyの学習リソースとして第一級の資料ですが、量が多く、初心者のうちはどのドキュメントから読めばいいか分からないと思います。膨大な公式ドキュメントのうち、どの文書を優先的に読めばいいのかわかれば、効率的に学習していくことができます。

5分でわかるSymfony Best Practices





Symfony公式ドキュメントの中で、The Symfony Bookと並んで重要なのがSymfony Best Practicesです。Symfony Best Practicesは、Symfonyを使う上でのベストプラクティスが書かれた文書です。

Symfony にはレールが無い」といわれることがあります。Symfonyを使う開発者は、他のフレームワークを使う場合に比べて、設計を自分で考える必要のある場面が多くなります。

Symfony Best Practicesは、Symfony Wayを強制するものではありませんが、Symfonyらしい設計・実装方法のヒントになります。

このスライドでは、Symfony Best Practicesのポイントを抜き出しています。短時間で要点を押さえたい方におすすめです。

おわりに



Symfonyは覚えることが多いですが、Advent Calendarのような形で日本語の情報が共有されると非常に助かりますね!

明日は @qckanemoto さんです!

2015年4月-6月におけるアシアルの活動 : スマートフォン&モバイルEXPO、Cordova勉強会、AngularU 他。

春はイベント開催時期ということもあり、アシアルでも各種展示会、勉強会、カンファレンスに参加してきました。

○ 4月20日-22日 「O'Reilly Fluent Conference 2015」出展


○ 5月12日-13日 「AppsWorld North America 2015」出展


○ 5月13日-15日 「スマートフォン&モバイルEXPO 2015春」出展


Monaca展示ブースを設置。多くのお客様にご来場いただき、ミニセミナーは通路にまで人が溢れるという大盛況ぶりでした。

また、ヘッドウォータース社より、イベント用Pepperアプリパッケージ「プロボ」に展示会支援をいただきました。


○ 5月26日-27日 「de:code 2015」講演

田中が「Visual Studio with Cordova クロス プラットフォーム開発の全貌」と題する講演を行いました。

関連記事:CodeIQ MAGAZINE
「Visual Studio+Cordova」は、なぜクロスプラットフォームでのアプリ開発に適しているのか?


○ Cordova勉強会 講演&開催協力

日本Cordovaユーザー会メンバーとして、Apache Cordova勉強会に参加、開催協力をしております。

・5月20日 第5回Apache Cordova勉強会
勉強会の模様は、アシアルブログ「第5回 日本Cordovaユーザ会レポート」でも紹介しています。

・6月19日 第6回Apache Cordova勉強会
勉強会の模様は、アシアルブログ「第6回Apache Cordova勉強会 に参加してきました」でも紹介しています。


○ 6月22日-25日 「AngularU」講演


Andreasが「Angular + Cordova + Monaca = Killer mobile app development」と題する講演を行いました。

関連動画:AngularU 主催者提供
Angular + Cordova + Monaca = Killer mobile app development


○ 6月27日 「PHPカンファレンス福岡2015」講演


久保田が「PHPで学ぶ仮想マシン正規表現エンジンの仕組み」と題する講演を行いました。



今後も積極的に活動していきますので、どこかでお会いすることがあれば、気軽にお声掛けいただけたらと思います!

PHPで日付時刻の処理を書くなら Carbon がおすすめ



どうも、筋トレにハマっているたきゃはしです。
本日はPHPにおける日付時刻のライブラリについて書いていきたいと思います。

突然ですが、日付や時刻が絡む処理って意外とやっかいだと思いませんか?おそらく皆さんもいくつか思い当たるフシがあるかと思いますが、そんなやっかい事も Carbon(カーボン)を使えば解消できるかもしれません!

Carbon - A simple PHP API extension for DateTime.

Carbon とはPHPのDateTimeクラスを継承して拡張された日時操作ライブラリです。

「Carbonってどうなの?流行ってるの?」という方向けに予め補足致します。
Carbonはすでに人気のフレームワークに統合されていたり、GitHubで☆3000に迫る評価もありますのでDateTimeライブラリとしてデファクトになるんじゃないかと予想できます。

ではインストールからはじめます。



composer require nesbot/carbon

Composerでインストールすれば



<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

echo Carbon::now(); // 2015-05-13 10:14:28(現在の日時)

Carbon::setTestNow(Carbon::create(2015, 5, 9, 15, 0, 0));
echo Carbon::now(); // 2015-05-09 15:00:00
echo Carbon::today()->toDateString(); // 2015-05-09

Carbon::setTestNow();
$carbon = Carbon::create(1980, 4, 15, 10, 20, 30);
echo $carbon->year;        // 1980
echo $carbon->age;         // 35

echo Carbon::today()->subYears(13)->addMonths(2)->subDays(10)->toDateString(); // 2002-07-02

Carbonはすぐに使えます。

基本的にCarbonのメソッドはインスタンスを返します。なのでメソッドチェーンによる直感的な表現が可能です。またインスタンスを文字列型へとキャストするとデフォルトフォーマットに変換してくれます。

もちろんフォーマットを指定することも可能です。



<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

$carbon = Carbon::create(1980, 4, 15, 10, 20, 30);
echo $carbon; // 1980-04-15 10:20:30
echo $carbon->format('Y年m月d日'); // 1980年04月15日

$carbon->setToStringFormat('Y/m/d H:i:s'); // デフォルトフォーマットを変更
echo $carbon; // 1980/04/15 10:20:30

$carbon->resetToStringFormat();
echo $carbon; // 1980-04-15 10:20:30


フォーマットの書式は date() と同じです。
- http://php.net/manual/ja/function.date.php

またCarbonではインスタンスを生成するための方法がいくつか準備されています。



<?php

use Carbon\Carbon;

class CarbonTest extends PHPUnit_Framework_TestCase
{

    /**
     * @test
     */
    public function instance()
    {
        $dt = new \DateTime();
        $carbon = Carbon::instance($dt);
        $carbon2 = Carbon::instance($dt);
        $this->assertTrue($carbon2 instanceof Carbon);
        $this->assertTrue($carbon !== $carbon2);
    }

    /**
     * @test
     */
    public function copy()
    {
        $carbon = new Carbon();
        $copiedCarbon = $carbon->copy();
        $this->assertTrue($copiedCarbon instanceof Carbon);
        $this->assertTrue($copiedCarbon !== $carbon);
    }

    /**
     * @test
     */
    public function parse()
    {
        $carbon = Carbon::parse('2002-07-02');
        $this->assertSame(2002, $carbon->year);
        $this->assertSame(7, $carbon->month);
        $this->assertSame(2, $carbon->day);
    }

    /**
     * @test
     */
    public function create()
    {
        $carbon = Carbon::create(2002, 7, 2);
        $this->assertSame(2002, $carbon->year);
        $this->assertSame(7, $carbon->month);
        $this->assertSame(2, $carbon->day);
    }

}


基本的にcreate()もしくはparse()を使って生成することになりそうです。
parse()なんかはDBのDATE型やDATETIME型の値をそのまま引数に使えるので便利そうです。

次は Carbon::setTestNow() についてです。
setTestNow() はCarbonが基準とする現在の日時をモックで設定できる機能です。つまりロジック内でCarbonを適切に扱うことで時間に関するテストをユニットテストでも書けるようになります。

ユニットテストの例を用意しました。
賞味期限(BestBefore)が過ぎていないか確認するだけのシンプルな機能です。



<?php

namespace Services;

use DateTime;

class BestBefore
{

    protected $dt;

    public function __construct(DateTime $dt)
    {
        $this->dt = $dt;
    }

    public function isSafe(DateTime $dt)
    {
        return $this->dt < $dt;
    }

}


次に BestBefore::isSafe() のテストを書いてみます。



<?php

use Carbon\Carbon;
use Services\BestBefore;

class BestBeforeTest extends PHPUnit_Framework_TestCase
{

    /**
     * @test
     */
    public function 賞味期限が切れていないかどうか()
    {
        Carbon::setTestNow(Carbon::createFromDate(2015, 5, 1));
        $expire = new BestBefore(new Carbon());

        $this->assertFalse($expire->isSafe(Carbon::createFromDate(2015, 4, 30)));
        $this->assertFalse($expire->isSafe(Carbon::createFromDate(2015, 5, 1)));
        $this->assertTrue($expire->isSafe(Carbon::createFromDate(2015, 5, 2)));

        Carbon::setTestNow(Carbon::createFromDate(2015, 4, 30));
        $expire2 = new BestBefore(new Carbon());

        $this->assertFalse($expire2->isSafe(Carbon::createFromDate(2015, 4, 30)));
        $this->assertTrue($expire2->isSafe(Carbon::createFromDate(2015, 5, 1)));
        $this->assertTrue($expire2->isSafe(Carbon::createFromDate(2015, 5, 2)));
    }

}


ご覧の通り Carbon::setTestNow() を設定することで冪等なテストを書くことができました。もしBestBeforeクラスのロジックでPHP標準の date() や time() を使っていたならこのようなテストは書けないですよね。個人的にすごく魅力的な仕組みだと感じました。

また例のBestBeforeクラスではタイプヒンティングにDateTimeを指定していますので、Carbonを拡張したMyCarbonを使いたいといった場合でもBestBeforeクラスに手を加えることなく利用することができます。

Carbonは単に「DateTime扱いやすいぞーやったー」というだけでなく、日時処理まわりのメンテナンス性およびコード品質の向上に一役買うライブラリであるということをお伝えできたかと思います。

ということで概ね私が伝えたかったのはここまでです。
ここからは覚えておくと便利なことや調べた機能をメモとして記載します。

アクセサ




<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

$dt = Carbon::parse('2012-9-5 23:26:11.123789');

var_dump($dt->year);                                         // int(2012)
var_dump($dt->month);                                        // int(9)
var_dump($dt->day);                                          // int(5)
var_dump($dt->hour);                                         // int(23)
var_dump($dt->minute);                                       // int(26)
var_dump($dt->second);                                       // int(11)
var_dump($dt->micro);                                        // int(123789)
var_dump($dt->dayOfWeek);                                    // int(3)          曜日。数値。0 (日曜)から 6 (土曜)
var_dump($dt->dayOfYear);                                    // int(248)        年間の通算日。数字。(ゼロから開始)
var_dump($dt->weekOfMonth);                                  // int(1)          月間の週番号。
var_dump($dt->weekOfYear);                                   // int(36)         ISO-8601に基づく月曜日に始まる年単位の週番号。
var_dump($dt->daysInMonth);                                  // int(30)         指定した月の日数。28 から 31。
var_dump($dt->timestamp);                                    // int(1346901971)
var_dump(Carbon::createFromDate(1975, 5, 21)->age);          // int(39)
var_dump($dt->quarter);                                      // int(3)          四半期。


小ネタですが、上記プロパティはメンバとして定義されておらず __get() で動的に処理されています。age等は計算が必要だからですね。またIDEでプロパティが補完されたのですがどうやらDocで宣言すれば出来るようです。なるほど〜

- https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Carbon.php#L42

比較




<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

$first = Carbon::create(2012, 9, 5, 23, 26, 11);
$second = Carbon::create(2012, 9, 5, 7, 26, 11, 'America/Vancouver');

echo $first->toDateTimeString();                   // 2012-09-05 23:26:11
echo $first->tzName;                               // Asia/Tokyo
echo $second->toDateTimeString();                  // 2012-09-05 07:26:11
echo $second->tzName;                              // America/Vancouver

var_dump($first->eq($second));                     // bool(true)
var_dump($first->ne($second));                     // bool(false)
var_dump($first->gt($second));                     // bool(false)
var_dump($first->gte($second));                    // bool(true)
var_dump($first->lt($second));                     // bool(false)
var_dump($first->lte($second));                    // bool(true)

$first->setDateTime(2012, 1, 1, 0, 0, 0);
$second->setDateTime(2012, 1, 1, 0, 0, 0);         // Remember tz is 'America/Vancouver'

var_dump($first->eq($second));                     // bool(false)
var_dump($first->ne($second));                     // bool(true)
var_dump($first->gt($second));                     // bool(false)
var_dump($first->gte($second));                    // bool(false)
var_dump($first->lt($second));                     // bool(true)
var_dump($first->lte($second));                    // bool(true)


範囲比較




<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

$first = Carbon::create(2012, 9, 5, 1);
$second = Carbon::create(2012, 9, 5, 5);
var_dump(Carbon::create(2012, 9, 5, 3)->between($first, $second));          // bool(true)
var_dump(Carbon::create(2012, 9, 5, 5)->between($first, $second));          // bool(true)
var_dump(Carbon::create(2012, 9, 5, 5)->between($first, $second, false));   // bool(false)


他には日時の差(diff)やis〜系メソッド(isBirthday()等の面白いメソッドもありました笑)で使えそうな機能もありますが、今回の記事ではこれくらいにしておきます。詳細は公式のドキュメント(http://carbon.nesbot.com/docs)をどうぞ。

・参考



- https://github.com/briannesbitt/Carbon/blob/master/src/Carbon/Carbon.php
- http://php.net/manual/ja/class.datetime.php
- http://php.net/manual/ja/function.date.php
- http://carbon.nesbot.com/docs

Burp Suiteで脆弱性診断

こんにちは。坂本です。

今回は脆弱性診断ツールができるBurp Suiteというツールを紹介します。



このセキュリティ界隈では知らない人はいない有名なツールです。

Burp Suiteは脆弱性診断に必要な機能を揃えています。
・Webサイトへリクエストする情報を改ざんして脆弱性がないか確認する機能
・WebサイトをクローリングしてURLのリストを取得する機能
・取得したURLに対して脆弱性がないか診断をする機能
などなど

この機能を実現するためBurp Suiteはプロキシサーバとなります。
プロキシとなったBurp SuiteはWebサイトとブラウザの間に入りリクエストを傍受し脆弱性診断を行うことができます。



Burp Suiteの購入から起動


Burp Suiteで脆弱性診断をするにはライセンスを購入する必要があります。
まずは公式サイトでBurp Suiteのダウンロードを行います。

http://portswigger.net/burp/

2015年4月3日の価格では1年当たり1人299ドルが必要になります。
高いと思われると思いますがBurp Suite以外の信頼性のあるツールに比べると100倍安かったりします。

Burp Suiteの本体はjarファイルとなっておりMacであればダブルクリックで
Windowsであればbatファイルからの起動となります。

起動するとシンプルが画面が表示されます。



Burp Suiteの設定


購入したライセンスをBurp Suiteに適用させると脆弱性診断の機能を利用することができます。

Burpのプロキシ設定


診断を行う前にプロキシの設定を行う必要があります。
Proxyタブ中のOptionsタブを開きます。


AddボタンをクリックしてBind to portにポート番号を入力します。
ここでは8080を使用します。


プロキシ設定が上手くいけばリストに127.0.0.1:8080が追加されます。
これでBurp SuiteはIPが127.0.0.1で8080ポートを使ったプロキシが立ち上がります。


ブラウザのプロキシ設定


ブラウザの設定にプロキシの設定があるはずなので以下のようにHTTPプロキシと
SSL(HTTPS)プロキシに127.0.0.1と8080ポートを指定します。


これにより以降のブラウザのアクセスはすべてBurp Suiteプロキシを介したアクセスとなります。

脆弱性診断サイト設定


このままでは脆弱性診断ができないので診断するサイトを設定します。

ScannerタブのLive scanningタブを開いてください。


Include in scopeのAddをクリックして診断するサイトを追加します。
ここでは仮にwww.test.comとなっていますが実際には自分のサイトのドメインを入力してください。
間違っても関係のない有名なサイトのドメインを入力してはいけません。


入力がうまくいけばリストに追加されます。


診断開始


以上の設定が終わりましたらブラウザで自分のサイトにアクセスしてみてください。
URLに対して脆弱性診断が開始されます。
1つのURLに対して行われるリクエスト数はGET、POSTパラメータによって変わります。


脆弱性を含むサイトだった場合はScannerタブのResultsタブに結果が表示されます。
以下の例では致命的な脆弱性は出ていませんがXSSSQLインジェクションが検出されると赤字で結果が表示されます。


どのような脆弱性を検出するかはScannerタブのOptionsタブで設定することができます。


さいごに


いかがでしたでしょうか?
脆弱性診断は面倒と思われますがこんなに簡単に検出できます。
逆に言うと少しの知識があればあっという間に脆弱性を悪用されてしまうことを意味しています。

最近はセキュリティを考慮したフレームワークを利用することがスタンダードだと思いますが
思いがけない穴が見つかることもあります。

くれぐれも気をつけてサイトを構築していってください。

Laravel5でシンプルなCRUDアプリを開発する

こんにちは〜たきゃはしです〜最近もホントにとにかくビール最高!って感じです!
今回はついにリリースされたLaravel5(以降はL5と略記する)を早速使ってみました!

◯ この記事の概要



L5の基本機能を扱いつつシンプルなブログアプリを作ってみようと思います。

こんな感じになります

この記事の目的はブログアプリの開発を通してL5でCRUDを作れるようになることです。構成をなるべくシンプルにしたかったこともあり沢山の機能は取り扱ったわけではありませんが「PHPMySQLは分かるけどFWはよくわからないな〜」とか「Laravel5 気になってるんだよな〜」という人には特におすすめですよ!

さっそくインストールからはじめたいと思います。

◯ インストール





	$ composer create-project laravel/laravel l5blog --prefer-dist


l5blog というディレクトリを作成しインストールします
以降に登場するファイルパスはこの l5blog を基準とします。

◯ 初期設定



・ storageのパーミッションの変更





$ chmod -R 777 storage


・ データベースの設定



・ .env


	DB_HOST=localhost
	DB_DATABASE=l5blog
	DB_USERNAME=root
	DB_PASSWORD=secret


データベースのドライバーはデフォルトで mysql です。
他のドライバーを使用したい場合は config/database.php で設定してください。

◯ データベースの作成





	mysql> CREATE DATABASE `l5blog`;


マイグレーション



ここでいうマイグレーションとは、データベースのテーブルやカラムの追加や変更の定義を管理することを指します。

マイグレーションファイルの生成





	$ php artisan make:migration create_articles_table
	Created Migration: 2015_02_17_114821_create_articles_table


・ テーブル定義の作成



・ database/migrations/2015_02_17_114821_create_articles_table.php


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


マイグレーションの実行





	$ php artisan migrate
	Migration table created successfully.
	Migrated: 2014_10_12_000000_create_users_table
	Migrated: 2014_10_12_100000_create_password_resets_table
	Migrated: 2015_02_17_114821_create_articles_table


これでデータベースに articles というテーブルが追加されました。
同時にいくつか追加されたテーブルがありますが今回使用しないため気にする必要はありません。

シーダー(初期データ)



シーダーを生成するコマンドは標準でないので通常通りファイルを作成します。

・ 記事シーダーの作成



・ database/seeds/ArticlesTableSeeder.php


	<?php
	
	use Illuminate\Database\Seeder;
	
	class ArticlesTableSeeder extends Seeder
	{
	
	    public function run()
	    {
	        DB::table('articles')->truncate();
	
	        DB::table('articles')->insert([
	            [
	                'title'      => 'Laozi',
	                'body'       => 'When there is no desire, all things are at peace.',
	                'created_at' => '2015-01-31 23:59:59',
	                'updated_at' => '2015-01-31 23:59:59',
	            ],
	            [
	                'title'      => 'Leonardo da Vinci',
	                'body'       => 'Simplicity is the ultimate sophistication.',
	                'created_at' => '2015-02-01 00:00:00',
	                'updated_at' => '2015-02-01 00:00:00',
	            ],
	            [
	                'title'      => 'Cedric Bledsoe',
	                'body'       => 'Simplicity is the essence of happiness.',
	                'created_at' => '2015-02-01 00:00:01',
	                'updated_at' => '2015-02-01 00:00:01',
	            ],
	        ]);
	
	    }
	
	}


記事シーダーを作成したら下記のコマンドを実行してください。



$ composer dump-autoload
//もしくは
$ php artisan optimize

※ 8egsさん、ご報告ありがとうございます!

・ database/seeds/DatabaseSeeder.php


	<?php
	
	use Illuminate\Database\Seeder;
	use Illuminate\Database\Eloquent\Model;
	
	class DatabaseSeeder extends Seeder
	{
	
	    /**
	     * Run the database seeds.
	     *
	     * @return void
	     */
	    public function run()
	    {
	        Model::unguard();
	
	        $this->call('ArticlesTableSeeder');
	    }
	
	}


シーダーの実行





	$ php artisan db:seed
	Seeded: ArticlesTableSeeder


デフォルトでDatabaseSeederが実行されるので記事データがインサートされます。
2回目以降であれば下記コマンドが便利です。



	$ php artisan migrate:refresh --seed
	Rolled back: 2015_02_17_114821_create_articles_table
	Rolled back: 2014_10_12_100000_create_password_resets_table
	Rolled back: 2014_10_12_000000_create_users_table
	Nothing to rollback.
	Migrated: 2014_10_12_000000_create_users_table
	Migrated: 2014_10_12_100000_create_password_resets_table
	Migrated: 2015_02_17_114821_create_articles_table
	Seeded: ArticlesTableSeeder


いよいよブログアプリの作成に進みます。

◯ 記事モデルの作成



はじめはモデルを作りましょう。



	$ php artisan make:model Article
	Model created successfully.


さっそく生成されたArticleモデルを確認してみます。

・ app/Article.php


	<?php namespace App;
	
	use Illuminate\Database\Eloquent\Model;
	
	class Article extends Model
	{
	
	    //
	
	}


モデルはappディレクトリ直下に生成されます。モデルがアプリの中枢を担うことになりますので妥当かもしれませんね。もちろん新たにModelディレクトリを作成してそこに配置してもLaravelでは何も問題ありません。

ひな形へ肉付けしていきます。

・ app/Article.php


	<?php namespace App;
	
	use Illuminate\Database\Eloquent\Model;
	
	class Article extends Model
	{
	
	    /**
	     * The table associated with the model.
	     *
	     * @var string
	     */
	    protected $table = 'articles';
	
	    /**
	     * The attributes that are mass assignable.
	     *
	     * @var array
	     */
	    protected $fillable = ['title', 'body'];
	
	}


対応するテーブル名や保存が可能なフィールドを定義しました。(テーブル名はDRYに従っていれば実質不要なのですが明示的にするため記載しました。)
もし時間があるならばAricleモデルが継承している Illuminate\Database\Eloquent\Model を確認してみてください。他にどのようなプロパティが設定できるのか知っておくことは大切です。例えば今回定義しなかった「$perPage」はページネーションで1ページに表示する件数を指定することができます。

◯ コントローラー



コントローラーのひな形を生成します。



	$ php artisan make:controller ArticlesController
	Controller created successfully.


生成されたファイルはリソースコントローラーを前提とするアクションがずらっと定義されています。ですが今回は暗黙的コントローラーとして実装していきたいので一旦すべてのメソッドを削除してください。(暗黙的コントローラーについては後ほど簡単に説明します)

・ app/Http/Controllers/ArticlesController.php


	<?php namespace App\Http\Controllers;
	
	use App\Http\Requests;
	use App\Http\Controllers\Controller;
	
	use Illuminate\Http\Request;
	
	class ArticlesController extends Controller
	{
	}


すっきりしたところで 一覧、詳細、作成、編集、削除 のアクションをそれぞれ定義していきます。



	<?php namespace App\Http\Controllers;
	
	use App\Article;
	use App\Http\Requests;
	use App\Http\Controllers\Controller;
	
	use Illuminate\Http\Request;
	
	class ArticlesController extends Controller
	{
	
	   /**
	    * @var Article
	    */
	   protected $article;
	
	   /**
	    * @param Article $article
	    */
	   public function __construct(Article $article)
	   {
	       $this->article = $article;
	   }
	
	   public function getIndex()
	   {
	
	   }
	
	   public function getShow()
	   {
	
	   }
	
	   public function getCreate()
	   {
	
	   }
	
	   public function postCreate()
	   {
	
	   }
	
	   public function getEdit()
	   {
	
	   }
	
	   public function postEdit()
	   {
	
	   }
	
	   public function postDelete()
	   {
	
	   }
	
	}


暗黙的コントローラーでは、URIとHTTPメソッドで対応するアクションが決定します。上記ではgetCreate()とpostCreate()では対応するURIは同じになりますが対応するHTTPメソッドが異なります。またPATCHやDELETEといった他のHTTPメソッドにも対応可能ですが今回は割愛します。

◯ ルーティング



作成したコントローラーをルーティングへ追加します。
'/'へのアクセスは記事一覧、'/articles'は暗黙的コントローラーとして定義します。

・ app/Http/routes.php


	<?php
	
	Route::get('/', 'ArticlesController@getIndex');
	Route::controller('articles', 'ArticlesController');


ここでコントローラーのネームスペースが指定する必要がないのは、 routes.php が App\Providers\RouteServiceProvider で読み込まれているためです。App\Providers\RouteServiceProviderで 'App\Http\Controllers' をネームスペースとするグループのルーティングだと定義されているおかげです。

◯ 記事の一覧



次に一覧の作成に取り掛かります。インサートした記事をすべて表示させましょう。
すべて記事を取得してビューへ渡すアクションを作成します。

・ 記事一覧アクション





<?php ...

    /**
     * 記事の一覧
     *
     * @return \Illuminate\View\View
     */
    public function getIndex()
    {
        $articles = $this->article->all();

        return view('articles.index')->with(compact('articles'));
    }


view()の中身はドット繋ぎでディレクトリ構造を表現できます。with()にはビューで使いたいデータを配列で渡してあげればOKです。

次にビューを作成します。テンプレートエンジンはLaravelデファクトのBladeを使用します。
アプリらしくViewExtends、共通のビューと個別のビューを別々に作成します。

・ 共通のビュー



・ resources/views/app.blade.php


	<!DOCTYPE html>
	<html lang="ja">
	<head>
	    <meta charset="UTF-8">
	    <title>L5Blog</title>
	    <link href="/css/app.css" rel="stylesheet">
	</head>
	<body>
	    <div class="container">
	        <div class="row">
	            <h1>L5Blog</h1>
	            <div class="col-md-12">
	                @yield('content')
	            </div>
	        </div>
	    </div>
	</body>
	</html>


・ 記事一覧のビュー(個別のビュー)



・ resources/views/articles/index.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事一覧</h2>
	    <table class="table table-striped table-hover">
	        <thead>
	        <tr>
	            <th>タイトル</th>
	            <th>本文</th>
	            <th>作成日時</th>
	            <th>更新日時</th>
	        </tr>
	        </thead>
	        <tbody>
	        @foreach($articles as $article)
	            <tr>
                   <td>{{{ $article->title }}}</td>
                   <td>{{{ $article->body }}}</td>
                   <td>{{{ $article->created_at }}}</td>
                   <td>{{{ $article->updated_at }}}</td>
	            </tr>
	        @endforeach
	        </tbody>
	    </table>
	@endsection


ホストのルート'/'へアクセスしてみてください!一覧が表示されるはずです!

◯ 記事の詳細



次は記事の詳細を表示するページを作成していきます。

・ 記事詳細アクション





<?php ...

    /**
     * 記事の詳細
     *
     * @param $id
     * @return \Illuminate\View\View
     */
    public function getShow($id)
    {
        $article = $this->article->find($id);

        return view('articles.show', compact('article'));
    }


データはview()の第二引数から渡す方法もあります。

・ 記事詳細のビュー



・ resources/views/articles/show.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事詳細</h2>
	    <table class="table table-striped">
	        <tbody>
	        <tr>
	            <th>タイトル</th>
	            <td>{{{ $article->title }}}</td>
	        </tr>
	        <tr>
	            <th>本文</th>
	            <td>{{{ $article->body }}}</td>
	        </tr>
	        <tr>
	            <th>作成日時</th>
	            <td>{{{ $article->created_at }}}</td>
	        </tr>
	        <tr>
	            <th>更新日時</th>
	            <td>{{{ $article->updated_at }}}</td>
	        </tr>
	        </tbody>
	    </table>
	@endsection


これで 'articles/show/2' へアクセスしてみてください!暗黙的コントローラーにより id:2 の記事の詳細が表示されます!

記事の一覧に詳細ページへのリンクを追加してみましょう。

・ resources/views/articles/index.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事一覧</h2>
	    <table class="table table-striped table-hover">
	        <thead>
	        <tr>
	            <th>タイトル</th>
	            <th>本文</th>
	            <th>作成日時</th>
	            <th>更新日時</th>
	            <th></th>
	        </tr>
	        </thead>
	        <tbody>
	        @foreach($articles as $article)
	            <tr>
                   <td>{{{ $article->title }}}</td>
                   <td>{{{ $article->body }}}</td>
                   <td>{{{ $article->created_at }}}</td>
                   <td>{{{ $article->updated_at }}}</td>
                   <td><a href="/articles/show/{{{ $article->id }}}" class="btn btn-default btn-xs">詳細</a></td>
	            </tr>
	        @endforeach
	        </tbody>
	    </table>
	@endsection


一覧から詳細リンクをクリックすると詳細ページに遷移されるようになりました。
Laravelが初めての方でもなんとなくイメージは掴めてきたでしょうか?

次は投稿処理です。

◯ 記事の投稿



投稿は一覧や詳細と違いPOSTを受け付けます。

- GET /articles/create の場合は getCreate()
- POST /articles/create の場合は postCreate()

上記を理解していればアクションとフォームで何をしなくてはいけないかハッキリしますね。

・ 記事投稿のアクション





<?php ...

    /**
     * 記事の投稿
     *
     * @return \Illuminate\View\View
     */
    public function getCreate()
    {
        return view('articles.create');
    }

    /**
     * 記事の投稿
     *
     * @param Request $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function postCreate(Request $request)
    {
        $data = $request->all();
        $this->article->fill($data);
        $this->article->save();

        return redirect()->to('articles/index');
    }


L5ではアクションなどでメソッドインジェクションによる依存性の注入が可能です。

・ 記事投稿のビュー



と、ここで問題発生です。

記事投稿用のフォームの作成をしていきますが、ワタクシここでハマりました。。。
L4ではFormクラスとHTMLクラスといういわゆるヘルパークラスがありそれを使おうとしてもなぜか使えずに悩んでいました。IDEファサードが補完されない時点で「オヤ?」と思いサービスプロバイダーを確認したところ見当たらずロードされていないことに気が付きました。どうやらL5からFormクラスとHTMLクラスは統合されなくなったようで別途Composerによるインストールが必要なことが分かりました。

という訳で、あった方が便利なので準備します。

インストール




	$ composer require "laravelcollective/html:5.0.*"


サービスプロバイダーとファサードの追加


・ config/app.php


<?php ...

    'Illuminate\Hashing\HashServiceProvider',
    Collective\Html\HtmlServiceProvider::class,
    'Illuminate\Mail\MailServiceProvider',


ファサードも変更します。

・ config/app.php


<?php ...

    'File'      => 'Illuminate\Support\Facades\File',
    'Form'      => Collective\Html\FormFacade::class,
    'Hash'      => 'Illuminate\Support\Facades\Hash',
    'HTML'      => Collective\Html\HtmlFacade::class,
    'Input'     => 'Illuminate\Support\Facades\Input',


これでHTMLクラスとFormクラスの準備は完了です。
L4からやってる人はハマるだろうなぁと思いました。笑

※ L5.1 リリース頃にまたリポジトリが変わってしまったことの指摘があり修正しました。トイキン君さんありがとうございます。


気を取り直して、記事投稿ビューの作成です。

・ resources/views/articles/create.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事投稿</h2>
	    {!! Form::open() !!}
	        <div class="form-group">
	            <label>タイトル</label>
	            {!! Form::input('text', 'title', null, ['required', 'class' => 'form-control']) !!}
	        </div>
	        <div class="form-group">
	            <label>本文</label>
	            {!! Form::textarea('body', null, ['required', 'class' => 'form-control']) !!}
	        </div>
	        <button type="submit" class="btn btn-default">投稿</button>
	    {!! Form::close() !!}
	@endsection


'/articles/create'へアクセスしてください!フォームが表示され投稿が可能になったはずです。

◯ 記事の編集



編集は既に投稿を作っているため特筆することがありませんが、せっかくなのでソースを見ないで自分で作ってみるのも理解への近道かもしれませんね。

・ 記事編集のアクション





<?php ...

    /**
     * 記事の編集
     *
     * @param $id
     * @return \Illuminate\View\View
     */
    public function getEdit($id)
    {
        $article = $this->article->find($id);

        return view('articles.edit')->withArticle($article);
    }

    /**
     * 記事の編集
     *
     * @param Request $request
     * @param         $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function postEdit(Request $request, $id)
    {
        $article = $this->article->find($id);
        $data = $request->all();
        $article->fill($data);
        $article->save();

        return redirect()->to('articles/index');
    }


with()はマジックメソッドとしても利用可能です。
上記の withArticle($article) は with('article' => $article) と同じです。

・ 記事編集のビュー



・ resources/views/articles/edit.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事編集</h2>
	    {!! Form::open(['action' => ['ArticlesController@postEdit', $article->id]]) !!}
	        <div class="form-group">
	            <label>タイトル</label>
	            {!! Form::input('text', 'title', $article->title, ['required', 'class' => 'form-control']) !!}
	        </div>
	        <div class="form-group">
	            <label>本文</label>
	            {!! Form::textarea('body', $article->body, ['required', 'class' => 'form-control']) !!}
	        </div>
	        <button type="submit" class="btn btn-default">編集</button>
	    {!! Form::close() !!}
	@endsection


編集ページヘの導線を作成しましょう。(先程の投稿もついでに)

・ resources/views/articles/index.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事一覧</h2>
	    <div>
	        <a href="/articles/create" class="btn btn-primary">投稿</a>
	    </div>
	    <table class="table table-striped table-hover">
	        <thead>
	        <tr>
	            <th>タイトル</th>
	            <th>本文</th>
	            <th>作成日時</th>
	            <th>更新日時</th>
	            <th></th>
	        </tr>
	        </thead>
	        <tbody>
	        @foreach($articles as $article)
	            <tr>
	                <td>{{{ $article->title }}}</td>
	                <td>{{{ $article->body }}}</td>
	                <td>{{{ $article->created_at }}}</td>
	                <td>{{{ $article->updated_at }}}</td>
	                <td>
	                    <a href="/articles/show/{{{ $article->id }}}" class="btn btn-default btn-xs">詳細</a>
	                    <a href="/articles/edit/{{{ $article->id }}}" class="btn btn-success btn-xs">編集</a>
	                </td>
	            </tr>
	        @endforeach
	        </tbody>
	    </table>
	@endsection


これで編集も出来ましたね!最後は削除です。

◯ 記事の削除



もしGETで受け付けた場合は'/articles/delete/1'にアクセスした時点で削除されます。それは厳しいと思いますのでPOSTでのみ受け付けることにします。

・ 記事削除のアクション





<?php ...

    /**
     * 記事の削除
     *
     * @param $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function postDelete($id)
    {
        $article = $this->article->find($id);
        $article->delete();

        return redirect()->to('articles/index');
    }


・ 記事削除のビュー



・ resources/views/articles/index.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事一覧</h2>
	    <div>
	        <a href="/articles/create" class="btn btn-primary">投稿</a>
	    </div>
	    <table class="table table-striped table-hover">
	        <thead>
	        <tr>
	            <th>タイトル</th>
	            <th>本文</th>
	            <th>作成日時</th>
	            <th>更新日時</th>
	            <th></th>
	        </tr>
	        </thead>
	        <tbody>
	        @foreach($articles as $article)
	            <tr>
	                <td>{{{ $article->title }}}</td>
	                <td>{{{ $article->body }}}</td>
	                <td>{{{ $article->created_at }}}</td>
	                <td>{{{ $article->updated_at }}}</td>
	                <td>
	                    <a href="/articles/show/{{{ $article->id }}}" class="btn btn-default btn-xs">詳細</a>
	                    <a href="/articles/edit/{{{ $article->id }}}" class="btn btn-success btn-xs">編集</a>
	                    {!! Form::open(['action' => ['ArticlesController@postDelete', $article->id]]) !!}
	                    <button type="submit" class="btn btn-danger btn-xs">削除</button>
	                    {!! Form::close() !!}
	                </td>
	            </tr>
	        @endforeach
	        </tbody>
	    </table>
	@endsection


・ resources/views/articles/show.blade.php


	@extends('app')
	
	@section('content')
	    <h2 class="page-header">記事詳細</h2>
	    <ul class="list-inline">
	        <li>
	            <a href="/articles/edit/{{{ $article->id }}}" class="btn btn-primary pull-left">編集</a>
	        </li>
	        <li>
	            {!! Form::open(['action' => ['ArticlesController@postDelete', $article->id]]) !!}
	            <button type="submit" class="btn btn-danger pull-left">削除</button>
	            {!! Form::close() !!}
	        </li>
	    </ul>
	    <table class="table table-striped">
	        <tbody>
	        <tr>
	            <th>タイトル</th>
	            <td>{{{ $article->title }}}</td>
	        </tr>
	        <tr>
	            <th>本文</th>
	            <td>{{{ $article->body }}}</td>
	        </tr>
	        <tr>
	            <th>作成日時</th>
	            <td>{{{ $article->created_at }}}</td>
	        </tr>
	        <tr>
	            <th>更新日時</th>
	            <td>{{{ $article->updated_at }}}</td>
	        </tr>
	        </tbody>
	    </table>
	@endsection


削除ボタンをクリックすればデータが削除されるようになりましたでしょうか?
確認ボックスもなしにイキナリ削除なんて驚いたと思いますが、これにてブログアプリは完成です!お疲れさまでした〜!

◯ まとめ



L5でブログアプリを開発してみましたがどうでしょうか?正直シンプルすぎる構成ゆえL5の魅力をお伝え出来たとは思っていません(自分も全然満足していません)。個人的に注目しているのは新しくなったバリデーション、扱いやすくなったサービスプロバイダー、モデルとルーティングのバインド、メソッドインジェクションによる依存性の注入、他にも様々な機能が盛りだくさんで正直一記事で説明するのは無理があると思いました。なので、それらも今後発信していけたらと思います。

記事のソースでエラーが出たとかあればコメントください!それでは〜!(ソースは欲しい方いれば公開します。)

安全なPHPを使っていますか? - PHPバージョンアップとの付き合い方を考える

こんにちは。宇都宮です。

昨年末、著名なPHP開発者・Anthony Ferrara(ircmaxell)氏のブログに、PHP Install Statisticsという記事が掲載されました。この記事では、W3Techsの統計を元に、現在Web上に公開されているPHP製サイトのバージョン情報を調べ、いかに多くのPHP製サイトが、脆弱でサポートの切れたバージョンを使用しているか、解説しています。

この記事によると、PHP製サイトのおよそ7割強は、脆弱性があるか、又は既にサポートが切れているバージョンのPHPを使用している、としています。記事の冒頭には、他の処理系やアプリケーションとの比較がありますが、PerlPythonでは安全なバージョンの使用率が8割前後なのに対して、PHPの安全なバージョンの使用率は25%と、非常に悪い数字になっています。

どのバージョンが安全か



では、安全なバージョンのPHPとは、どういったバージョンのことを指すのでしょう?

1つは、「PHP開発チームによるサポートが続いているバージョンの、最新安定版」です。2015年1月8現在、5.4.36、5.5.20、5.6.4が、これに該当します。

もう1つは、「LInuxディストリビューション等が独自にサポートしているバージョン」です。これについても、PHP Install Statisticsの中に表が掲載されています。注目すべきは、PHP 5.3.3や5.1.6等、5.4未満のバージョンの中にも、安全なバージョンがあることです。これらはすでにPHP開発チームによるサポートが終了したバージョンですが、Linuxディストリビューションの開発チームによる独自サポートが続いています。

(したがって、「5.3.3なら安全」というわけではなく、「CentOS 6で適切にアップデートしている5.3.3なら安全」です)

サポートとは、具体的には、セキュリティパッチのバックポート等です。PHP脆弱性が見つかり、その脆弱性CentOSの公式リポジトリPHPにも影響すると判明した場合は、その脆弱性を修正するパッチの当たったバージョンのPHPが、パッケージマネージャでインストールできるようになります。

CentOSはサポート期間の長さ(リリースから10年間)で有名で、たとえばCentOS 6は2020年11月末までサポートが続きます。このサポート対象には、CentOSのパッケージマネージャ(yum)でインストール可能なパッケージも含まれます。

CentOSを別格としても、UbuntuのLTS(10.04、12.04、14.04等。サポート期間5年)など長期に渡ってサポートを提供しているOSでは、PHP開発チームによるサポートが終了したバージョンのPHPのサポートを提供していることがあります。

安全なPHPを使うには



このような事情を踏まえると、安全なバージョンのPHPを使うための方法は、以下のとおりです。

(1) OSの公式リポジトリを使用する場合は、パッケージマネージャでアップデートを行う
(2) remi等の外部リポジトリを使用する場合は、そのリポジトリの最新版を保つ
(3) ソースからビルドを行った場合は、適切にパッチを当てる

上記選択肢のうち、メジャーバージョンアップの回数が最小になるのは、「サポート期間の長いOS」+「OS公式リポジトリ」の組み合わせです。CentOSなら、メジャーバージョンアップは10年に1回で済みます(ただし、10年越しのバージョンアップは容易な作業ではないでしょう)。

最新安定版の導入の容易さでは外部リポジトリが優れています。ただし、PHPのメジャーバージョンのサポート期間は3年程度です。外部リポジトリのサポートポリシー次第ですが、3年に1度はメジャーバージョンアップが必要になる、と考えておいたほうがいいでしょう。

ソースからビルドを行う場合というのは、OSの公式リポジトリや外部リポジトリではインストールできないバージョン(公式リポジトリよりも古いバージョン)を使用する場合や、PHP本体のソースコードに手を入れている場合などであると推測されます。このような場合でも、独自にセキュリティパッチを当てていくことで安全性を保つことは可能です。

いずれの方法においても重要なのは、マイナーバージョンアップに追従していくことです。PHPのリリース情報は、php.netなどで入手できます。

「壊れていないものを直すな」



ここまで読まれてきて、本番稼働しているPHPのバージョンアップをするのはいかがなものか、とお考えの方もいらっしゃるかもしれません。「バージョンアップして動かなくなったらどうするんだ」と。

しかし、PHPのマイナーバージョンアップは原則として下方互換性を崩さない形で実施されるため、バグや脆弱性に依存したコードを書いていない限り、PHPのマイナーバージョンアップによってアプリケーションに不具合が生じることは通常ありません(※)。

※5.2.7と5.3.7で、重大なバグを抱えたバージョンがリリースされた経緯が過去にあるため、絶対安全とは言い切れないのが辛いところです。

PHPのマイナーバージョンアップを行うかは、最終的には保守工数やセキュリティ要件との相談になります。

むすび



昨年末の徳丸浩氏の記事:『例えば、PHPを避ける』以降PHPはどれだけ安全になったかには、PHPのセキュリティ改善の歩みがまとめられています。PHPの処理系としての安全性は向上し、安全なプログラムの書き方も確立されてきました。

しかし、上記記事の末尾で徳丸氏も指摘しているように、安全なバージョンを使用する、という点では、PHPには大きな課題があります。

レバレジーズさん主催のヒカ☆ラボPHPイベントで「今、最もイケてるPHPフレームワークLaravel4」の発表をしました。

アシアルの坂本です。
最近は生ハムの原木を買ってしまい塩分過多な生ハム生活をエンジョイしています。

さて、昨日の12月10日にレバレジーズさん主催のヒカ☆ラボPHPイベントに登壇しました。
Laravel4がなぜ熱いか、その根拠と実際の業務で使うベストプラクティス、そしてLaravelの今後についての発表を行いました。

下が実際の発表資料です。



発表後に懇親会で質問を受けましたがLaravel4の導入について不安な方が多くいました。HHVMを使うべきかPhalconに移行すべきか、はたまたパフォーマンスが劇的に改善されるという噂のPHP7を待つべきか、色々な意見がありました。

私としてはLarael4はPHPフレームワークの中で優秀なものであり、かつ使われている技術や考え方は今後、どのフレームワークを使う場合でも必要になるものであると考えています。
実業務でのフレームワーク選定は本当に覚悟のいるものであると思いますがLaravel4を前向きに検討して頂けたらと思います。

また、徐々に全貌が見えてきたLaravel5もその内リリースされると思います。
その時はまた別のイベントで発表させて頂けるかもしれません。

その時はよろしくお願いします。

「最新SPA開発を学ぼう!ウェブエンジニアのためのAngularJS入門」という題でPHPカンファレンス2014で登壇してきました



10月11日に開催されたPHPカンファレンス2014にて、「最新SPA開発を学ぼう!ウェブエンジニアのためのAngularJS入門」という題で登壇してきました。

PHPのイベントですが、HTML5関連の話題もOKということでSPA開発とAngularJSについて話してきました。

SPA(Single Page Application)は、近年開発が増えているウェブアプリケーションの一形態ですが、このお話の中では、それが近年増えてきた理由についてまず説明しました。SPA開発ではAngularJSフレームワークがよく利用されているのですが、このフレームワークはSPA開発に適した機能セットを持っています。お話の後半では、AngularJSの紹介と、AngularJSが持つルーティング、データバインディング、DIコンテナ、ディレクティブなどの機能についてひと通り紹介しました。

PHPのイベントでAngularJSのこと話すのは正直どうなのかと少し心配していましたが、会場の入りはほぼ満席のようでした。話した後も、わかりやすくて良かった、面白かったという感想を頂けたので良かったです。

カンファレンスが終わった後の懇親会にも参加し、色んな方とお話することが出来て楽しかったです。運営に携わったスタッフの皆さん、お疲れ様でした。