はじめに
前職では素のPHPやCakePHPを使用したり、オレオレフレームワークを作ってましたが、現職ではLaravelを使用しております。
機能がありすぎてわからないぐらい、Laravelって便利ですよね。
そこで、初学者な私が日常的に参照している内容をまとめてみます。
腕に覚えのある諸兄姉にとってはつまらない内容かと思いますが、間違い等ありましたら、コメントいただけますと幸いです。
※全機能を網羅しているわけではありません。
勉強用の環境構築
こちらを参照しながら、Laravel Sailを使用しました。
Laravelとの出会い | ReadDouble
# 適当なフォルダを作った後、以下のコマンドを実行
curl -s https://laravel.build/example-app | bash
■ artisanコマンド
Laravel Sail を使用する場合、php artisan
のところを ./vendor/bin/sail artisan
に読み替えてください。
コントローラー作成
php artisan make:controller HogeController
リソースコントローラー作成
php artisan make:controller HogeController -r
リクエストクラス作成
# app/Http/Requests/User/StoreViewedContentRequest.php が作成される。
php artisan make:request User/StoreViewedContentRequest
モデル作成
# モデル名はテーブル名の単数形のパスカルケース
php artisan make:model Hoge
オプションスイッチ | 意味 |
---|---|
-c | コントローラを同時に作成する |
-r | リソースを同時に作成する |
-cr | コントローラとリソースを同時に作成する |
-R | リクエストを同時に作成する |
-m | マイグレーションを同時に作成する |
コマンドラインアプリケーション作成
php artisan make:command PayCommand
ルートの確認
php artisan route:list
キャッシュクリア
# キャッシュ
php artisan cache:clear
# 設定
php artisan config:clear
# ルート
php artisan route:clear
# ビュー
php artisan view:clear
マイグレーション
DBテーブル名は複数形
# マイグレーションファイル作成
php artisan make:migration ファイル名 --create=members
※ファイル名はわかりやすい方がよい
用途 | ファイル名の例 |
---|---|
新規テーブル作成 | create_テーブル名_table |
テーブル内容編集 | modify_テーブル名_table |
# すべてのマイグレーションを一括してロールバック
php artisan migrate:reset
# 全データをドロップしてから up() *down()は実行していない
php artisan migrate:fresh --seed
# すべてのマイグレーションをロールバック(down()) してから再度マイグレーション(up())
# down() の内容によっては up() に影響が出てしまう
php artisan migrage:refresh
初期データ(seeder)投入
php artisan db:seed
# seederを指定する方法
php artisan db:seed --class HogeSeeder
■ ヘルパー
100種類以上あるようです。
参考URL
■ サービスコンテナ
サービスコンテナとは
Laravelでいう「サービス」とは
- メール送信する
- 文字列を暗号化する
- ファイルを操作する
などなど。
これらサービスが入った入れ物(コンテナ)のようなものを サービスコンテナ と呼ぶ。
サービスコンテナの使い方
サービスコンテナへ入れる
bind メソッドを利用する。
app()->bind('myName', function(){
return 'あいうえお';
})
サービスコンテナから取り出す
make メソッドを利用する。
$name = app()->make('myName');
dd($name); // あいうえお
取り出し方法は、いくつか存在する。
$name = app()->make('myName');
$name = app('myName');
$name = resolve('myName');
$name = App::make('myName');
依存関係注入(Dependency Injection / DI)
例) 依存関係のある2つのクラス(MyClassクラス、Slackクラス)
namespace aaaa;
class MyClass
{
public $slack;
public function __construct(Slack $slack) {
$this->slack = $slack;
}
public function run() {
$this->slack->send();
}
}
namespace aaaa;
class Slack
{
public function send() {
dd('something happens');
}
}
通常ならば、下記のようにインスタンス化したSlackクラスをMyClassクラスの引数に渡す。
$slack = new \aaaa\Slack();
$myClass = new \aaaa\MyClass($slack);
$myClass->run();
bindメソッドを使う(自動解決機能)
app()->bind('myclass', \aaaa\MyClass::class);
$myClass = app()->make('myclass');
$myClass->run();
上記はSlackクラスについて何もコードを書いてないが、サービスコンテナの自動解決機能によって正常に実行される。
bindメソッドを使う(明示的に)
app()->bind('myclass', function(){
$slack = new \aaaa\Slack();
return new \aaaa\MyClass($slack);
});
singleton
サービスコンテナへの登録で bind()
以外にも singleton()
があるが、シングルトンはインスタンスを1つのみ作成・利用する。
参考URL
■MODEL / ORM
保存
// プロパティに1つずつ設定して保存
$contact = new Conatct();
$contact->last_name = '山田';
$contact->first_name = '太郎';
$contact->save();
マスアサイメント脆弱性対策のため、配列で保存する場合は
モデルクラスの中で fillable の設定が必要。
// モデル
class Contact extends Model
{
use hasFactory;
protected $fillable = ['last_name', 'first_name'];
}
// コントローラ
// 配列で一括指定して保存する。
$contact = new Contact([
'last_name' => '山田',
'first_name' => '太郎',
]);
$contact->save(); //保存
// create()メソッドで一括指定し、DBへ保存する
Contact::create([
'last_name' => '山田',
'first_name' => '太郎',
]);
with() (Eager Loading)
N+1問題の回避策として使用される。
join との違い
取得形式 | 親子関係 | リレーション定義 | |
---|---|---|---|
left join | フラット | 1対1 | 不要 |
with | ネスト | 1対多 | 必要 |
例) usersテーブル、user_shipping_addressesテーブルを使った顧客別届け先住所一覧の取得 (1対多の関係)
●left join での結果
同じ値である member_id と shipping_name も表示される。(というか、いつもの表)
user_id | name | zip_code | prefecture | city | address |
---|---|---|---|---|---|
100 | 山田太郎 | 530-0001 | 大阪府 | 大阪市 | 北区梅田 |
100 | 山田太郎 | 160-0023 | 東京都 | 新宿区 | 西新宿 |
100 | 山田太郎 | 450-0002 | 愛知県 | 名古屋市 | 中村区名駅 |
やってみる
<?php
// Userモデル
namespace App\Models;
// << 中略 >>
use App\Models\UserShippingAddress;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// << 中略 >>
/**
* withのための設定
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function user_shipping_address()
{
// hasMany を指定する際は文字列でモデル名を指定する。
// ::class を使った記法でも、出力されれる文字列は「App\Models\UserShippingAddress」となるので
// 完全修飾名を文字列として取得できる。
// ただし、 use 句で該当クラスを呼び出す必要がある。
// (use句で App\Modes\*** と記述するんだから、結局記述の省略にはなってない?)
// →今回はhasMany()のところかしみてないので、そう感じるが、他にモデルを使用する処理もありえるので
// 書いておいて損ではない。
// return $this->hasMany('App\Models\UserShippingAddress');
return $this->hasMany(UserShippingAddress::class);
}
}
<?php
// Withコントローラ
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class WithController extends Controller
{
public function index()
{
$user = User::where('id', 1)->first();
$queue = User::with([
'user_shipping_address' => function ($query) use ($user) {
$query->where('user_id', $user->id);
}
]);
return User::with([
'user_shipping_address' => function ($query) use ($user) {
$query->where('user_id', $user->id);
}
])
->get([
'users.id',
'users.name',
]);
}
}
●with() での結果
->get()
の第1引数では、usersテーブルのカラムを指定できる。
指定しない場合、usersテーブルのカラムをすべて取得する。
user_shipping_address
項目が配列になっている。(withで指定したテーブルで取得したいカラムは指定できない?)
→カラム指定はできなさそうだが、返戻データ(JSON)としてカラムを指定することができる。(APIリソース)
array (
0 =>
array (
'id' => 1,
'name' => '山田太郎',
'user_shipping_address' =>
array (
0 =>
array (
'id' => 1,
'user_id' => 1,
'zip_code' => '530-0001',
'prefecture' => '大阪府',
'city' => '大阪市',
'address' => '北区梅田',
'created_at' => '2023-03-27T06:36:57.000000Z',
'updated_at' => '2023-03-27T06:36:57.000000Z',
),
1 =>
array (
'id' => 2,
'user_id' => 1,
'zip_code' => '160-0023',
'prefecture' => '東京都',
'city' => '新宿区',
'address' => '西新宿',
'created_at' => '2023-03-27T06:36:57.000000Z',
'updated_at' => '2023-03-27T06:36:57.000000Z',
),
2 =>
array (
'id' => 3,
'user_id' => 1,
'zip_code' => '450-0002',
'prefecture' => '愛知県',
'city' => '名古屋市',
'address' => '中村区名駅',
'created_at' => '2023-03-27T06:36:57.000000Z',
'updated_at' => '2023-03-27T06:36:57.000000Z',
),
),
),
)
with() でのリレーションの深堀り
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
public function resource(Request $request)
{
$userData = User::with([
// 親モデルである 「user」をドットでつなげると、
// usersテーブルの中身も取得する。
'userShippingAddress.user'
])
->get([
'users.id',
'users.name',
]);
}
●結果
user_shipping_address オブジェクトの中に、 user オブジェクトが含まれている。
stdClass Object
(
[data] => stdClass Object
(
[0] => stdClass Object
(
[id] => 1
[name] => 山田太郎
[user_shipping_address] => Array
(
[0] => stdClass Object
(
[id] => 1
[user_id] => 1
[zip_code] => 530-0001
[prefecture] => 大阪府
[city] => 大阪市
[address] => 北区梅田
[created_at] => 2023-03-27T06:36:57.000000Z
[updated_at] => 2023-03-27T06:36:57.000000Z
[user] => stdClass Object
(
[id] => 1
[name] => 山田太郎
[email] => tarou.yamada@xxxxxxxx
[email_verified_at] => 2023-03-27T06:26:30.000000Z
[created_at] => 2023-03-27T06:26:30.000000Z
[updated_at] => 2023-03-27T06:26:30.000000Z
)
)
論理削除関係
物理削除 delete()
メソッド、destroy()
メソッド
$user = App\User::find(1);
$user->delete();
// モデルクラスを取得しなくても destroy() メソッドで削除できる
App\User::destroy(1);
App\User::destroy([1, 2, 3]); // 複数レコード削除
論理削除
- テーブルに
deleted_at
カラムが必要。 - モデルに
SoftDeletes
クラスが必要。 -
find()
などのデータ取得時も、 deleted_at カラムがNULLのレコードは対象外となる。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Profile extends Model
{
use SoftDeletes;
}
- 取得したデータ(モデルクラス)が削除されているか確認
if ($user->trashed()) {
// 処理を書く
}
- 論理削除あれたデータも対象とする場合
$users = App\User::withTrashed()->get();
- 論理削除だけを取得する場合
$users = App\User::onlyTrashed()->get();
参考URL
Laravelのソフトデリートについてメモ
トランザクション
トランザクション内で処理したい内容を DB::transaction()
の無名関数の中に書いておけばいいのが便利ですね。
※メモなので、「$userのリレーションがよくわからんぞ」というツッコミはご容赦。
namespace App\UseCase\Http\Controller\User;
use App\Models\User\User;
use DB;
use Illuminate\Support\Collection;
class StoreUserContent
{
public function handle(User $user, array $contentData): string
{
$content = DB::transaction(function () use ($user, $contentData) {
return $user->userContents()->create($value);
});
return $content;
}
}
手動も出来ます。
$user = User::find($id);
DB::beginTransaction();
try {
$user->name = '変更太郎';
$user->save();
DB::commit();
} catch (\Exception $e) {
DB::rollback();
}
■view
Blade
/resources/views
フォルダ配下に●●●.blade.php
というファイルを作成する。
// ルートの場合は welcome.blade.php というファイルを表示させる例
// ".blade.php" は記述しないこと。
Route::get('/', function() {
return view('welcome');
});
第2引数に連想配列を指定すると、viewの中に$name
と$faborite
という変数が生成される。
return View('hello', [
'name' => '山田',
'favorite' => 'Laravel10',
]);
第1引数のビューファイル指定は、resources/views
を起点にしている。
ビューファイルをサブディレクトリで管理したい場合は、ディレクトリの区切りをドット(.)で記述する。
// resources/views/admin/profile.blade.php を使用する場合
return View('admin.progile', $data);
HTMLエスケープ
{{$title}}
のように、{{}}
で囲むと、htmlspecialchars($title, ENT_QUOTES)
と同様のエスケープしてくれる。
// 文字列の連結
<p>{{$title . $name}}</p>
// あえてエスケープさせない場合
<p>{!! $title !!}</p>
コメント
{{-- $title --}}
のように、ハイフン2つで囲む。
PHPディレクティブ
@php
$title = '素晴らしいタイトル';
@endphp
↓↓これと同じ
<?php
$title = '素晴らしいタイトル';
?>
ifディレクティブ
@if (count($records) === 1)
<div>レコードが1件あります。</div>
@elseif (count($record) > 1)
<div>レコードが複数件あります。</div>
@else
<div>ありません。</div>
@endif
繰り返し
ループさせる @while
、@for
、@foreach
があるが、Bladeには@forelse
ディレクティブというものがある。
@forelse ($users as $user)
<p>{{$user->name}}</p>
@empty
<p>ユーザーはいません</p>
@endforelse
↓↓foreach文とif文の組み合わせのようなもの
@if (!empty($users))
@foreach ($users as $user)
<p>{{$user->name}}</p>
@endforeach
@else
<p>ユーザーはいません</p>
@endif
ループのディレクティブ
ループを続けたり脱出する@continue
や@break
以外に、$loop
変数が使用できる。
ディレクティブ | 機能 |
---|---|
$loop->index | 現在のインデックスを参照できる。 |
$loop->iteration | 現在のイテレータの値を参照できる。(初期値1) |
$loop->remaining | 反復の残り数 |
$loop->count | 反復している配列の総アイテム数 |
$loop->first | ループの最初か判定する |
$loop->last | ループの最後か判定する |
$loop->even | 現在のループが偶数回か判定する |
$loop->odd | 現在のループが奇数回か判定する |
$loop->depth | 現在のループのネストレベル |
$loop->parent | ループがネストしている場合、親のループ変数 |
vite
Vite manifest not found at
が表示される場合
-
npm run dev&
を実行する。 - または、
npm run build
を実行しておく。
おわりに
初学者は、とにかく試してみるっきゃないですね。