【PHP】Laravelの使い方メモ

はじめに

最近 Laravel を使うことが増えました。
だんだん使い方が分かってきたのですが、おそらく数年後には忘れてしまっていると思います。。
その時のために使い方を簡単にメモしておきます

目次

テンプレート

表示するビューの外側を共通化

表示するビューの外側を共通化したい場合です
例えば<head>タグとかですね。
正確には共通化というより継承ですね

  • 外側のコード

ファイル名の例:resources/views/layout/base.blade.php
基本的にはそのままコードを記載し、下のコードを呼び出す部分に@yield('content')を書く

  • 表示するコード
1
2
3
4
5
6
7
// 外側のコードのファイル名のviews以下のパスとファイル名を記載する
@extends('layout.base')

// 外側のコードで`@yield('content')`と記載した部分に以下が表示される
@section('content')
aaa
@endsection

表示するビューの内側(一部分)を共通化

表示するビューの内側(一部分)を共通化したい場合です
例えばフォーム部品とかですね。
正確には共通化というよりコンポーネント化ですね

  • コンポーネント化するコード

そのままコードを記載するだけ

  • 上でコンポーネント化したコードを呼び出す
1
2
3
// ファイル名が`resources/views/hoge/test.blade.php`の場合
@component('hoge.test', ['user' => $user])
@endcomponent

ソートしたい時

以下のライブラリを利用する
https://github.com/Kyslik/column-sortable

デフォルトのソート順を変更したい時は以下ファイルを変更する

config/columnsortable.php

1
2
3
4
5
- 'default_direction'             => 'asc',
+ 'default_direction' => 'desc',

- 'default_direction_unsorted' => 'asc',
+ 'default_direction_unsorted' => 'desc',

モデル

テーブル 作成(マイグレーション)

Laravel はマイグレーション機能があります
つまりコードから DB を自動作成できます

参考
https://readouble.com/laravel/5.5/ja/migrations.html

1
2
3
4
5
6
7
# マイグレーションファイル作成
# create_users_tableの部分はなんでも良いけど一意でないとエラーになる
php artisan make:migration create_users_table
# マイグレーション実行(DB反映)
php artisan migrate
# ロールバック(上のマイグレーション単位)
php artisan migrate:rollback

データベース から Model ファイルを作成したい時

1
php artisan code:models --table=users

初回は設定が必要
以下の手順に従う

Laravel + Eloquent で reliese/laravel を用いてモデルクラスを Scaffolding する

INSERT・UPDATE

1
2
3
4
5
6
7
8
if (User::where("hoge_id", $hoge["id"])->exists()) {
// UPDATE
$user = User::where("hoge_id", $hoge["id"])->first();
$user->fill($request->except(['_token']))->save();
} else {
// INSERT
User::create($request->except(['_token']));
}

複雑な WHERE 条件

1
2
3
4
5
6
$hoge = 1;
$users = User::where("status", 'new')
->where(function ($query) use ($hoge) {
$query->where('user_id', '=', $hoge)->orwhere('user_id2', '=', $hoge);
})
->get();

コントローラー

URL とコントローラーの紐付けを設定したい時

参考
https://readouble.com/laravel/5.5/ja/controllers.html

routes/web.phpを以下のように記載する

1
2
3
4
// GET
Route::get('/user/edit', 'User\HomeController@edit');
// POST
Route::post('/user/edit', 'User\HomeController@postEdit');

ログを出力したい時

  • ログ出力
1
\Log::info('test');
  • ログ確認
1
tail -f storage/logs/laravel.log

その他

  • PHP で配列はキーが数値の連想配列

ページネーションを利用したいとき

  • コントローラ
1
$users = DB::table('users')->paginate(15);
  • ビュー
1
2
3
4
5
6
7
<div class="container">
@foreach ($users as $user)
{{ $user->name }}
@endforeach
</div>

{{ $users->links() }}

参考
https://laravel.com/docs/7.x/pagination

全文検索したい時

基本的には以下の記事の通りにすれば OK
Laravel + MySQL5.7 で日本語全文検索をする方法とちょっとした注意点

上の記事はテーブル生成と同時にインデックスを作成してますが、後からインデックスを貼りたい場合は以下のようにすれば可能です。
対象の複数カラムを結合した新しいカラム(fulltext_column)を作成してます。

1
2
DB::statement("ALTER TABLE videos ADD fulltext_column TEXT AS (CONCAT(video_name, ' ', overview, ' ', product_name)) STORED");
DB::statement('ALTER TABLE videos ADD FULLTEXT index fulltext_index (`fulltext_column`) with parser ngram');

ページネーションを利用している場合

記事にあるようにクラスのスコープを利用すると取得データが 0 件になる不具合があるので以下のように記載すると良いです

1
2
- $videos = Video::freeword($request->search_word)->sortable()->paginate(2);
+ $videos = Video::whereRaw("match(`fulltext_column`) against (? IN NATURAL LANGUAGE MODE)", [$request->search_word])->sortable()->paginate(2);

ページネーション部分には以下のようにwithQueryString()を付けると2ページ目以降に検索ワードが引き継がれます

1
{!! $videos->withQueryString()->appends(\Request::except('page'))->render() !!}

変更が画面反映されない時

おそらくキャッシュが残ってしまっているのでキャッシュを削除する

1
2
3
4
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

コメントアウトしてるのにエラーになる

コメントアウトしている箇所も読み込まれます
ので内容が不正であればエラーになります

join したカラムが利用できない

join するテーブルに同じカラム名がある場合は以下のように select を使って別名にする必要があります。

1
2
3
$user = User::leftJoin("ranks", "ranks.id", "=", "users.rank_id")
->select("ranks.name as rank_name", "users.*")
->first();

link タグが読み込まれない

パスの最初に/がないとトップページ以外の画面で読み込まれませんでした

1
2
3
4
<!-- 修正前 -->
<link rel="stylesheet" type="text/css" href="fontawesome/css/all.css" />
<!-- 修正後 -->
<link rel="stylesheet" type="text/css" href="/fontawesome/css/all.css" />

Class ‘App\Http\Controllers\Hoge’ not found

コントローラーファイルの位置に合わせて namespace も変更する必要がある
修正箇所はHoge.phpの以下の行

1
namespace App\Http\Controllers\Hoge;

ログイン機能

以下の記事に従う
Laravel のログイン認証の基本(Authentication)を完全理解する

Laravel のバージョンによってはphp artisan make:authがエラーになる
その場合は以下で同じコマンドになる

1
2
3
4
5
composer require laravel/ui --dev
php artisan ui vue --auth
php artisan clear-compiled
php artisan optimize
php artisan view:clear

画像をアップロードする

blade

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<div>
<form method="POST" enctype="multipart/form-data">
@csrf

<div class="row file_upload">
<div class="col-2 col-form-label text-center"><label>関連ファイル</label></div>

<div class="col-10">
<!-- ファイルアップロード -->
<?php
foreach (range(1, 5) as $value) {
echo '<div class="custom-file mb-3">';
echo '<input type="file" class="custom-file-input" name="file' . $value . '" id="file' . $value . '">';
echo '<label class="custom-file-label" for="file' . $value . '" id="file' . $value . '_label">選択されていません</label>';
echo '</div>';
echo '<img id="file' . $value . '_preview" class="preview">';
}
?>
</div>
</div>

<div class="form-group text-center mt-5">
<input type="submit" name="submit" class="btn btn-primary" value="送信">
</div>
</form>
</div>

<script type="text/javascript">
// プレビュー表示
function show_preview(id) {
if ($(id).val() == '') return
const file = $(id).prop('files')[0];
$(id + '_label').text(file.name)
$(id + '_preview').attr('src', null);
if (file.type.slice(0, 5) != 'image') {
$(id + '_preview').attr('src', '/assets/images/file.png');
return
}

// 読み込み用の関数で読み込み完了時に、HTMLにcanvas追加
load(file, function(canvas) {
console.log('読み込み完了')
canvas.toBlob(function(blob) {
// プレビュー表示
var blobUrl = window.URL.createObjectURL(blob);
$(id + '_preview').attr('src', blobUrl);
// フォーム送信用に変換
$(id).attr('src', canvas.toDataURL());
});
});

function load(file, callback) {
// canvas: true にすると canvas に画像を描画する(回転させる場合は必須オプション)
var options = {
canvas: true,
};

loadImage.parseMetaData(file, function(data) {
if (data.exif) {

// 回転補正
options.orientation = data.exif.get('Orientation');
}
// 画像の読み込み。完了時に callback が呼び出される
loadImage(file, callback, options);
});
}
}
for (let i = 1; i <= 5; i++) {
$('#file' + i).on('change', function() {
show_preview('#file' + i)
});
}
</script>

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for ($i = 1; $i <= 5; $i++) {
$file = $request->file('file' . $i);
if ($request->hasFile('file' . $i) && $request->file('file' . $i)->isValid()) {

$file = $request->file('file' . $i);
$extension = $request->file('file' . $i)->extension();
$file_name = md5(uniqid(mt_rand(), true)) . '.' . $extension;
$path = Storage::putFileAs("public/hoge/" . $hoge["id"], $file, $file_name, 'public');

$input = [];
$input["hoge_id"] = $hoge["id"];
$input["file_name"] = $file->getClientOriginalName();
$input["file_path"] = "/storage/hoge/" . $hoge["id"] . "/" . $file_name;
$this->correct_image_rotation($input["file_path"]);
PatientFile::create($input);
}
}

iPhone で撮った写真は縦向き・横向きが逆になる場合があるので、correct_image_rotation 関数で回転補正してます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 回転補正
public function correct_image_rotation($img_path)
{
\Log::info('回転補正開始');
$imagick = new \Imagick();
$url = env("APP_URL") . $img_path;
$image = file_get_contents($url);
$imagick->readImageBlob($image);
$format = strtolower($imagick->getImageFormat());
if ($format === 'jpeg') {
$orientation = $imagick->getImageOrientation();
$isRotated = false;
if ($orientation === \Imagick::ORIENTATION_RIGHTTOP) {
$imagick->rotateImage('none', 90);
$isRotated = true;
} elseif ($orientation === \Imagick::ORIENTATION_BOTTOMRIGHT) {
$imagick->rotateImage('none', 180);
$isRotated = true;
} elseif ($orientation === \Imagick::ORIENTATION_LEFTBOTTOM) {
$imagick->rotateImage('none', 270);
$isRotated = true;
}
if ($isRotated) {
$imagick->setImageOrientation(\Imagick::ORIENTATION_TOPLEFT);
}
}
$imagick->writeImage(ltrim($img_path, "/"));
}

アップロードした画像を表示する

Controller

1
2
// ファイル
$hoge_files = HogeFile::where("id", $id)->get();

blade

1
2
3
4
5
@foreach( $hoge_files as $hoge_file)
<a href="<?= $hoge_file->file_path ?>" target="_blank">
<img src=' <?= $hoge_file->file_path ?>' style="width:100px">
</a>
@endforeach

view の変数を JavaScript 側で利用したい時

View

1
2
3
4
5
<script type="text/javascript">
const users = @json($users);
users.forEach(user => {
})
</script>

ローカルのファイルを読み込みたい時

1
<script src="{{url('assets/js/plugins/quagga.min.js')}}"></script>

ベストプラクティス

# PHP
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×