CakePHP5入門【CakePHP5応用編④】アップロード関係
A子
あくまでも試案と言うか、叩き台だけどね
(ここからさらにブラッシュアップしていけば良いんじゃないかな)
B美
A子
C菜と二人で「ああだ、こうだ」と結構時間をかけたよ
C菜
でも、良い経験になったと思います~
B美
A子
あんた私と同い年よね?
ま、まぁ良いわ
これがトップページの「index.php」よ!
C菜
A子
(CakePHP5基礎編⑤で習ったからね)
C菜
B美
あ、ついでに「PostsController.php」の「index」メソッドを変更しておきましょう
$this->Posts->find()->where(['delete_flag' => 0]);
$posts = $this->paginate($query, ['limit' => MAX_PAGE, 'order' => ['id' => 'desc']]); |
C菜
つまり、削除レコードの「delete_flag」には「0」以外(例えば「1」)をセットするわけですね~
B美
あと、並べ替えについては、「id」の降順でソートする…ってわけ
A子
つまり「bbsapp/config」ディレクトリ内の「const.php」にこの定義を追加すれば良い…ってことか
define("MAX_PAGE", 2); |
B美
定数化しておくと、あとになって変更するときにめっちゃ便利なのよね
あと、投稿番号の「背景色」とタイトルの「背景色」の両方についても定数化しておくと良いかもよ
(まぁ、別にやらなくても良いんだけど)
C菜
define("NUM_COLOR", "#b0e0e6"); //投稿番号の背景色
define("TITLE_COLOR", "#fff8dc"); //タイトルの背景色 |
を「const.php」に追記して…っと~
A子
C菜
A子
C菜
A子
あ、上から4行目の背景色指定の箇所については、さっきの定数で書き換えておいたから…
C菜
B美
予想以上の出来栄えと言っても過言ではないわね
A子
Controllerである「PostsController.php」なんだけど…
C菜
でも、ここから先が分かりません~
B美
ファイルとして保存するのだから、まずはその格納場所を決めましょう
C菜
B美
A子
B美
C菜
あ、だから「bbsapp/webroot」の中にCSSファイルを格納している「css」ディレクトリや画像ファイルを格納している「img」ディレクトリがあるんですね~
A子
B美
(学習の一環として…)
「MATE端末」を開いて、次のように入力してね
cd html/bbsapp/webroot[Enter]
mkdir files[Enter] chmod 757 files[Enter] |
これで「bbsapp/webroot」の下に「files」ディレクトリが生成されるわ
A子
B美
要するに、「アクセス権限」の設定ね
まぁ、あまり気にしなくても良いわ
(「webroot」の下にディレクトリを作るときに実行する「おまじない」みたいなものだと思っていればOK)
注:パーミッションについては、人によって異なる考え方があります
(ゆるくするか、厳しくするか…の違い)
A子
(データベース設計のときにそう言ってたし…)
B美
オリジナルのファイル名が日本語だったり、格納するファイル名が重複したりするとまずいからね
さて、どうする?
C菜
半角で20文字以上とかだったら、ファイル名が重複することはないと思うんですけど~
A子
(アップロードできるファイル数は、0個または1個なんだし…)
B美
ただし、C菜の方法では「重複しないことを担保する仕組み」が必要だし、A子の方法の場合「投稿番号をどのように取得するか」がポイントになるけどね
C菜
B美
その属性が付いている場合、データベース側で勝手に連番を付けてくれるのよ
(それが「投稿番号」ってわけ)
A子
だとすると、レコードを新規作成しないと「id」の値は決まらないのか…
あれ?
「filepath」に入れる文字列って、「id」の値が決まらないと付けられないわけだから、「ファイル名を投稿番号にする」案ってダメじゃん…
B美
まずは(アップロードするファイルが有ろうが無かろうが)「filename」と「filepath」フィールドに空文字列をセットしたレコードを新規作成します
(もちろん「タイトル」や「本文」、「削除フラグ」には値をセットしてね)
で、もしもファイルが有る場合には…
・「id」の値を取得し、その値に拡張子を付けたものをファイル名とし、ファイル保存
・次に、ファイル保存したパスを「filepath」フィールドに設定してレコードを更新
(もちろん「filename」フィールドにはオリジナルのファイル名をセットすること)
C菜
そっちのほうが簡単そうですし、A子社長の案でいきましょう~
B美
それじゃ、まずは全体の流れを説明するわね
1.もしもPOSTされたら「$this->request->getData('○○')」で送信されてきたデータを取得します
例えば
if ($this->request->is('post')) {
$○○ = $this->request->getData('○○'); ・・・ } |
A子
B美
例えば
$post = $this->Posts->newEmptyEntity();
$post->○○ = △△; |
…って感じね
C菜
$post = $this->Posts->newEmptyEntity();
$post->title = $title; $post->body = $body; $post->filename = ''; $post->filepath = ''; $post->delete_flag = 0; |
…ということでしょうか~?
B美
ただし、まだデータベースへの登録はできていないからね
そのためには…
3.「$this->Posts->save()」メソッドを呼び出して、戻り値が「true」だったら成功、「false」ならば失敗よ
(このメソッドへの引数は、さっきの「newEmptyEntity()」の戻り値を格納した変数を渡します)
A子
…ってことは
$this->Posts->save($post); |
で良いのかな?
C菜
if ($this->Posts->save($post) == true) {
} else { } |
…って感じで~
A子
C菜
if ($this->Posts->save($post)) {
} else { } |
これでどうでしょう~?
B美
4.画像ファイルが指定されたかどうかを判別するには、「$upload->getClientFilename()」メソッドの戻り値が空文字列かどうかで分かるの
(これによってオリジナルのファイル名を取得できるわ)
C菜
$original_filename = $upload->getClientFilename();
if ($original_filename != '') { (ファイルがあるときの処理) } |
B美
5.さっきデータベース登録(レコード挿入)したことで主キーの値が決まったから、その値を知るには「$post->id」でOKよ
A子
if ($original_filename != '') {
$new_id = $post->id; } |
B美
んじゃ、次!
6.ファイル名を決めるには「拡張子」が必要だから、それを取得しましょう
「pathinfo($original_filename, PATHINFO_EXTENSION)」関数を呼べば、その戻り値が「gif」や「png」って感じになるわよ
あ、ただし、人によっては「大文字」で拡張子を付けてるかもしれないから注意してね
C菜
B美
一般的には「小文字」に統一する人のほうが多いかな
A子
if ($original_filename != '') {
$new_id = $post->id; $extension = mb_strtolower(pathinfo($original_filename, PATHINFO_EXTENSION)); $filepath = $new_id.".".$extension; } |
…で、どうよ
B美
このWebアプリのドキュメントルートの下の「files」ディレクトリ内に格納したい場合、「$filepath」はこうなるの
$filepath = ".".DS."files".DS.$new_id.".".$extension; |
ちなみに、先頭の「.」はカレントディレクトリって意味なんだけど、ここでは「ドキュメントルート」って意味になるわ
C菜
B美
定数になっている理由は、実行環境(Webサーバ)がLinuxでもWindowsでもうまく動くように…
(Linuxでは「/」だけど、Windowsでは「\」が区切り文字だからね)
さぁ、いよいよファイルの保存を行います
7.「$upload->moveTo()」メソッドを呼べば、指定した場所に指定した名前で保存されるわ
(このメソッドの引数には、ファイルの「フルパス」を渡すってこと)
A子
if ($original_filename != '') {
$new_id = $post->id; $extension = mb_strtolower(pathinfo($original_filename, PATHINFO_EXTENSION)); $filepath = ".".DS."files".DS.$new_id.".".$extension; $upload->moveTo($filepath); } |
そんなに難しくないかな
B美
($original_filenameを「filename」フィールドに、$filepathを「filepath」フィールドにセットしてから更新しないとね)
レコード更新の手順も割と簡単よ
8.「$this->Posts->get(主キー番号)」の戻り値を変数へ代入し、その変数を使って値をセットしていくだけ
C菜
$new_post = $this->Posts->get($new_id);
$new_post->filename = $original_filename; $new_post->filepath = $filepath; |
もしかして、このあとsaveメソッドを呼ぶんですか~?
B美
どうするか分かる?
C菜
$new_post = $this->Posts->get($new_id);
$new_post->filename = $original_filename; $new_post->filepath = $filepath; if ($this->Posts->save($new_post)) { } else { } |
…って感じでしょうか~?
B美
あ、でも失敗したときだけ何らかの処理をすれば良いから
if ($this->Posts->save($new_post) == false) {
} |
または
if (!$this->Posts->save($new_post)) {
} |
…って感じかな
A子
B美
論理演算子の「not」を意味する記号よ
(要するに、真偽を逆転させるってこと)
C菜
B美
例えば「$this->Flash->success('成功時のメッセージ')」は成功時のメッセージで、「$this->Flash->error('失敗時のメッセージ')」が失敗時のメッセージよ
あと、リダイレクトというのは、別のページへ移動すること
例えば「return $this->redirect(['action' => 'index']);」を実行した瞬間、トップページである「index」に遷移するってわけ
A子
if (!$this->Posts->save($new_post)) {
$this->Flash->error('画像ファイル情報のデータベース登録に失敗しました。'); return $this->redirect(['action' => 'index']); } |
B美
C菜
if ($this->request->is('post')) {
$title = $this->request->getData('title'); $body = $this->request->getData('body'); $upload = $this->request->getData('upload'); $post = $this->Posts->newEmptyEntity(); $post->title = $title; $post->body = $body; $post->filename = ''; $post->filepath = ''; $post->delete_flag = 0; if ($this->Posts->save($post)) { $original_filename = $upload->getClientFilename(); if ($original_filename != '') { $new_id = $post->id; $extension = mb_strtolower(pathinfo($original_filename, PATHINFO_EXTENSION)); $filepath = ".".DS."files".DS.$new_id.".".$extension; $upload->moveTo($filepath); $new_post = $this->Posts->get($new_id); $new_post->filename = $original_filename; $new_post->filepath = $filepath; if (!$this->Posts->save($new_post)) { $this->Flash->error('画像ファイル情報のデータベース登録に失敗しました。'); return $this->redirect(['action' => 'index']); } } } else { $this->Flash->error('新規投稿のデータベース登録に失敗しました。'); return $this->redirect(['action' => 'index']); } $this->Flash->success('新たな投稿を登録しました。'); return $this->redirect(['action' => 'index']); } |
B美
よくまとまっているわ
それじゃ、実際に上記のコードを打ち込んでから、実行確認してみてね
A子
ちなみに、コメントも入れてみた
C菜
A子
なんだかあっさりと成功したよ
(B美先生のおかげなんだけど…(苦笑))
B美
C菜
A子
B美
なかなか良いんじゃないかしら
さて、それじゃ今回はここまでにしておきましょう
次回は、アクセスログ記録用テーブルに対する操作をやっていきます
(とは言っても、全然難しくはないからね)
A子