- 投稿日:2021-01-29T23:20:20+09:00
いいね機能をLaravelとVue.jsで実装②
こちらの記事はパート2です。
前回の記事はこちらから↓↓
https://qiita.com/tanaka2020/items/8ec8ae0fdd3ad4409c74実装
4.ルートの設定
routesディレクトリ内のweb.phpを編集
Route::get('/posts/{post?}/firstcheck', 'LikeController@firstcheck')->name('like.firstcheck');・・・1 Route::get('/posts/{post?}/check', 'LikeController@check')->name('like.check');・・・21.Vue読み込み時にLikeControllerのfirstCheck()アクション呼び出し
2.いいねボタンクリック時に記事id別にLikeControllerのCheck()アクション呼び出し5.Vue Components作成
resouces
|ーjs
|ーcomponents
|ーLikeComponent.vue<template> <div> <button v-if="status == false" type="button" @click.prevent="like_check" class="btn btn-outline-warning">♡</button><a v-if="status == false" href="#">{{count}}</a> <button v-else type="button" @click.prevent="like_check" class="btn btn-warning">♥</button><a v-if="status == true" href="#">{{count}}</a> </div> </template> <script> export default { props: ['post_id'],・・・1 data() { return { status: false,・・・2 count: 0, } }, created() { this.first_check()・・・3 }, methods: { first_check() { const id = this.post_id const array = ["/posts/",id,"/firstcheck"]; const path = array.join('') axios.get(path).then(res => { if(res.data[0] == 1) { console.log(res) this.status = true this.count = res.data[1] } else { console.log(res) this.status = false this.count = res.data[1] } }).catch(function(err) { console.log(err) }) }, like_check() { const id = this.post_id const array = ["/posts/",id,"/check"]; const path = array.join('') axios.get(path).then(res => { if(res.data[0] == 1) { this.status = true this.count = res.data[1] } else { this.status = false this.count = res.data[1] } }).catch(function(err) { console.log(err) }) }, } } </script>1.blade側はより投稿記事のidが渡されます。
<like-component :post_id="{{$post->id}}"></like-component>2.statusがfalseだといいねされていない状態です。
3.Vue読み込み時にLikeControllerのfirstCheck()アクションが実行されます。まとめ
今回はAxiousで実装しており非同期でLikeContorollerのアクションを呼び出して、いいね機能を実装しています。もっと簡単で出来る方法や綺麗な記述ありましたらご教授お願いします。
- 投稿日:2021-01-29T22:27:15+09:00
いいね機能をLaravelとVue.jsで実装①
投稿型のサイトを作成していて、
いいね機能を実装したいと思ったので作成しました。環境
・PHP 7.4.9
・Composer version 1.10.13
・Laravel Framework 7.30.1
・vue/cli 4.3.1流れ
1.投稿機能の実装(今回の記事では省略)
2.いいね機能用のdatabeseテーブルの作成
3.Vueコンポーネントの作成
4.Controllerの作成
5.ルート設定
6.view表示実装
1.投稿機能の実装
今回は省略!!
2.いいね機能用のdatabeseテーブルの作成
$ php artisan make:model Like -m-mをつけることでモデルと一緒にマイグレーションファイルも作ってくれます。
※databaseの設定は省略します。設定はconfigフォルダdatabase.phpと.envを編集作成したマイグレーションファイルの編集
public function up() { Schema::create('likes', function (Blueprint $table) { $table->id(); $table->integer('posts_id'); $table->integer('user_id')->unable(); $table->integer('like')->default(0); $table->timestamps(); }); }likeカラムの値が0か1かでいいねされているかを判断するように実装します
作成したモデルLike.phpの編集
class Like extends Model { protected $fillable = [ 'posts_id','user_id','like' ]; }"fillable"はホワイトリストです。$fillableで指定したカラムは値が代入可能です。
マイグレーションの実行
php artisan migrate3.コントローラーの作成
php artisan make:controller LikeControllerVueのコンポーネントから呼び出されるアクションを書いていきます。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Post;//投稿機能のモデル use App\Like; use Auth; class LikeController extends Controller { //コンポーネント初期読み込み時(created)に呼び出される public function firstcheck($post) { $user = Auth::user(); $likes = new Like(); $like = Like::where('posts_id',$post)->where('user_id',$user->id)->first(); if($like) { $count = $likes->where('posts_id',$post)->where('like',1)->count(); return [$like->like,$count]; } else { $like = $likes->create([ 'user_id' => $user->id, 'posts_id' => $post, 'like' => 0 ]); $count = $likes->where('posts_id',$post)->where('like',1)->count(); return [$like->like,$count]; } } //いいねボタンを押した時に呼び出される public function check($post) { $user = Auth::user(); $likes = new Like(); $like = Like::where('posts_id',$post)->where('user_id',$user->id)->first(); if($like->like == 1) { $like->like = 0; $like->save(); $count = $likes->where('posts_id',$post)->where('like',1)->count(); return [$like->like,$count]; } else { $like->like = 1; $like->save(); $count = $likes->where('posts_id',$post)->where('like',1)->count(); return [$like->like,$count]; }; } }長くなってきたので次に続く。。。。。。
- 投稿日:2021-01-29T22:04:08+09:00
laravel php artisanコマンドであってるはずなのに、何かしらのエラーが出る場面でまずやってみること
php artisanで何かしらのコマンドでエラーになり、間違ってないはずなのにエラーが出る。
これで何度も激おこになったことあるのでメモ。terminalphp artisan clear-compiled composer dumpautoload今現在記憶に残っているのは、
・socialite
・seeder思い出したら追記します。
- 投稿日:2021-01-29T22:04:08+09:00
laravel php artisanコマンドであってるはずのに、何かしらのエラーが出る場面でまずやってみること
php artisanで何かしらのコマンドでエラーになり、間違ってないはずなのにエラーが出る。
これで何度も激おこになったことあるのでメモ。terminalphp artisan clear-compiled composer dumpautoload今現在記憶に残っているのは、
・socialite
・seeder思い出したら追記します。
- 投稿日:2021-01-29T21:56:55+09:00
Laravel SailでM1 MacBookのDocker開発環境構築
前置き
ご購入検討中のエンジニアさん
結論、“まだ”M1のMacはおすすめできません。
VagrantやVirtualBox、何を入れるにも一苦労。
導入できたとしても仮想環境は起動しない・・・。
(ARM対応してないものが多い)私はかれこれ2週間近く試してやっとLaravelが動く方法を見つけました。
PHP + MySQL(mariaDB)使いたい方は参考になると思います。
私が実際に試して導入できた方法をご案内します。LaravelSail導入
Laravel公式のDocker 開発環境構築ツール使います。
ターミナルからコマンドを実行。curl -s https://laravel.build/sample-app | bash[sample-app]の部分はお好みで。
起動します
cd sample-app ./vendor/bin/sail upうまく行かない・・・ので
docker-compose.ymlの1箇所修正
image: 'mysql:8.0'の部分を↓
image: 'mariadb'再トライで、無事に起動
./vendor/bin/sail up以上です。
まとめ
今まではHomestead使っていたので、
M1 MacではVirtualBoxとVagrantが動かないのがわかったとき、めっちゃ焦りました。いろいろ調べてLaravelSailというのを見つけて、初めて導入しました。
かなりいいです。Laravel開発のスタンダードになりそうな予感。よし、開発すすめるぞー
開発中のツーリングアプリ
https://touringbook.com/
- 投稿日:2021-01-29T18:26:10+09:00
初心者必見!固定値(キー、URLなど)は.envファイルに書いて再利用しよう!!(Laravel偏)
皆さんこんにちは!
3日前くらいに書いた記事(初心者必見!固定値(キー、URLなど)は.envファイルに書いて再利用しよう!!)が中々好評だったので、改めて
env
ファイルについて書いていこうと思いました。前回書いた記事では、Vue.jsで
env
ファイルの使用方法を書きました。今回はタイトルにも書いてる通り、「Laravel」偏です。
今一度、
env
ファイルについて説明しようと思います。知っている方は、スルーで結構です。
Javascriptでバックエンドとやり取りを行うとき、恐らく多くの方が以下のように書かれると思います。
axios.get('http://127.0.0.1')ここで問題なのが、これを実際にサーバーにアップロードする時って、
http://127.0.0.1
のようなローカルホストとは接続を行いませんよね?また、その都度バックエンドのURLをコピーしてくるのもナンセンス。
そんなこんなで出てくるのが
env
ファイル!!・一度値を設定してしまえば、使いまわしOK!
・シークレットキーなどのセキュリティ面でも活躍!(ファイルに直接キーを入力するのは、githubにあげるときに見られてしまうため)
・ローカルと本番環境の使い分けができる!(今回はここはやりません)
こんなに魅力的なんです!
まぁ話はこの辺にしておいて、当然の如くVue.jsとLaravelではやり方が違うので、使い方を一緒に見ていきましょう!
envファイルに定数を設定
プロジェクト直下に
.env
というファイル名があると思うので、それを開いてみてください。そしたら、以下を例に値を設定して見て下さい。
RECAPTCHA_SECRET_KEY = 'ciascihsiachuhewurvuvurw'定数名は、自分で分かりやすいように設定してください!
configファイルで値の設定
次に、
config
ディレクトリを開き、app.php
を開いてください。Vue.jsでは
.env
ファイルに記述して終わりだったのですが、Laravelでは.env
ファイルを読み込まない設定なので、configで設定します。config/app.php'RECAPTCHA_SECRET_KEY' => env('RECAPTCHA_SECRET_KEY')先ほど
env
ファイルで設定した名前をenv(name)
で呼び出し、'RECAPTCHA_SECRET_KEY'
のように適当に名前を付けて下さい!呼び出し
最後に、ここで設定した値を呼び出します。
の前に、いったんキャッシュを消去します。
コマンドプロンプトにて、以下のコマンドをうちます
php artisan config:cache最後に呼び出していきます。
index.php$secret = config('app.RECAPTCHA_SECRET_KEY');この
app.
は何かというと、config
ディレクトリのapp.php
のRECAPTCHA_SECRET_KEY
を呼び出すという意味です。このようにして、固定値を呼び出すことができます。
いかがだったでしょうか?
簡単かつ作業効率もあがるのでぜひこの機会にガンガン使ってください!!
以上、「初心者必見!固定値(キー、URLなど)は.envファイルに書いて再利用しよう!!(Laravel偏)」でした!
良ければ、LGTM、コメントお願いします。
また、何か間違っていることがあればご指摘頂けると幸いです。
他にも毎日初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい!!
Thank you for reading
- 投稿日:2021-01-29T16:32:07+09:00
Laravelでほんの少しハイレベルな検索機能を作ってみた。(初心者向け)
こんにちは、しろうです。
現在、Laravelで小説サイトを作成しています。
そんな中で、(自分的には)ちょっとだけハイレベルな検索機能を作ってみたので、忘れないようにメモとして残しておきます。
検索機能を実装したい人はぜひ、参考にしてください。
もしミスとかあればコメントして頂けると大変有り難いですm(_ _)m
前置き
・クラス名とかひどいのあるかと思いますが、気にしないで頂けると幸いです。
・Formファサードを使ったり、素のformを使ったり混在していますが、許してくださいm(_ _)m
・cssは貼り付けていないので、皆さん側で好きなように変更してください。今回作る検索機能
・複数条件での絞り込み検索ができる。(例:キーワードとジャンルで絞り込む。)
・現在の検索条件がタグとして表示される。
・タグを削除すれば、検索条件から削除される。検索機能のイメージ動画
ちなみにイメージ動作は下記の通りです。
下記のように、他のページからジャンルとかをクリックしたら、そのジャンル名で検索してくれる機能もつけてます。
さて、じゃあどんどん作っていきます。
とりあえず検索機能を作る
viewファイルの作成(検索項目の部分)
とりあえず検索項目を作成していきます。
今回は「キーワード検索」と「ジャンル検索」のみを作成しています。
また、CSSは貼り付けないので、皆さんの方で好きなように変更よろしくお願いしますm(_ _)msearch.blade.php{{ Form::open(['route'=>'search','method'=>'GET','id'=>'side_search_form','autocomplete'=>"off"]) }} <div class="search_keyword"> <div class="search"> <input name="keyword" value="{{request('keyword')}}" type="search" class="searchTerm" placeholder="キーワード検索"> <button type="submit" class="searchButton" form="side_search_form"> <i class="fa fa-search fa-xs"></i> </button> </div> </div> <div class="search_genre search_side_item_border"> <h4>ジャンル</h4> <div class="ripples_radio"> {{ Form::radio('genre','', isset(request()->genre) ? false :true, ['id'=>'search_genre_none']) }} {{ Form::label('search_genre_none', '指定なし', []) }} </div> @foreach ($genres as $index => $genre) <div class="ripples_radio"> {{ Form::radio('genre',$genre, $genre == request()->genre ? true :false, ['id'=>'search_genre'.$index]) }} {{ Form::label('search_genre'.$index, $genre, []) }} </div> @endforeach </div> {{ Form::close() }}解説していきます。
・
'autocomplete'=>"off"
・・・これをつけると検索履歴が表示されなくなります。お好みでどうぞ。
・value="{{request('keyword')}}"
・・・このようにするとvalue値が「keywordというパラメーター(クエリ文字列)の値」になります。
・requestメソッド
・・・リクエストデータを取得できるやつです。・
@foreach ($genres as $index => $genre)
・・・この$genres
はコントローラーから検索できるジャンル名を返しています。単純にジャンル名を1つずつ処理しているだけです。下記少しわかりにくいかと思います。
{{ Form::radio('genre',$genre, $genre == request()->genre ? true :false, ['id'=>'search_genre'.$index]) }}
Form::radio
は第三引数がtrueならchecked
をつけるので、ジャンル名とパラメーターのジャンル名(現在検索しているジャンル名)が一致するなら、checked
をつけるようにしています。例えば、
http://127.0.0.1:8000/search?genre=恋愛(現世)
とかなら、request()->genre
によって、恋愛(現世)
という文字列を取得できます。なので、
$genre == request()->genre
とすることで、現在検索しているジャンルボタンにのみchecked
をつけることができます。
指定なし
というラジオボタンもほしいので、下記コードで作成しています。{{ Form::radio('genre','', isset(request()->genre) ? false :true, ['id'=>'search_genre_none'])}} {{ Form::label('search_genre_none', '指定なし', []) }}
value属性
を''(空文字)
にしておけば、コントローラー側の処理でいい感じにできます。(解説は後ほど。)web.phpにルーティングを追加
とりあえず、なんでもいいのでルーティングを追加します。
web.phpRoute::get('search', 'UsersController@search')->name('search');ジャンル用のファイルを作成
今回は
config/getValue/radio.php
にジャンル用の配列を作成しました。config/getValue/radio.php<?php return [ 'genre' =>[ '1' =>'恋愛(異世界)', '2'=>'恋愛(現世)', '3'=>'ラブコメ', '4'=>'ホラー', '5'=>'推理(ミステリー小説)', '6'=>'異世界ファンタジー', '7'=>'現代ファンタジー', '8'=>'コメディ', '9'=>'SF', '10'=>'詩・エッセイ・童話', '11'=>'歴史・戦国', '12'=>'その他', ], ];このようにすることで
config('getValue.radio.genre')
とすれば、ジャンル名の配列を取得することができます。コントローラー内でこのジャンル名の配列を取得します。
コントローラーに処理を追加
次にコントローラー側に検索処理を記述していきます。
UsersController.phppublic function search(Request $request) { //SQL文を書くためにqueryメソッドを使う。 $query = Novel::query(); //ジャンル名を取得。(viewファイルに返すだけ。) $genres = config('getValue.radio.genre'); //keywordがあるかどうか。 if ($request->filled('keyword')) { //検索キーワードとタイトルが一致するレコードを絞り込む $query->where('title', 'like', '%'.$request->get('keyword').'%'); } //genreがあるかどうか。 if ($request->filled('genre')) { //検索ジャンルとジャンル名が一致するレコードのidをgenresテーブルから取得 $genre_id = Genre::where('name', $request->genre)->first()->id; //genre_idが一致するレコードをnovelsテーブルから絞り込む $query->where('genre_id', $genre_id); } //条件に一致するレコードを作成日で降順に並び替えて取得 $novels = $query->latest('novels.created_at')->paginate(50); //viewファイルは好きなファイルにしてください。 return view("search", compact('novels', 'genres')); }たぶん、ほとんど読めばわかるんじゃないかと思います。
$request->filled('genre')
を使えば、リクエストに値が存在して、かつ、空でない場合
にtrueを返してくれます。似たものに
$request->has('genre')
というのがありますが、これだと空であってもtrueになります。それだと、下記のような
指定なし(value値が空文字列)
の場合でも実行されてしまうので、今回はfilledメソッド
を使用しています。{{ Form::radio('genre','', isset(request()->genre) ? false :true, ['id'=>'search_genre_none'])}} {{ Form::label('search_genre_none', '指定なし', []) }}ちなみに
latest()
を使えば、降順に並び替えてくれます。(下記の通り)$query->latest('novels.created_at')->paginate(50);あとは、好きなようにviewファイル側でデザインしてあげれOK。
試しに
search.blade.php
とかで{{dd($novels)}}
とかで中身を確認してみてください。現在の検索条件を表示する機能の作成
なんて説明すればいいかわかりませんが、ここからは上記の画像のやつを作っていきます。笑
方法としては、そこまで難しくありませんので、ご安心ください!!!
まずはview側に処理を追加
とりあえず、viewファイルに処理を追加していきます。
下記のようにすれば現在の検索条件を表示することができます。
search.blade.php<div class="search_condition"> <ul> @if(!empty(request()->keyword)) <li class="search_condition_item"> {{request()->keyword }}<a href="keyword" class="search_condition_a"><i class="fas fa-times search_condition_delete"></i></a> </li> @endif @if(!empty(request()->genre)) <li class="search_condition_item"> {{request()->genre }}<a href="genre" class="search_condition_a"><i class="fas fa-times search_condition_delete"></i></a> </li> @endif </ul> </div>見て大体わかると思いますので、ざっくり説明していきます。
まず
empty()
は引数が空ならtrueを返すので、!(エクスクラメーション)
をつけて、中身が空ではない時
にif文内のhtmlが表示されるようにします。そして
if文の条件
を「!empty(request()->keyword)
」みたいな感じにすることで、現在keywordで検索をしているのかどうか、がわかります。(このkeyword
というのはinputのname属性
のことです。)例えば、
http://127.0.0.1:8000/search?keyword=&genre=
みたいな感じで、keyword
が空になっているなら、処理は実行されません。逆に
http://127.0.0.1:8000/search?keyword=人生楽しいね&genre=
とかだと、「人生楽しいね」というキーワードで検索されていることになるので、if文内の処理が実行されます。そして、if文内の処理は下記のようにします。
<li class="search_condition_item"> {{request()->keyword }}<a href="keyword" class="search_condition_a"><i class="fas fa-times search_condition_delete"></i></a> </li>まず
{{request()->keyword }}
とすることで「人生楽しいね」が表示されます。
でもって、aタグのhref属性
にkeyword
と記述して、JavaScriptでこいつを操作していきます。JavaScriptを記述
ここからはJavaScript(今回はjQuery)の出番です。
なんでもいいので、JavaScritのファイルを作って、下記のように記述してください。
search.js$(function () { $('.search_condition_a').each(function () { //href属性を取得 attr = $(this).attr('href'); //初期化 var url = new URL(window.location); //パラメーター削除 url.searchParams.delete(attr); if (url.search) { $(this).attr('href', 'search' +url.search); } else { $(this).attr('href', 'search'); } }); });まず、
$('.search_condition_a').each(function () {}
とすることで、search_condition_aクラス
の要素(aタグの部分)をループさせています。
attr = $(this).attr('href');
とすることで、ループされたaタグのhref属性を取得しています。例えば、下記の場合は
keyword
という文字列が取得できます。<a href="keyword" class="search_condition_a">次にURLオブジェクトを作成し、
searchParamsメソッド
とdeleteメソッド
を使用して、先ほど取得した、href属性の初期値
と一致するパラメーターを削除します。//初期化(URLオブジェクトの作成) var url = new URL(window.location); //パラメーター削除 url.searchParams.delete(attr);もう少し詳しく解説していきます。
URLオブジェクトとは、URLを作成したり、編集したりするときに使えるメソッドが沢山入っている、JavaScriptが用意してくれている便利なやつです。()
参考:URL()
そして、引数には
window.location
として、現在開いているURLを与えます。次に
searchParams
で、URLのパラメーター(URLの?以降の部分)を取得、deleteメソッドで引数に与えたものと一致するパラメーターを削除します。例えば、
attr
にkeyword
という文字列が入っている場合は、url.searchParams.delete(attr);
によって、・
http://127.0.0.1:8000/search?keyword=人生楽しいね&genre=恋愛(異世界)
からkeywordの部分が削除されるので、
・http://127.0.0.1:8000/search?genre=恋愛(異世界)
こんな感じになります。最後にパラメーターがあるかないかで条件分岐させています。
こうしないと、他のページから単一条件で検索ページに飛んだときに、1つのパラメーターを削除すると、全てのパラメーターがなくなり、期待通りの動作をしてくれなかったからです。(もっと良い方法もあるかもです...)if (url.search) { $(this).attr('href', 'search' +url.search); } else { $(this).attr('href', 'search'); }
url.search
とすることで、URLのパラメーターを取得できます。あとは
attr
メソッドを使って、href属性
の値を変更してあげればOK。これでタグの部分は完成です。
別ページから条件検索をしたい時
別ページから検索したい時は下記のように、直接href属性にパラメーターを仕込んでおけばOK。
test.blade.php<a href="{{route('search')}}?genre={{$novel->genre->name}}">{{$novel->genre->name}}</a>並び替え機能も実装
ついでに並び替え機能の紹介もしておきます。(「新着順」と「更新順」だけ)
viewファイルに追加
下記のような感じで実装しました。
search.blade.php<div class="search_sort"> <button class="sort_btn {{strpos(request()->fullUrl(), 'new') !== false || strpos(request()->fullUrl(), 'sort') === false ? 'sort_active' : ''}}" name="sort" value="new" form="side_search_form" type="submit"> <span class="spot"></span>新着順 </button> <button class="sort_btn {{strpos(request()->fullUrl(), 'update') !== false ? 'sort_active' : ''}}" name="sort" value="update" form="side_search_form" type="submit"> <span class="spot"></span>更新順 </button> </div>順に解説していきます。
まず
formタグ
の外側でボタンを作る時はform属性
でformタグのid属性
を指定します。下記の2つの部分は現在のURLによって
sort_activeクラス
を付与するかどうか三項演算子を利用して、決定しています。{{strpos(request()->fullUrl(), 'new') !== false || strpos(request()->fullUrl(), 'sort') === false ? 'sort_active' : ''}} //省略 {{strpos(request()->fullUrl(), 'update') !== false ? 'sort_active' : ''}}
strpos()
は文字列の中に指定した文字列があるかどうかを判定するメソッドです。
request()->fullUrl()
で現在のURLが取得できます。コントローラーに処理を追加
最後に下記のように処理を追加・変更します。
SearchController.phppublic function search(Request $request){ //省略 if ($request->filled('sort') && $request->sort === 'new') { $query->latest('novels.created_at'); }elseif ($request->filled('sort') && $request->sort === 'update') { $query->latest('novels.updated_at'); } else { $query->latest('novels.created_at'); } //こっちのlatestは消す。 $novels = $query->paginate(50); return view("search", compact('novels', 'genres')); }これで並び替え機能も完成です。
簡単簡単。
最後に
これで一応、イメージ動画みたいな感じの検索機能が実装できたかと思います。
昔から検索機能を作るのは苦手なので、少し苦労しました...なんか条件分岐が多いですし疲れますね。(コードのせいかも。)
駆け足だったので、わかりにくい部分があるかも・・・その時はコメントしてくださいませm(_ _)m
たぶん、もう少し仕様変更とかしますが、ひとまずこれにて終了です!お疲れ様でした。
- 投稿日:2021-01-29T16:24:22+09:00
【Laravel】php artisanコマンドメモ(composerも少しだけあり)
自分用のメモとして残します。
3,4年前にメモしたものをqiitaに残すだけなので、未整理です。
もしかしたら、最新のLaravelバージョンでは使えないものもあるかも。よく使うやつは整理するかもです。
【基本】
▼Eloquentモデル作成(単数形が公式推奨)
php artisan make:model テーブル名▼シーダー作成(ダミーデータ作成)
php artisan make:seeder テーブル名(アッパーキャメル)TableSeeder▼シーダー実行(database\seeds\DatabaseSeeder.phpに登録されているものが対象)
php artisan db:seed▼シーダー実行(単体)
php artisan db:seed --class=シーダークラス名▼ルーティング確認
php artisan route:list▼マイグレーション作成
php artisan make:マイグレーション名例:php artisan make:migration create_users_table▼コントローラ作成(リソース(おまかせ)を使用する場合は-rを使う)
php artisan make:controller Admin/AdminUsersControllerphp artisan make:controller Admin/AdminUsersController -r
php artisan make:controller Admin/AdminUsersController --resource --model=Photo▼ページネーション作成
php artisan vendor:publish --tag=laravel-pagination▼バッチの作成
php artisan make:command バッチ名(アッパーキャメル)Command【その他】
▼キャッシュ削除
php artisan cache:clearphp artisan config:clearphp artisan route:clearphp artisan view:clear▼DB初期化(マイグレート実行&初期データ実行)
php artisan migrate:refresh --seed
▼IDEヘルパー更新
php artisan ide-helper:generate▼最適化まとめ
composer dump-autoloadphp artisan clear-compiledphp artisan optimizephp artisan config:cache
- 投稿日:2021-01-29T16:15:30+09:00
AWS S3のパッケージインストール時に、composer require league/flysystem-aws-s3-v3を叩くと、Your requirements could not be resolved to an installable set of packages.とエラーが出るときの解決策。
前提条件
・macOS
・Laravel6
・初学者composer require league/flysystem-aws-s3-v3を叩いた結果
test@test laravel % composer require league/flysystem-aws-s3-v3 Using version ^2.0 for league/flysystem-aws-s3-v3 ./composer.json has been updated Running composer update league/flysystem-aws-s3-v3 Loading composer repositories with package information Updating dependencies Your requirements could not be resolved to an installable set of packages. Problem 1 - league/flysystem-aws-s3-v3[2.0.0, ..., 2.x-dev] require league/flysystem ^2.0.0 -> found league/flysystem[2.0.0-alpha.1, ..., 2.x-dev] but the package is fixed to 1.1.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. - league/flysystem-aws-s3-v3[2.0.0-alpha.1, ..., 2.0.0-alpha.2] require league/flysystem 2.0.0-alpha.1 -> found league/flysystem[2.0.0-alpha.1] but the package is fixed to 1.1.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. - league/flysystem-aws-s3-v3[2.0.0-alpha.4, ..., 2.0.0-beta.1] require league/flysystem 2.0.0-alpha.3 -> found league/flysystem[2.0.0-alpha.3] but the package is fixed to 1.1.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. - league/flysystem-aws-s3-v3[2.0.0-beta.2, ..., 2.0.0-beta.3] require league/flysystem ^2.0.0-beta.1 -> found league/flysystem[2.0.0-beta.1, ..., 2.x-dev] but the package is fixed to 1.1.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. - league/flysystem-aws-s3-v3 2.0.0-RC1 requires league/flysystem ^2.0.0-RC1 -> found league/flysystem[2.0.0-RC1, ..., 2.x-dev] but the package is fixed to 1.1.3 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. - Root composer.json requires league/flysystem-aws-s3-v3 ^2.0 -> satisfiable by league/flysystem-aws-s3-v3[2.0.0-alpha.1, ..., 2.x-dev]. Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions. Installation failed, reverting ./composer.json and ./composer.lock to their original content.解決方法
バージョンがあってないよ〜。と言われている気がしたので、
test@test laravel % composer require league/flysystem-aws-s3-v3 ^1.0バージョン指定を入れることで解決できました。
参考記事
- 投稿日:2021-01-29T15:46:44+09:00
Permission Denied Error Doker Laravel
Dockerでlaradockを導入し、laravelを起動したとき、以下のエラー文が出ました。
docker The stream or file "/var/www/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied
chown -R www-data:www-data /var/www
chmod -R 777 /var/www/storage
権限を変更してあげると、無事にlaravelを表示することができました。
以下のサイトを参考に、しました。
https://stackoverflow.com/questions/48619445/permission-denied-error-using-laravel-docker
- 投稿日:2021-01-29T15:08:20+09:00
【PHP】peclとは何か?pecl install と docker-php-ext-installの違い
DockerにNginxとphp-fpmを使ってLaravel環境を構築する際に、php-fpm対して実行する処理の中にpeclがあったので調べてみた。
▼peclの使用例
php-fpmビルド用のDockerfileRUN pecl install zip \ && docker-php-ext-enable zip RUN pecl install redis \ && docker-php-ext-enable redis目次
- peclとは?
- 使い方(拡張機能のインストール)
- 拡張パッケージの有効化
- pecl installとdocker-php-ext-installの違い
- peclとpearの違い
- peclとapt-getの違い
- 参考リンク
peclとは?
PHPの拡張パッケージ(拡張モジュール)を提供しているサービス。
PHP Extension Community Libraryの略でピクルと読む。パッケージのインストール後は有効化する必要がある。(php.iniで有効化した場合はサービスの再起動が必要)
使い方(拡張機能のインストール)
peclはPHPにバンドルされているので、すぐにコマンドとして使える。
以下コマンドでパッケージをインストールできる。
・
pecl install 拡張パッケージ名[-バージョン]
バージョンの指定がなければ安定版をインストール。
▼バージョン指定の例$ pecl install extname-0.1
拡張パッケージの有効化
peclでインストールしたのみではパッケージは有効にならないので有効化する必要がある。
1. dockerの場合
・
docker-php-ext-enable 拡張パッケージ名
2. macやwindowsの場合
この場合2つの方法がある。
php.iniを編集する
php.iniを開いて編集する。
(1)まずはファイルの所在を確認。
php.ini##場所を確認 $ php -i | grep php.ini Configuration File (php.ini) Path => /etc ##ファイルの存在確認 $ ls /etc | grep php.ini php.ini.default ##ファイルの作成 $ cp /etc/php.ini.default /etc/php.ini上記状態だと、php.iniは存在しないので、php.ini.defaultをコピーして、php.iniを作成する。
▼php.ini.defaultとphp.iniの違いとは?
php.iniはデフォルトでは存在しない。phpをインストールした際に(OSによってはデフォルトで)php.ini.defaultがある。php.iniのテンプレートなので、これをコピーしてファイル名をphp.iniにする。
▼grepコマンドとは?
grepコマンドは前の処理結果の中で、指定した値を含む処理結果のみを表示する絞り込みコマンド。・
処理 | grep 値
(2)php.iniの編集
ファイルが出来たら、Dynamic Extensionsと書かれているところの下に
;extension=拡張パッケージ名
があるので、冒頭の;
を外す。拡張パッケージ名がない場合は、
extension=拡張パッケージ名
を追加する。これで有効化完了。
php.iniの例;;;;;;;;;;;;;;;;;;;;;; ; Dynamic Extensions ; ;;;;;;;;;;;;;;;;;;;;;; 省略 ;extension=bz2 ;extension=curl ;extension=fileinfo↓ curlを有効化
;extension=bz2 extension=curl ;extension=fileinfo
本番反映のため、php.iniで有効化した場合はサービスの再起動をする。
dl関数を使う
dl関数を使って指定した拡張パッケージを有効かできる。
・
dl ( string $extension_filename ) : bool
例$ dl (curl): true
pecl installとdocker-php-ext-installの違い
Dockerfileによっては、
pecl install
とdocker-php-ext-install
の2つが使われている場合がある。なぜPHP拡張パッケージをインストールにするのにコマンドがいくつもあるのか?について。
例RUN pecl install redis \ && docker-php-ext-enable redis RUN docker-php-ext-install pdo_mysql結論
docker-php-ext-install
はPHPに標準で備わっている拡張パッケージのインストール&有効化ができる。peclはPHPに標準で備わっていないパッケージをインストールできる。
PHPに標準で備わっている拡張パッケージ一覧
https://www.php.net/manual/ja/extensions.php
Zip, Zlib, memcache, podなどが用意されている。
peclでinstallできる拡張パッケージ一覧
公式ページで検索できる。
https://pecl.php.net/package-search.phpredis, xdebug, memcachedなどが用意されている。
peclとpearの違い
peclもpearもPHPの拡張パッケージをインストールできるコマンド。
pearはPHP言語で書かれている。peclはC言語。peclの方が高速。
pearで用意されている拡張ライブラリはpeclでも使える。
なお、アイコンは名前にちなんで、pearは洋梨、peclはピクルスとなっている。
peclとapt-getの違い
Dockerfileによっては、
apt-get install
も同時に使われている場合がある。例RUN apt-get install -y zlib1g-dev libzip-dev unzip \ && pecl install zip \ && docker-php-ext-enable zipaptはDebian系Linux(DebianやUbuntuなど)のパッケージ管理ツール。
なので、
apt-get
でインストールするのはOS用のパッケージ。peclでインストールするのはPHP用のパッケージ。
参考リンク
・PHP公式 pecl入門
・PHP公式 pecl installの使い方
・PHP公式 php.iniディレクティブ
・pecl公式 peckとは?
・docker hub公式 php-fpm
- 投稿日:2021-01-29T12:52:49+09:00
【Docker・Laravel】docker-composer.ymlとDockerfileの作り方。
docker-composeを使ってDocker上にLaravelを構築する方法(Nginx & php-fpm & MySQL)で使った、docker-compose.ymlとDockerfileの中身について。
docker-compoer.yml
docker-compoer.ymlversion: '3' services: #PHP Service app: build: context: . dockerfile: Dockerfile image: app_php-fpm container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network links: - webserver - db #Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network #MySQL Service db: image: mysql:5.7.22 container_name: db restart: unless-stopped tty: true ports: - "3307:3307" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: password SERVICE_TAGS: dev SERVICE_NAME: mysql volumes: - dbdata:/var/lib/mysql/ - ./mysql/my.cnf:/etc/mysql/my.cnf networks: - app-network #Docker Networks networks: app-network: driver: bridge #Volumes volumes: dbdata: driver: local・
version: '3'
docker-composeはバージョンにより記述方法が異なる。基本的にはバージョン3を使用する。
1. php-fpmのイメージとコンテナを作成
php-fpmのイメージとコンテナをサービス名appとして定義している。
docker-compoer.yml#PHP Service app: build: context: . dockerfile: Dockerfile image: app_php-fpm container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network links: - webserver - db1-1. イメージのビルド
PHPサービスのイメージを作成するための記述は
build
とimage
。
build
でイメージを作成し、作成したイメージ名をimage
で指定している。docker-compoer.ymlbuild: context: . dockerfile: Dockerfile image: app_php-fpmイメージの作成にはDockerfileを指定している。
context
で指定したパスから見て、dokerfile
で指定した場所の中のDockerfileを探す。・contextディレクティブ
Dockerfileを探すために基準となるパスを指定する。必須項目。
・dockerfileディレクティブ
dockerfileはファイル名の指定がなければ、デフォルトで指定したディレクトリのDockerfileを探す。
contextで指定した場所にDockerfileがある場合は記述しなくてもいい。
なので、上記設定は以下でも同じ動きになる。
build: context: . image: app_php-fpm
1-2. Dockerfile
DockerfileFROM php:7.3-fpm # Copy composer.lock and composer.json COPY composer.lock composer.json /var/www/ # Set working directory WORKDIR /var/www # Install dependencies RUN apt-get update && apt-get install -y \ build-essential \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ locales \ zip \ jpegoptim optipng pngquant gifsicle \ vim \ unzip \ git \ curl \ libonig-dev \ libzip-dev # Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/* # Install extensions RUN docker-php-ext-install pdo_mysql zip exif pcntl RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ RUN docker-php-ext-install gd # Install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer # Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www # Copy existing application directory permissions COPY --chown=www:www . /var/www # Change current user to www USER www # Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]・FROM イメージ名:タグ名
ベースとなるイメージを指定。
ここではphp:7.3-fpmを指定している。タグを指定していない場合はlatestとなる。buildを実行すると、まずはローカルで指定したイメージを探し、なければdocker hubを探しにいく。
・バンドル関連ファイルのコピー
# Copy composer.lock and composer.json COPY composer.lock composer.json /var/www/・
COPY <ファイル1> <ファイル2>,,, <ディレクトリパス>
指定したローカルのファイルをdockerのディレクトリにコピーする。
・WORKDIR パス
コンテナ起動時の
# Set working directory WORKDIR /var/www以降のコマンドCOPY、RUN、CMD、ENTRYPOINTを実行する大元となるDockerコンテナ内のディレクトリを指定する。
コンテナ内に指定したディレクトリが存在しない場合は作成してくれる(エラーにならない)
▼複数記述することも可能
複数記述した場合は、そのディレクトリが追加され、ディレクトも移動してく。(cdコマンドみたいな感じ)WORKDIR app WORKDIR b WORKDIR c RUN pwd //現在地 app/b/c
Linux(Debian系)依存パッケージのインストール
# Install dependencies RUN apt-get update && apt-get install -y \ build-essential \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ locales \ zip \ jpegoptim optipng pngquant gifsicle \ vim \ unzip \ git \ curl \ libonig-dev \ libzip-dev・
apt
Linux(DebianやUbuntuなど)のパッケージ管理コマンド。Advanced Package Toolの略。
・
apt-get
パッケージの操作・管理を行うコマンド。
・
apt-get update
インストール可能なパッケージの一覧を更新する。(一覧の更新のみでインストールはしない)
一覧は/etc/apt/sources.listにある。
・
apt-get install
sources.listに沿ってパッケージをインストール or アップデートする。
-y
オプション:すべてyesを選択する。
パッケージ 内容 build-essential Debian系のOSを使うために必須 libpng-dev png画像の読み込み・書き込み・編集 libjpeg62-turbo-dev jpeg画像の読み込み・書き込み・編集 libfreetype6-dev フォント関連の様々な操作をサポート(FreeType) locales 言語設定 zip zip操作の圧縮。zipコマンドが使えるようになる jpegoptim jpg画像の圧縮 optipng png画像の圧縮(画質を下げることなく圧縮。圧縮率低め) pngquant png画像の圧縮(optipngよりも圧縮。画質は低下) gifsicle gif画像とアニメーションの読み込み・書き込み・編集 vim テキストエディタ unzip zip操作の解凍。unzipコマンドが使えるようになる git ソースコードなどの変更履歴を記録・追跡 curl URLシンタックスを用いてファイルを送信または受信するコマンドラインツール libonig-dev 正規表現ライブラリ。鬼車(onigruma) libzip-dev zipアーカイブの読み取りと書き込み Linux(Debian系)の構築や必要なコマンドライブラリを一気にインストール。
・-devの意味
パッケージ名の後ろの-dev
は、developmentの略。プログラム開発で必要なヘッダーやツール、ライブラリなどが入っている。
aptのキャッシュ削除
apt-get installでパッケージをインストールすると、キャッシュが追加され容量が大きくなっていく。
キャッシュは不要なので削除する。
ダウンロードしたパッケージは.deb形式で、var/cache/apt/ archives/ディレクトリに保存されます。これらのインストーラパッケージは、アンインストール後もローカルストレージに保持されます。
# Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/*・
apt-get clean
キャッシュを一掃する。・
rm -rf /var/lib/apt/lists/*
パッケージリストが削除する。apt-getで再度パッケージがインストールされたり、キャッシュファイルが増加するのを防ぐ。▼補足
実行すると、パッケージに関する情報がないため、apt-get install
はE: Unable to locate package でエラーを返す。
apt-get update
を実行すればリストが復活する。
PHP拡張機能(エクステンション)のインストール
# Install extensions RUN docker-php-ext-install pdo_mysql zip exif pcntl RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ RUN docker-php-ext-install gd・
docker-php-ext-install
DockerにPHPの拡張機能をインストールするためのコマンド。
・インストールする拡張機能
PHP拡張機能 内容 pdo_mysql PHPからMySQLへのアクセスを可能にする zip zipのアーカイブとその内部のファイルに対する透過的な読み書きを可能にする exif 画像のメタデータ扱えるようになる。画像のヘッダ情報を読み込むなど pcntl プロセス制御機能。プロセスの並行処理を行う gd 画像をPHPスクリプトで生成する。画像のリサイズやグラフ作成などの描画機能を持たせることができる。 docker-php-ext-configure
docker-php-ext-configure <拡張機能> <オプション>
指定した拡張機能の環境設定をする。環境設定は各拡張機能に割り当てられたオプションを使う。
GDの主要オプション
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/GDの環境設定をしている。jepgやpngを使うGDをビルドするためにはlibpegやfreetype, libpngが必要なためインストールの設定をする。
オプション 意味 --with-gd[=DIR] GDライブラリの保存場所を指定 --with-jpeg-dir[=DIR] libjpegの保存場所 --with-freetype-dir[=DIR] FreeType 2の保存場所 --with-png-dir[=DIR] libpngの保存場所 次の行でGDをインストール。
RUN docker-php-ext-install gdGDインストール時の注意点
PHP7.4以降からGDインストール時のオプションが変更となっている。(上記設定だとエラーが出る)
configure: error: unrecognized options: --with-gd, --with-freetype-dir, --with-jpeg-dir, --with-png-dir
PHP7.4以降は引数での環境設定が不要になる。必要なパッケージは
apt-get install
でインストールしておく。なので、
docker-php-ext-config gd ~
は不要。修正後# Install extensions RUN docker-php-ext-install pdo_mysql zip exif pcntl RUN docker-php-ext-install gd
composerのインストール
# Install composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer・
curl -sS
-s
オプション
--silent。通常表示される進捗状況を表示しない。エラーメッセージも表示しない。
-S
オプション
--show-error。エラーメッセージを表示する。-sとセットで使うことでエラーを表示する。curl --helpでオプション一覧が確認可能。
・
curl -sS https://getcomposer.org/installer
composerのinstallerをダウンドロードする。(composer-setup.phpを取得)
・
|
コマンドA | コマンドB
パイプライン。コマンドAの実行結果をコマンドBに渡す。
・
php -- --install-dir=/usr/local/bin --filename=composer
php [options] -- [args...]
なので、引数で、インストール先のディレクトリとファイル名を指定。
- --install-dir=/usr/local/bin
- -filename=composer
ユーザーとグループの追加
# Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www・groupadd
groupaddコマンドで新しいグループ
groupadd [オプション] [グループID] <グループ名>
-g
: --gidと同じ。作成するグループのID(GID)を指定。
指定がない場合は、未使用の番号を自動で割り振る。
▼作成したグループの確認
グループの情報は /etc/group に保存されている。コンテナの中で確認できる。$ docker exec -it app bash www@a1984dd1d4a4:/var/www$ cat /etc/group www:x:1000:・
①グループ名:②PW:③GID[④:所属メンバー名]
- グループ: www
- PW: x (セキュリティのため表示されない。設定なしの場合は*)
- GID: 1000
- 所属メンバー:なし
・useradd
useraddコマンドは新規ユーザーを作成し、ユーザーごとの設定ができる。
コマンドは長いが、最初のuseraddと最後のwww以外はすべてオプション。
useradd [オプション] ユーザー名
-u
: ユーザーID(UID)の指定
指定がない場合は、未使用の番号を自動で割り振る。
-m
: ユーザーのhomeディレクトリが存在しない場合に作成。
指定しない場合は、「/」配下にユーザー情報が保存される。
-s [dir]
: ユーザーのログインシェルをフルパスで指定(指定しなかった場合はデフォルト設定)
-g [グループ名/gid]
: 指定したユーザーをグループに追加する。
▼作成したユーザー情報の確認
ユーザー情報は /etc/passwd に保存されている。コンテナの中で確認できる。$ docker exec -it app bash www@a1984dd1d4a4:/var/www$ cat /etc/passwd www:x:1000:1000::/home/www:/bin/bash・
①ユーザー名:②PW:③UID:④GID::⑤/home/<ユーザー名>:⑥シェルのパス
- ユーザー名: www
- PW: x (セキュリティのため表示されない。設定なしの場合は*)
- UID: 1000
- GID: 1000
- ユーザーのホームディレクトリ: home/www
- ユーザーが使用するシェル: /bin/bash
ローカルのPJをコンテナ内にコピー & ファイルオーナーの変更
# Copy existing application directory permissions COPY --chown=www:www . /var/www・
COPY [--chown=<user>:<group>] <ローカルdir> <コンテナdir>
ローカルの「.」をコンテナ内の「/var/www」にまるごとコピー。ユーザー名をwww、グループ名をwwwに変更。
ユーザー名を指定
# Change current user to www USER www・
USER <user名 or uid>[:<group名 or gid>]
ポート開放とphp-fpmの起動
# Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]・
EXPOSE ポート番号
指定したポートでリッスン(外部からのアクセスに備えて待機)する。
ここでは、php-fpmの9000番ポートを開けておく。
・
CMD [
コマンド,
引数1,
引数2,,,]
CMDはコンテナ実行時のデフォルト処理を定める。Dockerfileに必須。記述は1つのみ。
php-fpmコマンドを実行して、php-fpmを起動。
以上がDockerfileの中身。
php:7.4-fpmイメージをベースに、必要なパッケージをインストール、ユーザー名や権限変更をしたのち、9000番ポートを開けてphp-fpm起動するイメージが出来上がり。
イメージ名の設定
最後に作成したイメージに名前をつけて完了。(ここではapp_php-fpm)
image: app_php-fpm
1-3. コンテナの設定
1-2までがイメージのビルド。続いてコンテナ起動時に実行される処理の確認。
docker-compose.yml#php-fpmサービスの続き container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network links: - webserver - dbcontainer_name: コンテナ名
コンテナを作成した時の名前。起動・停止・削除に使うのでわかりやすく短めの名前がおすすめ。
restart: コンテナ停止時の処理フラグ
コンテナが停止した時の処理をフラグで指定する。
フラグ 意味 no 再起動しない。デフォルト on-failure エラー発生時にコンテナを再起動(終了コードがゼロ以外) always 常に再起動。手動で停止時はDockerデーモンが再起動した場合にコンテナを起動する unless-stopped 手動停止時を除いて再起動。Dockerデーモンを起動しても再起動しない。 ▼再起動ポリシー
- コンテナ起動後が10秒以上立たない場合は自動再起動しない。(再起動ループを避ける)
- 手動でコンテナを停止すると、Dockerデーモンを再起動するか、コンテナを手動で再起動するまで再起動ポリシーを無視する。(再起動ループを避ける)
- 再起動ポリシーを適用できるのはコンテナのみです。(swarm サービスは対象外)
・
tty: <boolean>
疑似ターミナル (pseudo-TTY) の割り当て。
tty: true
で、ポート開放していないコンテナも起動状態をキープできる。(ないとコンテナがすぐに終了してしまう)
docker exec
コマンドでコンテナに入って操作する際に必要。
environment
環境変数の設定。
書き方は2種類。Key: Value
または、- Key=Value
。environment: SERVICE_NAME: app SERVICE_TAGS: dev以下と同じ。
environment: - SERVICE_NAME=app - SERVICE_TAGS=dev
- 値を指定しない場合は、シェルの値を適用する。
- DockerfileのARGやENVで指定した環境変数と被る場合は、docker-composeの環境変数が優先される。
env_file
で読み込んだ環境変数と被る場合は、environmentが優先される。
▼環境変数の優先度
- environment
- env_file
- DockerfileのARG, ENV
env_fileは環境変数を定義したファイルを指定する。たくさんの環境変数を一括で読み込む場合に便利。
.env_fileの例env_file: - ./Docker/api/api.env
working_dir
コンテナ内のデフォルトの作業用ディレクトリを指定する。
working_dir: /var/wwwコンテナに入ると/var/wwwに入る。ここはプロジェクトがコピーされるディレクトリ(Dockerfileにて設定)。
Volumes
ローカルのディレクトリをコンテナ内のディレクトリにマウントする。
volumes: - ./:/var/www - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
- ホストのパス:コンテナ内の絶対パス
コンテナ側は絶対パス。ホスト側は相対でも絶対でもいい。ボリューム名を定義指定いる場合は、ボリューム名でもいい。
- ./:/var/www
ローカルの現在のディレクトリをコンテナの「var/www」と同期させる。これで、ローカルのプロジェクトディレクトリで行った変更はコンテナ内に自動コピーされる。
自動同期したくないディレクトリやファイルは、.dockerignoreに記載する。
▼ボリューム名について
ボリューム名はserviceの外でvolumesを使って定義する。参照する際のボリューム名は「プロジェクト名_ボリューム名」となる。ボリューム名を指定しない(ホストのパスを指定した)場合はハッシュ値となる。
docker-compose.ymlの例#PJ名:laravel version: '3' services: #MySQL Service db: #省略 volumes: - dbdata:/var/lib/mysql/ - ./mysql/my.cnf:/etc/mysql/my.cnf volumes: dbdata: driver: local↓
ボリューム名$ docker volume ls DRIVER VOLUME NAME local laravel_dbdata local f0f9aa131ed1d74c0d2e9ff87b065170a1b142bd6a90a76ce9dcddbee3c69984networks
docker-composeはnetworkを指定しない場合、プロジェクト名のネットワークが自動生成され、すべてのサービスがそのネットワーク内に入る。
networkを自分で定義すると、指定したサービスだけをそのnetworkで繋ぐことができる。
version: '3' services: app: #省略 networks: - app-network #省略 networks: app-network: driver: bridge▼ネットワークの確認
ネットワーク名は指定の有無により名前が変わる
- 指定なし:
プロジェクト名_default
- 指定あり:
プロジェクト名_ネットワーク名
ネットワークの確認$ docker network ls NETWORK ID NAME DRIVER SCOPE 2cee68b9dc5a django_default bridge local 761212ed4b49 laravel_app-network bridge▼bridgeとは?
bridgeとはネットワークのブリッジモードのこと。複数のネットワークをつなげて、ひとまとめのネットワークにしている状態。links
サービスを関連づける。サービスを起動すると、linksで記述されているサービスも一緒に起動する。
version: '3' services: app: #省略 links: - webserver - db上記の場合、appにdbとwebserverがlinkしているため、appのみを起動すれば自動的にdbとwebserverも起動する。
起動の順序はlinksに記述したコンテナから。
起動例$ docker-compose up -d app Starting webserver ... done Starting db ... done Starting app ... done
以上で一つ目のphpサービスのdocker-composeの記述内容が完了。処理をまとめると、
(1)イメージのビルド
Dockerfileを元に、php:7.4-fpmをベースイメージとして、必要なパッケージなどを保持した新たなイメージを作成。(2)コンテナの起動
環境変数、ボリューム、ネットワーク、関連サービスを指定(リンク)してコンテナを起動。最後はDockerfileに記載されたCMDが実行され、イメージ通りのphp-fpmが起動する。
2. Nginxのコンテナを作成
php-fpmのコンテナが作成できるようになったので、続いてWEBサーバーとなるNginxを作成する。
.yml#Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network2-1. イメージのビルド
イメージの作成は1行のみ。
image: nginx:alpinedocker-hubの
nginx:alpine
をイメージとして使用。(alpine上に構築したnginx)
▼alpineとは?
alpineは超軽量のLinuxディストリビューション。▼Linuxディストリビューションとは?
LinuxのコアとなるプログラムをLinuxカーネルという。この状態では普通のOSとして使うことが難しい。使いやすいようにパッケージをくっつけたのがLinuxディストリビューション。distributionは配布という意味なので、配布用Linuxということ。
Debian, Ubuntu, CentOSと同じ並びにalpineがくる。
2-2. コンテナの定義
container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./:/var/www - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-networkphp-fpmサービスで使った内容とほぼ同じ。
作成したコンテナ名をwebserverとし、コンテナ手動停止(&エラー)以外は自動再起動する。
コンテナのbashを起動できるように擬似ターミナル(tty)を開けておく。
ports
ホスト側:コンテナ側
でポート番号を指定する。
- "80:80"
であれば、localhost:80にアクセスすると、Nginxの80番ポートに繋がるという意味。443ポートはhttpsプロトコル用のポート。
https://xxx
で接続すると、xxxのIPアドレスの443ポートに接続する。volumes
・
- ./:/var/www
ローカルの現在のディレクトリを、Dockerコンテナ内のNginxの/var/wwwと同期。・
- ./nginx/conf.d/:/etc/nginx/conf.d/
ローカルの./nginx/conf.d/をnginxの/etc/nginx/conf.d/と同期。networks
「project名_app-network」というネットワークに入ったサービス同士とつなげる。
2-3. DBの定義
#MySQL Service db: image: mysql:5.7.22 container_name: db restart: unless-stopped tty: true ports: - "3307:3307" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: password SERVICE_TAGS: dev SERVICE_NAME: mysql volumes: - dbdata:/var/lib/mysql/ - ./mysql/my.cnf:/etc/mysql/my.cnf networks: - app-network設定はNignxサービスとほぼ同じ。
ベースイメージに、mysql:5.7.22を使用。
ポート番号はホスト側の3307をローカル側の3307につなげるよう設定。environment
環境変数でDB名やPWなどを渡している。
2-4 networkとvolumeの定義
全体で使用するnetworkと、volume名をつけるものを定義している。。
#Docker Networks networks: app-network: driver: bridgeapp-networkを定義。ドライバーはbridge(複数のネットワークを一つとみなす)
#Volumes volumes: dbdata: driver: local名前ボリュームを設定する。名前ありボリュームを使うとデータを永続化できる。
ここでは、DBのデータはコンテナを終了しても残しておきたいので、dbdataボリュームを作成し、/var/lib/mysql/と連動させている。
コンテナ終了時にDocker内のデータは消えるが、ローカルにデータが残る。
Dockerではボリューム名dbdataでローカルのパスを保存してあるため、次のコンテナ起動時にそのローカルに接続し、/var/lib/mysql/配下にデータを保存する。(ローカルと同じ状態にする)
まとめ
php-fpmは自分のLaravelプロジェクトを反映するためイメージの再構築をしているのでビルドに時間がかかる。
NginxとDB(MySQL)は公式のイメージを取得するのみなのでビルドもあっさり。(ただdocker-hubからプルするだけ)
コンテナ起動時にポート番号、環境変数、ボリューム、ネットワークを指定している。
- 投稿日:2021-01-29T11:14:05+09:00
[PHP] 親クラスで定義した変数を呼ぶ
子クラス共通の変数を扱いたい場合、下記のように親クラスで定義する必要があります。
class TestCase extends Illuminate\Foundation\Testing\TestCase { protected static $userId = 1; }このように定義した後、下記のように子クラスで呼び出します。
class ControllerTest extends \TestCase { public function setUp(): void { $this->user = User::find(self::$userId); } }このようにself::$userIdと書くことにより、extendsしているクラスなら扱うことができます。
参照
会社の紹介
私は現在、株式会社ダイアログという物流×ITの会社に勤務しております。
様々な職種を募集しているので、Wantedlyのページをご覧ください。
- 投稿日:2021-01-29T10:38:43+09:00
npm run watch-pollがエラーになった
Laravel初心者です。
以下環境でアプリを作成しています。
バージョン Docker 20.10.2 Docker-compose 1.27.4 PHP 7.4.14 Laravel 8.24.0 mysql 8.0.23 npm run watch-pollでファイルの変更を反映
cssなどを変更してブラウザをリロードしても反映されず、
この場合はDockerコンテナ内でコンテナnpm run watch-pollを入力するとファイルの変更を監視してくれて
リアルタイムでコンパイルされるのだそうです。ターミナルがこのような画面になりブラウザに変更が反映されます。
ちなみに
npm run watchとの違いは
npm run watch
はファイルの変更を監視しているため特定のシステムの場合はうまくいかないようです。
npm run watch-poll
は変更を1秒単位で確認するようです。npm run watch-pollでエラーが出た
ここからが本題です。
いつものようにnpm run watch-poll
をしようとしたところこのようにエラーになってしまいました。
どこがエラー文なのかわからないけど
ターミナルLocal package.json exists, but node_modules missing, did you mean to install?って出てるので
package.json
とnode_modules
がなんかおかしいのかなと推測。解決
こちらの記事がとても分かりやすくて助かりました!
ありがとうござます。一般的な解決策としてnode_modulesとpackage-lock.jsonを消してキャッシュをクリアにし、再度npmをインストールしたあと、実行しなおすという手があります
とのこと。
あ、なんかこれっぽいと思ってコンテナrm -rf node_modules && rm package-lock.json && npm cache clear --force && npm cache clean --force && npm iとした後に再度
コンテナnpm run watch-pollで無事に解決しました!
ありがとうございました!
- 投稿日:2021-01-29T10:26:39+09:00
LaravelでFullcalendarを実装する方法
はじめに
今回はLaravelでFullcalendarを実装する方法について解説します。
LaravelでFullcalendarを実装するにあたり、参考記事があまりなく、実装に苦労したので、同じような方の助けになればと思います。-各バージョン
-Laravel 6.x
-PHP 7.4.9
-MySQL 5.7.30
-Fullcalendar v5Fullcalendarはバージョンによって記述方法が異なるので注意してください。
v4の記事を参考にしてもうまくいかないことが多かったです。Fullcalendarをダウンロードする
公式ドキュメントよりダウンロードできます。
https://fullcalendar.io/docs/getting-startedNPMやCDNを利用する方法がありますが、ダウンロード方法はお好きな方法で問題ないです。
今回はzipファイルをダウンロードする方法し、以下のように追加しました。event.blade.php<!-- fullcalendar dependencies --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.0/moment.min.js"></script> <!-- fullcalendar script --> <script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.0.1/fullcalendar.min.js"></script> <!-- fullcalendar style --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.0.1/fullcalendar.min.css">モデルとデーブルを作成する
わたしの場合はModelsファイルの中にモデルを格納したいので、以下のように書いています。必要がなければ、Modelsのところはカットして問題ないです。
php artisan make:model Models/Event -mまずはテーブルから書いていきます。
Fullcalendarには様々なプロパティが用意されていて、以下のサイトより確認することができます。
https://fullcalendar.io/docs/event-parsing
今回はシンプルに、予定のタイトルと登録する日付、文字の色を変更できるように設定していきます。events.table.phppublic function up() { Schema::create('events', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title', 100); $table->date('start'); $table->string('textColor'); $table->timestamps(); }); }マイグレーションします。
php artisan migrateカレンダーに登録したデータを表示する
カレンダーの表示とカレンダーに登録したデータを表示するControllerとルーティングを書いていきます。
php artisan make:controller EventControllerindexアクションにカレンダーの表示とDBに保存されているデータをカレンダーに表示させるコードを書きます。
今回はDBファサードを利用するので、useで宣言が必要です。EventController.phpuse App\Models\Event; public function index(Request $request) { if ($request->ajax()) { $data = DB::table('events')->select('title', 'start', 'textColor')->get(); return response()->json($data); } return view('/event'); }
$request->ajax()
でデータがajax通信かどうかを確認できるメソッドです。
if内では、eventsテーブルからデータを持ってきて、response()->json()
を利用し、json形式で保存するようにしています。
このようにすることによって、PHPで保存したデータをJavaScriptで利用できるようになります。続いて、ルーティングは以下のように設定します。
web.phpRoute::get('/index', EventController@index);カレンダーの情報を登録する
カレンダーの情報を登録するためのコードを先ほど作成したEventControllerに書いていきます。データの登録後、カレンダーが表示されているページに戻るようにしています。
EventController.phppublic function store(Request $request) { $event = new Event; $event->title = $request->input('title'); $event->start = $request->input('start'); $event->textColor = $request->input('textColor'); $task->save(); return redirect('/event'); }続いてルーティングです。
web.phpRoute::post('/store', EventController@store);カレンダーの表示と登録フォームを設定する
カレンダーの表示自体はとても簡単で、カレンダーを表示させたい場所に以下のコードを書くだけで表示できます。
event.blade.php<div id="calendar"></div>フォームはCSSを考慮せずに書いているのでご了承ください。
event.blade.php<form method="POST" action="{{ route('/store') }}"> @csrf <input type="text" name="title"> <input type="date" name="start"> <input type="color" name="textColor"> <button type="submit">登録</button> </form>データの登録なので、methodは
POST
を指定し、actionにはフォームが送信されたらEvercontrollerのstoreアクションにつながるようにしたいので、先ほどweb.phpで指定したroute('store')
を指定しています。type属性にはそれぞれ入力方式に適したものを入力し、name属性にはeventsテーブルで指定したカラム名を入力します。
続いてはJSを書いていきます。
今回はevent.blade.phpに直接書きましたが、便宜JSだけディレクトリを分けてもいいかと思います。event.blade.php<script> $(document).ready(function () { $('#calendar').fullCalendar({ // はじめりの曜日を月曜日に変更 デフォルトは日曜日になっており、日=0,月=1になる firstDay: 1, headerToolbar: { right: 'prev,next' }, events: '/home', }); }); </script>
events
にはカレンダーに保存している情報を表示するアクションが書かれている、EventControllerのindexメソッドにつながるように書く必要があります。
そのため、先ほどweb.phpで指定した/home
を指定しています。今回はシンプルなカレンダーになっていますが、Fullcalendarには様々なオプションが用意されています。
簡単なものだと、headerToolbarはrightだけではなく、left,centerのカスタマイズもできますし、eventをドラッグしたり、日にちを選択して登録することなんかもできます。
詳しいオプションに関しては、Fullcalendarの公式マニュアルをご確認ください。
https://fullcalendar.io/docs#tocこれで、カレンダーにデータの登録と、登録したデータの表示ができるようになりました!
さいごに
今回は登録フォームを直接書いていますが、今後は日付から選択をしてデータの登録をしたりなど、いろいろとカスタマイズをしてみたいと思います!
最後まで読んでいただきありがとうございました!
- 投稿日:2021-01-29T10:02:00+09:00
手軽にLaravelのクエリをログ出力する
Laravelで実行されたSQLを確認する方法は幾つもありますが、
必要な時だけ、手軽に確認ができるようにする方法をメモしておきます。試した環境
Laravel: v6.14.0
コード
まずは、切り替え用の config を作成します。
本番環境等では特に意識しなくて良いよう、デフォルトは false です。.envSQL_DEBUG=trueconfig/app.php'sql_debug' => env('SQL_DEBUG', false),また、コードを追記するのは、以下のファイルのみです。
app/Providers/AppServiceProvider.php<?php namespace App\Providers; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { //... /** * Bootstrap any application services. * * @return void */ public function boot() { if (config('app.sql_debug')) { DB::listen(function ($query) { Log::info([ 'sql' => $query->sql, 'bindings' => $query->bindings, 'time' => $query->time.' ms' ]); }); } } }
- 投稿日:2021-01-29T05:31:17+09:00
[Laravel7.x]外部キー制約の記述に関する備忘録
今回の題
Laravelの外部キー制約について、7.xからよりシンプルに書けるようになっていた事を最近になって知ったので備忘録として書きました。
内容薄いですが勘弁してください。前提
前提として、主テーブル
users
と従テーブルposts
は1対多の関係にあるとする。
そして、posts
にusers
のidカラムを参照するuser_idカラムを外部キーとして定義する。何がどうなったのか
外部キーを定義するとき、6.xまでは以下の書き方だけだった。
Schema::table('posts', function (Blueprint $table) { $table->unsignedBigInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); });ただこの書き方は冗長であるという事で、
7.xからは加えて以下の書き方も出来るようになった。Schema::table('posts', function (Blueprint $table) { $table->foreignId('user_id')->constrained(); });1行でカラムの定義と外部キーとしての設定が出来るようになったのですごく楽。
参考
- 投稿日:2021-01-29T02:52:36+09:00
Artisanコマンドをスクリプトから呼ぶときのメモ
Laravel外のスクリプトからArtisanコマンドを呼びたくなったときに
<?php require_once(PATH_TO.'/vendor/autoload.php'); $app = require(PATH_TO.'/bootstrap/app.php'); $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); // stdoutをstringで受け取りたいとき↓ $output = new Symfony\Component\Console\Output\BufferedOutput; // そのままコンソールに表示したいとき↓ // $output = new Symfony\Component\Console\Output\ConsoleOutput; $kernel->call('migrate', [ '--force'=>true, '--quiet'=>true ], $output); // ↓ stringで取り出す echo $output->fetch();
shell_exec('php artisan migrate')
とかしてもいいのだろうけど…
- 投稿日:2021-01-29T02:52:36+09:00
ArtisanコマンドをLaravel外のスクリプトから呼ぶときのメモ
ArtisanコマンドをLaravel外のスクリプトから呼びたくなったときに
<?php require_once(PATH_TO.'/vendor/autoload.php'); $app = require(PATH_TO.'/bootstrap/app.php'); $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); // stdoutをstringで受け取りたいとき↓ $output = new Symfony\Component\Console\Output\BufferedOutput; // そのままコンソールに表示したいとき↓ // $output = new Symfony\Component\Console\Output\ConsoleOutput; $kernel->call('migrate', [ '--force'=>true, '--quiet'=>true ], $output); // ↓ stringで取り出す echo $output->fetch();
shell_exec('php artisan migrate')
とかしてもいいのだろうけど…
- 投稿日:2021-01-29T00:24:43+09:00
Quill editor 画像をS3にアップロードし表示する方法
使用言語
・PHP(Laravel)
・javascript(jQuery)
・HTML使用ライブラリ
https://quilljs.com/実装したい機能
画像を選択 → apiを叩く → s3にアップロード → s3のパスで画像を表示※Quillのeditor内に画像を挿入すると、
デフォルトだと、ローカルのパスで画像が表示される。作業フロー
①quill jsを読み込む
②javascriptを書く(ajax通信を行う)
③PHP(laravel)でajaxのリクエスト受け取ってs3に保存などもろもろ行う
④apiのルート確保①quill jsを読み込む
https://quilljs.com/
公式サイトよりドキュメント参照。↓ページquill jsクイックスタート
https://quilljs.com/docs/quickstart/<!-- Include stylesheet --> <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet"> <body> <!-- エディター部分 --> <form> <div id="editor" style="height: 200px;"></div> <input type="hidden" name="main" id="project_contents_inner"> <button type="submit" name="subbtn">投稿</button> <form> </body> <!-- Include the Quill library --> <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script> <!-- 後ほど書くjavascript読み込む --> <script src="{{ asset('/js/main.js') }}"></script>②javascriptを書く(ajax通信を行う)
main.js//ここのツールバーはカスタムできます。 var toolbarOptions = [ ['bold', 'italic', 'underline', 'strike'], // toggled buttons [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown ['link'], ['image'], ['video'], [{ 'list': 'ordered' }, { 'list': 'bullet' }], // superscript/subscript [{ 'align': [] }], [{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme ['clean'] // remove formatting button ]; const editor = new Quill('#editor', { bounds: '#editor', modules: { toolbar: this.toolbarOptions }, placeholder: 'なんか書いてー ', theme: 'snow' }); // /** // * Step1. select local image // * // */ function selectLocalImage() { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('name', 'up_file'); input.click(); // Listen upload local image and save to server input.onchange = () => { const file = input.files[0]; // file type is only image. if (/^image\//.test(file.type)) { saveToServer(file); } else { console.warn('You could only upload images.'); } }; } // /** // * Step2. save to server // */ function saveToServer(file) { /* Ajax経由で画像登録 */ var fd = new FormData(); fd.append('up_file', file); // 画像 $.ajax({ url: 'https://hoge/upload/image', // 画像登録処理を行うPHPファイル(api) type: 'POST', data: fd, cache: false, contentType: false, processData: false, }).done(function (data) { const url = data.path; console.log(data) insertToEditor(url); }).fail(function (jqXHR, textStatus, errorThrown) { console.log('ERROR', jqXHR, textStatus, errorThrown); }); return false; } // /** // * Step3. insert image url to rich editor. // * // * @param {string} url // */ function insertToEditor(url) { // push image url to rich editor. const range = editor.getSelection(); editor.insertEmbed(range.index, 'image', url); } // quill editor add image handler //画像が選択されたら↑の関数がstep1~順番に走る editor.getModule('toolbar').addHandler('image', () => { selectLocalImage(); }); //プロジェクトを投稿する際にエディター内のものを#project_contents_innerに入れる。 $(function () { $("#project_form").on("submit", function () { $("#project_contents_inner").val($(".ql-editor").html()); }) })③PHP(laravel)でajaxのリクエスト受け取ってs3に保存などもろもろ行う
php artisan make:controller Api/ProjectUploadController<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class ProjectUploadController extends Controller { public function upload_image(Request $request) { $filePath = '/picture/contents'; $image = $request->file('up_file'); $t = Storage::disk('s3')->putFile($filePath, $image, 'public'); $imagePath = Storage::disk('s3')->url($t); return response()->json([ 'message' => 'ok', 'path' => $imagePath, ], 200, [], JSON_UNESCAPED_UNICODE); } } ※s3への保存方法は別途調べてください。④apiのルート確保
api.phpRoute::post('hogehoge/image/upload','Api\ProjectUploadController@upload_image');完成
これでeditor内部にs3からのパスで画像を表示できているでしょう。
あとはquillで作成したHTML入りの文章をDBに保存するなりで大丈夫でしょう。動かなかったら質問お願いします。
日本語の記事見かけてないので書きました。