読者です 読者をやめる 読者になる 読者になる

Homestead をリロードしようとしたらエラーになる

What?

  • カフェに行ってPCを開いて、起動しっぱなしのHomesteadサーバにアクセスしたらエラー
  • vagrantからリロードしても以下のエラーが出て立ち上がらない
$ vagrant reload
==> homestead-7: Attempting graceful shutdown of VM...
==> homestead-7: Checking if box 'laravel/homestead' is up to date...
==> homestead-7: Clearing any previously set forwarded ports...
==> homestead-7: Clearing any previously set network interfaces...
The specified host network collides with a non-hostonly network!
This will cause your specified IP to be inaccessible. Please change
the IP or name of your host only network so that it no longer matches that of
a bridged or non-hostonly network.

Why?

  • 英語を読もう(雑訳)

    • ホストオンリーではないネットワークで、特定のホストネットワークが衝突してます
    • これは、あなたが設定したIPにアクセス出来ないということです
    • そのホストはブリッジされているもしくはホストオンリーではないネットワークにおいて、もうマッチしていないので、あなたのホストオンリーネットワークのIPか名前を変えてください。
  • つまりは、外で作業を初めて、今のIP(homesteadのデフォルトIP: 192.168.10.10)が他の人と被ったから

Solution

  • .homestead/Homestead.yaml
ip: "192.168.10.10"

ip: "192.168.10.11"

とかに変更する。

  • /etc/hosts も変更することを忘れずに

Laravel 5.4: Model, Controller, Viewワークフロー

Why?

  • Articleの一覧、詳細を作ることで、一通りのModel, Controller, Viewワークフローを流す

How to

Articleの一覧を作る

  • app/Http/routes.phpにRoutingを追加
Route::get('articles', 'ArticlesController@index');
  • Controllerを追加 (laravel 5.2から–plainオプションはデフォルトになったので不要)
    • app/Http/Controllers/ArticlesController.phpが追加される
$ php artisan make:controller ArticlesController
  • app/Http/Controllers/ArticlesController.phpにindexメソッドを追加 (まずはviewに渡さずにプレーンに表示)
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticlesController extends Controller
{
    public function index() {
        $articles = Article::all();
        return $articles;
    }
}
  • プレーンなレスポンスが帰ってくる
[
    {
        id: 1
        title: "My Update First Article"
        body: "Lorem ipsum"
        ...
    }
]
  • app/Http/Controllers/ArticlesController.phpViewからViewに渡す
    public function index() {
        $articles = Article::all();
        return view('articles.index', compact('articles'));
    }
  • View resources/views/articles/index.blade.php を追加
@extends('app')

@section('content')
  <h1>Articles</h1>
  <hr />
  @foreach ($articles as $article)
    <article>
      <h2>{{ $article->title }}</h2>
      <div class="body">{{ $article->body }}</div>
    </article>
  @endforeach
@stop

Articleの詳細を作る

  • app/Http/routes.phpにRoutingを追加
Route::get('articles/{id}', 'ArticlesController@show');
  • app/Http/Controllers/ArticlesController.phpにshowメソッドを追加
    public function show($id) {
        $article = Article::findOrFail($id);
        return view('articles.show', compact('article'));
    }
  • View resources/views/articles/show.blade.php を追加
@extends('app')

@section('content')
  <h1>{{ $article->title }}</h1>
  <article>
    <div class="body">{{ $article->body }}</div>
  </article>
@stop

一覧と詳細ページを繋げる

  • resources/views/articles/show.blade.phpにリンク追加
@extends('app')

@section('content')
  <h1>{{ $article->title }}</h1>
  <article>
    <div class="body">{{ $article->body }}</div>
  </article>
  <a href="/articles/">&lt; Back to list</a>
@stop
  • resources/views/articles/index.blade.phpにリンク追加
@extends('app')

@section('content')
  <h1>Articles</h1>
  <hr />
  @foreach ($articles as $article)
    <article>
      <h2><a href="/articles/{{ $article->id }}">{{ $article->title }}</h2></a>
      <div class="body">{{ $article->body }}</div>
    </article>
  @endforeach
@stop

参考

Laravel 5.4: Eloquent ORMを使ってみる

What?

  • Eloquent ORM: Laravel5にデフォルトで組み込まれているORマッパー
  • RailsのActive Recodeみたいに使える

How to

DBにモデルを保存

  • モデルを作る -> app/Article.php ができている
$ php artisan make:model Article
$ cat app/Article.php
<?php namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model {
 
    //
 
}
  • モデルができているかtinkerで確認
$ php artisan tinker

>>> $article = new App\Article;                                                                      => App\Article {#668}
  • モデルを使う (オブジェクトを作って、値を入れる)
>>> $article = new App\Article;
=> App\Article {#673}
>>> $article->title = 'My First Article';
=> "My First Article"
>>> $article->body = 'Lorem ipsum';
=> "Lorem ipsum"
>>> $article->published_at = Carbon\Carbon::now();
=> Carbon\Carbon {#682
     +"date": "2017-03-04 11:53:09.134952",
     +"timezone_type": 3,
     +"timezone": "UTC",
   }
>>> $article;
=> App\Article {#673
     title: "My First Article",
     body: "Lorem ipsum",
     published_at: Carbon\Carbon {#682
       +"date": "2017-03-04 11:53:09.134952",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
   }
  • 作ったオブジェクトをセーブする
>>> App\Article::all()->toArray();
=> []
>>> $article->save();
=> true
>>> App\Article::all()->toArray();
=> [
     [
       "id" => 1,
       "title" => "My First Article",
       "body" => "Lorem ipsum",
       "created_at" => "2017-03-04 11:53:44",
       "updated_at" => "2017-03-04 11:53:44",
       "published_at" => "2017-03-04 11:53:09",
     ],
   ]
$ mysql --user=homestead --password=secret homestead
mysql> select * from articles\G;
*************************** 1. row ***************************
          id: 1
       title: My First Article
        body: Lorem ipsum
  created_at: 2017-03-04 11:53:44
  updated_at: 2017-03-04 11:53:44
published_at: 2017-03-04 11:53:09
1 row in set (0.00 sec)

DBからモデルを読み込みアップデートしてみる

  • tinker でDBから情報を1行読み込んで、更新後、セーブする
>>> $article = App\Article::all()->first();
=> App\Article {#689
     id: 1,
     title: "My First Article",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 11:53:44",
     published_at: "2017-03-04 11:53:09",
   }
>>> $article->title = 'My First Article Updated';
=> "My First Article Updated"
>>> $article->save();                                                                                => true
  • DBを確認
mysql> select * from articles\G;
*************************** 1. row ***************************
          id: 1
       title: My First Article Updated
        body: Lorem ipsum
  created_at: 2017-03-04 11:53:44
  updated_at: 2017-03-04 12:09:32
published_at: 2017-03-04 11:53:09
1 row in set (0.00 sec)

色々な方法でデータを検索する

>>> App\Article::find(1);
=> App\Article {#684
     id: 1,
     title: "My First Article Updated",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 12:09:32",
     published_at: "2017-03-04 11:53:09",
   }
>>> App\Article::where('body', 'Lorem ipsum')->get();
=> Illuminate\Database\Eloquent\Collection {#656
     all: [
       App\Article {#692
         id: 1,
         title: "My First Article Updated",
         body: "Lorem ipsum",
         created_at: "2017-03-04 11:53:44",
         updated_at: "2017-03-04 12:09:32",
         published_at: "2017-03-04 11:53:09",
       },
     ],
   }
>>> App\Article::where('body', 'Lorem ipsum')->first();
=> App\Article {#693
     id: 1,
     title: "My First Article Updated",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 12:09:32",
     published_at: "2017-03-04 11:53:09",
   }

createメソッドを使ってみる

  • createメソッドはまとめて値を入力できるので便利

    • ただし、ユーザの突っ込んできた値の全てを受け入れると危険
    • $fillableで、createでアサインできる値のホワイトリストを指定できる
    • $guardedで、createでアサインできる値のブラックリストを指定できる
  • App\Article::create()でまとめて作ろうとするとMassAssignmentExceptionエラーが出る

>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
Illuminate\Database\Eloquent\MassAssignmentException with message 'title'
  • app/Article.phpモデルを修正する
<?php namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model {
 
    protected $fillable = [ // 
        'title',
        'body',
        'published_at'
    ];
 
}
  • ん?まだ、MassAssignmentExceptionエラーが出る
>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
Illuminate\Database\Eloquent\MassAssignmentException with message 'title'
  • tinkerを立ち上げなおしてリトライ。できた。
>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
=> App\Article {#670
     title: "New Article",
     body: "Lorem ipsum",
     published_at: Carbon\Carbon {#661
       +"date": "2017-03-04 12:17:28.029855",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
     updated_at: "2017-03-04 12:17:28",
     created_at: "2017-03-04 12:17:28",
     id: 2,
   }

参考

php artisan tinker の使い方

Why?

  • Laravel チュートリアルやってて、突然出現した
  • pryっぽいけど、なにができるのか疑問

What?

  • https://github.com/laravel/tinker
  • REPL (Read-Eval-Print Loop) 。対話的シェル。
    • Ruby の pry と似たもの
    • 初めての言語を試しに(とりま、echoしたいとか)触りたいときとかに便利
    • 対話的にデバッグするときに便利
  • コンソールにどういう状況のものが表示されるのか?
    • 再起動するまで変数値などは保存される。再起動したら、変数とかがリセットされる。
    • モデルとかを使うときは、立ち上げ時のコードの状態が反映されているので、コードをいじったらコード反映のために再起動する必要がある
    • デバッグのときは、ブレークポイントの状態のものが表示される。
  • ラッパー

How to use?

  • プロジェクトディレクトリ内で $ php artisan tinker すると立ち上がる。
$ php artisan tinker
Psy Shell v0.8.1 (PHP 7.1.1-1+deb.sury.org~xenial+1 — cli) by Justin Hileman

New version is available (current: v0.8.1, latest: v0.8.2)
>>>
  • phpコマンドを逐次実行できる (hogeは標準出力、=> nullは返り値)
>>> echo "hoge";
hoge⏎
=> null
  • tinderコマンドは色々ある
>>> help
  help             Show a list of commands. Type `help [foo]` for information about [foo].      Aliases: ?
  ls               List local, instance or class variables, methods and constants.              Aliases: list, dir
  dump             Dump an object or primitive.
  doc              Read the documentation for an object, class, constant, method or property.   Aliases: rtfm, man
  show             Show the code for an object, class, constant, method or property.
  wtf              Show the backtrace of the most recent exception.                             Aliases: last-exception, wtf?
  whereami         Show where you are in the code.
  throw-up         Throw an exception out of the Psy Shell.
  trace            Show the current call stack.
  buffer           Show (or clear) the contents of the code input buffer.                       Aliases: buf
  clear            Clear the Psy Shell screen.
  history          Show the Psy Shell history.                                                  Aliases: hist
  exit             End the current session and return to caller.                                Aliases: quit, q
  clear-compiled   Remove the compiled class file
  down             Put the application into maintenance mode
  env              Display the current framework environment
  migrate          Run the database migrations
  optimize         Optimize the framework for better performance
  up               Bring the application out of maintenance mode
  inspire          Display an inspiring quote
>>> ls -gl

Global Variables:
  $app                         Illuminate\Foundation\Application {#3 …15}
  $argc                        2
  $argv                        [ …2]
  $GLOBALS                     [ …13]
  $input                       Symfony\Component\Console\Input\ArgvInput {#20}
  $kernel                      App\Console\Kernel {#22}
  $_COOKIE                     []
  $_FILES                      []
  $_GET                        []
  $_POST                       []
  $_REQUEST                    []
  $_SERVER                     [ …35]
  $__composer_autoload_files   [ …8]

参照

Laravel 5.4: DBマイグレーション

What?

Why?

  • そもそもなぜDBマイグレーションが必要なのか?
    • MySQL Workbenchとかを使う
      • 問題:
        • .mwbファイルとかSQLファイルとかの管理対象が増える
        • DBのバージョンとソースコードのバージョンがずれたりして死ぬことがある(死んだことがある)

Migration

  • 保存場所
    • database/migrations/
$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
$ mysql --user=homestead --password=secret homestead
mysql> show tables;
+---------------------+
| Tables_in_homestead |
+---------------------+
| migrations          |
| password_resets     |
| users               |
+---------------------+
3 rows in set (0.00 sec)

mysql> desc migrations;
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| migration | varchar(255)     | NO   |     | NULL    |                |
| batch     | int(11)          | NO   |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> desc users;
+----------------+------------------+------+-----+---------+----------------+
| Field          | Type             | Null | Key | Default | Extra          |
+----------------+------------------+------+-----+---------+----------------+
| id             | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name           | varchar(255)     | NO   |     | NULL    |                |
| email          | varchar(255)     | NO   | UNI | NULL    |                |
| password       | varchar(255)     | NO   |     | NULL    |                |
| remember_token | varchar(100)     | YES  |     | NULL    |                |
| created_at     | timestamp        | YES  |     | NULL    |                |
| updated_at     | timestamp        | YES  |     | NULL    |                |
+----------------+------------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

mysql> desc password_resets;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| email      | varchar(255) | NO   | MUL | NULL    |       |
| token      | varchar(255) | NO   | MUL | NULL    |       |
| created_at | timestamp    | YES  |     | NULL    |       |
+------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

Rollback

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

Make migration

$ php artisan make:migration create_articles_table --create="articles"
Created Migration: 2017_02_14_184315_create_articles_table
$ less database/migrations/2017_02_14_184315_create_articles_table.php
<?php

use Illuminate\Support\Facades\Schema;
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();
            $table->timestamp('published_at')->useCurrent();
            // ここまで追加
        });
    }

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

Edit migration

php artisan migrate
mysql> desc articles;
+--------------+------------------+------+-----+-------------------+----------------+
| Field        | Type             | Null | Key | Default           | Extra          |
+--------------+------------------+------+-----+-------------------+----------------+
| id           | int(10) unsigned | NO   | PRI | NULL              | auto_increment |
| title        | varchar(255)     | NO   |     | NULL              |                |
| body         | text             | NO   |     | NULL              |                |
| created_at   | timestamp        | YES  |     | NULL              |                |
| updated_at   | timestamp        | YES  |     | NULL              |                |
| published_at | timestamp        | NO   |     | CURRENT_TIMESTAMP |                |
| exceprt      | text             | NO   |     | NULL              |                |
+--------------+------------------+------+-----+-------------------+----------------+
7 rows in set (0.00 sec)

参考

http://site.oganity.pw/232/

Laravel 5.4: DBマイグレーションでエラーになる

What?

このバージョンで

laravel vagrant master %$ php artisan --version
Laravel Framework 5.4.10
laravel vagrant master %$ mysql --version
mysql  Ver 14.14 Distrib 5.7.17, for Linux (x86_64) using  EditLine wrapper
laravel vagrant master %$ php -v
PHP 7.1.1-1+deb.sury.org~xenial+1 (cli) (built: Jan 20 2017 09:20:20) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.1-1+deb.sury.org~xenial+1, Copyright (c) 1999-2017, by Zend Technologies
    with blackfire v1.14.3~linux-x64-non_zts71, https://blackfire.io, by Blackfireio Inc.

この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();
            $table->timestamp('published_at');
            // ここまで追加
        });
    }

migrateすると、エラーになる

$ php artisan migrate


  [Illuminate\Database\QueryException]
  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'published_at' (SQL: create table `articles` (`id
  ` int unsigned not null auto_increment primary key, `title` varchar(255) not null, `body` text not null, `created_at` timestamp nul
  l, `updated_at` timestamp null, `published_at` timestamp not null) default character set utf8mb4 collate utf8mb4_unicode_ci)



  [PDOException]
  SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'published_at'

Solution

この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();
            $table->timestamp('published_at')->useCurrent(); // ここ追加
            // ここまで追加
        });
    }

こうする

$ php artisan migrate:refresh --seed
Rolled back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2017_02_14_184315_create_articles_table
Migrated: 2017_02_23_172823_add_excerpt_to_articles_table

Why?

  • マイグレーションを全部ロールバックし、再度実行する力技コマンド php artisan migrate:refresh --seed を使って、強制帳消し
  • published_atnot nullなのに値が入ってなかったので、useCurrent()を使って突っ込んだ

失敗例

published_atだけ触ればいいのに、その前のtimestampも色々いじってしまった結果。

laravel vagrant master %$ php artisan migrate


  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Call to a member function useCurrent() on null


laravel vagrant master *%$ php artisan migrate


  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Call to a member function default() on null


laravel vagrant master *%$ php artisan migrate


  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Call to a member function nullable() on null

config/database.phpの確認したけど、ちゃんとstrictになってた

'strict' => true,

参考

https://github.com/laravel/framework/issues/3602

Laravel 5.4: メンテナンスモードの使い方、仕組み

メンテナンスモードの使い方

  • 開始
$ php artisan down
  • 復帰
    • Be right back.と表示される
php artisan up

メンテナンスモードの仕組み

  • storage/framework/down が存在していたらメンテナンスモードになっている
$ ls -la storage/framework/down
ls: cannot access 'storage/framework/down': No such file or directory

$ php artisan down
Application is now in maintenance mode.
$ ls -la storage/framework/down
-rw-r--r-- 1 vagrant vagrant 66 Feb 14 03:44 storage/framework/down

$ php artisan up
Application is now live.
$ ls -la storage/framework/down
ls: cannot access 'storage/framework/down': No such file or directory
  • メンテナンスページのテンプレート
    • vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/503.blade.php にある

TODO

  • 部分的にメンテナンスモードにできる?
  • あるIPだけ限定でアクセスさせることができる?

参照

Laravel 5 メンテナンスモード IP制限