駆け出しエンジニアの勉強記録

アラフォー女の未経験すぎる業界での勉強記録

【laravel】get Attributeの色々な使い方

get Attributeとは、Eloquentモデルのアクセサメソッドで、モデルのデータを取得する際に、変換データを作成するためのメソッド。

方法
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
変換したいデータがあるモデルにfunction get Attribute()で書く

そのデータをbladeで使用するのであれば必要ないが、vueで使用する場合は    protected $appends = []の中に指定必要。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

使用事例
・半角カナから全角カナへ変換 & null対策

//普通はこの書き方でOKだがDBにデータが無い時や、新規フォームの場合エラーがでてしまうから、
function getNameKanaHalfToFullAttribute()
{
return mb_convert_kana($this->attributes['electronic_application_kana'], 'ASKV', 'UTF-8');
}
// DBが空の場合nullを返してエラーにならないようにしておく
function getNameKanaHalfToFullAttribute()
{
$value = $this->getAttribute('electronic_application_kana');
if ($value !== null) {
return mb_convert_kana($value, 'ASKV', 'UTF-8');
}
return null;
}


・last_name(名字)カラムとfirst_name(名前)カラムを一つにしてvueに表示したい時

protected $appends = [
'full_name',
];

function getFullNameAttribute()
{
return $this->last_name.' '.$this->first_name;
}


・last_name(名字)カラムとfirst_name(名前)カラムを全角で表示したい時

protected $appends = [
'representative_full_name_half_to_full',
];

function getRepresentativeFullNamehalfToFullAttribute()
{
$convertedLastName = mb_convert_kana($this->representative_last_name, 'ASKV', 'UTF-8');
$convertedFirstName = mb_convert_kana($this->representative_first_name, 'ASKV', 'UTF-8');
return $convertedLastName.' '.$convertedFirstName;
}


・半角文字を全角に変換(↓は名前に半角があった場合)

protected $appends = [
'full_name_half_to_full',
];

function getFullNamehalfToFullAttribute()
{
// mb_convert_kanaを使用 'A(英数字)S(スペース)K(カタカナ)V(濁点付きを1文字にする)'
$convertedLastName = mb_convert_kana($this->last_name, 'ASKV', 'UTF-8');
$convertedFirstName = mb_convert_kana($this->first_name, 'ASKV', 'UTF-8');
return $convertedLastName.' '.$convertedFirstName;
}

※全角を半角に。等詳しいことはここ

・郵便番号が【1040032】と登録されている状態から3桁と4桁に分けたい場合

protected $appends = [
'zip_code_parent',
'zip_code_child',
];

function getZipCodeParentAttribute()
{
return substr($this->zip_code, 0, 3); // 先頭の3桁を取得
}
function getZipCodeChildAttribute()
{
return substr($this->zip_code, -4);   // 末尾の4桁を取得
}


・住所が都道府県、市町村などバラバラで登録されてるものを一行で表示したい場合

protected $appends = [
'full_address_without_zip_code',
];

function getFullAddressWithoutZipCodeAttribute()
{
return
$this->prefecture.
$this->address1.
$this->address2.
$this->address3;
}


・電話番号【090-1234-5678】と登録されている状態からハイフンのとこで3部に分けたい場合

protected $appends = [
'tel_1',
'tel_2',
'tel_3',
];

function getTel1Attribute()
{
$tell_array = explode('-', $this->tel);
return $tell_array[0] ?? ''; // 090の部分
}
function getTel2Attribute()
{
$tell_array = explode('-', $this->tel);
return $tell_array[1] ?? '';   // 1234の部分
}
function getTel3Attribute()
{
$tell_array = explode('-', $this->tel);
return $tell_array[2] ?? '';   // 5678の部分
}


・データの文字の一部を抽出
novaの場合、fieldで->displayUsingで抽出することも可能(リンク)

protected $appends = [
'age',
];

public function getAgeAttribute()
{
// 「1952/02/17 生」というデータを「1952/02/17」「生」に分割
$birthParts = explode(' ',$this->representative->birthdate);
// $dateParts の値は次のようになる
// array(2) {
// [0] => "1952/02/17"
// [1] => "生"
// }
$birthdateParts = $birthParts[0];
return $birthdateParts;
}

カスタムバリデーション

確認用入力した値(cinfirm_my_number)が入力した個人番号(my_number)と一致しない時にバリデーションメッセージを表示させる。

vueファイル

<div>
<InputLabel for="my_number" value="個人番号" />

<TextInput 
id="my_number"   //ここの入力値と
type="text"
class="mt-1 block w-full"
v-model="form.my_number"
required
autocomplete="my_number"
/>

<InputError class="mt-2" :message="form.errors.my_number" />
</div>

<div>
<InputLabel for="confirm_my_number" value="確認用" />

<TextInput
id="confirm_my_number"  //ここの入力値が一致しているか
type="text"
class="mt-1 block w-full"
v-model="form.confirm_my_number"
required
autocomplete="confirm_my_number"
/>
// 以下に設定したエラーメッセージが表示される
<InputError class="mt-2" :message="form.errors.confirm_my_number" />
</div>


「一致確認のルールを設定するファイル」を作成するコマンド
php artisan make:rule MatchMyNumber(ファイル名)

/app/Rules/MatchMyNumber.php作成される

public function validate(string $attribute, mixed $value, Closure $fail): void
{
$otherValue = request()->input('my_number'); // $otheValueにmy_numberを代入

if ($value !== $otherValue) { // $valueにはconfirm_my_numberの値が入ってる  ※別途下記参照
$fail('確認用は個人番号と一致している必要があります。'); // 表示させたいメッセージ
}
}


リクエストファイルに

use App\Rules\MatchMyNumber; // リクエストファイルをuseする
 
public function rules(): array
{
return [
'my_number' => ['max:12'],
'confirm_my_number' => ['max:12' , new MatchMyNumber], // new ファイル名で設定
];

public function attributes() // エラーメッセージで表示させたいform名を書く
{
return [
'my_number' => 'マイナンバー',
];
}


※リクエストファイルの
'confirm_my_number' => ['max:12', new MatchMyNumber],  
で'confirm_my_number'がMatchNumberファイルのフィールド名(要はname)として設定されてて、そのフィールドに入力されたものが、ルールファイルで$valueにセットされている。

【Vue3】HTML,チェックボックス、セレクトボックス

【Checkbox】
resources/js/ComponentsにCheckbox.vueのコンポーネント作成し、

import Checkbox from '@/Components/Checkbox.vue';

した上で使える。

Q:form編集時、DBの値を元にチェックボックスに最初からチェックが入っている状態にしたい時

A: :checked を使う

Q:チェックボックスに✓を入れて送信すると「true」が送られるがそれを「1」に変えたい時

A:true-value="1" false-value="0"を使う。(1,0は数値型で渡される)
※ちなみに文字列型の1,0を渡したい場合は  :true-value="'1'"  :false-value="'0'" になる。

<div>
<InputLabel for="notification_format" value="通知希望形式" />

<Checkbox
id="notification_format"
name="notification_format"
v-model:checked="form.notification_format" // :checkedをつけるとチェックが入った状態になる
:true-value="1"  // DBに数値型の1,0で登録したい時
:false-value="0"
/>
</div>


【Selectbox】
resources/js/ComponentsにSelectbox.vueのコンポーネント作成し、

import SelectBoxInput from '@/Components/SelectBoxInput.vue';

した上で使える。

Q:keyとvalueの設定について

A:2つの方法がある。
①config/params.php内に記述→コントローラーでinertiaを使ってvueに知らせる
②script内にconstで定義する

①の場合
params.php

<?php

return [
//適用除外の事由
"reason_exception" => [
"海外在住"=>"海外在住",
"短期在留"=>"短期留学",
"その他"=>"その他",
]
];

コントローラー

return Inertia::render('SocialInsurance/Edit', [
'reason_exception' => config('params.reason_exception'),
]);

vue

<script setup>
const
props = defineProps({
reason_expention: {   // 受け取る (その前にEdit.vueでも受け取る作業をしているが省略)
type: Object,
},
</script>

<SelectBoxInput
id="reason_exception"
type="text"
name="reason_exception"
class="mt-1 block w-full"
v-model="form.reason_exception"
:options="reason_exception" // :optionsで書く
autocomplete="reason_exception"
@update:modelValue="updateValue"  // これがないとoptionsのデータが変更された時にuiに反映されない
/>


②の場合

<script setup>
const
is_options = {0:'無' , 1:'有'} // この場合、文字列の0or1が送信される
</script>

<SelectBoxInput
id="other"
type="text"
name="other"
class="mt-1 block w-full"
v-model="form.other"
:options="is_options"  // script内で定義したもの。
autcomplete="other"
/>

コールバック関数とは?

sbfl.net

Promiseとは
JavaScriptにおいて、非同期処理の操作が完了したときに結果を返すもの。
非同期処理とは、ある処理が実行されてから終わるまで待たずに、次に控えている別の処理を行うこと。

なぜこのような仕組みがあるのか?

JavaScriptはシングルスレッドでしか動かない性質があるため、複数の処理を並列で走らせることができない。 そのため効率的に処理をするために考えられた仕組みが非同期処理と呼ばれるものになる。



非同期処理とは
「ある処理が実行されてから終わるまで待たずに、次に控えている別の処理を行うこと」

Promiseは処理が実行中の処理を監視し、処理が問題なく完了すればresolve、反対に問題があればrejectを呼び出してメッセージを表示する。

コールバック関数とは?

コールバックとは、ある関数へ別の関数を渡すことです。

以下のようなイメージの場合、関数Bがコールバック関数になる。

関数A(関数B、引数) {
    //実行内容
}

ミドルウェア(すべて自作する場合)

【routes】
ーーー auth.php ーーー
Route::middleware('guest')->group(function () {
guest =
ログインしていない人に表示されるページへのルート。
ユーザーがログインしている場合、指定されたリダイレクト先にリダイレクトする。

Route::middleware('auth')->group(function () {
auth =
ログインしている人のみに表示されるページへのルート。
ログインしていない場合、指定されたリダイレクト先にリダイレクトする。

以下の通り自作することも可能。

ミドルウェアの作成手順
ーーーーーーーーーーーーーーーーーーーーーーー

1、Middlewareファイルを作る
(今回はAdminAuthという名前)
(認証成功時、失敗時どのような動きをするか登録するファイル)
2、app/Http/Kernel.phpに登録
(1で作成したファイルを使えるようにする)

3、config/auth.phpに諸々追記

4、Adminモデルにも諸々追記

5、config/app.phpにサービスプロバイダ追記
ーーーーーーーーーーーーーーーーーーーーーーー

1、Middlewareファイルを作る(今回は)AdminAuthという名前

ターミナルに php artisan make:middleware AdminAuth コマンド入力。

ファイルを以下に編集

public function handle(Request $request, Closure $next)
{
if(auth()->guard('admin')->check()){
return $next($request);  // 認証された企業アカウントの場合は次のリクエストを処理します
}else{
return redirect()->route('manage.login');  // 認証されていない場合はログインにリダイレクトします
}
}


2、app/Http/Kernel.phpに登録

1のファイルをrouteで Route::middleware(['auth:admin'])->group(function(){ で使えるようにKernelクラスに登録する

protected $middlewareAliases = [
'auth.admin' => \App\Http\Middleware\AdminAuth::class,
]; 


3、config/auth.phpに諸々追記

config/auth.php ファイルのguardsセクションに企業アカウント用のガード(認証されているかどうかを確認するための仕組み)を追加する。

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],

// 以下、追記
'admin' => [
'driver' => 'session',
'provider' => 'admins',  // 複数形にする
],
],


providers セクションにプロバイダを追加する。

'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],

// 以下追記
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],


4、Adminモデルにも諸々追記

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

// Authenticatableトレイト(※)を使用する必要があるが、Authenticatableトレイトは
デフォルトのユーザー認証の際、Userモデルで使用されるているので以下の書き方で追記
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable // extendsを変更
{
use HasFactory, HasApiTokens, Notifiable;  // HasApiTokens(※)Notifiable(※)追記

protected $fillable = [
'name',
'email',
'password',
];

Authenticatableトレイトとは?
 Illuminate\Foundation\Auth\Userトレイトは Laravel でユーザー認証を実装するための基本的な機能を提供するトレイトで、このトレイトはIlluminate\Auth\Authenticatableトレイトを使用している。
Authenticatableトレイトを使用することで、Laravel の認証機能が適切に機能する。

HasApiTokensとは?

外部のサービスやAPIとの通信において、アプリケーションがユーザーを識別し、認証するための手段。

通常、APIトークンはユーザーがログインした後に発行され、リクエストごとに送信される。
トークンはリクエストのヘッダーやリクエスト パラメーターとして含まれ、サーバーはトークンを検証してユーザーを認証する。
API トークンはセッションを使用せず、通常は長寿命で、ユーザーの認証情報を保持する。

Notifiableとは?
通知機能を提供する。
通知はメール、データベース、Slack など、さまざまなチャンネルで送信できる。
これにより、アプリケーション内でイベントが発生した場合にユーザーや他のシステムとコミュニケーションすることができる。
例えば、パスワードリセットの通知などが Notifiable を使用している。

5、config/app.phpにサービスプロバイダ追記

↑でuseしたHasApiTokens、Notifableのプロバイダを追記

'providers' => [
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
],


【レイアウトでデータを使用表示したい場合】
ちなみに、Middleware/HandleInertiaRequests.phpに以下のuser、companyを書いておくと

public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
'company' => Auth::guard('company')->user(), // ガードを使用して認証された企業を取得
],

コントローラーからデータを渡さなくても、vue内のscriptに

const page = usePage()
const company = page.props.auth.company;

テンプレート内に
{{ $page.props.auth.company.name(カラム名) }} 様
という感じで使用して表示できる。

【Vue3 】ボタンの表示、非表示

1つのvueで新規登録、編集など行う際、通ってきたルートによってボタンを表示させたり、非表示にしたりする方法。

(今回の例)
・会社データ新規登録の際は「アカウント発行」ボタンは非表示にしたい

・既に会社データ登録済の場合は編集画面に「アカウント発行」ボタンを表示させておきたい

<script setup>
const company = usePage().props.company;
const select_company = usePage().props.select_company;
var postUrl = null;
if (select_company && select_company.id) postUrl = route('user.company.list', {select_company: select_company});
else postUrl = route('user.company');
if (company && company.id) postUrl = postUrl+'/'+company.id
else postUrl = postUrl+'/add'
console.log(postUrl)
// companyとselect_companyが存在し、それぞれのidが存在する場合に[true]を返し、
// そうでない場合には[false]を返す関数を定義する
function shouldShowcreateaccount(){
return company && company.id && select_company && select_company.id;
}
// その[true]か[false]かをshouldShow変数に格納する
const shouldShow = shouldShowcreateaccount();
// ボタン押された時にダイアログ表示。OKならそのルートに飛ぶ→コントローラーに飛ぶ
function createAccountButton(){
if(confirm("企業アカウントを作成しますがよろしいでしょうか?企業メール宛にパスワードを発行します
\n既にアカウント発行済パスワード設定済みの場合も再設定されます")){
let url = null;
url = postUrl + '/create_account';
router.get(url);
}
}
</script>

<template>
// v-ifでtrueなら表示されるし、falseなら表示されなくなる。
// 他にもボタンが多数あるためpreventつけてる。
<PrimaryButton
v-if="shouldShow" @click.prevent="createAccountButton">アカウント発行
</PrimaryButton>
</template>

vueの:disabledについて

import PrimaryButton from '@/Components/PrimaryButton.vue';
 
<PrimaryButton :disabled="form.processing">保存する</PrimaryButton>

:disabled="form.processing"
Vue.jsのディレクティブで、この場合は<PrimaryButton>コンポーネントがform.processingの値に応じて有効または無効になるように制御されていることを示している。

ここで、:disabledはVue.jsのバインディングを行うディレクティブであり、form.processingがtrueの場合にボタンを無効にし、falseの場合に有効にする。

これは通常、非同期処理やフォームの送信などの間、ボタンが連続してクリックされることを防ぐために使用され、フォームが処理中(form.processingがtrue)の間はボタンを無効にし、処理が完了すると再度有効にするといった使い方が一般的。