59
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめに

前職では素の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 愛知県 名古屋市 中村区名駅

●with の場合
image.png

やってみる

<?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を実行しておく。

おわりに

初学者は、とにかく試してみるっきゃないですね。

59
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?