【第3回】「みんなのポートフォリオまとめサイト」を作ります~苦悩の連続編~

## はじめに
この連載記事は、僕が「みんなのポートフォリオまとめサイト」を作る過程をゼロから発信しながらみなさんに見てもらいつつ、作っている途中からみなさんにアドバイスをいただいて、よりよいサービスにしていきたいというお話です。

あとは「サービスを作っていく過程って初学者の人にとっては結構興味ある内容だったりするのでは？（**少なくとも自分は知りたかった！**）」と思い、このようなスタイルで記事を書いています。

一週間に一度のペースで更新しますとか言っちゃいましたが、前回更新からなんと2ヶ月も経ってしまった！
今後も完成までゆるく更新していく予定です。

## 前回までの記事
[【第0回】「みんなのポートフォリオまとめサイト」を作ります~宣言編~](https://qiita.com/kiwatchi1991/items/58b53c5b4ddf8d6a7053)
「とりあえずこれから作るからみんな見てて！」と宣言しただけの記事。

[【第1回】「みんなのポートフォリオまとめサイト」を作ります~着手編~](https://qiita.com/kiwatchi1991/items/62ecde8289887965aff3)
仕様を決めたり、サービス名考えたり、デザインを作ったり。

[【第2回】「みんなのポートフォリオまとめサイト」を作ります~SPA認証で死闘編~](https://qiita.com/kiwatchi1991/items/2fdd533236cd32237bb2)
いよいよ実装に入っていきます。


今回は、実装に入って途中からTypeScriptやテストを導入してみたりした話です。

## これまでやったことと作業時間
集計期間 ： 10/7〜12/8（単なる自分用のメモです）


| やったこと | 前回までの作業時間 ( h ) | 今回の作業時間 ( h ) |
|-----------|------------|------------|
| 仕様決め（競合調査など) | 3.0 |  |
| サービス名考案     | 2.0   |  |
| WF作成  | 2.0  |  |
| デザイン作成 | 8.0 |  |
| DB設計 | 4.0 | |
| 実装（環境構築） | 4.5 | **15.0** |
| 実装 | 25.0 | **82.0** |
|その他|2.0|  |

### 作業時間計 ( h )  

|前回まで|今回|合計
|---|---|---|
|50.5|97.0|147.5|

んー 2ヶ月で97hか。

、、、**少ない！**
もちろんプライベートの全ての時間をこの開発に費やしていた訳ではないけれど、なにをやっていたんだろうか？（[前回](https://qiita.com/kiwatchi1991/items/2fdd533236cd32237bb2#comments)も[前々回](https://qiita.com/kiwatchi1991/items/62ecde8289887965aff3)も同じこと言ってる。）

目標は、**2月中にリリース！！！**（宣言しちゃう！）
粛々と進めるしかないわよ。

## 今回やったこと
この2ヶ月でやったことを時系列で列挙していきます〜

### デザイナーさん音信不通、、、からの秒速で次のデザイナーさんへ
早速ですが、タイトルロゴのデザインをお願いしていたデザイナーさんが音信不通になってしまいました。。（シンプルに悲しい

以前副業案件でお世話になったデザイナーさんで、クオリティ高いデザインを作ってくださりコミュニケーションも非常に気持ちのいい方だったので、迷わず今回もお願いしたんですケドね。。

まあのっぴきならない事情があったのでしょう。。
いつまでも待ってる訳にはいかないので代わりのデザイナーさんを探すことに。

正直他のデザイナーさんのツテはなかったので、「あーまたココナラで探さなきゃだな。。」とこの後またイチからデザイナーさんを探す労力を想像して落胆しつつ、Twitterで嘆きを投稿しました。
https://twitter.com/kiwatchi1991/status/1314719951777792000?s=20
[![スクリーンショット 2020-12-08 22.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/dce49a38-aec7-22c1-ecab-a8aaf74a83b5.png)](https://twitter.com/kiwatchi1991/status/1314719951777792000?s=20)

すると、すぐにたくさんのコメントを頂きました。（本当にありがとうございます。やっぱりTwitterはすごい。。

その中の1つに、以前オンラインのワークショップで知り合ったデザイナーさんを紹介してくださるコメントがあり、ソッコーでDMを送らせてもらってその日のうちに代わりのデザイナーさんを見つけることができました（ 柳田さん（[@asami_lin](https://twitter.com/asami_lin)) 本当にありがとうございました！）。



最初は「タイトルロゴだけ」のつもりだったんですが、やっぱりTOPページは集客する上で**超大事**だろうってことで、TOPページのデザインもお願いすることにしました。

自分で作ったデザインをお渡しして、「これをいい感じにしてください！」という**超ざっくり**としたオーダーだったにも関わらず、マジでヤバいクオリティで仕上げてくださいました。**テンション爆上がり。**

### ESlint, Prettierの導入

これはどうしても入れたかったヤツらです。入れておけば、インデントやクォーテーション（シングル or ダブル）などの細かい点は気にせずとも保存時に自動で書式を統一して整形くれるので、コーディングが非常にラクになります。

細かい設定に関してはもうソースを見てください（雑）

`.eslintrc.js`、`.prettirerc`, `webpack.mix.js` あたりが設定ファイルになります。

ESlintはreactに対応するように色々と設定の変更が必要です。

react用のプラグインを入れたそのままの設定だと、正しい書き方をしているはずなのにエラーが出たり、逆に「そのエラーは吐かなくてもいいよ」てのも多かったりするので、個別で設定を変更していきます。

VSCodeだと、ESlintでエラーになっている箇所は波線で示してくれて（**黄色は警告、赤はエラー**）、波線にカーソルを合わせるとエラーの内容を表示してくれます。

![スクリーンショット 2020-12-09 21.07.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/a0cfad5b-11ad-d6d1-4ef3-ee572ba0739d.png)

今回で言うと、ESlintの`@typescript-eslint/explicit-modele-boudary-types`に関するエラーだよってことを教えてくれています。

そのまま`@typescript-eslint/explicit-modele-boudary-types`の部分にカーソルを合わせると、外部サイトへのリンクになっています。

![スクリーンショット 2020-12-09 21.13.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/08ff6835-4a65-6bdd-94e2-06cd3b3c3477.png)

![スクリーンショット 2020-12-09 21.05.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/e783d93f-013b-4dcd-ae85-e24a2aa750ad.png)

開いてみると、`@typescript-eslint/explicit-modele-boudary-types`のリファレンスへ飛びます。

![スクリーンショット 2020-12-09 21.06.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/a884d383-83d3-8da4-0f90-34666ab79088.png)

こんな感じでエラーの内容を確認しては、

 1. エラーを解消するようにソースを変更
 1. 設定を変更し、エラーが出ないようにする

などしてエラーを消していく作業をひたすら地道に行っていきます。発狂しそうになります。

保存時に `--fix` する設定にしていますが、全部のエラーを解消してくれるわけではありません。なので、結構手動で修正しないといけない。

あとで入れるTypeScriptの設定も入っているので、`Laravel(laravel-mix)` x `React` × `TypeScript` × `ESlint` × `Prettire` の場合は、このプロジェクトの環境をそのまま使えばとりあえず動くと思います。（ 他のソースも含めて気になる方は、[kiwatchi1991/folicolle -Github](https://github.com/kiwatchi1991/folicolle) をご覧ください）


```javascript:.eslintrc.js
module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
        jest: true,
    mocha: true
    },
  extends: [
    "airbnb",
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:prettier/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
    "plugin:import/typescript",
    "prettier/@typescript-eslint",
    "prettier/react",
    ],
  globals: {
    Atomics: "readonly",
    SharedArrayBuffer: "readonly",
      "__DEV__": true,
    "React": false
    },
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaFeatures: {
      jsx: true
        },
    project: "./tsconfig.json",
    sourceType: "module"
    },
  plugins: [
    "@typescript-eslint",
    "import",
    "prettier",
    "react",
    ],
  root: true,
    rules: {
    "react/jsx-filename-extension": [1, { "allow": "as-needed" }],
    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
    "newline-before-return": "error",
    "no-console": "off",
    "no-continue": "off",
    "require-yield": "error",
    "spaced-comment": [
      "error",
      "always",
            {
        markers: ["/"
                ],
            },
        ],
    "no-use-before-define": "off",
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": ["off"],
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/explicit-member-accessibility": "off",
    "@typescript-eslint/no-unnecessary-type-assertion": "error",
    "@typescript-eslint/no-var-requires": "off",
    "react/jsx-filename-extension": [
      "error",
            {
        extensions: ["jsx", "tsx"
                ]
            }
        ],
    "react/jsx-props-no-spreading": [
      "warn",
            {
        custom: "ignore",
            },
        ],
    "react/prop-types": "off",
    "react/require-default-props": ["off", { forbidDefaultForRequired: false, ignoreFunctionalComponents: false }],
    "react/no-unused-prop-types": ["off", {}],
    "react/default-props-match-prop-types": ["off", { "allowRequiredDefaults": false }],
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
    "import/extensions": [
      "error",
      "always",
            {
        js: "never",
        jsx: "never",
        ts: "never",
        tsx: "never"
            }
        ],
    },
  settings: {
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx"
            ],
        },
    "import/resolver": {
      node: {
        extensions: [".js", "jsx", ".ts", ".tsx"
                ],
        paths: ["src"
                ],
            }
        },
    react: {
      version: "detect"
        }
    },
};
```


```json:.prettirerc
{
  "printWidth": 120,
  "useTabs": false,
  "semi": true,
  "singleQuote": false,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "jsxBracketSameLine": false
}

```
```javascript:webpack.mix.js
const mix = require("laravel-mix");
require("laravel-mix-react-css-modules");
require('laravel-mix-stylelint');

mix.webpackConfig({
    module: {
        rules: [
            {
                test: /\.scss/,
                enforce: "pre",
                loader: "import-glob-loader",
            },
            // ==============================
            // ESlintの設定はここから　↓↓↓
            // ==============================
            {
                enforce: "pre",
                exclude: /node_modules/,
                loader: "eslint-loader",
                test: /\.(js|jsx)?$/,
                options: {
                    fix: true,
                    cache: false,
                },
            },
            // ==============================
            // ESlintの設定はここまで　↑↑↑
            // ==============================
        ],
    },
});
/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.ts("resources/js/app.tsx", "public/js")
    .sass("resources/sass/app.scss", "public/css")
    .stylelint({
        files: ['**/*.scss'],
    })
    .reactCSSModules()
    .sourceMaps()
    .browserSync({
                https: false, 
        files: ["./resources/**/*", "./app/**/*", "./config/**/*", "./routes/**/*", "./public/**/*"],
        proxy: {
            target: "http://127.0.0.1:8000", 
        },
        open: true,
        reloadOnRestart: true,
    });
```

### スタイリングを`emotion`から`CSS Modules`へ

今回の開発を始めるにあたって調査をしていると、どうやらReactプロジェクトのスタイリングには、ノーマルな`css/scss`だけでなく`styled-components`や`emotion`といった、`css-in-js`というジャンルのスタイリング手法が存在するということを知りました。

そしてこれは僕の調査が甘いからかもしれませんが、`css-in-js`を使うことが「**ナウ**で**イケてる**らしいぞ」という情報が多かったので、ワケもわからず一番人気と思われる **`styled-components`**を導入することにしました。

しかし、実際に導入してから気づいたんですが、`styled-components`の書き方はマークアップに慣れた人間からすると非常に「直感的でない」んですね。htmlのタグがコンポーネントの名前になるのでDOM構造がぱっと見わかりづらいし、cssもキャメルケースで書かなきゃいけない。

```
margin-top: 10px;  //普通のcss記法
marginTop : 10px;  //styled-componentsの記法
```
**これだと確実に作業効率落ちる**、、、と思い色々調べていると、普通のcssのように書ける**`emotion`**というライブラリがあると知り、`emotion`を導入してここまで認証系3ページのスタイリングをしてきました。

ただやっぱり使いづらいところはあって、シンプルにコンポーネント内にスタイルのコードを書くので**コード量が多くなって**見通しが悪くなります。それに、`emotion`でも「`styled-components`風の記述ができる」というのが大きな強みのように語られていて、「`styled-components`の何がそんなにいいんだ？」てのがずっと疑問でした。

この疑問を会社のリーダーとの1on1でぶつけてみたら、返ってきた答えが予想外すぎて**目から鱗がボロボロ**と剥がれ落ちました。。

「`styled-components`は、我々のようにマークアップに抵抗がない人間からすると『直感的でなくて使いづらい』というのは本当にそうだと思う。あれはたぶんcssを嫌う人たちによって作られたんだ（あくまで予想）。
だから、普通の`css/scss`の方が使いやすいのなら、そっちを使えばいいんだよ。別に`styled-components`が優れているわけじゃない。」

なるほどそうなのか。。。
自分の中にあった**違和感**は正しかったし、自信を持ってcssを書けばいいんだと背中を押されたような気分でした。

というわけで、`styled-components`も`emotion`も無理に使う必要は全くないってことが分かったので、`CSS設計`をしなくてよいコンポーネント単位のスタイリング`CSS Modules`でシンプルにSCSSを使うことにしました。

こんな感じでモジュール呼び出して使います。

```typescript:Header/index.tsx
import React from "react";
const styles = require("./index.modules.scss");

const Header = () => {
    return (
        <div className={styles.header}>
            // 中略
        </div>
    );
};

export default Header;
```

```scss:Header/index.modules.scss
.header {
    height: 54px;
}
```
普段からある程度マークアップに慣れている人間からすると、SCSS普通に書くのが結局一番早い。`CSS Modules`でCSS設計しなくてよくなるのは、それだけでマークアップのストレスを**5割減**させてくれる。

### GithubActionsを使った自動デプロイ

まだまだリリースできる段階にはほど遠いですが、人に触ってもらいたくてとりあえずサーバーにデプロイしました。

これまでもいくつかサービス作って本番デプロイを経験しましたが、`「ローカルで更新」→「リモートにプッシュ」→「サーバーにssh接続してログイン」→「リモートからプル」`という一連の流れを毎回**手動**で行っており、非常に**面倒臭い**なと感じていました。

そこで今回はどうしても**「自動デプロイ」**を導入したかったので、巷で流行っているっぽい**`CircleCI`**にチャレンジしてみました。

ただ、慣れないインフラ寄りの知見は乏しく、一人でやってもうまくいかず、`CircleCI`での自動デプロイ環境を構築するには至りませんでした。。

しかし諦めません。

**「もっと簡単な他の方法はないのか？」**とさらに調査を進めると、「『masterブランチをリモートにプッシュしたら自動デプロイする』程度のものなら**`GithubActions`**が最も簡単に実現できる」との情報を得たので、`GithubActions`での実装に切り替えることに！

、、、しかし、「最も簡単」な`GithubActions`ですら自力で実装することができず、ここで完全に諦めて「[MENTA](https://menta.work/)」を使うことにしました。

[MENTA](https://menta.work/)は、「メンターが出品しているサービスを見つけて買う」だけでなく、「自分でメンター募集をかけて、見つけてもらう」という使い方があります。

僕が実際に投稿したメンター募集は[こちら](https://menta.work/bosyu/722) 
![スクリーンショット 2020-12-09 23.16.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/6fd58089-a82d-6637-ab51-02c3507b670f.png)

メンター募集をかけて待っていると、内容と予算を見て**「助けてやってもイイゼ〜」**と思った方がいたら手を挙げてくれます。（この時は、予算2,000円で募集しましたが結局1,000円でやってもらえました。）

これくらいの内容だと、詳しい人からすれば**朝飯前**。

DMで自分の今の状態**（やりたいこと・今わかっていること・分からなくて行き詰まっているところ）**を伝えると、「[このサイト](https://developer.yukimonkey.com/article/20200420/)参考にしてみてください」とリンクを１つ共有してくださり、、、、なんと、実装できちゃいました。

**DMで１往復**やりとりしただけです。。

自分でも散々調べたのに見つけられなかったサイトでした。やはりつよつよな人は情報へのアクセスが**素早く的確**です。

こうして無事にGithubActionsを使った自動デプロイ環境の構築ができました。

設定は[こちら](https://github.com/kiwatchi1991/folicolle/blob/master/.github/workflows/cd.yml)


### Laravelの認証テスト

**「今回の開発ではしっかりテストも書くぞ！」**ということで、**見よう見まね**でテスト書いてみました。

認証系のテストを書いたんですが、まとまった記事がなかったので自分でQiita記事書いちゃいました！
[【Laravel テスト】フォームリクエスト 複数項目バリデーションのテストコードを書いてみた](https://qiita.com/kiwatchi1991/items/fd0e9cfae0e1bdcf1121) -Qiita

### SNSログイン

Laravelには`Socialite`という便利なOAuth用パッケージがあるので、こいつをインストールして使います。

[Socialiteの使い方を世界一丁寧に解説した](https://php-junkie.net/framework/laravel/socialite/)がタイトル通りめちゃくちゃ分かりやすく解説してくれています。Twitterログインだけならこの記事で実装できちゃうと思います。

（参考）

- Googleログイン: [【Laravel】GoogleのOAuth認証でログイン機能を実装する(PHP)](http://fresh-engineer.hatenablog.com/entry/2018/01/10/020821)
- Githubログイン: [GithubのOAuth2.0の仕様について理解する（Githubログイン）](https://tikasan.hatenablog.com/entry/2017/05/02/232719)

このあたりの記事が分かりやすかった気がします。

#### （余談）Zennのログイン画面

今話題の[Zenn](https://zenn.dev/)のログイン画面って、Googleログインオンリーなんです。

![スクリーンショット 2020-12-10 1.03.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/deb4a3e4-7c4c-ea68-e67a-6ba74721cab0.png)

**メチャクチャ斬新だなー**と思ったとともに、メチャクチャ**「あり」**だなと思いました。

これだけ世の中にサービスが溢れている中で、「そのサービス用にメアドでアカウント登録したのか、TwitterだったかGoogleだったかFacebookだったか」なんていちいち覚えてられないですよね？

選択肢が多ければ多いほど迷います。「毎回おれはTwitterで登録している！」と決め打ちの人もいるとは思いますが、まあ少数でしょう。

Zennは選択肢を一つにすることでその迷いをなくすことができています。

「これはぜひ取り入れたい！」と思ったので、せっかくフロントのバリデーションまで実装したユーザー登録/ログインのフォームをきっぱりと捨てて、ソーシャルオンリーにしてユーザーに迷いを与えない仕様にしていきます。

### `TypeSctipt`導入

「やっぱりモダンフロントエンドに`TypeScript`は欠かせないでしょ！」ってことで、導入。

認証の数画面をすでに実装し終えていた状態で、しかもプロジェクト全体に一気に`TypeScript`を入れたので、コンパイル時のエラーが100近く出ました（絶望）。

`TypeScript`自体初めてだったので全部の型を適切に定義することは一旦諦め、`:any`を多用してなんとか全エラーを解消するまで8時間かかりました(あかん)。

開発途中のプロジェクトに一気に全体適用するなんて、良い子は絶対にマネしないでね。

(参考)
TypeScriptの学習には、Udemyの講座を使いました。

- [超TypeScript入門 完全パック(2020)](https://www.udemy.com/share/102AoGB0ocdF5aQng=/) -Udemy

### フロントテストの環境構築

業務へのキャッチアップも兼ねている今回の個人開発。

いわゆるモダンフロントエンド盛り盛りセットのレベルまでもっていかなければならないので、フロントエンドのテストも外せません。

当時のブログ [86日目 個人開発記14 -フロント環境構築完了-](https://blog.kiwatchi.com/86%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%9814-%e3%83%95%e3%83%ad%e3%83%b3%e3%83%88%e7%92%b0%e5%a2%83%e6%a7%8b%e7%af%89%e5%ae%8c%e4%ba%86/)で、環境構築に苦労した話や`babel.config.js`の設定について書いてますが、今見てもなぜこの設定になってるのか思い出せない笑

とりあえず、これで動くみたいですよ。

```javascript:babel.config.js
module.exports = {
    presets: [
        [
            "@babel/preset-react",
            {
                modules: "false",
                useBuiltIns: "usage",
                targets: "> 0.25%, not dead",
            },
        ],
    ],
    env: {
        test: {
            presets: [["@babel/preset-env", { targets: { node: "current" } }]],
        },
    },
};
```

とりあえずテスト動く状態にはなっているのですが、「そもそも何をテストするのか？」がイマイチ分かっていないので、実際にコードを書くのはこれからですネ。

### README書いてみた

各所でGithubリポジトリのURLを共有しているので、「せっかく見てもらうのならREADMEをちゃんと書こう」と思い、書いてみました。
[kiwatchi1991/folicolle - Github](https://github.com/kiwatchi1991/folicolle)

こんな感じ
![スクリーンショット 2020-12-10 21.22.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/34e0699c-c26f-a33a-0395-bee2754a1445.png)
![スクリーンショット 2020-12-10 21.22.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/d90c946b-d79e-9ee6-38e9-1777210b79a3.png)


### 「ポートフォリオ一覧」表示ロジックの実装
ようやくメイン部分の実装らしい実装。

#### seeder, factory, fakerの理解

まずは`CRUD`の`R`のみ実装していきます。seederを使ったダミーデータの生成で、**factory**・**faker**の理解が深まりました。

メインコンテンツのモデル`Product`に、**一対一**でリレーションする`User`と**多対多**でリレーションする`Category`を紐づけた状態でダミーデータを作るための設定はこんな感じ。

(`Product`をcreateする際に、新規で`User`も作成してその`id`を`user_id`に格納、その後`Categories`テーブルに登録してあるカテゴリーの中からランダムで**7つ**カテゴリーを選んで登録する　という流れ。）

```php:database/seeds/ProductsSeeder.php
<?php

use App\Category;
use Illuminate\Database\Seeder;
use App\Product;

class ProductsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $products = factory(Product::class, 10)->create([
            'title' => "タイトル",
            'description' => 'これは説明です。これは説明です。これは説明です。これは説明です。これは説明です。これは説明です。これは説明です。これは説明です。これは説明です。',
            'body' => "これは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文ですこれは記事本文です"
        ]);

        //各プロダクトに、ランダムで３つカテゴリーを紐づける
        $categories = Category::all();

        $count = 7;
        $arr = [];
        foreach ($products as $product) {
            for ($i = 0; $i < $count; $i++) {
                $r = mt_rand(0, count($categories) - 1);
                $arr[$i] = $categories[$r]->id;
            }
            $product->categories()->sync($arr);
        }
    }
}
```

```php:database/factories/ProductFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Product;
use App\User;
use Faker\Generator as Faker;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(Product::class, function (Faker $faker) {
    return [
        'title' => $faker->text(10),
        'description' => $faker->text(100),
        'body' => $faker->text(255),
        'user_id' => factory(User::class)->create()->id,
        'img' => $faker->text(40),
    ];
});

```

```php:database/factories/UserFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'comment' => '僕は〇〇な人です',
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
    ];
});

```

(参考)
「fakerのメソッド一覧」みたいなのは公式リファレンスが見当たらなかったので、こちらのQiita記事が参考になりました。（見つけられなかっただけかも。知っていたら教えてください。。）

- [[Laravel5.1]Fakerチートシート -Qiita](https://qiita.com/tosite0345/items/1d47961947a6770053af)

### Githubのissueでタスク管理始めました

現場ではGithubを使っていないので、**Githubの使い方に慣れる**目的でissueでのタスク管理を始めました。

立てたissueがcloseしていくと**「確実に前に進んでいるのが目に見える」**ので、精神衛生上非常に良いですね。

アプリ完成まで長くモチベーションを維持していくうえでメンタルの健康はとても大切。

（参考）

- [GitHubのissueを活用した、個人アプリの開発手順を書いてみた](https://qiita.com/tkmd35/items/9612c03dc60b1c516969)
- [GitHubのissueとcommitを紐付ける](https://qiita.com/tkmd35/items/9612c03dc60b1c516969)


### Stylelintの導入

「SCSSも自動で整形してほしい！」と思い導入。自動整形いいですねぇ〜。

自分で書いたブログ記事がよくまとまっているので、参考にしてみてください。

（参考）

- [109日目 個人開発記 16 -stylelint入れてみた-](https://blog.kiwatchi.com/109%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%98-16-stylelint%e5%85%a5%e3%82%8c%e3%81%a6%e3%81%bf%e3%81%9f/)


### 「ポートフォリオ一覧」ページのコーディング

見た目の部分。マークアップですね。

「全くCSSが書かれていない」な０→１フェーズは結構しんどいけど、だんだん形になってくると無機質なデータの集合体に命が吹き込まれていくような感じに思えてきてイイですね（？）

CSS書くのもそんなに嫌いじゃない。

### 「ポートフォリオ詳細」表示ロジックの実装

特にコメントはないです。

### SQLクライアントツールを使おう → やっぱりまずはターミナルからでしょ！

これまでDBは`MySQL`しか使ったことなく、クライアントツールは`phpMyAdmin`を使っていました。

たまたま見た[【2020年版】フロントエンドのおすすめMac無料ツール](https://qiita.com/amita/items/a103210bdab8bfb6a889)で、**TablePlus**というクライアントツールが紹介されていたので、試しに使ってみよう！

、、、と思ってたんですが、「クライアントツールもいいけど、まずはターミナル操作ができるようになった方がいい。現場ではターミナルでがんがんDB触るよ！」という情報をフォロワーさんからいただいたので、**素直**な僕はターミナル操作を覚えることにしました。

SQL文を書くなんて、素のPHPでCRUDアプリ作ったとき以来だから完全に忘れてる。。
徐々に思い出していきます。

## まとめ

超ボリューミーな内容になりました。

**「なんかよく分からんけど、苦労してるっぽい」**のは存分に伝わったんじゃないかと思います。

でもこれが、**「実務ではReactもLaravelも触っていない歴1年ちょっとのエンジニア」の個人開発のリアル**です。**ドキュメンタリー**。

## おわりに
Qiitaは不定期更新でやっておりますが、Twitter[ ( @kiwatchi1991 ) ] (https://twitter.com/kiwatchi1991) では日々感じたことや細かい試行錯誤の様子などを発信しております。

また、ブログを毎日更新していて（今は110日を超えたあたり）、数日に一度個人開発に関する記事を書いています。

- [56日目 個人開発記⑧-emotionを使おう-](https://blog.kiwatchi.com/56%e6%97%a5%e7%9b%ae-emotion/)
- [68日目 個人開発記-自動デプロイしてみた-](https://blog.kiwatchi.com/68%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%98-%e8%87%aa%e5%8b%95%e3%83%87%e3%83%97%e3%83%ad%e3%82%a4%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/)
- [70日目 個人開発記 10 -firstOrCreateでハマった-](https://blog.kiwatchi.com/70%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%98-10-firstorcreate%e3%81%a7%e3%83%8f%e3%83%9e%e3%81%a3%e3%81%9f/)
- [72日目 個人開発記11 -typescriptはじめました-](https://blog.kiwatchi.com/72%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%9811-typescript%e3%81%af%e3%81%98%e3%82%81%e3%81%be%e3%81%97%e3%81%9f/)
- [77日目 個人開発記12 -技術選定ってむずかしい-](https://blog.kiwatchi.com/77%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%9812-%e6%8a%80%e8%a1%93%e9%81%b8%e5%ae%9a%e3%81%a3%e3%81%a6%e3%82%80%e3%81%9a%e3%81%8b%e3%81%97%e3%81%84/)
- [85日目 個人開発記 -フロントテストを導入しよう-](https://blog.kiwatchi.com/85%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%98-%e3%83%95%e3%83%ad%e3%83%b3%e3%83%88%e3%83%86%e3%82%b9%e3%83%88%e3%82%92%e5%b0%8e%e5%85%a5%e3%81%97%e3%82%88%e3%81%86/)
- [86日目 個人開発記14 -フロント環境構築完了-](https://blog.kiwatchi.com/86%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%9814-%e3%83%95%e3%83%ad%e3%83%b3%e3%83%88%e7%92%b0%e5%a2%83%e6%a7%8b%e7%af%89%e5%ae%8c%e4%ba%86/)
- [87日目 個人開発記15 -emotionからの脱却-](https://blog.kiwatchi.com/87%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%9815-emotion%e3%81%8b%e3%82%89%e3%81%ae%e8%84%b1%e5%8d%b4/)
- [109日目 個人開発記 16 -stylelint入れてみた-](https://blog.kiwatchi.com/109%e6%97%a5%e7%9b%ae-%e5%80%8b%e4%ba%ba%e9%96%8b%e7%99%ba%e8%a8%98-16-stylelint%e5%85%a5%e3%82%8c%e3%81%a6%e3%81%bf%e3%81%9f/)


各論はブログの方が細かく書いてたりするので、よかったらこちらもぜひのぞいてみてください。

それではまた次回！