やりたいこと
- WordPress の データベース構造をそのまま使って、管理者の投稿一覧ページを作りたい。 Laravel 5.5 を使いたい。
学んだことまとめ
WordPress
Laravel 5.5
投稿だけが欲しい
これはつまり、 wp_posts.post_type の値が post のレコードのみが欲しい、ということです。データベース構造 – WordPress Codex 日本語版 を見ると、取りうる値は次です。
投稿種別
- ‘post’: 投稿
- ‘page’: ページ
- ‘attachment’: 添付ファイル
- ‘revision’: 改訂履歴・自動保存
attachment と revision が謎です。そのまんまの意味だろうと言われればその通りですけれども、 post 、 page 、どちらの attatchment (revision) か? と問われれば今の私には分かりません。よって、謎です。今は置いておきましょう。
追記: 後でちょっと探してみましたら、投稿タイプのページがありました。
where で絞り込む
簡単に実現するために、モデルの ローカルスコープ を作りました。
"app/Models/Wp/Post.php" に次を追加し、
/**
* 投稿種別が投稿のレコードに限定するクエリスコープです。
* @param Builder $query
* @return Builder
*/
public function scopePostTypePost(Builder $query): Builder
{
return $query->where('post_type', 'post');
}
次のように使います。
# php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.3 — cli) by Justin Hileman
>>> App\Models\Wp\Post::select('ID', 'post_title', 'post_status', 'post_type')->postTypePost()->get();
=> Illuminate\Database\Eloquent\Collection {#3043
all: [
App\Models\Wp\Post {#3042
ID: 4,
post_title: "自動下書き",
post_status: "auto-draft",
post_type: "post",
},
App\Models\Wp\Post {#3041
ID: 1,
post_title: "Hello world!",
post_status: "publish",
post_type: "post",
},
],
}
>>>
専用のモデルを作る
Post モデルを継承したモデルを用意してみました。
- static::addGlobalScope メソッドの第1引数の名前は、ユニークにすること。同じ名前が存在すると framework/HasGlobalScopes.php at 5.5 · laravel/framework を読む限り、上書きすることになるはずで、良からぬことが起きると予想される。
<?php
namespace App\Models\Wp\PostType;
use App\Models\Wp\Post as WpPost;
use Illuminate\Database\Eloquent\Builder;
class Post extends WpPost
{
/**
* モデルの「初期起動」メソッド
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope('postTypePost', function (Builder $builder) {
$builder->where('post_type', 'post');
});
}
}
次のように使えました。いい感じです。ただ、本当に使えるのかどうかはよく分かりません。 Post モデルと PostType¥Post モデルと、併用して進めてみたいと思います。
# php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.3 — cli) by Justin Hileman
>>> App\Models\Wp\PostType\Post::select('ID', 'post_title', 'post_status', 'post_type')->get();
=> Illuminate\Database\Eloquent\Collection {#3057
all: [
App\Models\Wp\PostType\Post {#3058
ID: 4,
post_title: "自動下書き",
post_status: "auto-draft",
post_type: "post",
},
App\Models\Wp\PostType\Post {#3059
ID: 1,
post_title: "Hello world!",
post_status: "publish",
post_type: "post",
},
],
}
>>>
管理画面の投稿一覧に表示すべき投稿
post_status が何のレコードを投稿一覧に表示するべきか? という問いとなります。 上述の PostType\Post の結果と、 WordPress 管理画面を見比べてみますと、 publish は表示し、 auto-draft は表示しない、ということが少なくとも分かります。
そもそも、 post_status の取りうる値は何でしょうか? 本家ドキュメントがありました♪。
これを見ると、 8 つです。投稿一覧に表示するのは、このうち 6 つです。
- publish: 公開。パスワード保護、も含む。投稿一覧に表示する。
- future: 予約。投稿一覧に表示する。
- draft: 下書き。投稿一覧に表示する。
- pending: 承認待ち。投稿一覧に表示する。
- private: 非公開。投稿一覧に表示する。
- trash: ゴミ箱。投稿一覧に表示する。
- auto-draft: 自動保存。投稿一覧に表示しない。
- inherit: 継承。投稿一覧に表示しない。
コードは次のようになりました。
$ git diff app/Models/Wp/Post.php
diff --git a/app/Models/Wp/Post.php b/app/Models/Wp/Post.php
index 6b931f2..be667f5 100644
--- a/app/Models/Wp/Post.php
+++ b/app/Models/Wp/Post.php
@@ -74,4 +74,22 @@ class Post extends Model
{
return $query->where('post_type', 'post');
}
+
+ /**
+ * 投稿ステータスが管理画面の投稿一覧の "すべて" に表示するレコードに限定するクエリスコープです。
+ * @param Builder $query
+ * @return Builder
+ */
+ public function scopePostStatusAll(Builder $query): Builder
+ {
+ return $query->where(function ($query) {
+ $query
+ ->where('post_status', 'publish')
+ ->orWhere('post_status', 'future')
+ ->orWhere('post_status', 'draft')
+ ->orWhere('post_status', 'pending')
+ ->orWhere('post_status', 'private')
+ ->orWhere('post_status', 'trash');
+ });
+ }
}
$
app/Models/Wp/Post.php に書きましたけれども、これを継承した App\Models\Wp\PostType\Post で次のように使えました。
# php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.3 — cli) by Justin Hileman
>>> App\Models\Wp\PostType\Post::select('ID', 'post_title', 'post_status', 'post_type', 'post_parent')->orderBy('ID', 'asc')->postStatusAll()->toSql();
=> "select `ID`, `post_title`, `post_status`, `post_type`, `post_parent` from `wp_posts` where (`post_status` = ? or `post_status` = ? or `post_status` = ? or `post_status` = ? or `post_status` = ? or `post_status` = ?) and `post_type` = ? order by `ID` asc"
>>>
コントローラーを作成
php artisan make:controller Admin/PostController --resource --model=Models/Wp/Post
今回は、管理者向けの投稿一覧ページを作りますので、自動生成されたコントローラーの index メソッドを作り込んできます。
といっても、継承して定義したモデルと、モデルで定義したスコープを繋げるだけです。短く、意味がわかりやすいようにし、コントローラーを薄くします。
$ git diff app/Http/Controllers/Admin/PostController.php
diff --git a/app/Http/Controllers/Admin/PostController.php b/app/Http/Controllers/Admin/PostController.php
index 35ac414..4e5ae8e 100644
--- a/app/Http/Controllers/Admin/PostController.php
+++ b/app/Http/Controllers/Admin/PostController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Admin;
use App\Models\Wp\Post;
+use App\Models\Wp\PostType\Post as PostTypePost;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
@@ -15,7 +16,9 @@ class PostController extends Controller
*/
public function index()
{
- //
+ return view('admin.posts.index', [
+ 'posts' => PostTypePost::postStatusAll()->get(),
+ ]);
}
/**
$
次に、ルーティングです。どの URL を入力した時にこの PostController@index が実行されるようにするかを定義します。
これはちょっと複雑になってしまいました。
- 管理者ページなので、 URL は admin/posts にしたい。管理者ページは他にも多く作られるため、 Route::prefix で囲む。
- admin プレフィックスを URL につける場合、 対応するコントローラーの namespace は Admin\XxxController となるため、 Route::namespace で囲む。
- 典型的な「CRUD」ルートをコントローラへ割り付けたいので、 Route::resource を使う。
routes/web.php
Route::prefix('admin')->group(function () {
Route::namespace('Admin')->group(function () {
Route::resources([
'posts' => 'PostController'
]);
});
});
リソースコントローラーについては、対応するアクションがコントローラーに無ければ意味がないです。 リソースコントローラにより処理されるアクション コントローラ 5.5 Laravel を元にした表です。今回は、次のアクションとなります。
動詞 | URI | アクション | ルート名 | 内容 |
---|---|---|---|---|
GET | /posts | index | posts.index | 一覧ページを表示する |
GET | /posts/create | create | posts.create | 作成ページを表示する |
POST | /posts | store | posts.store | 入力内容を元に作成する |
GET | /posts/{photo} | show | posts.show | 詳細ページを表示する |
GET | /posts/{photo}/edit | edit | posts.edit | 更新ページを表示する |
PUT/PATCH | /posts/{photo} | update | posts.update | 入力内容を元に更新する |
DELETE | /posts/{photo} | destroy | posts.destroy | 削除する |
php artisan make:controller
時に --resource
オプションを付けましたけれども、これを付けることによって Route::resources
に対応するアクションを過不足なく付けてくれます。便利です。
ビューを作成
- 本当は、 レイアウト定義 し、他のページでレイアウトを使う方が良い。ただ、最初のページなのでレイアウト定義をせず、次のページを作る時にレイアウトを切り出す。
- Laravel は Bootstrap を簡単に使えるようになっているので使う。 Starter template – Introduction · Bootstrap をコピペして土台とした。
- CSS 、 JavaScript は JavaScriptとCSSスカフォールド 5.5 Laravel を利用し、コンパイルしたものを利用する。
- 表は、 Examples – Tables · Bootstrap をコピペしたものを土台とした。
とりあえず固定文字列としていますけれども、 カテゴリーとタグを DB から取得して動的に表示するのが次の作業となりそうです♪
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<title>投稿 - WorldPlace</title>
</head>
<body>
<h1>投稿</h1>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">タイトル</th>
<th scope="col">作成者</th>
<th scope="col">カテゴリー</th>
<th scope="col">タグ</th>
<th scope="col">コメント</th>
<th scope="col">日付</th>
</tr>
</thead>
<tbody>
@foreach ($posts as $post)
<tr>
<th scope="row">{{ $post->ID }}</th>
<td>{{ $post->post_title }}</td>
<td>{{ $post->post_author }}</td>
<td>categories</td>
<td>tags</td>
<td>{{ $post->comment_count }}</td>
<td>{{ $post->post_date }}</td>
</tr>
@endforeach
</tbody>
</table>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
できたもの
https://localhost/admin/posts
おわりに
Laravel 5.5 に慣れ親しんでいる状態です。
ですけれども、それをアウトプットするのをサボっています><。別のものを触り、また戻ってきた時に、なにもかも忘れていると思いますので、五月雨でまとまっていなくても良いので投稿いたしました。
以上です。