Docker×Laravel8でもくもく会アプリの一覧画面パート4です。
この記事では、データベースの値を表示させます。
休日で空いた時間の暇つぶしを探せるアプリを公開しています。
データベースの値を表示させて一覧画面を完成させる
前回の記事では、もくもく会イベント一覧画面のレイアウトをデータを仮置きで表示させました。
今回は、実際にデータベースの値を一覧画面に組み込んでいきますが、PHPの関数の知識を使うのでPHP公式や解説ページを確認しながら理解していきましょう。
まずは完成形のソース
@extends('layouts.app')
@section('content')
<style>
#mokumoku-lists {
filter:drop-shadow(2px 4px 6px #000);
}
.content-filed {
width: 60%;
}
</style>
{{-- ナビゲーション --}}
<nav class="navbar navbar-expand-lg navbar-light bg-light container">
<div class="container-fluid">
<a class="navbar-brand" href="{{ route('event.index') }}">もくもく会</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mokumoku" aria-controls="mokumoku" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mokumoku">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ route('event.index') }}">一覧</a>
</li>
<li class="nav-item">
<a class="nav-link" href="">開催する</a>
</li>
</ul>
</div>
</div>
</nav>
{{-- もくもく会開催一覧リスト --}}
@foreach ($events as $event)
<div class="card container text-center mb-5" id="mokumoku-lists">
<div class="card-header font-weight-bold bg-white">
<a href="">{{ $event->title }}</a>
</div>
<div class="card-body">
<div class="category text-left">
<label for="category-label"><span class="badge badge-primary p-2">{{ 'Laravel' }}</span></label>
</div>
<div class="entry-fee-wrapper d-flex">
<label for="entry-fee"><span class="badge badge-success p-2">{{ '参加費' }}</span></label>
<p class="text-danger font-weight-bold p-1 h5">{{ $event->entry_fee.'円' }}</p>
</div>
<div class="content-wrapper d-flex">
<div class="content-filed">
<p class="card-text text-left">
{{ mb_substr($event->content, 0, 100, 'UTF-8').'...' }}
</p>
</div>
<div class="btn-filed ml-auto d-flex">
<button class="btn btn-primary mr-3">{{ '詳細' }}</button>
<button class="btn btn-info mr-3">{{ '編集' }}</button>
<button class="btn btn-danger mr-3">{{ '削除' }}</button>
</div>
</div>
</div>
@php
// 指定の日付を△△/××に変換する
$date = date("m/d" ,strtotime($event->date));
//指定日の曜日を取得する
$getWeek = date('w', strtotime($event->date));
//配列を使用し、要素順に(日:0〜土:6)を設定する
$week = [
'日', //0
'月', //1
'火', //2
'水', //3
'木', //4
'金', //5
'土', //6
];
// 開始時間 ex.15:00:00→15:00に変換。秒部分を切り捨て
$start_time = substr($event->start_time, 0, -3);
// 終了時間 ex.19:00:00→19:00に変換。秒部分を切り捨て
$end_time = substr($event->end_time, 0, -3);
@endphp
<div class="card-footer text-left font-weight-bold bg-white">
{{ '開催日時:'.$date.'('.$week[$getWeek].')'. $start_time. '-' .$end_time }}
</div>
</div>
@endforeach
@endsection
大事なポイントを以下にまとめました。
- 文字は”で囲み、文字と変数を組み合わせる場合は、’文字’.$eventと.を使う
- mb_substr関数で文字数を制御。超過したら…にする
- @php …@endphpでbladeの中で、PHPの変数定義をできる状態にする
- date関数とstrtotimeを使って、日付を△△/××に変換
- 曜日の配列を用意する
- substr関数で指定の箇所の文字列を削除
こんな感じです。
文字は”で囲み、文字と変数を組み合わせる場合
bladeのなかで、文字列と変数を組み合わせる場合は、.で繋ぎます。
例えば、
// 以下のように.でつなげる
{{ $変数.'文字列' }}
{{ '文字列'.$変数 }}
{{ '文字列'.$変数.'文字列' }}
// OK
{{ $event->entry_fee.'円' }} // 500円
// 構文エラーが出る
{{ $event->entry_fee'円' }}
開催日時のところがちょっと複雑に見えますが、上記を知っていれば問題ないです。
{{ '開催日時:'.$date.'('.$week[$getWeek].')'. $start_time. '-' .$end_time }}
// 分解すると、、、
{{ '文字列'.$変数.'文字列'.$変数.'文字列'.$変数.'文字列'.$変数 }}
mb_substr関数で文字数を制御。超過したら…にする
次に、イベントの詳細にて文字の超過分は…で制御しましょう。
コードの該当箇所は、以下の部分です。
{{ mb_substr($event->content, 0, 100, 'UTF-8').'...' }}
// UTF-8は現在のwebサイトにおける標準文字
// PHPのmb_substr関数を使って制御する
mb_substr(指定文字列, 開始位置, 終了位置, 文字のエンコード)
日本語を指定する場合は、mb_substrを使いましょう。(英語ならsubstr関数でもできるが、日本語に適用すると、2文字カウントになったりするから。)
この辺の詳細は、PHPの公式(mb_substr)をご参照ください。
今回のアプリでは、100文字超えたら…で表示できるようにしました。
@php …@endphpでbladeの中で、PHPの関数を使える状態にする
blade内でPHPの変数を定義したい場合は、@php…@endphpでくくりましょう。
この辺りは以下の記事で解説しています。
date関数とstrtotimeを使って、日付を△△/××に変換
データベースで、dateのカラム値は2021-11-07となっています。
一覧画面に表示する場合は、11/7と表示できるようにPHPのdate関数を使って制御します。
コードの該当箇所は以下です。
// 指定の日付を△△/××に変換する
$date = date("m/d" ,strtotime($event->date));
// date関数の使い方
date(表示形式, strtotime(指定日付))
date関数では、strtotime関数も合わせて使われます。(タイムスタンプに変換)
この辺りもPHPの公式(date関数)やPHPで日付を変換する解説記事に目を通して理解を深めましょう。
曜日の配列を用意する
date関数を使えば、曜日も取得できます。
ただ、曜日の場合はちょっと工夫が必要です。
なぜなら以下のコードで返ってくる値は、日とかではなく0,1,2,…6だからです。
//指定日の曜日を取得する
$getWeek = date('w', strtotime($event->date)); // 11/7なら0,11/8なら1,11/9なら2が返ってくる
なので、曜日の配列を用意する必要があります。
//配列を使用し、要素順に(日:0〜土:6)を設定する
$week = [
'日', //0
'月', //1
'火', //2
'水', //3
'木', //4
'金', //5
'土', //6
];
// 例
$week[0] →日
$week[1] →月
// $getWeekは0,1,2...6のいずれかの値が入るから、以下で曜日を取得できる
$week[$getWeek]
substr関数で指定の箇所の文字列を削除
最後に、開始時間と終了時間を整形です。
データベースで、start_timeとend_timeの値は15:00:00,19:00:00と入ってくるので、表示する際には15:00.19:00と制御します。
substr関数を使って、指定の文字列の特定文字列を削除する方針でいきましょう。
該当ソースコード
// 開始時間 ex.15:00:00→15:00に変換。秒部分を切り捨て
$start_time = substr($event->start_time, 0, -3);
// 終了時間 ex.19:00:00→19:00に変換。秒部分を切り捨て
$end_time = substr($event->end_time, 0, -3);
// substr関数の基本的な使い方
// substr関数は指定文字列を切り取って返す関数
substr(指定文字列, 開始位置, 終了位置);
// 結果:he
substr('hello', 0, 2);
// 結果:hel 終了位置を−に指定すると、末尾も文字列を削除してくれる
substr('hello', 0, -2);
こちらもPHPの関数の説明になるので、詳しくはPHP公式(substr)や末尾の文字列の削除のやり方をご参照ください。
イベントとカテゴリーをリレーションさせてカテゴリーを表示させる
次に、eventモデルとcategoryモデルをリレーションさせて、カテゴリーを表示させていきます。
該当のソースコード
// {{ 'Laravel' }}となっている箇所
<div class="category text-left">
<label for="category-label"><span class="badge badge-primary p-2">{{ 'Laravel' }}</span></label>
</div>
// 最終的なコード
<div class="category text-left">
<label for="category-label"><span class="badge badge-primary p-2">{{ $event->category->category_name }}</span></label>
</div>
コードを見てもらうとわかるとおり、$event->カラムではなくて、$event->category->categoriesテーブルのカラム名となっていますね。
これは、eventモデルとcategoryモデルをリレーション(データ紐付け)させることで、eventsテーブルにあるcategory_idとcategoriesテーブルにあるcategory_idで一致するデータを一緒に取得できます。
ちょっとわかりにくいので図で表すと、こんな感じです。
また、categoryはたくさんのeventをもち、逆にeventは一つのカテゴリーに属する関係です。
なので、それぞれのモデルにリレーションを書いていきます。
Eventモデル
<?php
namespace App\Models;
use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
use HasFactory;
// モデルに関連づけるテーブル
protected $table = 'events';
// テーブルに関連づける主キー
protected $primaryKey = 'event_id';
// 登録・編集ができるカラム
protected $fillable = [
'category_id',
'title',
'date',
'start_time',
'end_time',
'content',
'entry_fee',
];
/**
* カテゴリーリレーション
*/
public function category()
{
return $this->belongsTo(Category::class, 'category_id', 'category_id');
}
/**
* eventsテーブルのレコードを全件取得
*
* @param void
* @return Event eventsテーブル
*/
public function allEventsData()
{
return $this->get();
}
}
categoryモデル
<?php
namespace App\Models;
use App\Models\Event;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
// モデルに関連づけるテーブル
protected $table = 'categories';
// テーブルに関連づける主キー
protected $primaryKey = 'category_id';
// 登録・編集ができるカラム
protected $fillable = [
'category_name'
];
/**
* イベントリレーション
*/
public function events()
{
return $this->hasMany(Event::class, 'category_id', 'category_id');
}
/**
* categoriesテーブルのレコードを全件取得
*
* @param void
* @return Category categoriesテーブル
*/
public function allCategoriesData()
{
return $this->get();
}
}
リレーションが組めたので、categoryを繋げることでcategory_nameにアクセスできます。
細かい部分は解説しないので、ぜひLaravel8公式などに目を通して理解を深めましょう。(実務では公式リファレンス読めないと話にならないので。)
{{ $event->category->category_name }}
ナビゲーションを共通化する
最後に、ナビゲーションを共通化させます。
現在は、カテゴリー一覧、イベント一覧ページに同じようなナビゲーションの記述を書いてますし、この後作る登録画面でもまた書くので冗長です。
まずは、親ファイルであるapp.blade.phpにナビゲーションを@includeさせます。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<main class="py-4">
@include('common.nav')
@yield('content')
</main>
</div>
</body>
</html>
@includeを使うと、別のbladeファイルを読み込めます。
そして、commonファイルを作成し、nav.blade.phpにナビゲーションを書きます。
{{-- ナビゲーション --}}
<nav class="navbar navbar-expand-lg navbar-light bg-light container">
<div class="container-fluid">
<a class="navbar-brand" href="{{ route('event.index') }}">{{ 'もくもく会' }}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mokumoku" aria-controls="mokumoku" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mokumoku">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ route('event.index') }}">{{ 'もくもく会一覧' }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ route('category.index') }}">{{ 'カテゴリ一覧' }}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{{ route('event.register') }}">{{ '開催する' }}</a>
</li>
</ul>
</div>
</div>
</nav>
各bladeファイルでは、@extends(‘layout.app’)を読み込んでいるので、各bladeファイルにナビゲーションの記述はもう不要です。
@extends('layouts.app')
@section('content')
<style>
#mokumoku-lists {
filter:drop-shadow(2px 4px 6px #000);
}
.content-filed {
width: 60%;
}
</style>
{{-- もくもく会開催一覧リスト --}}
@foreach ($events as $event)
<div class="card container text-center mb-5" id="mokumoku-lists">
<div class="card-header font-weight-bold bg-white">
<a href="">{{ $event->title }}</a>
</div>
<div class="card-body">
<div class="category text-left">
<label for="category-label"><span class="badge badge-primary p-2">{{ $event->category->category_name }}</span></label>
</div>
<div class="entry-fee-wrapper d-flex">
<label for="entry-fee"><span class="badge badge-success p-2">{{ '参加費' }}</span></label>
<p class="text-danger font-weight-bold p-1 h5">{{ $event->entry_fee.'円' }}</p>
</div>
<div class="content-wrapper d-flex">
<div class="content-filed">
<p class="card-text text-left">
{{ mb_substr($event->content, 0, 100, 'UTF-8').'...' }}
</p>
</div>
<div class="btn-filed ml-auto">
<button class="btn btn-primary mr-3">{{ '詳細' }}</button>
<button class="btn btn-info mr-3">{{ '編集' }}</button>
<button class="btn btn-danger mr-3">{{ '削除' }}</button>
</div>
</div>
</div>
@php
// 指定の日付を△△/××に変換する
$date = date("m/d" ,strtotime($event->date));
//指定日の曜日を取得する
$getWeek = date('w', strtotime($event->date));
//配列を使用し、要素順に(日:0〜土:6)を設定する
$week = [
'日', //0
'月', //1
'火', //2
'水', //3
'木', //4
'金', //5
'土', //6
];
// 開始時間 ex.15:00:00→15:00に変換。秒部分を切り捨て
$start_time = substr($event->start_time, 0, -3);
// 終了時間 ex.19:00:00→19:00に変換。秒部分を切り捨て
$end_time = substr($event->end_time, 0, -3);
@endphp
<div class="card-footer text-left font-weight-bold bg-white">
{{ '開催日時:'.$date.'('.$week[$getWeek].')'. $start_time. '-' .$end_time }}
</div>
</div>
@endforeach
@endsection
@extends('layouts.app')
@section('content')
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">カテゴリーID</th>
<th scope="col">カテゴリー名</th>
</tr>
</thead>
<tbody>
@foreach ($categories as $category)
<tr>
<td>{{ $category->category_id }}</td>
<td>{{ $category->category_name }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
ナビゲーションも共通化できました。
一覧画面の表示がちょっと長くなりましたが、これで一覧表示は終了です。
次回は、登録画面の実装をしていきます。
休日で空いた時間の暇つぶしを探せるアプリを公開しています。
コメント