20191222のlaravelに関する記事は12件です。

【開発環境】Operation not permittedでphp artisan migrateできないときは権限周りを確認!

permission.png

どうも、たかふみです。

現在携わっているシステムの開発が終わり、「さぁそろそろステージング環境の準備をしようかなぁ」と思い、テーブルを作成するコマンド php aritisan migrate を実行しようとしたそのとき。

Operation not permitted

「migrateができない!」

今日の記事は、AWSのEC2を使って用意したステージング環境でmigrateができない問題を解決します!

解決策

自分が所有ユーザーになる&所有グループに書き込み権限を与える

やったこと

1. まずエラーメッセージを読む。

UnexpectedValueException  : The stream or file "/var/www/mcfhfs/storage/logs/laravel-2019-11-20.log" could not be opened: failed to open stream: Permission denied
  at /var/www/mcfhfs/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php:111
    107|             restore_error_handler();
    108|             if (!is_resource($this->stream)) {
    109|                 $this->stream = null;
    110|
  > 111|                 throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
    112|             }
    113|         }
    114|
    115|         if ($this->useLocking) {
  Exception trace:
  1   Monolog\Handler\StreamHandler::write()
      /var/www/mcfhfs/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php:120
  2   Monolog\Handler\RotatingFileHandler::write()
      /var/www/mcfhfs/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php:42
  Please use the argument -v to see more details.

エラーメッセージを見てみると「ログファイルへの書き込み権限がありません。」となっていました。どうやらログファイルへの書き込み権限が無いようです。

2. ログファイルの権限を確認&変更

ls -la で権限を確認すると、rw- - r -- - r --となっていました。

この場合「所有ユーザ(読/書) - 所有グループ(読) - その他のユーザ(読)」であり、
僕は所有ユーザーではなく、所有グループに属するので読み取り権限しかありません。

「そしたら、所有グループの権限に書き込み権限(w)を追加すれば良いのでは?」

ということでchmod 664 laravel-2019-11-20.log を実行。
結果、 Operation not permitted が表示されました。

調べてみると、ファイルの権限はファイル所有者でないと変更できないようです。
(参考:https://marunouchi-tech.i-studio.co.jp/3341)

3. ファイルを作り直す

「そしたら、自分がファイルの所有ユーザーになればいいのでは?」

該当のログファイルを一旦削除して再度作成。
ファイル所有者が自分になっていることを確認した後、再度migrateを実行。

php artisan migrate:fresh
Dropped all tables successfully.
Migration table created successfully.

「成功!」

4. webサーバーからも書き込みができるように権限を変更

これで解決だと思い、ブラウザからステージング環境に接続してページを表示しようとしたところ、再び「ログファイルに書き込み権限がありません」との表示がされました。
どうやらwebサーバーからの書き込み権限を与える必要があるようです。

chmod 664 laravel-2019-11-20.log
ls -la
-rw-rw-r--

これで所有ユーザー、所有グループに書き込み権限を付与できたので再度サイトへアクセス。無事に表示されていました!

■まとめ

・権限はファイル所有者でないと変更できない。
・webサーバーからの書き込み権限付与を忘れずに!
・「Operation not permitted」「Permission denied」のときには権限を確認!

今まで権限でエラーが起きたことがなかったので、これを機に知ることができて良かったです。これでPermission deniedが出ても安心ですね。それでは!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】migrateができないのは環境設定が間違っているからかも!あるあるエラーを.envと共に振り返る。

Laravel's error.png

どうも、たかふみです。

Laravelで開発を行っていると、必ず使うであろう php artisan migrateコマンド。僕も何度もお世話になったコマンドです。

今回は php artisan migrate を実行したときに出会ったエラーと共に解決策を書きたいと思います。

あるあるエラー1:Connection refused

エラーメッセージ

  Illuminate\Database\QueryException  : SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = sample and table_name = migrations and table_type = 'BASE TABLE')

  at /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:665
    661|         // If an exception occurs when attempting to run a query, we'll format the error
    662|         // message to include the bindings with SQL, which will make this exception a
    663|         // lot more helpful to the developer instead of just the database's errors.
    664|         catch (Exception $e) {
  > 665|             throw new QueryException(
    666|                 $query, $this->prepareBindings($bindings), $e
    667|             );
    668|         }
    669| 

  Exception trace:

  1   PDOException::("SQLSTATE[HY000] [2002] Connection refused")
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  2   PDO::__construct("mysql:host=127.0.0.1;port=3306;dbname=sample", "root", "password", [])
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  Please use the argument -v to see more details.

   Whoops\Exception\ErrorException  : Module 'zip' already loaded

  at Unknown:0
    1| 

  Exception trace:

  1   {main}()
      /var/www/html/artisan:0

解決策:DB_HOSTの設定を見直す。

.envファイルにあるDB_HOSTの値を確認したところ、DB_HOST=127.0.0.1となっていました。調べると、dockerの場合はコンテナ名に設定する必要があるとのことです。

【修正後】
DB_HOST=mysql

これで解決しました。

エラー2:Connection refused

 Illuminate\Database\QueryException  : SQLSTATE[HY000] [1049] Unknown database 'sample' (SQL: select * from information_schema.tables where table_schema = sample and table_name = migrations and table_type = 'BASE TABLE')

  at /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:665
    661|         // If an exception occurs when attempting to run a query, we'll format the error
    662|         // message to include the bindings with SQL, which will make this exception a
    663|         // lot more helpful to the developer instead of just the database's errors.
    664|         catch (Exception $e) {
  > 665|             throw new QueryException(
    666|                 $query, $this->prepareBindings($bindings), $e
    667|             );
    668|         }
    669| 

  Exception trace:

  1   PDOException::("SQLSTATE[HY000] [1049] Unknown database 'sample'")
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  2   PDO::__construct("mysql:host=mysql;port=3306;dbname=sample", "root", "password", [])
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  Please use the argument -v to see more details.

解決策:DB_DATABASEの値を確認

エラーメッセージから「'sample'というDBは存在しません。」ということが分かります。
この場合は、接続しようとしているDBの名前を見直す必要があります。

僕の場合はDB「bookmark」に接続をしたかったので、.envファイルの該当箇所を下記のように修正しました。

【修正後】
DB_DATABASE=bookmark

これで接続できました。

エラー3:修正しても接続できない。

解決策:envファイルが複数存在していないか確認

このエラー(?)が起きたときに一番ハマりました。笑
修正してもキャッシュを削除してもエラーが起きるため、もう一度ファイルを確認しようとファイル名で検索したところ、「.env」が2つあることに気づきました。

不要なenvファイルを削除したら上手くいったのでこれが原因だったようです。

migrateに成功すれば晴れて下記の状態になるはずです。
このババババッと実行される感じが気持ちいいんですよね。

root@592de04c5bde:/var/www/html/web# php artisan migrate       
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.01 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.01 seconds)

まとめ:エラーメッセージを読んでenvファイルを見直そう。

こうするとmigrateが上手くいかないのは.envファイルの値、つまり環境設定が間違っていることが多かったように感じます。DBが無いと開発に着手できないので、ここはスムーズに乗り越えたいですね。それでは!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】migrateができないのは環境変数が間違っているからかも!あるあるエラーを.envと共に振り返る。

Laravel's error.png

どうも、たかふみです。

Laravelで開発を行っていると、必ず使うであろう php artisan migrateコマンド。僕も何度もお世話になったコマンドです。

今回は php artisan migrate を実行したときに出会ったエラーと共に解決策を書きたいと思います。

あるあるエラー1:Connection refused(Dockerの場合)

エラーメッセージ

  Illuminate\Database\QueryException  : SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.tables where table_schema = sample and table_name = migrations and table_type = 'BASE TABLE')

  at /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:665
    661|         // If an exception occurs when attempting to run a query, we'll format the error
    662|         // message to include the bindings with SQL, which will make this exception a
    663|         // lot more helpful to the developer instead of just the database's errors.
    664|         catch (Exception $e) {
  > 665|             throw new QueryException(
    666|                 $query, $this->prepareBindings($bindings), $e
    667|             );
    668|         }
    669| 

  Exception trace:

  1   PDOException::("SQLSTATE[HY000] [2002] Connection refused")
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  2   PDO::__construct("mysql:host=127.0.0.1;port=3306;dbname=sample", "root", "password", [])
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  Please use the argument -v to see more details.

   Whoops\Exception\ErrorException  : Module 'zip' already loaded

  at Unknown:0
    1| 

  Exception trace:

  1   {main}()
      /var/www/html/artisan:0

解決策:DB_HOSTの設定を見直す。

.envファイルにあるDB_HOSTの値を確認したところ、DB_HOST=127.0.0.1となっていました。調べると、dockerの場合はDBのコンテナ名に設定する必要があるとのことです。

【修正後】
DB_HOST=mysql

これで解決しました。

エラー2:Connection refused

 Illuminate\Database\QueryException  : SQLSTATE[HY000] [1049] Unknown database 'sample' (SQL: select * from information_schema.tables where table_schema = sample and table_name = migrations and table_type = 'BASE TABLE')

  at /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php:665
    661|         // If an exception occurs when attempting to run a query, we'll format the error
    662|         // message to include the bindings with SQL, which will make this exception a
    663|         // lot more helpful to the developer instead of just the database's errors.
    664|         catch (Exception $e) {
  > 665|             throw new QueryException(
    666|                 $query, $this->prepareBindings($bindings), $e
    667|             );
    668|         }
    669| 

  Exception trace:

  1   PDOException::("SQLSTATE[HY000] [1049] Unknown database 'sample'")
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  2   PDO::__construct("mysql:host=mysql;port=3306;dbname=sample", "root", "password", [])
      /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70

  Please use the argument -v to see more details.

解決策:DB_DATABASEの値を確認

エラーメッセージから「'sample'というDBは存在しません。」ということが分かります。
この場合は、接続しようとしているDBの名前を見直す必要があります。

僕の場合はDB「bookmark」に接続をしたかったので、.envファイルの該当箇所を下記のように修正しました。

【修正後】
DB_DATABASE=bookmark

これで接続できました。

エラー3:修正しても接続できない。

解決策:envファイルが複数存在していないか確認

このエラー(?)が起きたときに一番ハマりました。笑 修正してもキャッシュを削除してもエラーが起きるため、もう一度ファイルを確認しようとファイル名で検索したところ、「.env」が2つあることに気づきました。

不要なenvファイルを削除したら上手くいったのでこれが原因だったようです。

migrateに成功すれば晴れて下記の状態になるはずです。このババババッと実行される感じが気持ちいいんですよね。

root@592de04c5bde:/var/www/html/web# php artisan migrate       
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.01 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.01 seconds)

まとめ:エラーメッセージを読んでenvファイルを見直そう。

こうするとmigrateが上手くいかないのは.envファイルの値、つまり環境変数が間違っていることが多かったように感じます。DBが無いと開発に着手できないので、ここはスムーズに乗り越えたいですね。それでは!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】boolean型へのキャスト

boolean型(理論型)とは

phpのマニュアルによれば、「boolean は、真偽の値を表します。 この値は、TRUE または FALSE のどちらかになります。」とのこと。

つまりboolean型には「TRUE」と「FALSE」の2種類の値にしかならない。

型をキャスト(変換)すると

boolean型へのキャストを行うと次の値は「FALSE」と判定される。

・boolean の FALSE
・integerの0
・floatの0.0
・空の文字列、および文字列の "0"
・要素の数がゼロである配列
・NULL
・空のタグから作成された SimpleXML オブジェクト

var_dump((bool) "false");  // bool(false)
var_dump((bool) 0);        // bool(false)
var_dump((bool) 0.0);      // bool(false)   
var_dump((bool) "");       // bool(false)
var_dump((bool) "0");      // bool(false)
var_dump((bool) []);       // bool(false)
var_dump((bool) NULL);     // bool(false)

これら以外の値はTRUEとなる

判定処理への利用

DBテーブル内にその値があるかどうかの判定
例)指定したIDのユーザが存在するかどうか

User.php
return (boolean) $this->where("id",$user->id)->first();

first()で取り出したデータはオブジェクト型のデータとなるが、存在しない場合NULL値となるためFALSEを返す。

これだったらis_null()やempty()でいいかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Using left join and name column in Model at Laravel Eloquent.

Environment

Laravel Framework 6.5.2

Example

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB; // for giving alias to base table

class TableA extends Model
{

    protected $table='table_a';

    public static function getList()
    {
        return DB::table('table_a as a')
            ->leftJoin('table_b as b', 'b.id', '=', 'a.b_id')
            ->get([
                'a.id',
                'b.id as b_id',
                'a.created_at as created_at'
            ]);
    }
}

Note

If each table have created_at and you don't narrow one created_at explicitly, you'll face an error like below

SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'created_at' in order clause is ambiguous
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsのtemplateの中でURLとかの文字列をフィルタで置き換えする方法

タイトルからすごく直感的に分かりづらいタイトル!

【本記事の目的】

vue.jsの中で、文字列の中の一部をフィルターで変更したい!
(phpでいうところのstr_replace)

【前置き】(長いので飛ばしてもokです)

laravelでは画像データを投稿すると、デフォルトでpublicに保管されます。
それを実際に表示させるには画像をシンボリックリンクでstorage側で表示させる事を推奨されています。
参考:https://qiita.com/koru1893/items/1d2f522e20744b03e3ad

でもそれをlaravel経由のvue.jsで表示させようとするとうまくいかず。
vue.jsの中でimgタグの参照urlをpublic→storage に置き換える
という作業にハマったのでメモです。

**publicだと読み込めず、storageだと読み込める状態です。
Sky_Light_Lover.png

なお、php(view)でそのデータを読み込むだけなら
str_replaceで画像のurlを変数にして置き換えて、imgタグの読み込み先として指定すれば行けます。

//$image_urlがdbから持ってきたurl情報。urlの一部を置き換えるということ。
$image_url = str_replace('public/', 'storage/', $image_url);

phpのview内でやる方法で完了しているじゃん!て思うんですが、
vue.jsとか使って非同期でdb上のURL文字列を取ってくる場合、img参照用のURLをjsファイル内で(今回の場合vue.jsん中で)差し替える必要があります。


やっと本題

カスタムフィルタを作りました!
(※vue.jsのフィルタ機能は今は自作しないとダメぽい。vue.1系はフィルタのメソッドあったぽいです。なんで削除されたんだろう?)

filtersの中に、
「public」という文字列を「..storage」に置き換える
「replace」という処理を作りました。
(置き換え前、置き換え先のコードはもちろん任意です。)

vue.js
<script>
    export default {
        props:['hoge'],
        data:function(){
            return{
             (略)
            }
        },
        mounted(){
          (略)
        },
        filters:{ //カスタムフィルタ。こんな感じ!
            replace:function(val){
                return val.replace("public", "../storage");   
            }
        },
       }
    }
</script>


replaceのフィルタは準備できました。
あとはtemplateの中で、フィルタします!
(※vue.jsの中では {{ フィルタしたいデータ|フィルタの処理名 }} という書き方でフィルタリングできます。マスタッシュ!)

vue.js
<template>
(略)
<img :src="image_url | replace('public','../storage')" alt="">
(略)
</template>

これで、urlのなかの一部を置き換えられます。
公開サイト側でソースをみると、下記のようになります。

フィルタなしの結果

<img src="public/post_images/hoge.jpg">

フィルタありの結果

<img src="../storage/post_images/hoge.jpg">

これできちんと画像が公開サイトで読み込めました。やった!

参考:https://www.itsolutionstuff.com/post/how-to-replace-string-in-vue-jsexample.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】 Vue.jsのtemplateの中で文字列置き換えする方法【フィルタ】

タイトルからすごく直感的に分かりづらいタイトル!

【前置き】

まず、larabelでは投稿した画像はpublicに保管されてしまうんですが
それを実際に表示させるには画像をシンボリックリンクでstorage側で表示させないといけません。
参考:https://qiita.com/koru1893/items/1d2f522e20744b03e3ad
で、本記事でやりたいことは
vue.jsの中でimgタグの参照urlをpublic→storage に置き換える
という作業。

Sky_Light_Lover.png

php(view)でそのデータを読み込むだけなら
str_replaceで画像のurlを変数にして置き換えて、imgタグの読み込み先として指定すれば行けます。

//$image_urlがdbから持ってきたurl情報。urlの一部を置き換えるということ。
$image_url = str_replace('public/', 'storage/', $image_url);

本題

長くなりましたが本題。
vue.jsで文字列置き換え、前述のreplace的なことをやろうとして「フィルタが良さそう!」
となったんですが、いい感じのreplace処理がない!
参考情報調べても少しややこいreplaceばかり出てきたけど、簡単そうな方法がありました。
参考:https://www.itsolutionstuff.com/post/how-to-replace-string-in-vue-jsexample.html

調べるのに少し時間かかったので、備忘録を兼ねて。

やろうとしたこと

  1. コントローラーでDBから取得したデータを
  2. view に持ってきて、そこからvue.jsに受け渡す。
  3. vue.js内で受け取ったデータのimage出力するときに、url情報を一部書き換えたい(書き換えたい理由は[前提]参照)

1.2は割愛。この辺りが参考になるかもです!

3でやった方法。
カスタムフィルタで特定のコードをreplaceしたい!
※vue.jsのフィルタ機能は今は自作しないとダメぽい(vue.1系はフィルタのメソッドあったぽいです。なんで削除されたんだろう)

filtersの中に、
「public」という文字列を「..storage」に置き換える
「replace」というフィルタを作りました。

vue.js
<script>
    export default {
        props:['hoge'],
        data:function(){
            return{
             (略)
            }
        },
        mounted(){
          (略)
        },
        filters:{ //カスタムフィルタ。こんな感じ!
            replace:function(val){
                return val.replace("public", "../storage");   
            }
        },
       }
    }
</script>


replaceのフィルタは準備できました。
あとはtemplateの中で、フィルタします!
vue.jsの中では
{{ フィルタしたいデータ|フィルタの処理名 }}
という書き方でフィルタリングできます

vue.js
<template>
(略)
<img :src="image_url | replace('public','../storage')" alt="">
(略)
</template>

これで、urlのなかの一部を置き換えられます。
公開サイト側でソースをみると、下記のようになります。

フィルタなしの結果

<img src="public/post_images/hoge.jpg">

フィルタありの結果

<img src="../storage/post_images/hoge.jpg">

これできちんと読み込めました。やった!

参考:https://www.itsolutionstuff.com/post/how-to-replace-string-in-vue-jsexample.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】 Vue.jsのtemplateの中でURLとかの文字列置き換えする方法【フィルタ/非同期】

タイトルからすごく直感的に分かりづらいタイトル!

【前置き】

まず、larabelでは投稿した画像はpublicに保管されてしまうんですが
それを実際に表示させるには画像をシンボリックリンクでstorage側で表示させないといけません。
参考:https://qiita.com/koru1893/items/1d2f522e20744b03e3ad
で、本記事でやりたいことは
vue.jsの中でimgタグの参照urlをpublic→storage に置き換える
という作業。

Sky_Light_Lover.png

php(view)でそのデータを読み込むだけなら
str_replaceで画像のurlを変数にして置き換えて、imgタグの読み込み先として指定すれば行けます。

//$image_urlがdbから持ってきたurl情報。urlの一部を置き換えるということ。
$image_url = str_replace('public/', 'storage/', $image_url);

phpのview内でやる方法で完了しているじゃん!て思うんですが、
vue.jsで非同期でも取ってくるのをやるのも試そうとして、img参照用のURLを差し替える方法を探すのに時間がかかったのです。


本題

長くなりましたが本題。
vue.jsで文字列置き換え、前述のreplace的なことをやろうとして「フィルタが良さそう!」
となったんですが、いい感じのreplace処理をやるのに簡単そうな方法がありました。
参考:https://www.itsolutionstuff.com/post/how-to-replace-string-in-vue-jsexample.html

もう少し細かく全体の流れをかくと

  1. コントローラーでDBから取得したデータを
  2. view に持ってきて、そこからvue.jsに受け渡す。
  3. vue.js内で受け取ったデータのimage出力するときに、url情報を一部書き換えたい(書き換えたい理由は[前提]参照)

1.2は割愛。この辺りが参考になるかもです!

で、3でやった方法。

カスタムフィルタで特定のコードをreplaceしました!
(※vue.jsのフィルタ機能は今は自作しないとダメぽい。vue.1系はフィルタのメソッドあったぽいです。なんで削除されたんだろう?)

filtersの中に、
「public」という文字列を「..storage」に置き換える
「replace」という処理を作りました。
(置き換え前、置き換え先のコードはもちろん任意です。)

vue.js
<script>
    export default {
        props:['hoge'],
        data:function(){
            return{
             (略)
            }
        },
        mounted(){
          (略)
        },
        filters:{ //カスタムフィルタ。こんな感じ!
            replace:function(val){
                return val.replace("public", "../storage");   
            }
        },
       }
    }
</script>


replaceのフィルタは準備できました。
あとはtemplateの中で、フィルタします!
(※vue.jsの中では {{ フィルタしたいデータ|フィルタの処理名 }} という書き方でフィルタリングできます。マスタッシュ)

vue.js
<template>
(略)
<img :src="image_url | replace('public','../storage')" alt="">
(略)
</template>

これで、urlのなかの一部を置き換えられます。
公開サイト側でソースをみると、下記のようになります。

フィルタなしの結果

<img src="public/post_images/hoge.jpg">

フィルタありの結果

<img src="../storage/post_images/hoge.jpg">

これできちんと画像が公開サイトで読み込めました。やった!

参考:https://www.itsolutionstuff.com/post/how-to-replace-string-in-vue-jsexample.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laradockでXdebugを使おうとしたらエラーと戦うことになった話

どうもやまうちです。

PhpStormとLaradockを使ってXdebugの導入をしようと思っていたところエラーとの戦いになりました。
docker-compose up --build nginx mysql workspace
これをしたらめっちゃエラー出ました(;´Д`)
エラー出ると冷静になります。
間違えました、放心状態になります。
かっこよく言うと茫然自失です(今調べました)。

とはいえXdebugの導入がスタートラインなので立ち止まるわけには行きません。
結果としては無事導入できました。
ということで今回の記事は解決記録です。

この記事を書くまでの話

※いきなり本編の裏話的な話なので次まで飛ばしても1ミリの支障もございません

「今週はデバッグの記事書くぞ~!」
と意気込む午後10時。
Qiitaを開き導入文を書き始める。
ちょっと知識を得て調子に乗っているボクは、デバッグをまだ使っていない後輩たちを煽るために奮い立たせるために、手を止めることなく導入文を書き終えた。
叩くキーボードの音に心地良ささえ感じていた。

その余韻に浸かっているさなか、ボクは気づいた。
「そういや職場のPCにはデバッグ環境あるけど、自宅PCにはねえな」
「軽く導入でもするか---」
ナッパがあいさつがわりに「クン」と東の都を消したときのように、
ボクもXdebugの導入を「クン」した。

その「クン」が戦いの合図になるとも知らずに・・・。

環境

  • Winddows10 pro
  • Docker for windows
  • Laradock
  • PhpStorm

PhpstormにXdebugを導入

何も見ずに導入できるハズもなくググりました。
そこでたどり着いたこの記事を参考に導入を進めました。

参考記事:Laradock+PhpStormでXdebug - けけずんセルフハッキング

  1. xdebug.iniを編集
  2. .envを編集
  3. docker-compose up --build nginx mysql workspaceを実施
  4. エラー出る
  5. (;・∀・)

docker-compose up --build nginx mysql workspaceこいつでエラーか。
ちっ、しゃーねーなぁ。

workspaceのエラー

こんなエラーが出ました。

E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
ERROR: Service 'workspace' failed to build: The command '/bin/sh -c if [ ${INSTALL_XDEBUG} = true ]; then     apt-get install -y php${LARADOCK_PHP_VERSION}-xdebug &&     sed -i 's/^;//g' /etc/php/${LARADOCK_PHP_VERSION}/cli/conf.d/2
0-xdebug.ini &&     echo "alias phpunit='php -dzend_extension=xdebug.so /var/www/vendor/bin/phpunit'" >> ~/.bashrc ;fi' returned a non-zero code: 100

これから分かることは
1. workspace関連のエラー
以上!!
要するになんもわからん!!!!wwwwwwwwwwwwww
いや草も生えねえよ、こんなエラー。

とりあえずググってこのページにたどり着く。
Php 7.2 XDebug install error · Issue #1847 · laradock/laradock · GitHub

英語アレルギーの症状が出ているがそんなこと言っている場合でもなく、冷静にボタンを押す。
このページを翻訳しますか? [翻訳] ポチ
読めるっ!読めるぞおおおお!!!!!

laradock/workspace/Dockerfileを編集する

なんかこんなことが書いてあった。

edit file laradock/workspace/Dockerfile line 261
apt-get install -y php\${LARADOCK_PHP_VERSION}-xdebug && \
to
apt-get update && apt-get install -y php\${LARADOCK_PHP_VERSION}-xdebug && \
then run docker-compose build workspace

何を言ってるのかはわからないが()、なんかそれっぽいのでDockerfileの該当箇所を編集してみた。
そしてdocker-compose up --build nginx mysql workspace

お!挙動が変わった!うまくいっ・・・ん?

php-fpmのエラー

またエラー。

E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
ERROR: Service 'php-fpm' failed to build: The command '/bin/sh -c if [ ${INSTALL_IMAGEMAGICK} = true ]; then     apt-get install -y libmagickwand-dev imagemagick &&     pecl install imagick &&     docker-php-ext-enable imagick ;fi'
returned a non-zero code: 100

上の方にもっと色々出てたけど、とりあえずこちら↑のみ掲載。
例のごとく何言ってるのかわからないので、ググる。
そしてQiitaの記事にたどり着く。
laradock(Docker)+PhpStorm環境のxdebug導入でphp-fpmのエラーが起きる - Qiita

laradock/php-fpm/Dockerfileを編集

laradock/php-fpm/DockerfileのImageMagickのこれ↓を

RUN if [ ${INSTALL_IMAGEMAGICK} = true ]; then \
    apt-get install -y libmagickwand-dev imagemagick && \
    pecl install imagick && \
    docker-php-ext-enable imagick \
;fi

こう↓しろって書いてあったからする

RUN if [ ${INSTALL_IMAGEMAGICK} = true ]; then \
    rm -rf /var/lib/apt/lists/* && \
    apt-get update && \
    apt-get install -y libmagickwand-dev imagemagick && \
    pecl install imagick && \
    docker-php-ext-enable imagick \
;fi

んでdocker-compose up --build nginx mysql workspace
よし!いい感じ!今度こそうまk・・・おいいいぃぃぃ!!

nginxのエラー

今度はこんなエラー

adduser: group 'www-data' in use
ERROR: Service 'nginx' failed to build: The command '/bin/sh -c apk update     && apk upgrade     && apk --update add logrotate     && apk add --no-cache openssl     && apk add --no-cache bash     && adduser -D -H -u 1000 -s /bin/ba
sh www-data' returned a non-zero code: 1

まあ似たような内容のエラー。
workspaceのエラーとphp-fpmのエラーの経験を活かして頑張ろうとしたけど、1ミリもわからずにググった。
Laradockコンテナのビルドで ”adduser: group 'www-data' in use” が発生したときの対応【メモ】 - I am a software engineer

laradock/nginx/Dockerfileを編集

記事にはこう書いてた。

RUN apk update \
     && apk upgrade \
     && apk add --no-cache openssl \
-    && apk add --no-cache bash \
-    && adduser -D -H -u 1000 -s /bin/bash www-data
+    && apk add --no-cache bash
+
+RUN set -x ; \
+    addgroup -g 82 -S www-data ; \
+    adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1

 ARG PHP_UPSTREAM_CONTAINER=php-fpm
 ARG PHP_UPSTREAM_PORT=9000

昔の自分ならよくわからん書き方だが、今なら分かるっ!
ということでlaradock/nginx/Dockerfileのこれ↓を

RUN apk update \
    && apk upgrade \
    && apk --update add logrotate \
    && apk add --no-cache openssl \
    && apk add --no-cache bash \
    && adduser -D -H -u 1000 -s /bin/bash www-data

こう↓した

RUN apk update \
    && apk upgrade \
    && apk --update add logrotate \
    && apk add --no-cache openssl \
    && apk add --no-cache bash

RUN set -x ; \
    addgroup -g 82 -S www-data ; \
    adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1

んで、docker-compose up --build nginx mysql workspace
うまく行きました!!!

あとは進むだけ

Laradock+PhpStormでXdebug - けけずんセルフハッキング
あとはこの↑記事通り進んで無事にデバッグできるようになりました。
ということでデバッグに関する記事はまた後日・・・。

エラーとの戦い

とりあえず無事にXdebugの導入という目標は達成したわけですが、
エラーに関しては裏側で何が起きてて、Dockerfileを編集したことどう変わったのかはわかりません!!!(;・∀・)
それはおいおい知識を付けていきますm(_ _)m

エラーが発生したらググる!
ボクがやっているレベルくらいの話であれば、たいていこれで解決できるはずです。
解決しないときはググり力が足りていないんだと思います。

どういうワードで検索したら良いかわからないことはよくあると思います。
とりあえずそれっぽいワードなんでもいいので調べましょう。
そしたらそれっぽいワードを拾えたりするので、それをまたググる。
そんなことをしてたら答えに近づいていけます!

先輩が教えてくれた記事を共有しますので、読んでみてください。
Vue.js未経験のベテランプログラマーに作業依頼したところ難なくこなしてしまうのですが、隠れて努力してるのでしょうか?それとも経験により新しいフレームワークにも対応できてしまうのでしょうか?に対する阿部 誠 (Makoto Abe)さんの回答 - Quora

エラーに負けずに戦おう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel-Excelでセル結合した複数シートを出力する

この記事は、Fusic その2 Advent Calendar 2019 22日目の記事です。

はじめに

LaravelでExcel出力するなら Laravel-Excel を使っている方が多いかと思います。

ただこのライブラリ、PHPSpreadSheet の完全なラッパーではありません。
それ故に少々詰まったので、書き残しておきます。

環境

PHP 7.4.1
Laravel 6.9.0
Laravel-Excel 3.1

クラス構成

Userモデルを介して取得した全データを、
UsersExportクラスにDIし、各ユーザごとにUsersSheetクラスを作成します。

├── app
│   ├── Exports
│   │   ├── UsersExport.php
│   │   └── UsersSheet.php
│   └── User.php

最終的には各ユーザごとにレイアウトが変動する複雑なExcelファイルの出力が必要だったのですが、
今回はその前段階として、結合セルのある複数シートの出力を目指します。
image.png

セル結合

Laravel-Excel では、PHPSpreadsheet の mergeCells() がラッパーされていません。
即ちセル結合をする際は、ライフサイクルの中で PHPOffice\PHPSpreadSheet\WorkSheet\WorkSheet インスタンスを取得する必要があります。

Laravel-Excelのライフサイクル

以下4つのイベントが順に発火します。

  • BeforeExport
  • BeforeSheet
  • AfterSheet
  • BeforeWriting

公式ドキュメントの記述によると、 PHPOffice\PHPSpreadSheet\WorkSheet\WorkSheetが生成されるのはBeforeSheetイベントの直前です。
しかし、肝心の取得方法は記載されていません。

PHPOffice\PHPSpreadSheet\WorkSheet\WorkSheetの取得

以下のようにして、BeforeSheetイベント内で取得できました。
あとはPHPSpreadSheetのドキュメントに従うだけです。
$event->getSheet()で取得されるのが Maatwebsite\Excel\Sheet なのはややこしいですね。

namespace App\Exports;

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeSheet;

class UsersExport implements WithEvents
{
    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeSheet::class => function (BeforeSheet $event) {
                $sheet = $event->getSheet()->getDelegate();
                // セル結合
                $sheet->mergeCells('A2:D2');
            },
        ];
    }
}

複数シートの出力

続いて、複数シートの出力を行います。

失敗例

Laravel-Excel # Multiple Sheets を参考に、
先ほどのコードに WithMultipleSheetssheets() メソッドを追加してみました。

namespace App\Exports;

use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\BeforeSheet;

class UsersExport implements WithEvents, WithMultipleSheets
{
    protected $users;

    public function __construct($users)
    {
        $this->users = $users;
    }

    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeSheet::class => function (BeforeSheet $event) {
                $user = $event->getSheet()->user;
                $sheet = $event->getSheet()->getDelegate();
                // セル結合
                $sheet->mergeCells('A2:D2');
                $sheet->setCellValue('A2', $user->name);
            },
        ];
    }

    /**
     * @return array
     */
    public function sheets(): array
    {
        $sheets = [];
        foreach ($this->users as $user) {
            $sheets[] = new UsersSheet($user);
        }
        return $sheets;
    }
}
<?php

namespace App\Exports;

use App\User;

class UsersSheet
{
    public $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

これを実行すると…

image.png

まっさら!!!

ライフサイクルの確認

各イベントが発火しているのか確認します。

<?php

namespace App\Exports;

use App\User;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Events\BeforeExport;
use Maatwebsite\Excel\Events\BeforeWriting;
use Maatwebsite\Excel\Events\BeforeSheet;
use Maatwebsite\Excel\Events\AfterSheet;

class UsersExport implements WithEvents, WithMultipleSheets
{

    protected $users;

    public function __construct($users)
    {
        $this->users = $users;
    }

    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeExport::class => function (BeforeExport $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeExport');
                \Log::debug('===========================================');
            },
            BeforeSheet::class => function (BeforeSheet $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeSheet');
                \Log::debug('===========================================');
            },
            AfterSheet::class => function (AfterSheet $event) {
                \Log::debug('===========================================');
                \Log::debug('AfterSheet');
                \Log::debug('===========================================');
            },
            BeforeWriting::class => function (BeforeWriting $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeWriting');
                \Log::debug('===========================================');
            },
        ];
    }


    /**
     * @return array
     */
    public function sheets(): array
    {
        $sheets = [];
        foreach ($this->users as $user) {
            $sheets[] = new UsersSheet($user);
        }
        return $sheets;
    }
}
[2019-12-22 03:42:15] local.DEBUG: ===========================================  
[2019-12-22 03:42:15] local.DEBUG: BeforeExport  
[2019-12-22 03:42:15] local.DEBUG: ===========================================  
[2019-12-22 03:42:15] local.DEBUG: ===========================================  
[2019-12-22 03:42:15] local.DEBUG: BeforeWriting  
[2019-12-22 03:42:15] local.DEBUG: ===========================================  

BeforeSheet, AfterSheet イベントが発火していませんね。

UsersSheet のほうにもデバッグコードを追加してみます。

class UsersSheet implements WithEvents
{
    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeExport::class => function (BeforeExport $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeExport -- Sheet');
                \Log::debug('===========================================');
            },
            BeforeSheet::class => function (BeforeSheet $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeSheet -- Sheet');
                \Log::debug('===========================================');
            },
            AfterSheet::class => function (AfterSheet $event) {
                \Log::debug('===========================================');
                \Log::debug('AfterSheet -- Sheet');
                \Log::debug('===========================================');
            },
            BeforeWriting::class => function (BeforeWriting $event) {
                \Log::debug('===========================================');
                \Log::debug('BeforeWriting -- Sheet');
                \Log::debug('===========================================');
            },
        ];
    }
}
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeExport  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: AfterSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: AfterSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: AfterSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: AfterSheet -- Sheet  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  
[2019-12-22 04:03:45] local.DEBUG: BeforeWriting  
[2019-12-22 04:03:45] local.DEBUG: ===========================================  

BeforeSheet, AfterSheet はどちらも UsersSheet 内での発火になっています。
Sheetイベントなので、確かに各Sheetクラスに委任するのが自然ですね。

最終コード

以下のコードで、望んでいたExcelが出力できました。

<?php

namespace App\Exports;

use App\User;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;

class UsersExport implements WithMultipleSheets
{
    protected $users;

    public function __construct($users)
    {
        $this->users = $users;
    }

    /**
     * @return array
     */
    public function sheets(): array
    {
        $sheets = [];
        foreach ($this->users as $user) {
            $sheets[] = new UsersSheet($user);
        }
        return $sheets;
    }
}
<?php

namespace App\Exports;

use App\User;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Events\BeforeSheet;

class UsersSheet implements WithEvents, WithTitle
{
    private $user;

    /**
     * @param User $user
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * @return array
     */
    public function registerEvents(): array
    {
        return [
            BeforeSheet::class => function (BeforeSheet $event) {
                $sheet = $event->getSheet()->getDelegate();
                $sheet->mergeCells('A2:D2');
                $sheet->setCellValue('A2', $this->user->name);
            },
        ];
    }

    public function title(): string
    {
        return $this->user->name;
    }
}

まとめ

今回はセル結合のみを行いましたが、
PHPOffice\PHPSpreadSheet\WorkSheet\WorkSheetインスタンスの取得方法がわかったので、ほかにもいろいろできそうです。
BeforeSheet以外のイベントも上手く使っていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【入門】Laravel×Vue.js②〜実装編〜

はじめに

PHPのフレームワークであるLaravelで作成したアプリケーションに
JavaScriptのフレームワークであるVue.jsを連携させる方法について説明します。
簡単な機能を実装します。自分のコンポーネントを登録する方法について説明します
実装する機能
  メッセージを表示して、ボタンを押すと、メッセージを反転する

コンポーネントの作成

まず、resources/js/components/内にHelloComponent.vueを作成してください
そして、内容を以下のようにしてください

resources/js/components/HelloComponent.vue
<template>
    <div id="app">
      <p>{{ message }}</p>
      <button v-on:click="reverseMessage">Reverse Message</button>
    </div>
</template>
<script>
export default {
      data:function(){
          return {
            message: 'Hello Vue!',
          };
      },
      methods: {
        reverseMessage: function () {
          this.message = this.message.split('').reverse().join('')
        }
      }
}
</script>

コンポーネントの登録

上で作成したコンポーネントを登録しましょう

resource/js/app.js
require('./bootstrap');

window.Vue = require('vue');

Vue.component('example-component', require('./components/ExampleComponent.vue').default);
Vue.component('hello-component', require('./components/HelloComponent.vue').default);


ビューファイルへの組み込み

ビューファイルの追加したい時に下記を記述してください

hello/index.blade.php
<div id="app">
  <hello-component></hello-component>
</div>

以上で実装完了です。

疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【IT専門学生の就活】僕がこれからやること

自己紹介

僕はIT系の専門学生1年で、現在はWeb系の企業を目指して学習を進めています。今回この記事を書こうと思った理由は、就活が迫ってきたのですが学校にはWeb系企業の求人がなく、ポートフォリオを作成し、技術を証明する必要があると考えたからです。

今までやったこと

僕がWeb開発を始めたのは大学受験に落ちた日からです。もちろん最初は Progateさんから始めました:)

そしていつの間にかIT系の専門学校に

独学

  • ポートフォリオページ作成(HTML&CSS)
  • Ruby
  • Ruby on Rails
  • AWS(EC2)
  • adobeXD
  • PaizaスキルチェックBランク(Java,Ruby)

学校

  • MySQL
  • CentOS
  • GUI電卓作成(JavaFX)
  • GUIポーカー作成(JavaFX)
  • シューティングゲーム作成(JavaFX)
  • 基本情報技術者試験合格
  • 成績全てA(1年前期)

書き途中に気付いたのですが、自主学習時間の割に形になっているものが少ないなと思ったので、作成物や資格合格を目標として取り組むことにします。

これからやること

自分でやることで何をすればよいかを整理するために書くので、学校のみで勉強するものは省いています。

就活までにやること(専門1年)

先頭の番号は優先順位です

  1. Webアプリ作成(Rails&HTML&CSS&BT)  [独学]
  2. PaizaスキルチェックAランク(Python) [独学]
  3. ポートフォリオサイト作成(HTML&CSS&BS) [独学]
  4. 成績全てA [自宅+学校]
  5. 応用情報技術者試験合格 [自宅+学校]

プラスしてやること(専門2年)

まだ具体的に定まっていないため優先順位はつけられないのでつけません。

  • Webアプリ作成(Laravel)
  • Webアプリ作成(Java Spring Framework)
  • TOEIC600点?(受けたことないからわからない)

ここからは趣味でやりたいことかもしれません。余裕があればやります

  • 高校数学復習+大学数学
  • 機械学習・人工知能
  • Pythonでいろんな分野で遊ぶ

タスクに分解

やることを明確にするため、目標をタスクに分解していきます。

1.Webアプリ作成(Rails)

  • Ruby
  • Rails
  • HTML・CSS
  • Git

主役はRailsですがもちろん基礎文法としてRuby、デザインとしてHTML・CSS、バージョン管理としてGitをやる必要があります。デプロイはHerokuで行います(デプロイについてはRailsチュートリアルで学習できます↓)

タスク

【Ruby】
・[完了]Progate Rubyコース
・[完了]Paiza Rubyコース
・[完了]プロを目指す人のためのRuby

【Rails】
・[完了]Progate Railsコース
Railsチュートリアル

【HTML・CSS】
・[完了]Progate HTML&CSS

【Git】
Udemy Git:はじめてのGitとGitHub

2.PaizaスキルチェックAランク

  • Python
  • アルゴリズム

Pythonは応用情報と組み合わさった良い講座があったのでそれを見ることにします。アルゴリズムは演習もやりながらできるものがQiitaにたくさんあったのでそれをやることにします。

タスク

【Python】
基本情報技術者試験+応用情報技術者試験+Python+SQL

【アルゴリズム】
AtCoder に登録したら次にやること 過去問精選10問
AtCoder 版!蟻本 (初級編)

3.ポートフォリオサイト作成(HTML&CSS&BS)

サーバーサイドを見せるためのポートフォリオ程度のHTML・CSSで新たに学習することはないので適当に調べながらやることにします。

4.成績全てA

テスト範囲が発表されてから考えます

5.応用情報技術者試験合格

基本情報で基本的なことは覚えたのでやるべきことは過去問演習だけです

タスク

・過去問演習

おわりに

応用情報合格したいな...?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む