20200517のlaravelに関する記事は14件です。

valetを一度アンインストールしたら使えなくなったので、色々試行錯誤してみた

brew install dnsmasq
brew services restart dnsmasq

sudo brew services start --all で治った。
ping example.testみたいので、試すと楽

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

【Laravel・Eloquent】modelの関数名になぜscopeをつけるのか

scopeの定義方法

class Hoge extends Model {
  public function scopeFuge($query, $code) {
    //処理
  } 
}

関数名にscopeを追加し、引数$query$code(引数名は自由)を定義します。

引数

$queryとは

この引数は、whereで取得されるのと同じBuilderインスタンスが渡されます。

$code(引数名は自由)

コントローラから渡された引数がこの値に代入されます。

Person.php(Model)

class Person extends Model {

    public function scopeNameEqual($query, $str) 
        {
            return $query->where('name', $str);
        }
}

PersonController.php(Controller)

use App\Person;

class PersonController extends Controller {

    public function search(Request $request) {
        $item = Person::nameEqual($request->input)->first();
        //〜その他処理は割愛〜
        return view('person.find, $param);
    }
}

scopeを呼び出す時は、関数名のscopeを省略します。

なぜscopeを使うのか

scopeを使うことにより、上記の2つの引数が利用できるようになります。

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

【VSCode】Laradockでデバッグできるようにしようぜ!!【Mac】

VSCodeの拡張機能を使ってデバッグ環境を作る方法を解説します。

拡張機能のダウンロード

予めPHP Debugという拡張機能をインストールしておきます。

.envの書き換え

laradockディレクトリ下の.envを下記の2つの変数をfalseからtrueに書き換えます。

WORKSPACE_INSTALL_XDEBUG=true
PHP_FPM_INSTALL_XDEBUG=true

Xdebugの書き換え

  • laradock/php-fpm/xdebug.ini
  • laradock/workspace/xdebug.int の変数を下記のように書き換えます。
xdebug.ini
xdebug.remote_host=docker.for.mac.localhost
xdebug.remote_connect_back=0
xdebug.remote_port=9001
xdebug.idekey=Listen for XDebug

xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.cli_color=1
xdebug.profiler_enable=0
xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling"

xdebug.remote_handler=dbgp
xdebug.remote_mode=req

xdebug.var_display_max_children=-1
xdebug.var_display_max_data=-1
xdebug.var_display_max_depth=-1

containerの再構築

docker-compose up -d --build nginx mysql

--buildオプションで起動します。イメージがある状態でも再度ビルドしてcontainerを立ち上げてくれます。

Visual Studio Codeの設定

プログラム実行の設定(デバッグの設定)を行うのが.vscode/launch.jsonファイルです。

作業ディレクトリのlaunch.jsonファイルを作成して、下記を記述します。user_app_nameのところには自分のappディレクトリの名前をいれてください。

vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9001,
            "pathMappings": {
                "/var/www": "${workspaceFolder}/user_app_name"
            },
            "log": true
        }
}

デバッグの実行

ソースの行番号の左側を押して、ブレークポイントを設定。
サイドバーのデバッグのボタンを押して、[Listen for XDebug]を選択し、RUNの△を押せば準備完了。ブレークポイントのところで止まるようになります。

Screen_Shot_2020-05-17_at_22_11_34.png

参考

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

HomesteadのBoxインストールエラー対策(Windows + Vagrant アップデート手順)

環境

OS:Windows10 Pro
Vagrant:2.2.4

1.背景

Laravel5.x系の環境を利用していたが、laravel6.x系をインストールするのに、Homesteadを新しくしようとしたら、BOXのインストールエラーが出てしまったので、その時の対応メモ

$ vagrant box add laravel/homestead
    homestead: Calculating and comparing box checksum...
The specified checksum type is not supported by Vagrant: sha512.
Vagrant supports the following checksum types:

md5, sha1, sha256

2.対応

どうやらVagrant2.2.6からはCheckSumのサポートが変わった模様。Vagrantを2.2.9(2020.05 最新)にアップデートする。
VagrantのWindowsインストーラーを使ってアップデート(インストールすると、古いバージョン情報もクリーンしてくれるので、手動クリーニング作業などは不要でした)

・Vagrantのインストーラー
https://www.vagrantup.com/downloads.html

C:\Users>vagrant -v
Vagrant 2.2.9

以前のバージョンで利用していたローカルLaravel5.8系のサイトもVagrant upで起動・確認し影響ないことを確認。

再度実行

C:\Users\murat>vagrant box add laravel/homestead
==> box: Loading metadata for box 'laravel/homestead'
    box: URL: https://vagrantcloud.com/laravel/homestead
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) hyperv
2) parallels
3) virtualbox
4) vmware_desktop

Enter your choice: 3
==> box: Adding box 'laravel/homestead' (v9.5.1) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/9.5.1/providers/virtualbox.box
Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com
    box:
    box: Calculating and comparing box checksum...
==> box: Successfully added box 'laravel/homestead' (v9.5.1) for 'virtualbox'!

HomeStead + Vagrantで仮想サーバ起動してブラウザ経由でPHPファイル表示できたので、とりあえずここまで。

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

phpの「=>」(ダブルアロー演算子)と「->」(アロー演算子)

「=>」:ダブルアロー演算子

連想配列のこの「キー」にこの「値」を入れますよー
というのを表すための演算子。

phpの
ダブルアローがわからなければこれを読むんだ!!

http://raichel.hatenablog.com/entry/2015/01/15/012116

「->」:アロー演算子

インスタンスの持つ、プロパティにアクセスするための演算子。

この記事で私は理解した。

https://techacademy.jp/magazine/19296

インスタンスは「クラスを実体化したもの」。
インスタンスがわからないときには、オブジェクト指向を学ぼう。

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

LaravelでJSONリクエストを受け取る

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Controllerにメソッド追加

(1) /sample/app/Http/Controllers/SampleController.phpにrequestJson1メソッド、requestJson2メソッドを追記

    public function requestJson1(Request $request)
    {
        return view('sample.requestJson');
    }

    public function requestJson2(Request $request)
    {
        $data = [
            'a' => $request->input('a'),
            'b' => $request->input('b.bb'),
            'c' => $request->input('c')['cc']
        ];
        return $data;
    }

(2) /sample/routes/web.phpに下記を追記
Route::get('sample/request-json1', 'SampleController@requestJson1');
Route::post('sample/request-json2', 'SampleController@requestJson2');

viewの作成

/sample/resources/views/sample/requestJson.blade.phpファイル作成

requestJson.blade.php
<html>
    <head>
        <title>sample</title>
        <script type="text/javascript">
            function ajax(){

                var form = document.getElementById("sampleForm");

                var reqData = {"_token": null, "a": null, "b": {"bb": null}, "c": {"cc": null}};
                reqData._token = form._token.value;
                reqData.a = form.a.value;
                reqData.b.bb = form.b.value;
                reqData.c.cc = form.c.value;

                var req = new XMLHttpRequest();
                req.open(form.method, form.action);
                req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
                req.responseType = 'json';
                req.send(JSON.stringify(reqData));
                req.onload = function () {
                    var json = req.response;
                    alert("a:" + json['a']  + "\n" + "b.bb:" + json['b']+ "\n" + "c.cc:" + json['c']);
                }
            }
        </script>
    </head>
    <body>

        <form id="sampleForm" action="{{ url('sample/request-json2') }}" method="post"  onsubmit="ajax(); return false;">
            @csrf
            <input type="text" name="a" >
            <input type="text" name="b" >
            <input type="text" name="c" >
            <input type="submit" >
        </form>

    </body>
</html>

Laravelでjsonリクエストを受け取るようにするために
req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
を書きました
LaravelのControllerクラスはjsonを受け取るからと言って特別な記載は要りません
Content-Typeヘッダプロパティにapplication/jsonが指定されていれば、inputメソッドで値を取得できます

動作確認

http://localhost/laravelSample/sample/request-json1

インプットフォームに値を適当に入力して送信ボタンをクリック
インプットフォームに入力した値がalertで表示された

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

laravel ルーティング

Route::get('/hoge', 'HogeController@index');

【意味】
→「/hoge」パスにゲット送信でリクエストが来たら、HogeControllerのindexアクションへつなぐ(indexアクションの中で処理する)

という意味。

名前つきルーティング

Route::get('/hoge', 'HogeController@index')->name('hoge');

【使い所】
→コントローラやビューの中で URL を指定する時。

素のPHPではソースコードに直にパスを書いていた。
しかし、この方法だと、パスに変更があった時に、多くの箇所を変更する必要がある。
そこで、ルートに名前を付け、ルート名で URL を指定することで、この問題を解決できる。

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

【Laravel】.env.testingの使用方法と注意点

LaravelでPHPUnitを使うときにテスト用の`.env`を設定するのにハマったので記事にしました。

環境

Docker 19.03.8
PHP 7.3.16
Laravel 6.18.3

.env.testingの設定方法

本番DBを汚さないようにPHPUnitを実行する時のみテスト用のDBを使うようなことはよくあると思います。

その方法の一つとして.env.testingを作成し、phpunitにその設定値を読み込ませるやり方があります。

.env.exampleをコピーして.env.testingを作成し、以下の箇所を変更します。

env.testing
APP_ENV=testing // testingに変更
APP_KEY= // 空にしておく

// 接続したいDBの情報を設定する
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=test_database
DB_USERNAME=root
DB_PASSWORD=

そうしたらphpunit.xmlを開き、APP_ENVの値をtestingに変更します。

phpunit.xml
<php>
    <env name="APP_ENV" value="testing" force="true"/>
    <env name="DB_CONNECTION" value="mysql"/>
    <env name="BCRYPT_ROUNDS" value="4"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="MAIL_DRIVER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
    <env name="SESSION_DRIVER" value="array"/>
</php>

最後に.env.testingに新規にアプリケーションキーを設定したら完了です。
これをしないと.envAPP_KEYを参照しに行ってしまい、本番用のDBのデータが全部飛ぶので注意しましょう。

php artisan key:generate --env=testing

ハマったポイント

docker-compose.ymlなどで環境変数にAPP_ENVDB_DATABASEなどの設定をしている場合、注意しなくてはいけない点があります。
phpunit.xmlで環境変数を上書きする際に、デフォルトだと<server>タグになっていますが、これを<env>タグに変更する必要があります。

phpunit.xml
<php>
    <server name="APP_ENV" value="testing" force="true"/>
    <server name="DB_CONNECTION" value="mysql"/>
    ...
</php>
phpunit.xml
<php>
    <env name="APP_ENV" value="testing" force="true"/>
    <env name="DB_CONNECTION" value="mysql"/>
    ...
</php>

<server>タグと<env>タグは以下のような違いになっています。

$_SERVER['APP_ENV'] = 'testing'; // <server>
$_ENV['APP_ENV'] = 'testing'; // <env>

今回はdocker-compose.ymlで環境変数としてAPP_ENVなどを定義していたので、さらにそれを上書きする必要がありました。

参考文献

Laravel 7.x テスト: テストの準備
Laravel × Docker でテスト用のデータベースコンテナを使う

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

ControllerからViewへの変数受け渡し

概要

Laravelの諸機能について忘れないようにアウトプットする自分用メモ。

ControllerからViewへの変数受け渡し

参考:https://qiita.com/ryo2132/items/63ced19601b3fa30e6de
参考:https://qiita.com/_Mizuki/items/df3a62fbe18bbbbc9ced
参考:http://blog.tojiru.net/article/230164798.html

Controllerからviewに値を渡す場合、ベターな方法が複数あるらしいので調べた。

phpのcompact関数

viewに値を渡す際、compact関数を使用する方法がわりとベターらしい。
compact関数の理解が怪しかったので確認した。
公式の解説では変数名とその値から配列を作成する。とのこと。

公式の例

<?php
$city  = "San Francisco";
$state = "CA";
$event = "SIGGRAPH";

$location_vars = array("city", "state");

$result = compact("event", $location_vars);
print_r($result);
?>

// result 
Array
(
    [event] => SIGGRAPH
    [city] => San Francisco
    [state] => CA
)

良くわからん...。
自分なりに分かりやすかった例。

$arr = array(
  'apple' => $apple,
  'orange' => $orange,
  'lemon' => $lemon,
);
// ↑
// 同じ意味
// ↓
$arr = compact('apple', 'orange', 'lemon');

連想配列のkeyと変数名を勝手に紐付けてくれるらしい。

Controllerからviewへの変数の受け渡し

viewに値を渡すときにcompact関数を使用する場合は以下のようにするといい。

public function fruit()
{

  $apple  = 'apple';
  $orange = 'orange';
  $lemon  = 'lemon';

  return view('fruit', compact('apple', 'orange', 'lemon'));
}

Laravel独自のコレクションなのかと思ってたけど、そういうワケじゃないんですね...。
そもそもviewヘルパ関数がどんなだったっけと思って見直した。

第二引数に連想配列渡すだけなんだ。それを作りやすいからcompact使っているだけ。
つまりこの書き方でも一緒です。

public function fruit()
{
    $apple  = 'apple';
    $orange = 'orange';
    $lemon  = 'lemon';
    $fruit = ['apple'=>$apple, 'orange'=>$orange, 'lemon'=>$lemon];

    return view('fruit', $fruit);
}

withメソッド

Controllerからviewへの受け渡しにwithメソッドを使用する場合。

public function fruit()
{
    $apple  = 'apple';
    $orange = 'orange';
    $lemon  = 'lemon';

    return view('fruit')->with('apple', $apple)->with('orange', $orange)->with('lemon', $lemon);
}

もしくはこう

public function fruit()
{
    $apple  = 'apple';
    $orange = 'orange';
    $lemon  = 'lemon';

    return view('fruit')->with([
        "apple"  => $apple,
        "orange" => $orange,
        'lemon'  => $lemon,
     ]);
}

ちょっと可読性悪いですね。

個人的ベスト

  • 配列を渡す

状況にもよりますが、配列作って渡してあげる方法が一番好きです。
viewヘルパ関数の第二引数が大きくなるのはあまり好きではない。

// 画面にフルーツを渡す
public function fruit()
{
    $fruit = $this->returnfruit();

    return view('fruit', $fruit);
}

// フルーツの配列を返す
private function returnfruit()
{
    $apple  = 'apple';
    $orange = 'orange';
    $lemon  = 'lemon';

    return compact('apple', 'orange', 'lemon');
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel

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

webpackでビルドしたscriptに、HTMLから値を渡すアレ

LaravelやRailsみたいな環境で、バックエンドからフロントエンドに値を渡したいときのアレです。i18n対応とか。方法忘れて2時間くらい溶けたのでメモ。

今回の用途

LaravelのViewから @lang('hoge.fuga') ヘルパ関数で、i18n済みメッセージ文字列を渡したい

今回は省略しますが、セキュリティ対策は各環境でお願いします。

Laravel View

コツってほどでもないですが mix() は一番最後。

dokoka.blade.php
... 中略

<script>
  var __SYS_GLOBALS_ = {
    HOGE_FUGA_MESSAGE: "@lang('hoge.fuga')"
  };
</script>

... 中略
/
<script src="{{ mix('/js/app.js') }}"></script>
</body>
</html>

Typescript側

モジュールとして、どこか新規ファイルに下記のように書いておけば良いです。
ただしインポートするとエラーになります。インポートはしない。不思議ですよね。

__SYS_GLOBALS__.ts
export {};
declare global {
  const __SYS_GLOBALS__: any;
}

インポートするとTS内で再定義されてしまうのでしょうか、その場合確かに(TS内には)実体はないのでエラーになりそう。

TS2304: Cannot find name '__SYS_GLOBALS__'.

あとはお好みで定数を使います。

nanika.ts
alert(__SYS_GLOBALS__.LOADING_NOW_MESSAGE);

まとめ

こんな設計はいけません。わかってはいるのですが、そういうときもあるのです。[終]

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

有料のWebサービスをリリースするまでに取り組んだこと・知見をまとめました【個人開発】

1. 作ったサービス

twikeshi-ogp.png

ツイ消し職人
https://twikeshi.net/

ツイ消し職人は大量のツイートを消したい方向けのツールです。
3,200件を超えるツイートを一括削除できます。
無料のツールなどでうまく削除できなかった方は是非ご利用ください。
既存のフォロワーをそのままに、Twitterをやり直すことができます。

2. 自己紹介

こんにちは、ひろと申します。
今年の3月に会社を辞め、現在はフリーランスエンジニアとして活動しています。

メガバンクのシステムエンジニア →
広告代理店(東証一部上場企業)のWebエンジニア →
フリーランスエンジニアという経歴です。

仕事でコードを書き始めたのは前職からで、プログラミングの経験年数は2年と9ヶ月くらいです。

3. なぜ作ったのか

私は今年の3月にフリーランスとして独立しました。
それに伴い、学生時代から使っていたTwitterアカウントの運用を変えようと思い、今までのツイートを削除してやり直すことにしました。
アカウントを作り直す選択肢もあったのですが、フォロワーを減らしたくなかったため、ツイ消しの道を選びました。
調べてみるとツイートの一括削除ツールがいくつか見つかったため、それを使ってツイ消しをすることにしました。

しかし、既存のツールではツイートの削除ができませんでした。
私の今までのツイート数は19万件で、Twitterアーカイブをダウンロードしたところzipファイルのサイズはなんと31GB。スマホの7GBプランなら4.5ヶ月分の通信量が必要になってしまう、とんでもない容量です。
そう、私がツイ廃だったのが全ての原因です。

普通のツイ消しサービスは、API制限の関係で3,200ツイートが削除の上限となってしまいます。私の19万ツイートに対してはあまりに無力すぎました。

もちろん、API制限を回避するためにTwitterアーカイブをアップロードして削除を行うサービスもあります。
しかし、31GBのzipファイルを送りつけると必ず500エラーが返ってきてしまい、私の試した範囲では、まともに動くものはありませんでした。海外の有料サービスでさえダメでした。(具体的なサービス名は出しませんが、日本円で1,600円払いました。手痛い出費です)

そこで私は、「ツイ廃でもツイートを削除できるサービス」が必要だと思い、ツイ消し職人の開発を始めました。

4. リリースするまでに取り組んだこと

取り組んだ全てのことを記載しています。

一. サービスの命名

最初は「ツイートクリーナー」という名前にしていました。
開発中盤に「ツイ消し職人」という名前を思いつき、変更しました。
ランサーズなどで募集するのも良いと思います。

二. ドメインの取得

ムームードメインでtwikeshi.netを取得しました。
欲しいドメインが埋まっている場合は、twikeshi-app.netのように工夫するのも良いと思います。

三. 商標権の取得

今回はお金がなかったので保留しています(いつでも取れるように商標調査は終えています)。
今はToreruなどの便利なサービスがあり、ものすごく簡単に出願できます。
48,000円で5年間有効になります。
商標権の取る取らないを選択するのは自由ですが、後から商標を第三者に取得されて商標権の侵害警告を受けた場合、サービス名やドメインを変える必要があるリスクは認識しておく必要があります。

四. プライシング

海外の同じようなサービスを参考に値付けを行いました。
現在は700円(税込)で提供しています。

その後、プライシングに関する本を3冊読んで(この本この本この本)考え方が変わったので、そのうち値上げするかもしれません。

どんなに高くても、その価格で買いたい人がいます。私自身も、このサービスを他の人が作っていたとしたら、喜んで利用していました。

間違っても、本来ターゲットでない人を取り込むために値下げするのはやめてください
例えば、私は友人達に「ツイ消し職人の適正価格はいくらだと思う?」と質問をすると、2人が「100円」と答えました。
しかし、断言しますが彼らは100円でも絶対に利用しません。何故ならば、彼らはこのツールの価値を理解していないからです。ツイ消しをしようと思ったことがない人に相場感を聞いても意味がありません。
逆に、本っっ当にツイ消しをしたくて困っている人からすれば、このツールが例え1万円でも喜んでお金を払うはずです。

五. 技術選定

1. バックエンド

バックエンドはLaravelで開発しました。
私は前職でSpring Bootを使っていましたが、このフレームワークではTwitterログインを実装するのに苦労しそうだったため、一からLaravelを学ぶことにしました。
Railsと悩みましたが、後述する理由によりレンタルサーバーで運用したかったので、Railsは諦めました。
Laravelはコードもドキュメントも読みやすいため、使っていて楽しいですね。
ドットインストールのLaravel入門がとても分かりやすかったのでオススメです。

役割 技術
PHPフレームワーク Laravel
データベース MySQL
Twitterログイン Laravel Socialite
Twitter APIライブラリ TwitterOAuth
メール送信 SendGrid

2. フロントエンド

CSSフレームワークにはMaterializeを採用しました。これも初めて使ったのですが、ドキュメントが分かりやすく情報量も多いのでオススメです。
今回はフロントで処理をする必要が無かったので、基本的にJavaScriptは使っていません。ファイルアップロードの画面は、アニメーションを付けるためにVue.jsを使いました。Vue.jsは以前から使っていたので、特に困ることはありませんでした。
次はNuxt.jsに挑戦するために勉強中です。

役割 技術
CSSフレームワーク Materialize
JavaScriptフレームワーク Vue.js
決済 Stripe Checkout

3. インフラ

バックエンドの項目で触れましたが、サーバーにはレンタルサーバーを採用しています。

役割 技術
レンタルサーバー エックスサーバー

Heroku / VPS / AWS EC2 / GCP App Engineなどの選択肢もありましたが、主にコストと運用の観点から除外しました。個人開発は自分でインフラを選べるのが良いですね。
今後もどんどんサービスを作っていく予定なので、サーバー費がかさむのはイヤだし、サービスを作る度に環境を構築するのも避けたかったのです。

もちろん要件によってはレンタルサーバーが使えない場合もあります(ミドルウェアの設定変更や追加インストールが必要な場合など)。
rootユーザーが使えないと困る場合は、状況に応じて各サービスを比較検討しましょう。
サーバーはHerokuだけどストレージにはAWS S3を使って、DBにはGCP Cloud SQLを使うといったトリッキーなこともできます。柔軟な発想で最適な構成を作りましょう。

参考に、私の考える主なインフラサービスのメリット・デメリットをまとめておきます。
※App Engineは詳しくないので簡易的な記載になってます

インフラサービス メリット デメリット
VPS(IaaS) ・安い
・root使える
・借りる度にお金がかさむ
・環境構築や設定が必要
EC2(IaaS) ・root使える
・マイクロサービス沢山ある
・高い
・借りる度にお金がかさむ
・環境構築や設定が必要
App Engine(PaaS) ・環境構築不要 ・高い
・借りる度にお金がかさむ
Heroku(PaaS) ・安い
・環境構築不要
・借りる度にお金がかさむ
・30秒タイムアウト辛い
レンタルサーバー(ほぼPaaS) ・安い
・1台でアプリ沢山動かせる
・環境構築ほぼ不要
・root使えない

六. 設計

小規模なサービスなので、ここにはほぼ時間をかけていません。
ワイヤーフレームなどは作らず、実際に画面をコーディングしてレイアウトを決めました。
DB設計もパパッと考えて終わり。
開発の中で必要になったときに都度、テーブルやカラム・画面を増やしていきました。
サービスによっては色々な機能を思いつくと思いますが、まずはスモールスタートでリリースすることをゴールにしましょう。YAGNIは正義。

七. 開発

一番時間をかけたのはこの工程です。
他の仕事が並行していたため正確ではありませんが、全体で2〜3週間はかかったと思います。
伝えたい情報がある場合はコメントを書いています。
有料サービスのみ必要になる項目には「☆」を付けています。

1. サービスの機能開発

一. Twitterログイン

ツイ消し職人では、決済完了時とツイート削除完了時に確認メールを送信しています。
そのため、Twitter AppのAdditional permissionsとして、Request email addressにチェックを入れています。

二. ☆決済

Stripe Checkoutは神。
JavaScriptをちょろっと書くだけで決済を提供できます。返金もボタンポチるだけです。
ツイ消し職人はクレジットカード、Google Pay、Apple Payに対応しています。

三. アーカイブアップロード

Twitterが生成したデータを読み取らないといけないので、アーカイブのどのファイルに何の情報があるのかを全て自分で調べました。
そして、ツイートの削除に本当に必要なファイルだけをアップロードさせることで、ファイルサイズを31GB→200MBまで減らすことができました。

アーカイブからはツイートの削除に必要な情報を正規表現で抽出する必要があります。
最初は、JavaScriptを使いフロント側で情報を抽出し、サーバーには最低限のデータだけ送るようにする予定だったのですが、少し時間がかかりそうだったので諦めました。

FileReader.readAsText()に100MBのファイルを食わせるとクラッシュしてしまうことが判明し、ファイルをチャンクして処理する必要が出てきたためです。
コンソールにエラーは出力されず、サイレントでクラッシュするので問題の特定に時間がかかりました。マジでやめてほしい。
サーバ側で抽出処理をやっても特に問題はないので、サーバ側で処理するようにしました。

四. ツイート削除

ノーコメント

五. 非同期処理

アップロードされてそのままツイートの削除を行うと、画面がタイムアウトしてしまいます。
そのため削除処理はLaravelのキューを使って非同期にしています。
失敗時の再実行もできるようになるので便利ですね。

六. メール送信

必ずユーザーに到達するようにSendGridを使っています。
返信や問い合わせを受けるためにはメールサーバーの設定が必要なので注意してください。
サーバーが用意できない場合は、G Suiteなどのホスティングサービスを利用しましょう。

七. ログ出力 + Slack通知

本番での例外発生時にはSlackにスタックトレースを飛ばすようにしています。
他にも、ツイート削除処理成功時など、正常系でも重要なものはSlackに通知を飛ばしてます。

ログは、出せる項目をなるべく出すようにしています。
Laravelのログ出力について記事書いてるので興味あったら読んでください↓
【Laravel】ログのフォーマットを変更してIPアドレスやユーザー名などを出力する

2. リリース準備

一. LP(トップページ)作成

ユーザーに効果的に訴求できる文言を考える必要があります。
デザイナーの人は腕の見せどころだと思います。
文字や画像・アニメーションを使っていい感じのレイアウトにしましょう。

ペライチなどのツールを使っても良いと思います。
コンバージョンに直結するので、一番力を入れるべき部分です。外注も考えましょう。

二. 利用規約・プライバシーポリシーの制定

この本が大変参考になりました。コピペできるひな形データも付いてくるのでオススメです。

三. ☆特定商取引法に基づく表示の作成

同上。
有料サービスの場合は必須です。
本名や住所、電話番号を晒さないといけないので、ここが一番の難関ではないでしょうか。
私の場合、IP電話アプリ(SMARTalk)を使い050から始まる電話番号を載せています。

四. Googleアナリティクス・Search Console設定

ノーコメント

五. meta description設定

Googleの検索結果でタイトルとともに出るやつです。
meta keywordは不要です。

六. ファビコン設定

GIMPで作りました。
サービスのロゴがある場合はファビコンにも活かせます。

七. OGP設定

GIMPで作りました。
OGP画像を動的に生成するサービスでは、トップページ用の画像を同じ方法で作るのも良いかも。
CTRに直結するので、ここも外注を検討しましょう。

八. サイトマップ設定

Search Consoleで送信します。
sitemap.xml Editorを使うと簡単に作成できます。
サイトが新しく、外部からのリンクが少ない場合はあったほうが良いみたいです。

九. SNSシェアボタンの設置

ツイ消し職人 - twikeshi.net.png
↑こういうのです。
ユーザーに拡散してもらえる仕組みを作っておくことは重要です。
ちなみにツイ消し職人は全く拡散されていません。悲しい。

十. お問い合わせフォームの設置

自分で作るのがめんどくさい場合は、Googleフォームformrunなどを活用しましょう。
お問い合わせフォームの代わりに、チャットサポートツールを入れるのもオススメです。

3. リリース

一. デプロイ

Laravel + レンタルサーバーの場合はgit pullすればほぼ終わりです。
あとは.env書いてマイグレーションしてキャッシュ系のコマンドを叩くだけです。
もちろん、GitHub ActionsなどのCIを設定するのも良いと思います。

私の場合は、以下のようなデプロイスクリプトを用意しています。

deploy.sh
#!/bin/sh

git pull

composer install --optimize-autoloader --no-dev

php artisan config:cache

php artisan view:cache

php artisan route:cache

二. テスト

本番環境で全ての機能がうまく動くことを確認しました。
中規模〜大規模サービスの場合は、検証環境の用意とテスト自動化がされていないと運用がしんどくなります。

八. リリース後

1. 知り合い・友人への拡散

LINE, Twitter, Facebookなどで拡散して使ってもらいましょう。

2. プレスリリースを出す

お金があればPR TIMESなどの有名サイトに出すのがオススメです。
(もしくは、法人ならスタートアップチャレンジの条件を満たすと無料になります)

私はお金がなかったので、valuepressのフリープランで配信しました。
3,200件を超えるツイートを一括削除できるツイ消しサービス「ツイ消し職人」を提供開始

3. アプリ紹介サイトに登録

私の場合、AnyMakemakepostEggineerApplishowを活用しています。

4. 新聞の広告枠に出稿する

リリース直後にスポーツ新聞の方から電話があり、新聞とサイトに広告を載せないかと打診がありました。
条件が合わなかったためお断りしましたが、人によっては選択肢になり得ると思います。

5. アフィリエイト広告に出稿する

現在検討中です。
お金がある場合は、A8.netなどの大手ASPを使うのが良いと思います。
もしもアフィリエイトマネートラックならば初期費用0円・月額費用0円で始められるようです。

6. SNS広告に出稿する

現在検討中です。
私の場合はTwitterユーザーをターゲットにしたサービスなので、Twitter広告と相性が良いです。
とりあえず試してみて、どれくらい成果が出るかチェックしてみようと思います。

7. 保守開発

本番環境でのエラーを監視し、新しく発現したバグがあれば修正しましょう。
手元で再現しないエラーは...ユーザー問い合わせを待つしか無い。。

もちろん、機能追加などサービス改善のための開発は怠らないようにしましょう。
大幅リニューアルや作り直しなどの選択肢もあります。

8. ブログなどでの発信

この記事のことですね。
Qiita, Crieit, Note, ブログなどの選択肢があります。
サービスを知ってもらうだけでなく、転職活動などでも役に立ちます。

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

PHP Laravel オブジェクト思考 用語集

目的

  • Laravelの最低限理解しておきたい用語をまとめる

事前情報

  • 必要な物が出てきたら随時追記してゆく。
  • Laravelに限らず、PHPやその他のフレームワーク、オブジェクト指向に関する用語なども一緒に記載する。
  • 筆者のメモ的要素が強いので

クラス

  • オブジェクト思考でよく設計図に例えられるものである。

インスタンス

  • クラスを元に作られた複製の事である。
  • $変数名 = new クラス名(引数);として作ることができて変数の中にインスタンスが格納される。

インスタンス化

  • クラスからインスタンスを作成することである。

オブジェクト変数

  • インスタンスが格納された変数の事である。
  • $変数名 = new クラス名(引数);$変数名に当たるものを指す。

メソッド(メンバ関数)

  • クラスの中に書かれた関数の事を指す。
  • オブジェクト変数内のメンバ関数の戻り値を変数に格納したい場合は$戻り値を格納したい変数名 = オブジェクト変数->メソッド名(引数);とする。

プロパティ(メンバ変数)

  • クラスの中に書かれた変数の事を指す。
  • オブジェクト変数->プロパティ名;で呼び出すことが可能である。

クラスメソッド(静的メソッド)

  • オブジェクトを生成せずにクラス(設計図)の状態で呼び出せる関数のことである。
  • ::(ダブルコロン演算子)で呼び出すことができる。
  • クラス名::メソッド名;で呼び出すことが可能である。
  • 戻り値を変数に格納したい場合は$戻り値を格納したい変数名 = クラス名::メソッド名(引数);とする。
  • 呼び出し元がクラスである点に注意すること。

クラスプロパティ(静的プロパティ)

  • オブジェクトを生成せずにクラス(設計図)の状態で呼び出せる変数のことである。
  • ::(ダブルコロン演算子)で呼び出すことができる。
  • クラス名::プロパティ名;で呼び出すことが可能である。
  • 呼び出し元がクラスである点に注意すること。

インスタンスメソッド

  • インスタンス内にある関数の事を指す。
  • ->(アロー演算子)で呼び出すことができる。
  • オブジェクト変数->メソッド名(引数);で呼び出すことが可能である。
  • 戻り値を変数に格納したい場合は$戻り値を格納したい変数名 = クラス名->メソッド名(引数);とする。

インスタンスプロパティ

  • インスタンス内にある変数の事を指す。
  • ->(アロー演算子)で呼び出すことができる。

ビジネスロジック

  • システムの実際に処理をしている部分の事を指す。
  • 変数に格納したり、値を処理したりする部分の事を指す。
  • 意外と何を指すかは意外と曖昧である。
  • システムの処理する部分のことである。
  • システム固有の処理のことである。

ルートプレフィックス

  • グループ内の各ルートのURLの最初に指定した共通の文字列を付与する。
  • 例えばいくつかのルーティングで指定するURLの最初に「admin」と付けたい場合は下記の様に記載する。

    web.php
    //グループ化で URLの最初にadminと付けたいルーティング情報を包む
    Route::prefix('admin')->group(function () {
        //URLの最初にadminとつけるルーティング情報を記載する
        Route::get('users', function () {
            // このルーティング情報にはURL"/admin/users"でアクセスできる
        });
        Route::get('home', function () {
            // このルーティング情報には URL"/admin/home"でアクセスできる 
        });
        // 下記のルーティング情報には URL"/admin/test"でアクセスできる
        Route::get('test', 'TestController@index');
    });
    

名前付きルート

  • ルーティング情報に名前がついているものである。
  • 下記の様なルーティング情報が記載されている物とする。

    web.php
    Route::get('user/index', 'UserController@index');
    Route::post('user/index', 'UserController@save');
    
  • 先に記載したルーティング情報のget側に「index」、post側に「input」、ルートパラメータを含むものに「user_detaile」という名前をつける際は下記の様に記載する。(つける名前は一意にすること)

    web.php
    Route::get('user/index', 'UserController@index')->('index');
    Route::post('user/index', 'UserController@save')->('input');
    Route::get('user/detail/{user_id}', 'UserController@detail')->('user_detail')
    
  • 先に記載したルート情報のget側にリダイレクトしたい場合はroute関数を用いてリダイレクトさせることが可能になる。例を下記に記載する。

    //どこかのコントローラのアクション内などで下記を記載する。
    return redirect()->route('index');
    
  • 先に記載したルート情報のpost側にデータを送りたい場合はroute関数を用いてデータを送ることが可能になる。例を下記に記載する。

    //ビューファイルなどで下記を記載する。
    <form action="{{ route('input') }}" method="POST">
        <input type="submit">
    </form>
    
  • 先に記載したルート情報のルートパラメータを含むものにリダイレクトしたい場合はroute関数に引数を用いることでリダイレクトさせることが可能になる。下記に例を記載する。

    //どこかのコントローラのアクション内などで下記を記載する。"user/detail/1"にリダイレクトさせたい時
    return redirect()->route('user_detail', ['user_id' => 1]);
    

ルートアズ(筆者が勝手に付けた、本当は違う名前かもしれない、すいません)

  • 名前付きルートの「名前」の最初の部分にグループ化されたルート情報に共通した文字列を付与する。
  • 下記に記載例を載せる。
web.php
    //グループ化で 名前の最初にtestと付けたいルーティング情報を包む
    Route::as('test.')->group(function () {
        //名前の最初にtestとつけるルーティング情報を記載する
        Route::get('users', function () {
            // // 下記のルーティング情報にリダイレクトする際はreturn redirect()->route('test.users_1')とする
        })->name(users_1);
        Route::get('home', function () {
            // 下記のルーティング情報にリダイレクトする際はreturn redirect()->route('test.home_1')とする 
        })->name(home_1);
        // 下記のルーティング情報にリダイレクトする際はreturn redirect()->route('test.test_1')とする
        Route::get('test', 'TestController@index')->name(test_1);
    });
    ```

# 

# クロージャ

- 無名関数のことである
- 関数名を指定せず作成した関数のことである
- 名も無き関数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでTenancyを使用し、DBをユーザーごとに動的に切り替える方法

Laravelの利点にDBの扱いやすさがありますよね、これまでDB周りで詰まったことはほとんどありませんでしたが、DBを動的に変更する機能で詰まったので、備忘録として残しておきます。

使用環境

・MAC
・Laravel7.5
・Tenancy  ←本記事の主役

作りたかったもの

Laravelで※シングルサインオン(SSO)機能のAPI認証基盤の構築。
DBはユーザーが新規で登録されるごとに新規で構築され、ログイン中のユーザーごとに切り替える。
※Single Sign Onの略。1つのIDとパスワードを入力して、複数のWebサービスやアプリケーションにログインする仕組み。

詰まった箇所

・ユーザーが新規登録される毎にDBをプログラム内で(自動で)構築する
・DBが構築される毎に構築されたDBにテーブルを作成する(マイグレーションを実行する)
・ログイン中のユーザーごとにDBを切り替える

詰まった原因

LaravelでDBを扱うにはconfig/database.phpにDB情報を、前もって記入しておく必要があります。
(PDOでDBに直接接続するなど別方法もありますが、ここでは割愛させてください。)
しかし、今回作りたかった機能としてDBを動的に切り替える必要があるのですが、config/database.phpに前もって記入することができなかったからです。

解決策

・Tenancyを使用し、マルチテナント環境を構築する。

https://reffect.co.jp/laravel/laravel-multi-tenant-stancl-tenancy
Tenancyの使用方法については?に記載されています。

?のコードでテナント(ユーザーごとの環境)・テナントごとのDBとテーブルの構築までします。

//ユーザー登録機能内にテナント作成機能を追加(一部抜粋)
//リクエストされたユーザー名
$user_name = $request['user_name'];
//メインドメイン
$baseURL = 'qiita.com';
//ユーザー名でテナントを作成
$tenant = Tenant::new()->withDomains($user_name . '.' . $baseURL)->save();
//migrate実行(実行したいマイグレーションファイルは/database/migrations/tenantに格納しておく)
$exit = \Artisan::call('tenants:migrate', ['--force' => true,]);

これで以後は、「リクエストされたユーザー名.メインドメイン」のURLにアクセスすると、
そのユーザー用のページにアクセスすることができ、ユーザーに対応したDBに自動で切り替えられます。
(ここら辺の動きを詳しく知りたい方は?をどうぞ)
https://tenancy.dev/

実際にはこんな感じでテナント用ページにアクセスし、API認証機能を実装しました。

$tenant_name = 'foo.';
$baseURL = $tenant_name . 'qiita.com/';
$client = new \GuzzleHttp\Client(['base_uri' => 'http://'. $baseURL]);
       $response = $client->request(
            'POST',
            'changeProfile',
            [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => 'Bearer '.$api_token
                ],
                'form_params' => [
                    'profile' => $profile,
                    'email' => $email,
                ],
            ]
        );
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む