質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • PHP

    20395questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Laravel 5

    1894questions

    Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

ファイルサーバに保存したプロフィール画像→loginしているuserの画像をとってくるには?(laravel5.5)

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 211

kazoogon

score 214

laravel5.5 にてユーザがloginしprofile画像をupload

//省略しますが画像を保存する部分のコードです
$image->save(public_path() . '/images/teachers/'. Auth::user()->id.'-'.$file);


保存先は(例)public/images/teachers/4-hoge.jpgという感じになります。
ここでhtml側での表示

{!! Html::image('images/teachers/4-'(ここをどう書けば??),'profile', array('class' => 'thumb', 'width' => 235)) !!}


コード内にも記載しましたが、path指定部分にどう書いたらよいか?
→正規表現を使用するのだと思うのだが、preg_matchを使ってもtrueかfalseで返ってくるので意味はない。。
→DBに保存したら簡単に取ってこれるが、https://teratail.com/questions/81233 のページにもあるように画像をDBに保存する意味はほとんどない
→file serverに保存。

しかしその場合には指定したuserごとの画像を取ってくる方法が分からず困っております。
よろしくお願いいたします。

質問を受けての追記

例えば「メインのプロフィール画像」「そのほかプロフィール画像」「userがそれぞれ投稿するブログの画像」
の3つを扱いたい場合はImageテーブルに下記カラムを作成

id(autoincrement)
user_id(Userモデルとリレーション)
blog_id(Blogモデルとリレーション)
profile_main_name
profile_sub_name
blog_name

保存するパスはそれぞれ

//一日ごとにディレクトりがどんどん作られるということですよね??
profile/images/teachers/profile/main/YYYY/MM/DD
profile/images/teachers/profile/sub/YYYY/MM/DD
profile/images/teachers/blog/YYYY/MM/DD

(また違うユーザが同じ日に同じ名前のファイルをuploadする可能性があるので、ファイル名の最初にAuth::user()->id; でとってきた値を挿入する必要有?)

そして表示するときは
・メインprofile画像 
→ {!! Html::image("images/teachers/profile/main/.{{$user->image->profile_main_name}}.","profile" !!}
・サブprofile画像 
→ 上記をforeachでまわす(profile_main_name→profile_sub_nameに変更)
・ブログ画像 
→ {!! Html::image("images/teachers/blog/.{{$blog->image->blog_name}}.","pic" !!}

という感じになるのでしょうか?
またこれだとパスに日付を入れる意味が「ただ見たらいつupされたかがわかる」になり回答者様の意図と違ってしまいますが。。
そのあたりも含めまだ設計で曖昧な部分があります、申し訳ございませんが、よろしくお願いいたします。

質問を受けての追記②

 return Image::create([
    'name' => "$fileName",
 ]);


このコードによりimagesテーブルにfile名(unip()でとった値)がinsert
→image_userのimage_id&user_idにもinsertが必要かと思いますがただ省略されているだけですよね???

$path = 'public/images/' .date('Y/m/d/', strtotime($user->images[0]->created_at)) . "$image->name";
$image->nameでloginしているユーザのidを取ってこれるのか???
→controller側で

   $user = Auth::user()->id;
   $image = DB::table('images')
         ->where('id', '<>', $user)
         ->groupBy('status')
         ->get();


いやしかしこれだとimagesのidはただauto_incrementで入ってる数値なので意味がない・・

すみません、たぶん何かおおいなる勘違いをしているような気がしますが、、よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

例として記載されている4-hoge.jpghoge.jpgは、ユーザがアップロードした際のファイル名そのものでしょうか。(ユーザごとに可変でしょうか)

対応として3つ考えました。いかがでしょうか。

  • ファイル名をDBに保存し、表示の際にDBから取得して「(ここをどう書けば??)」部分に入れる
  • ファイル名がid-xxxx.yyyという形式が守られるのであれば、public/images/teachers/フォルダ内をユーザID+ハイフン(例でいうと"4-")の前方一致検索でファイル名を取得して「(ここをどう書けば??)」部分に入れる
  • いっそのこと、ファイル名を「ユーザID.jpg」というファイル名にして保存するようにし、表示時も「ユーザID.jpg」として扱う

 質問を受けての追記への追記

ディレクトリパスについては、「運用上画像ファイルをどのように扱いたいか」で仕様を決めるのが良さそうに感じました。この辺りを明記されるとさらに良い回答が得られそうです。
運用が考慮できていないかもしれませんが、例えばこういった規約にしてみるのもありですかね。

  • プロフィール画像はユーザIDに紐づくような規約(ディレクトリにユーザIDまたはそれに準ずる情報を使う)
  • ブログ画像は日付や記事IDなど一意性のある情報と連番を組み合わせた規約

ユーザ退会のような業務がある場合、関連する画像を削除しやすく設計しておくと良さそうですね。
またプロフィール画像は最新版だけあればいい、のであれば、ユーザごとにファイル名を固定にしておいて上書きする運用もありかと思います。
ブログ画像は記事削除時に画像ファイルも削除したいようであれば、記事IDに紐づく形で管理できるといいですね。

この場合、プロフィール画像とブログ画像は別のテーブルで管理する方が良さそうです。(保持の単位が異なるので)

  • プロフィール画像の情報はユーザ情報と同一テーブル、または1対1で紐づくテーブルで管理する
  • ブログ画像の情報は記事情報に1対Nで紐づくテーブルで管理する

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/21 11:25 編集

    回答ありがとうございます。
    「前方一致検索」についてですが「'images/teachers/'./^{{Auth::user()->id}}/,」という書きかたでしょうか?(これはsyntax errorが出るので違うと思いますが。。) それともpreg_matchを使うのか??と思いましたがこれはやはりtrue or falseでreturnされるので違いますし。。 
    何か勘違いしていたら申し訳ないです、よろしくお願いいたします。

    キャンセル

  • 2018/04/21 16:34

    誤解を与えてしまったかもしれません。
    『public/images/teachers/フォルダ内をユーザID+ハイフン(例でいうと"4-")の前方一致検索でファイル名を取得して』とは、サーバローカルのディレクトリ内ファイルの検索です。readdir等を利用した検索処理を自前で実装することを想定しています。(http://php.net/manual/ja/function.readdir.php
    imageタグに正規表現でのファイル名指定は行えません。

    キャンセル

  • 2018/04/21 16:36

    方法論としては、他の回答者さまからもあるように、「ファイル名をDBに保存し、表示の際にDBから取得して「(ここをどう書けば??)」部分に入れる」が、性能面も鑑みた上で良いかと思います。

    キャンセル

  • 2018/04/23 17:12

    ありがとうございます。
    takyafuminさんのおっしゃるテーブル構造はyukkenoottunaさんの提案された中間テーブルを使用するのではなく
    「user_imagesテーブル(profileカラム作成し0,1で管理??)」
     →profile画像1枚・userその他の画像の管理を考えているので、modelは1対N?
    「blog_imagesテーブル」
     →1対N
     
     またそれぞれの画像保存pathはimages/teachers, images/blogs などと分ける設計でしょうか??

    キャンセル

  • 2018/04/24 00:29

    > takyafuminさんのおっしゃるテーブル構造はyukkenoottunaさんの提案された中間テーブルを使用するのではなく
    そうですね。そのような主旨で回答致しました。

    > 「user_imagesテーブル(profileカラム作成し0,1で管理??)」
    >  →profile画像1枚・userその他の画像の管理を考えているので、modelは1対N?
    そのような要件であれば、1対Nで良いかと思います。
    画像の種類を判別するための区分でprofile、その他を分けると拡張性も持てますね。

    > 「blog_imagesテーブル」
    >  →1対N
    1記事に複数の画像が使われる可能性を考慮し、このように考えました。

    キャンセル

  • 2018/04/24 00:30

    > またそれぞれの画像保存pathはimages/teachers, images/blogs などと分ける設計でしょうか??
    この点は、どのように扱いたいかで設計するのが良いかと思います。
    回答としては、一例として上記のように分ける想定でした。

    キャンセル

  • 2018/04/24 00:31

    保存時のファイル名については重複するとまずいので、
    yukkenoottunaさまの回答にあるようにユニークなファイル名を付与する仕組みがあるといいですね。

    キャンセル

  • 2018/04/24 21:49

    丁寧な回答ありがとうございました、今回はtakayafuminさんのテーブル構造+yukkenoottunaさんのファイル作成・保存方法を合体させた方法でいこうと思います。
    非常に勉強になりました、ありがとうございました!

    キャンセル

  • 2018/04/24 23:43

    頑張ってください!!

    キャンセル

+1

質問主さんが参考に上げているページにもあるように、画像のパスをDBに保存するのが得策かと思います。
また、プロフィール画像は更新する可能性があるので、ファイル名やパスはタイムスタンプを入れるなど識別しやすい形にしたほうがいいのではないでしょうか。

それをふまえ、以下のような仕様はいかがでしょうか。

  • 画像アップロード時
    画像ファイルはpublic/image/YYYY/MM/DD/ディレクトリに格納。
    Imageモデルを作り、nameカラムにファイル名を格納。
    UserモデルからはImageモデルへのリレーションを貼る。

  • 画像表示時
    Userオブジェクト取得時に、同時に取得されるImageオブジェクトのcreated_atnameから画像のパスを割り出す。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/21 11:10 編集

    回答ありがとうございます。
    こちら表示時はAuth::user()->id; でとってきた値を参照してImageオブジェクトのcreated_atとnameより画像のパスを取ってくる。
    こちらは理解できたのですが、profile画像は1userにつき1つの写真だけなので、「userテーブルに「img」みたいなカラムをただ追加して名前を保存しとく」という方法が単純に思いつくのですが、なにかデメリットがあるのでしょうか??

    キャンセル

  • 2018/04/21 11:45

    すぐに思いつく限りで3つほどありますので記載します。

    ①タイムスタンプからファイルパスが割り出せなくなる
    created_at もしくは、modified_atからファイルパスを特定する仕様の場合、そのどちらかのカラムのタイムスタンプを写真がアップロードされた日付で固定する必要があります。
    しかしながら、プロフィールは更新される可能性が高いものですので、これが難しいと思います。

    ②画像をアップロードしたユーザーの特定ができなくなる
    プロフィール画像自体も更新される可能性があります。
    リレーションを貼っていない場合、過去の画像が誰がアップロードしたものかわからなくなってしまいます。
    プロフィール画像として違法画像がアップロードされる→別画像をアップロードし直す とされた場合、
    その違法画像をアップロードした犯人がわからないなど困った事態に陥ります。
    (このためImageモデルからUserモデルへのリレーションも必要です。 User対Imageが1対多)

    ③プロフィール以外にも画像を使う可能性がある
    サービスの機能が増えていく可能性を考えると、画像ファイルが別のコンテンツにも使用される可能性が出てくると思います。
    現状の延長線でいけばプロフィール画像のサムネイル画像を作りたい場合などです。
    また、ユーザーごとにブログ記事を書けるような機能が追加されるかもしれません。
    そう言った場合、Imageモデルがあると便利かと思います。

    キャンセル

  • 2018/04/21 13:36 編集

    すみません、たぶん誤解をあたえてしまったかもしれません。
    usersテーブルにimgカラム作成
     →パスをnameカラムに挿入する場合はuser_idを参照し、データがあればupdate, なければinsertします(profile画像は1userにつき1つなので)
    →ですので①に関してはuser_idを参照しプロフィール画像を取ってこれる + ②に関してはそのuser_idのimgカラムにprofileのパス名があるので特定はできるかと?思われます

    しかし確かに③を考えるとImageモデルは作っておいた方がbestだとおもわれますので、そうしようとは思っております。

    キャンセル

  • 2018/04/21 14:10

    本文に設計について追記いたしました、申し訳ございませんがそちらもよろしくお願いいたします。

    キャンセル

checkベストアンサー

0

追記を拝見し、自分の過去のコードや設計を再度確認しました。
この設計が100%正しいというわけでは無いと思いますが、一例として御覧ください。

 モデルとリレーション

  • Userモデル対Imageモデルは一対多の関係だが、リレーション上多対多にする。
  • Userモデル以外からImageモデルを使用する場合も、多対多のリレーションを貼る。
  • この対応にて、User, Imageモデル共にテーブルにて双方のidを格納するカラムが必要なくなる
  • 中間テーブルimage_userテーブルにて管理

 マイグレーション

かかるマイグレーションは下記のようになる(up以外省略)

 create_images_table.php

public function up()
{
    Schema::create('images', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name')->comment('写真のファイル名');
        $table->softDeletes();
        $table->timestamps();
    });
}

 create_image_user_table.php

public function up()
{
    Schema::create('image_store', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('image_id')->comment('imageテーブルのID');
        $table->unsignedInteger('user_id')->comment('userテーブルのID');
        $table->timestamps();
    });
}

 画像の格納処理

public function storeImage($image)
{
    // [storage/app/public/images/年/月/日]のディレクトリを作成
    Storage::makeDirectory('public/images/' . date('Y/m/d'));
    // 一意のファイル名を作る
    $fileName = uniqid();
    $directoryPath = public_path(). "/storage/images/" . date('Y/m/d');
    // 基本の横幅を設定
    $basicWidht = 250;
    // 保存をする
    $this->makeImage($image, $basicWidht, null, "$directoryPath/$fileName");
    // ファイル名をimagesテーブルにレコードし、Imageモデルを返す
    return Image::create([
        'name'      => "$fileName",
     ]);
}

画像はその性質によらず、[storage/app/public/images/年/月/日]のディレクトリに保存します。
また、ファイル名はユーザーがアップロードしたものをそのまま使うのではなく、uniqid()によりユニークになるようにしてあげます。
このディレクトリ+ファイル名でユニーク性を保ち、かつ使われていない過去ファイルをディレクトリごと圧縮する際などに便利になります。

 画像パスの取得

$path = 'public/images/' .date('Y/m/d/', strtotime($user->images[0]->created_at)) . "$image->name";
$user->image->url = Storage::url($path);


画像パスは取得処理が面倒なので、コントローラやサービスクラスにて処理し、その後viewに渡すようにしています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/21 19:40 編集

    回答ありがとうございます。
    申し訳ございませんが、まだ疑問点がありますので本文に追記させていただきました(質問を受けての追記②)。
    恐縮ですがよろしくお願いいたします。

    キャンセル

  • 2018/04/21 21:16


    そうです。省略しています。
    プロフィール画像の保存は大枠で見れば、ユーザーの更新に内包されているはずです。
    コントローラにて、`$user->images()->sync([$image->id]);`で中間テーブルが更新されるはずです。


    > $image->nameでloginしているユーザのidを取ってこれるのか???

    これは私の理解力不足で意図が汲みきれません。ごめんなさい。
    ログイン済みのユーザーであれば、ユーザー情報がすでに取得できている認識です。
    このため、リレーションの貼ってあるImageオブジェクトはユーザー情報に内包され取得できていると思います。
    つまり、`$user->images[0]`に最新のプロフィール画像情報がImageオブジェクトとして格納されている認識ですがいかがでしょうか。
    また、Imageオブジェクトのidの必要性はわかりかねますが、必要であれば`$user->images[0]->id`で取れるのではないかと思います。

    キャンセル

  • 2018/04/24 21:47

    丁寧な回答ありがとうございました。
    最終的には中間テーブルは作らずにteachersやusers,blogsのdirectoryを作成、DBにはuser_images[id, user_id, profile]などどし、profile写真にはカラムに1を入れるなどして対応していこうと思っております。
    しかしyukkenoottunaさんのおっしゃられたファイル名を作るときにはuniqid();を使用します、こちらも大変勉強になりました。
    非常に助かりました、ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    20395questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Laravel 5

    1894questions

    Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

  • トップ
  • PHPに関する質問
  • ファイルサーバに保存したプロフィール画像→loginしているuserの画像をとってくるには?(laravel5.5)