20200804のlaravelに関する記事は15件です。

【Laravel】リポジトリパターン実装におけるエラー Target [App\Repositories\UserRepositoryInterface] is not instantiable while building

3行で

  • Userリポジトリが読み込めない
  • なぜか他のリポジトリは読める(例えばArticlesRepositoryとか)
  • AppServiceProvider.php を見直す

概要

「マイページにユーザーが投稿した記事を、ユーザー名に基づいてArticlesテーブルから持ってくる」というパターンで実装する

リポジトリパターン実装における各ファイル

実装クラス

UserRepository.php
<?php

namespace App\Repositories\User;

// Modelディレクトリを作るパターンで実装している
use App\Models\User;

class UserRepository implements UserRepositoryInterface
{
  protected $user;

    /**
    * @param object $user
    */

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

  public function getUser($name)
  {
    return $this->user->where('name', $name)->first();
  }
}

インターフェース

UserRepositoryInterface.php
<?php

namespace App\Repositories\User;

interface UserRepositoryInterface
{
    public function getUser($name);
}

コントローラ

UserController.php
<?php

namespace App\Http\Controllers;
use App\Repositories\User\UserRepositoryInterface;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{    
    protected $user;

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

    public function show(String $name)
    {
        $user = $this->userRepository->getUser($name);

        $articles = $user->articles->sortByDesc('create_at');

        return view('users.show', [
            'user' => $user,
            'articles' => $articles,
        ]);
    }
}

サービスプロバイダ

下記のコードでエラーが起きた。

AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */

    /**
     * Bootstrap any application services.
     *
     * @return void
     */

    public function boot()
    {
        //
    }

    // インターフェースと実装クラスを登録
    public function register()
    {
        $this->app->bind(
            \App\Repositories\Article\ArticleRepositoryInterface::class,
            \App\Repositories\Article\ArticleRepository::class,
            \App\Repositories\User\UserRepositoryInterface::class,
            \App\Repositories\User\UserRepository::class
        );
    }
}

これが問題だったが、自分の知る限り(あと調べた限り)ではこのパターンで実装できている。
3日くらい悩んだりいろいろ試してたらこの記事を見つけた。

Target [App\Interfaces\User\UserInterface] is not instantiable while building #1

試しに修正してみる。↓

AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */

    /**
     * Bootstrap any application services.
     *
     * @return void
     */

    public function boot()
    {
        //
    }

    // インターフェースと実装クラスを登録
    public function register()
    {
        $this->app->bind(
            \App\Repositories\Article\ArticleRepositoryInterface::class,
            \App\Repositories\Article\ArticleRepository::class
        );
            $this->app->bind(
            \App\Repositories\User\UserRepositoryInterface::class,
            \App\Repositories\User\UserRepository::class
        );
    }
}

そしたら直った。
理由が正直わからないのでもしわかる方がいたら教えてください。

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

【PHP・laravel】クエリパラメータを送る時まとめ(getとrequest)

初歩的なことですが、調べてもなかなか出てこなかったため、記録します。

HTMLからPHP側に送る

sample1.laravel.php
<a harf="http://ここがURL?suji=1">送信</a>
sample2.laravel.php
<a harf="http://ここがURL?moji='文字'">送信</a>

php側

index.php
$get1 = $_GET['suji'];//1
$get2 = $_GET['moji'];//'文字'

PHP内のやり取り(関数から関数)

数値や文字を送るとき

index.php
function a(){
  redirect('/b?suji=1');
}

function b(){
  $request1 = $_REQUEST['suji']//1
}
index.php
function a(){
  redirect("/b?moji='文字'");
}

function b(){
  $request2 = $_REQUEST['moji']//文字
}

変数を送るとき

index.php
function a(){
  $count = 2;
  redirect('/b?count='.$count);
}

function b(){
  $request3 = $_REQUEST['count']//2
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのブログ開発でmarkdown対応させてみた

はじめに

Laravelを使ってブログを開発している際に見出しや装飾を本文に反映させてみたいな
と思い、markdown対応させてみました。
至らない点がありますが暖かい目でみてやってください。

使用するmarkdown

Markdown Extraを使用します。
また、ライブラリはcebe\markdownを使用します。

markdown実装

markdownの実装自体は簡単なコードで完了します。

convertMarkdown.php
 $converter = new \cebe\markdown\MarkdownExtra();
 $String = $converter->parse("# AAA"); // <h1>AAA</h1>

Laravelプロジェクトに組み込む

convertMarkdown.phpで使用したコードをLaravelプロジェクトに組み込みます。
今回はブログでの使用なのでブログの本文が表示される画面で反映させたいので、
記事一覧画面(index.blade.php)と記事詳細画面(show.blade.php)に以下のコードを組み込みます。

index.blade.php&show.blade.php
<div class="body">
   @php
     $converter = new \cebe\markdown\MarkdownExtra();
     $item->contents = $converter->parse($item->contents);
   @endphp  
   {!! $item->contents !!}
</div>

{!! !!}でHTMLの要素をそのまま表示させています。(Laravel標準のhtmlspecialcharsを解除)

動作確認

記事一覧画面

markdown反映前

スクリーンショット 2020-08-04 19.50.55.png

markdown反映後

スクリーンショット 2020-08-04 19.52.48.png

 記事詳細画面

スクリーンショット 2020-08-04 19.54.39.png

markdown反映後

スクリーンショット 2020-08-04 19.55.22.png

終わりに

今回はLaravelを使ったブログにmarkdownを反映させる処理を書きました。
view部分にmarkdownの処理を書いたり、共通関数にしなかったりですっきりしたコードではないのですが、ひとまず実現できたのでOKとしています。
この記事に関して何かあればコメントや編集リクエスト投げていただけると幸いです!

参考文献

PHPでMarkdownをいい感じにやる
Laravel 5.5 Bladeテンプレート

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

Laravelのリソースコントローラのルーティング

Laravelのリソースコントローラのルーティング
参考:公式リファレンス

$ php artisan make:controller PhotoController --resource
web.php
Route::resource('photos', 'PhotoController');

スクリーンショット 2020-08-04 17.52.42.png

このルーティング Route::resource() をバラして書くと以下のようになる。

web.php
Route::get('/photos', 'PhotoController@index')->name('photos.index');
Route::get('/photos/create', 'PhotoController@create')->name('photos.create');
Route::post('/photos', 'PhotoController@store')->name('photos.store');
Route::get('/photos/{photo}', 'PhotoController@show')->name('photos.show');
Route::get('/photos/{photo}/edit', 'PhotoController@edit')->name('photos.edit');
Route::put('/photos/{photo}', 'PhotoController@update')->name('photos.update');
Route::delete('/photos/{photo}', 'PhotoController@destroy')->name('photos.destroy');

確認

$ php artisan route:list

出力結果

+--------+-----------+---------------------+----------------+----------------------------------------------+------------+
| Domain | Method    | URI                 | Name           | Action                                       | Middleware |
+--------+-----------+---------------------+----------------+----------------------------------------------+------------+
|        | GET|HEAD  | api/user            |                | Closure                                      | api        |
|        |           |                     |                |                                              | auth:api   |
|        | GET|HEAD  | photos              | photos.index   | App\Http\Controllers\PhotoController@index   | web        |
|        | POST      | photos              | photos.store   | App\Http\Controllers\PhotoController@store   | web        |
|        | GET|HEAD  | photos/create       | photos.create  | App\Http\Controllers\PhotoController@create  | web        |
|        | GET|HEAD  | photos/{photo}      | photos.show    | App\Http\Controllers\PhotoController@show    | web        |
|        | PUT|PATCH | photos/{photo}      | photos.update  | App\Http\Controllers\PhotoController@update  | web        |
|        | DELETE    | photos/{photo}      | photos.destroy | App\Http\Controllers\PhotoController@destroy | web        |
|        | GET|HEAD  | photos/{photo}/edit | photos.edit    | App\Http\Controllers\PhotoController@edit    | web        |
+--------+-----------+---------------------+----------------+----------------------------------------------+------------+
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでテーブルのcreated_at, updated_atに時刻をミリ秒単位で入れる

初めに

PHP(laravel)の開発で、
データベースにinsertする際、created_at、updated_atの時刻を、ミリ秒(3桁)まで指定する要件があり、ネットで探しても良記事が見つからなかったのでメモ。

1.getDateFormat()をModelに記載

例として、productsテーブルに商品を登録するという想定。

Product.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model 
{
    //~省略~

    //この関数をモデルに追加
    public function getDateFormat()
    {
        return 'Y-m-d H:i:s.v';
    }
}

2.現在時刻をミリ秒単位に成型

コントローラにDBの処理を書くことはバットプラクティスかもしれませんが、
今回簡単な例としてコントローラーに下記のように記述。
*Carbonを使用。
(PHP7.2以降であればdate()->format('Y-m-d H:i:s.v')でもミリ秒に変換できるみたいです)

ProductController.php
//carbonを利用
use Carbon\Carbon;

function createProduct ($product)
{
    //現在時刻をミリ秒に変更("2020-08-04 12:45:07.105"のような形式になる)
    $now = Carbon::now()->format('Y-m-d H:i:s.v');

    $products = new Product();
    //~略~
    $products->created_at = $now;
    $products->updated_at = $now;
    $products->save()
}

これでcreated_at、updated_atからむに現在時刻がミリ秒の単位で登録される。

参考

https://stackoverflow.com/questions/50208932/laravel-model-trailing-data-when-save-the-model
https://hnw.hatenablog.com/entry/20180401

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

laravelを使ったときにハマったエラーの解決方法

状況

laravel new laravelapp

Macでlaravelをインストールして上記のlaravelコマンドを使おうとするとこんなエラーが出た。

[RuntimeException]                                                       
The Zip PHP extension is not installed. Please install it and try again. 

こちらのエラーの解消に大分時間を費やしたので、同じエラーにハマってる人のために記事を残しておこうと思う。

解決方法

composerのインストール

まずはcomposerをインストールする
下記をターミナルで実行する

brew install composer

composerコマンドを代わりに使う

laravel new laravelapp

macの場合これでは無理なので代わりに,composer経由で行う。

composer create-project --prefer-dist laravel/laravel test_app

test_appは作りたいアプリ名。

このコマンドが使えない場合はパスが通ってない可能性があるので、下記を実行してください。

echo "export PATH=~/.composer/vendor/bin:$PATH" >> ~/.zprofile
source ~/.zprofile

解決方法2

composerコマンドを使って自分の場合は解決できたのですが、それでは解決できないこともあるかもしれません。

そういう場合はdockerを使うことを勧めます。

https://github.com/hiroto2929/laravel_docker

こちらを参考に環境構築してください。

参考記事

エラーについて調べた際に出てきた記事
https://qiita.com/endeavor/items/77f7017473c95c42fc29
https://iiiso.ti-da.net/e8647248.html

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

CakePHP3.8 と Laravel7.x の「書き方」の違い ~ORM周りその2~

CakePHP3.8 と Laravel7.x の「書き方」の違い ~ORM周りその2~

今回はアクセサー周り

前: クエリの要約

そもそもクエリオブジェクトの返り値が別

1件取得

  • CakePHP

Table クラスとは別に、Entity クラスがあり、それが返ってくる

// \App\Model\Entity\Users が返ってくる(Table クラスとは別)
$veteranHyphen = $usersTable->find('hyphen')->find('veteran')->first();
  • Laravel

Model が Cake で言う Table, Entity の役割を持っている。
なので Model 自身のインスタンスが返ってくる。

// \App\Users が返ってくる
$veteranHyphen = Users::hyphen()->veteran()->first();

複数件取得

  • CakePHP

Collection インスタンスが返ってくる。
Collection の要素には Entity インスタンスが入っている。

// \Cake\Collection\Collection
$veteranHyphenUsers = $usersTable->find('hyphen')->find('veteran')->all();
foreach ($veteranHyphenUsers as $veteranHyphenUser) // 中身は \App\Model\Entity\Users
  • Laravel

Collection インスタンスが返ってくる。
Collection の要素には Model インスタンスが入っている。

// \Illuminate\Database\Eloquent\Collection
$veteranHyphenUsers = Users::hyphen()->veteran()->get();
foreach ($veteranHyphenUsers as $veteranHyphenUser) // 中身は \App\Users

アクセサー

fetch したレコードの値を、参照するときにフォーマットすることができます。
DB からとってくるとき、ちょっとした整形が面倒くさいなぁ(CASE文大量など)といったときに便利です。

個人的には表示系をここに書いても良いんじゃないかなって思います。
(郵便番号 => 住所 とか 0,1 => 男女 とか)

  • CakePHP

CakePHP では「仮想プロパティ」と呼ばれています。
Entity クラスの中で、「_get」+ {仮想プロパティ名}で定義できます。

namespace App\Model\Entity;

use Cake\ORM\Entity;

class User extends Entity
{
    protected function _getFullName()
    {
        return $this->_properties['first_name'] . '  ' .
            $this->_properties['last_name'];
    }

}

// 参照する時
echo $user->full_name; // 横丁 小町

仮想プロパティはデータを保存するときには干渉しません。

  • Laravel

Laravel では「アクセサ」と呼ばれています。
get + {プロパティ名} + Attribute で定義できます。

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーのフルネームを取得
     *
     * @return string
     */
    public function getFullNameAttribute()
    {
        return $this->first_name . '  ' .
            $this->last_name';
    }
}

// 参照する時
echo $user->full_name; // 横丁 小町

保存時にも影響を与えたい時

入力した値を、自動的に保存時にフォーマットしてほしい時

  • CakePHP3

ややこしいですが、CakePHP ではこれを アクセサ と呼びます。
値を参照するときにはもちろん、DBにデータを保存するときにも適用されます。

値を保存する時、値を参照する時、両方向に影響があるので、冪等性が担保されていなければなりません

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity
{
    protected function _getTitle($title)
    {
        return ucwords($title);
    }
}
  • Laravel

Laravel では、カスタムキャスト でこれを実現します。
CakePHPと違って、値を取得する時、保存する時で処理を分けられるので、冪等性をそんなに気にしなくても良いです。

キャストを定義して、

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Ucwords implements CastsAttributes
{
    /**
     * 指定された値をキャストする
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  mixed  $value
     * @param  array  $attributes
     * @return array
     */
    public function set($model, $key, $value, $attributes)
    {
        return ucwords($value);
    }
}

Model で設定

namespace App;

use App\Casts\Ucwords;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    /**
     * キャストする属性
     *
     * @var array
     */
    protected $casts = [
        'title' => Ucwords::class,
    ];
}

 おわりに

フレームワークごとに単語を統一してほしい。

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

2020年のLaravelパフォーマンス最適化のための12のヒント

概要

12 Tips for Laravel Performance Optimization in 2020
から引っ張ってきたものです。結構Google翻訳の質が向上していた。

掻い摘んで抜粋していきます。

目次

  1. 構成キャッシング
  2. ルートキャッシング
  3. 未使用のサービスを削除
  4. クラスマップの最適化
  5. Composer最適化オートロード
  6. 含まれるライブラリを制限する
  7. JITコンパイラ
  8. Fast Cache and Sessionドライバーを選択する
  9. クエリ結果のキャッシュ
  10. Eager loadingを使用する
  11. アセットのプリコンパイル
  12. PHPの最新バージョンを実行する
  13. CDNを使う
  14. アセットバンドル
  15. ファイル圧縮
  16. PHPの最新バージョンを実行する
  17. Laravelデバッグバー
  18. Laravelの一般的なパフォーマンスチューニングのヒント

12のヒントという割には結構項目が多い...

前提条件

  • Laravel 5.5
  • PHP 7.1
  • MySQL (Version不明)

1. 構成キャッシング

Artisan Cache Configでパフォーマンスを向上させます。

$ php artisan config:cache

設定を更新する場合は、キャッシュをクリアしましょう。

$ php artisan config:clear

アプリケーションをさらに最適化するには、PHPコードをキャッシュするOPcacheを使用して、再コンパイルする必要がないようにします。
OPcache PHP公式ドキュメント

2. ルートキャッシング

$ php artisan route:cache

configまたはroutesファイルが変更されるたびにコマンドを実行することを忘れないでください。

キャッシュをクリアするには、次のコマンドを使用します。

$ php artisan route:clear

3. 未使用のサービスを削除

設定ファイルで未使用のサービスを必ず無効にしてください。
使用していないサービスプロバイダーにコメントアウトを追加します。

4. クラスマップの最適化

$ php artisan optimize --force

5. Composer最適化オートロード

$ composer dumpautoload -o

6. 含まれるライブラリを制限する

ライブラリなしで実行できると思われる場合は、 config/app.phpから削除してLaravelアプリを高速化します。もう1つの重要な場所はcomposer.jsonです。

7. JITコンパイラ

Laravelアプリの場合、推奨されるJITコンパイラはFacebookのHHVMです。

@ytake さんからの指摘です。

HHVM4.0以降はPHPの動作をサポートしていませんので、
現時点でHHVM上でLaravelを動かすのはお勧めしません。(PHPのコードはもう動きません)
現在の最新バージョンはPHP互換環境ではなくなっていますよ。

8. Fast Cache and Sessionドライバーを選択する

Laravel5のパフォーマンスのための最速のキャッシュおよびセッションドライバーはMemcachedだと思われます。

セッションドライバーを変更するためのドライバーキーは、通常 app/config/session.php にあります。同様に、キャッシュドライバーを変更するためのドライバーキーは app/config/cache.php にあります。

9. クエリ結果のキャッシュ

頻繁に実行されるクエリの結果をキャッシュすることは、Laravel5.5のパフォーマンスを向上させる優れた方法です。
次のように使用される記憶機能をお勧めします。

$posts = Cache::remember('index.posts', 30, function(){
    return Post::with('comments', 'tags', 'author', 'seo')->whereHidden(0)->get();
});

10. Eager loadingを使用する

Eloquentは最初のクエリに応答して、関連付けられているすべてのオブジェクトモデルを取得します。これにより、アプリケーションの応答が増加します。

遅延ローディングの例です。

$books = App\Book::all();
foreach ($books as $book) {
    echo $book->author->name;
}

Eager loadingの例です。

$books = App\Book::with('author')->get();
foreach ($books as $book) {
    echo $book->author->name;
}

11. アセットのプリコンパイル

$ php artisan optimize
$ php artisan config:cache
$ php artisan route:cache

12. CDNを使う

CDNサーバーから静的アセットファイルをロードすると(ファイルをホストするサーバーから直接ロードするのではなく)、Laravelアプリケーションのパフォーマンスが向上します。

13. アセットバンドル

Laravel Mixで複数のスタイルシートやJSファイルを1つのファイルに効率的に連結できます。

webpack.mix.js
mix.styles([
'public/css/vendor/normalize.css',
'public/css/styles.css'
], 'public/css/all.css');

14. ファイル圧縮

Laravel Mixを使用してアセットを縮小できます。

$ npm run production

15. PHPの最新バージョンを実行する

PHPの最新バージョンでは、パフォーマンスが大幅に向上しています。したがって、Laravelアプリケーションが最新バージョンのPHPを実行していることを確認して、アプリケーションの新しいバージョンで導入されたすべてのパフォーマンス改善を活用できるようにします。

16. Laravelデバッグバー

最適化手法ではなく、パッケージです。Laravel Debugbarは、PHPデバッグバーをLaravel 5と統合するためのパッケージです。

ServiceBarには、デバッグバーを登録して出力に添付するServiceProviderが含まれています。

Laravelパフォーマンスモニターとして使用できるパッケージです。アプリケーションの開発中にこのパッケージを使用することをお勧めします。

これにより、アプリケーションの実行状況を簡単に検査して、それに応じて改善することができます。

17. Laravelの一般的なパフォーマンスチューニングのヒント

Webサイトのパフォーマンスを最適化するには、次のようないくつかの調整を実装するだけです。

  1. Laravelページスピードコンポーザーパッケージ
  2. プロバイダーの詳細を更新する
  3. パッケージを公開する
  4. ミドルウェアを登録する
  5. ページ上にルートを作成する
  6. ブレードファイルを作成
  7. アプリを実行

Laravelページスピードコンポーザーパッケージ

Composerを使用して、renatomarinho/laravel-page-speedパッケージをダウンロードと展開します。

$ composer require renatomarinho/laravel-page-speed

プロバイダーの更新

config/app.phpファイルに移動し、サービスプロバイダーとエイリアスを展開します。

config/app.php
'providers' => [
....
....
RenatoMarinho\LaravelPageSpeed\ServiceProvider::class,

パッケージの公開

$ php artisan vendor:publish --provider="RenatoMarinho\LaravelPageSpeed\ServiceProvider"

Webアクセス用のミドルウェアを追加し、ルーティングに追加する

Kernel.php
protected $middlewareGroups = [

   'web' => [

       ........

       ........

       \RenatoMarinho\LaravelPageSpeed\Middleware\InlineCss::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\ElideAttributes::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\InsertDNSPrefetch::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\RemoveComments::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\TrimUrls::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\RemoveQuotes::class,

\RenatoMarinho\LaravelPageSpeed\Middleware\CollapseWhitespace::class,

  ]

]
web.php
// Define Route
// I  going to add a route to check Optimized Website Speed and Performance in Laravel 5.5
Route::get('/listView', function () {
  return view('listView');
});

カスタムスタックによるパフォーマンスの向上

ThunderStackとして知られるカスタムスタックは、Laravelアプリのパフォーマンスを向上させるように設計されています。受賞したレシピは、ApacheとNGINX、Varnish、Memcachedで構成されています。

参考

12 Tips for Laravel Performance Optimization in 2020

感想とか

Laravelアプリが遅い。重い。
色々言われている所もあると思いますが、パフォーマンスチューニングで実施できることは結構ありますね。
すっごい遅いし重いしLaravelクッソだなぁと思ってブーメラン

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

Laravelで和暦を扱う

和暦を扱う必要があったので,その実装をまとめてみました

環境

  • Laravel 5.8

作るもの

  • 年号を管理するテーブル(era_names)
  • 和暦クラス
  • 和暦と西暦の変換処理

年号テーブルの作成

年号を管理するテーブルには

  • 年号の名前
  • 略称
  • 開始日
  • 終了日

の4つのカラムを作成します.テーブル名をera_namesとし,マイグレーションファイルは次のようになります.

public function up()
{
    Schema::create('era_names', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('short_name');
        $table->date('start_at');
        $table->date('end_at');
        $table->timestamps();
    });
}

作成したマイグレーションを実行しテーブルを作成したら,モデルを作成します.

app\Models\EraName.php
class EraName extends Model
{
    /** @var string */
    protected $table = 'era_names';

    /** @var array */
    protected $dates = [
        'start_at',
        'end_at',
    ];

    /** @var array */
    protected $guarded = [
        'id',
    ];
}

最後にデータを追加します.追加する方法はシーダやtinkerを用いてください.

name short_name start_at end_at
昭和 S 1926-12-25 1989-01-07
平成 H 1989-01-08 2019-04-30
令和 R 2019-05-01 2100-01-01

和暦-西暦変換処理の作成

プロジェクトの方針にしたがって,和暦-西暦の変換処理を作成します.ここでは和暦クラスを作成し,Carbon(CarbonImmutable)と相互変換する形で実装します.他の実装として変換処理をすべてServiceに書き,ファサードやヘルパを通して変換することなどが考えられます.

Carbonに和暦への変換メソッドを実装するために,次のようなサービスプロバイダを用意します.

app\Providers\DateServiceProvider
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Date;
use App\Models\EraName;
use App\Models\Wareki;
use Carbon\CarbonImmutable;

class DateServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        Date::use(CarbonImmutable::class);  // 日付を扱うクラスをCarbonImmutableに変更

        Date::macro('toWareki', function () {  // 日付を扱うクラスにtoWarekiメソッドを追加
            return new Wareki($this);
        });
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->instance('era_names', EraName::all());  // サービスコンテナに年号を登録
    }
}

Date::macro(...)を用いることで,日付オブジェクトにメソッドを追加することができます.ここではtoWarekiメソッドを追加しています.

$this->app->instance('era_names', EraName::all());

この部分ではサービスコンテナに年号を登録し,どこでも同じインスタンスが取得できるようにします.取得方法は

app('era_names')

で取得することができます.これを行うことで,処理の途中でera_namesテーブルの内容が変更されても,処理開始時の年号データが使われるようになります.また,年号を取得するSQLが1度しか実行されなくなるなどの利点があります.

次に和暦クラスを実装します.場所はプロジェクトの方針にしたがいましょう.ここではapp\Models下に作成します.

app\Models\Wareki.php
namespace App\Models;

use Illuminate\Support\Facades\Date;
use Carbon\CarbonImmutable;
use App\Models\EraName;


class Wareki
{
    /** @var int */
    public $year;
    public $fiscalYear;
    public $month;
    public $day;

    /** @var EraName */
    public $eraName;

    /**
     * @param Carbon|CarbonImmutable
     */
    public function __construct($ymd) {
        $this->eraName = app('era_names')->first(function ($eraName) use ($ymd) {
            return $eraName->start_at->startOfDay() <= $ymd && $ymd <= $eraName->end_at->endOfDay();
        });

        $this->year = $ymd->year - $this->eraName->start_at->year + 1;
        $this->fiscalYear = $this->year - ((1 <= $ymd->month && $ymd->month <= 3) ? 1 : 0);
        $this->month = $ymd->month;
        $this->day = $ymd->day;
    }

    /**
     * @return Carbon|CarbonImmutable
     */
    public function toYmd() {
        return Date::createMidnightDate(
            $this->eraName->start_at->year + $this->year - 1,
            $this->month ?? 1,
            $this->day ?? 1,
        );
    }

    /**
     * @param string $format
     * @return string
     */
    public function format(string $format) {
        $ret = '';
        foreach (mb_str_split($format) as $str) {
            if ($str === 'Y') $ret .= sprintf('%s%02d', $this->eraName->name, $this->year);
            else if ($str === 'S') $ret .= sprintf('%s%02d', $this->eraName->short_name, $this->year);
            else if ($str === 'y') $ret .= sprintf('%s%02d', $this->eraName->name, $this->fiscalYear);
            else if ($str === 's') $ret .= sprintf('%s%02d', $this->eraName->short_name, $this->fiscalYear);
            else if ($str === 'm') $ret .= sprintf('%02d', $this->month);
            else if ($str === 'd') $ret .= sprintf('%02d', $this->day);
            else $ret .= $str;
        }

        return $ret;
    }
}

和暦-西暦の変換処理は和暦クラスにまとめることにしてあります.コンストラクタで西暦->和暦の変換を行い,toYmdメソッドで和暦->西暦の変換を行います.formatメソッドでは和暦を文字列に変換します.例えば

$ymd = Date::parse('2019-03-01');  \\ 平成年31年3月1日

$ymd->toWareki()->format('Y年');  \\ 平成31年
$ymd->toWareki()->format('S');  \\ H31
$ymd->toWareki()->format('y年度');  \\ 平成30年度
$ymd->toWareki()->format('s');  \\ H30
$ymd->toWareki()->format('m月d日');  \\ 03月01日

このようになっています.この和暦クラスでは時分秒を扱わない形で実装しています.

まとめ

Laravelで和暦を扱ってみました.年号データをDBではなくconfigなどに設定する場合も,$this->app->instance()の部分を調整することで実装することができます.また,Data::macroを用いて年度初めを取得する処理などを追加してもいいかもしれません.

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

Laravel5.6以降のログの機能をちょっと調べた

目的

  • Laravelのログに関する情報を調べてみたので簡単にまとめる

実施環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提情報

  • Laravelのログ出力方法はMonologと言う名前のライブラリを使用している。
  • 本記事より公式ドキュメントの方がわかりやすいかもしれない。ログについて詳しく書いてあるLaravel5.6のドキュメントのリンクを下記に記載する。

ログチャンネル

  • ログの出力にはチャンネルと言う機能を使用する。
  • チャンネルは複数存在する。
  • チャンネル = ログ出力の方法と考えていただいても差し支えないと思う。
  • 下記にログチャンネルの種類を記載する。

    チャンネル 説明
    stack チャンネルをまとめるためのチャンネル
    single シングルチャンネル、一つのファイルにずっとログを書き込む。デフォルトの設定はこれ
    daily デイリーチャンネル一日毎にファイルを分けてログを出力してくれる。デフォルトだと14日間ログファイルを保持する。
    slack slackチャンネル、社内のコミュニケーションツールであるslackにログを送信するためのチャンネル
    syslog システムログチャンネル、アプリのシステムログを出力するためのチャンネル
    errorlog エラーログチャンネル、エラーメッセージを出力するためのチャンネル
    monolog そのほかのライブラリで定義されているログを出力するためのチャンネル
    custom カスタムチャンネル、開発者が任意のチャンネルを作成するためのチャンネル
  • 公式ドキュメントのログチャンネルの表を下記に記載する。

    ログ_5_6_Laravel.png

ログレベル

  • laravelのログにはレベルが存在する。
  • 出力の優先度によってレベル分けされている。
  • 8個のレベルが存在し、最も重要性の高いログのレベル名はemergencyで開発段階の情報など重要度の低いログを出力するレベルはdebugとなる
  • ログのレベルはチャンネル毎に設定可能である。ログ出力の指令を行うコードでも、出力したいログのレベルを指定することができる。
  • ログレベルの種類を下記に記載する。上位に行けば行くほど危険度が高いログレベルとして設計することが一般的である。

    ログレベル
    emergency
    alert
    critical
    error
    warning
    notice
    info
    debug
  • ログレベルは下記の規約で決められたものを使用している。

  • ログレベルはアプリ開発段階でのログに出力したい情報とデプロイしてユーザが使用している段階でのログに出力したい情報を分けるために存在する。

  • 後述するログの設定ファイルでdebugを選択していれば全てのログレベルのログがログファイルに記載される。

  • 後述するログの設定ファイルでを選択していれば全てのログレベルのログがログファイルに記載される。

ログの設定ファイル

  • laravel5.6からログの設定ファイルの場所が変更になった。
  • アプリ名ディレクトリ/config/直下のlogging.phpファイルがログの設定ファイルである。
  • 下記にloggingファイルの内容を記載する。

    アプリ名ディレクトリ/config/loggign.php
    <?php
    
    use Monolog\Handler\NullHandler;
    use Monolog\Handler\StreamHandler;
    use Monolog\Handler\SyslogUdpHandler;
    
    return [
    
        /*
        |--------------------------------------------------------------------------
        | Default Log Channel
        |--------------------------------------------------------------------------
        |
        | This option defines the default log channel that gets used when writing
        | messages to the logs. The name specified in this option should match
        | one of the channels defined in the "channels" configuration array.
        |
        */
    
        'default' => env('LOG_CHANNEL', 'stack'),
    
        /*
        |--------------------------------------------------------------------------
        | Log Channels
        |--------------------------------------------------------------------------
        |
        | Here you may configure the log channels for your application. Out of
        | the box, Laravel uses the Monolog PHP logging library. This gives
        | you a variety of powerful log handlers / formatters to utilize.
        |
        | Available Drivers: "single", "daily", "slack", "syslog",
        |                    "errorlog", "monolog",
        |                    "custom", "stack"
        |
        */
    
        'channels' => [
            'stack' => [
                'driver' => 'stack',
                'channels' => ['single'],
                'ignore_exceptions' => false,
            ],
    
            'single' => [
                'driver' => 'single',
                'path' => storage_path('logs/laravel.log'),
                'level' => 'debug',
            ],
    
            'daily' => [
                'driver' => 'daily',
                'path' => storage_path('logs/laravel.log'),
                'level' => 'debug',
                'days' => 14,
            ],
    
            'slack' => [
                'driver' => 'slack',
                'url' => env('LOG_SLACK_WEBHOOK_URL'),
                'username' => 'Laravel Log',
                'emoji' => ':boom:',
                'level' => 'critical',
            ],
    
            'papertrail' => [
                'driver' => 'monolog',
                'level' => 'debug',
                'handler' => SyslogUdpHandler::class,
                'handler_with' => [
                    'host' => env('PAPERTRAIL_URL'),
                    'port' => env('PAPERTRAIL_PORT'),
                ],
            ],
    
            'stderr' => [
                'driver' => 'monolog',
                'handler' => StreamHandler::class,
                'formatter' => env('LOG_STDERR_FORMATTER'),
                'with' => [
                    'stream' => 'php://stderr',
                ],
            ],
    
            'syslog' => [
                'driver' => 'syslog',
                'level' => 'debug',
            ],
    
            'errorlog' => [
                'driver' => 'errorlog',
                'level' => 'debug',
            ],
    
            'null' => [
                'driver' => 'monolog',
                'handler' => NullHandler::class,
            ],
    
            'emergency' => [
                'path' => storage_path('logs/laravel.log'),
            ],
        ],
    
    ];
    

ログチャンネルの指定

  • どのログチャンネルを指定してログを出力するのかの設定はアプリ名ディレクトリ/直下にある.envファイルに記載する。
  • .envの下記の部分でログチャンネルの設定を行っている。

    アプリ名ディレクトリ/.env
    LOG_CHANNEL=stack
    
  • 上記の様に記載されていると、stackチャンネル(チャンネルをまとめるためのチャンネル)が選択されていることになる。

  • dailyチャンネルでログを出力したい場合は下記の様に.envファイルに記載する。

    アプリ名ディレクトリ/.env
    LOG_CHANNEL=daily
    
  • 複数のログを出力するための方法は下記の記事にまとめてみた。(stackチャンネルに出力したいログチャンネルを配列状に追加することがセオリーの様である。)

ログの初期設定の状態

  • $ laravel new アプリ名を実行した直後の何もいじっていない状態のログの設定がどうなっているのか確認してみる。
  1. アプリの設定ファイルである.envファイルのログチャンネルの設定の確認

    • 下記に.envファイルのログチャンネルの記載部分のみ抜粋して記載する。

      アプリ名ディレクトリ/.env
      LOG_CHANNEL=stack
      
    • ログチャンネルはstackが選択されている。

  2. ログの設定ファイルlogging.phpの記載の確認

    • 下記にlogging.phpファイルの内容を記載する。

      アプリ名ディレクトリ/config/loggign.php
      <?php
      
      use Monolog\Handler\NullHandler;
      use Monolog\Handler\StreamHandler;
      use Monolog\Handler\SyslogUdpHandler;
      
      return [
      
          /*
          |--------------------------------------------------------------------------
          | Default Log Channel
          |--------------------------------------------------------------------------
          |
          | This option defines the default log channel that gets used when writing
          | messages to the logs. The name specified in this option should match
          | one of the channels defined in the "channels" configuration array.
          |
          */
      
          'default' => env('LOG_CHANNEL', 'stack'),
      
          /*
          |--------------------------------------------------------------------------
          | Log Channels
          |--------------------------------------------------------------------------
          |
          | Here you may configure the log channels for your application. Out of
          | the box, Laravel uses the Monolog PHP logging library. This gives
          | you a variety of powerful log handlers / formatters to utilize.
          |
          | Available Drivers: "single", "daily", "slack", "syslog",
          |                    "errorlog", "monolog",
          |                    "custom", "stack"
          |
          */
      
          'channels' => [
              'stack' => [
                  'driver' => 'stack',
                  'channels' => ['single'],
                  'ignore_exceptions' => false,
              ],
      
              'single' => [
                  'driver' => 'single',
                  'path' => storage_path('logs/laravel.log'),
                  'level' => 'debug',
              ],
      
              'daily' => [
                  'driver' => 'daily',
                  'path' => storage_path('logs/laravel.log'),
                  'level' => 'debug',
                  'days' => 14,
              ],
      
              'slack' => [
                  'driver' => 'slack',
                  'url' => env('LOG_SLACK_WEBHOOK_URL'),
                  'username' => 'Laravel Log',
                  'emoji' => ':boom:',
                  'level' => 'critical',
              ],
      
              'papertrail' => [
                  'driver' => 'monolog',
                  'level' => 'debug',
                  'handler' => SyslogUdpHandler::class,
                  'handler_with' => [
                      'host' => env('PAPERTRAIL_URL'),
                      'port' => env('PAPERTRAIL_PORT'),
                  ],
              ],
      
              'stderr' => [
                  'driver' => 'monolog',
                  'handler' => StreamHandler::class,
                  'formatter' => env('LOG_STDERR_FORMATTER'),
                  'with' => [
                      'stream' => 'php://stderr',
                  ],
              ],
      
              'syslog' => [
                  'driver' => 'syslog',
                  'level' => 'debug',
              ],
      
              'errorlog' => [
                  'driver' => 'errorlog',
                  'level' => 'debug',
              ],
      
              'null' => [
                  'driver' => 'monolog',
                  'handler' => NullHandler::class,
              ],
      
              'emergency' => [
                  'path' => storage_path('logs/laravel.log'),
              ],
          ],
      
      ];
      
    • コード'default' => env('LOG_CHANNEL', 'stack'),では.envファイルにてログチャンネルの設定が行われなかった場合に指定されるデフォルトのログチャンネルが記載されている。デフォルトではログチャンネルの設定が.envファイルにて行われなかった場合、stackチャンネルを指定する様に記載されている。本記載は基本的に変更しない。

    • 配列状に格納されているログチャンネルの情報について説明する。下記に.envファイルで指定されているstackチャンネルの記載部分のみ抜粋したコードを記載する。

      アプリ名ディレクトリ/config/loggign.php
      'stack' => [
          'driver' => 'stack',  //ライブラリの実行ドライバを指定している(チャンネル名を指定していると考えていただきたい)
          'channels' => ['single'],  //まとめて実行するログチャンネルを配列状に指定している デフォルトだとsingleチャンネルのみが指定されている。
          'ignore_exceptions' => false,  //本記載の意味を現在調査中です。
      ],
      

ログチャンネルを変更するには?

  • デフォルトのログチャンネルから変更する方法は主に二通りあると考える。下記に二通りの方法を記載する。
    • .envファイルにてLOG_CHANNEL=の記載を設定したいログチャンネルに変更する。
    • .envファイルのLOG_CHANNEL=の記載は弄らずに「stack」チャンネルを選択し、アプリ名ディレクトリ/config/loggign.phpの「stack」チャンネルの設定部分'channels' =>にて出力チャンネルを変更する。
  • 単一のログのみを出力するなら.envにてログチャンネルを設定するだけで良いが、「single」チャンネルと「daily」チャンネル両方のログが欲しい時はアプリ名ディレクトリ/config/loggign.phpの「stack」チャンネルの設定部分で指定する必要がある。下記に筆者が以前まとめた二種類のログを出力する方法の記事のリンクを記載する。

参考文献

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

助産師が退職して1年後に初めてアプリリリース

Qiita久しぶりです!
毎日自分のブログを更新するので精一杯のため全然書けませんでした(という言い訳w)。
大学病院で助産師としての仕事を退職してちょうど1年、
プログラミングを勉強して10ヶ月で初めて簡単なアプリをリリースしたので、
ここまでの経緯を簡単に振り返りたいと思います。
(まぁまぁ長くなってしまったので興味がある部分だけサラーっと読んで下さいw)

アプリリリースを決めるの簡単な振り返り

ジーズアカデミーで半年、プログラミング必死に勉強

マジで大変でした〜
病院の古いUI電子カルテしか使ったことない私が急にコード書くって、
昭和時代にタイムスリップしてそこの人にiPhone渡して「ググって〜」と言うのと同じです。
つまりさっぱりなんです。
だからすごい大変でした、でも私なりに出来る努力をしました。

関連記事
学校と家事と育児の2ヶ月半を振り返って

在学中にサーバーサイドインターン

勉強すればする程、こんなポンコツな自分では開発とかITで業界良くするとかマジで無理!
と思い一旦エンジニア就職しようと決めます。
あまりにも扱える武器が少なすぎる。

関連記事
2ヶ月プログラミング学んで起こった心境の変化

だから在学中にインターン先を探しました。
ありがたいことにとある医療系ベンチャー企業でインターンをさせて頂くことになりました。

開発言語はRuby, フレームワークrails。
今までPHPとLaravelしかしたことない私にとっては不安しかありませんでしたが、
それでももう会社を選んでいる余裕なんて余裕でありません。
業界未経験育児中30overの私がインターンできるだけでも奇跡です。
ここは頑張るしかありません。

幸いにもLaravelのおかげでMVCをなんとなく理解していたので、
railsはすんなり入ってきました。
Rubyの書き方も直感的にわかる部分もありました。
わからないメソッドや書き方はその都度調べました。
優しいエンジニアの方にかなり助けられながら毎日必死になんとか這いつくばりました。

そしてやっぱり一番学び、毎日強く感じたのは、
個人開発と会社の開発の圧倒的なスケールの違いです。
インターンをすればする程、自分の開発はおもちゃに感じました。
関連記事
webサービスの質

卒業制作発表会(GGA)で優勝

日中ほぼフルタイムでインターンをしながらGGAの準備をしました。
ちょうど子ども達の自粛期間だったので、正直ここが一番大変でした。
容赦ないインターンの開発と発表の準備、でも日中は子ども3人が自宅にいます。
夫にもかなり協力してもらい、なんとインターンの開発を進めながら発表の準備も進めました。
発表会の結果はまさかのまさか、ポンコツな私が優勝しました。

関連記事
卒業制作発表会が終わりました
優勝と卒業

想定外の結果に正直驚き、
卒業後「本当にエンジニア就職で良いのか?」と自分の中で考えました。
そしてとりあえず自分のやりたいことをできる所までやってみよう!
そういう結論に至りました。

関連記事
とりあえずやってみる
会社を設立する意味

会社設立

インターンをしながらとりあえず会社も設立しました。
インターンが終了したすぐに動けるように手続き関連はチョコチョコと進めていました。
https://nsmart.work

インターン終了

この機会をを逃すとエンジニアにはもうなれない、そんなん重々承知の上でインターンを終了することになりました。
3ヶ月、たくさんのことを教えてくれたエンジニアの方には本当に感謝してもしきれません。
関連記事
エンジニアインターン終了

とりあえず1つリリースしてみよう

これから会社としてどうするか、ビジネスのビの字も知らない私なりに考えました。
たくさん考えたし今も毎日考えていますが、
とりあえず発表会で作ったアプリを1つリリースしてみよう、そう思いました。
幸いにも私の想いに共感してくれたジーズアカデミーの同期2人が開発を手伝ってくれることになり、
発表会よりもブラッシュアップしてアプリをリリースすることになりました。

アプリリリースまで

アプリの機能

機能は時間を計算するという非常にシンプルなアプリです。
陣痛が始まった時間〜胎盤が出たまでの時間(いわゆるお産何時間かかりました、とうやつです。専門用語で分娩所要時間と言います)を計算します。
この時間計算を、電子カルテがない産院では助産師や看護師が頭で計算しているという実情があります。
例えば、
陣痛が始まった時間 7/30 11:10
胎盤が出た時間 8/1 0:21
この間が何時間何分かをすぐに計算できる人はほぼいないと思います。
これを陣痛が始まった時間と胎盤が出た時間だけ入力したらすぐに計算できるという昨日です。
これしか出来ません!
だから卒業した時は、HTMLとCSS,JSでさらっと作りました。
時間計算は moment.jsというライブラリーを使いました。

アプリの改善点

計算項目の追加

実はこの分娩所要時間、時期にⅠ期 Ⅱ期 Ⅲ期と分かれています。
Ⅰ期+Ⅱ期+Ⅲ期=分娩所要時間
となります。
どうせだったらこのⅠ期 Ⅱ期 Ⅲ期も出して欲しい、
これが私の友人助産師5人にヒアリングしてほぼ一致した意見でした。
確かにそれはそうや!
よって本来の分娩所要時間にプラスしてⅠ期 Ⅱ期 Ⅲ期も出せるように項目を増やしました。

dbとの接続

マジでHTMLとCSS,JSだけで作っていたのですが、それではこのアプリをリリースした時にどれくらいユーザーがいてどれくら使われているか全くわかりません。
よって、簡単な会員登録をしてログインして頂き、計算した値をdbに飛ばす設計にしました。
Laravelとmysqlを使用して実装しました。
これでどれくらいの人が使っているのかわかるので、インフラを増強するタイミングもわかります。
あとは些細ですが会社の実績にもなるかなと思いました。

PWA

可能な限りネイティブアプリに近くしたい。
忙しい現場の看護職にいちいちsafari開いてお気に入りからアプリのURL探して、
もしくはいちいちQRコード読み取っては面倒くさ過ぎます。
でももちろん私にはネイティブアプリにするお金も技術もありません。
よってPWAを実装することにしました。
調べながらなんとか実装しました。
正直これが一番難しかった・・・
多分まだあまりわかっていない・・・

開発以外でしたこと

アプリ紹介のLP作成

これはお洒落でセンスの良い友人達がほぼしてくれました。
ダサい私にはこんなお洒落なサイト作れません!!
PWAの利用方法もわからない方がほとんどなので、ここもなるべく丁寧にわかりやすく説明したPDFを作ってもらいました。
https://nsmart.work/app1

実機テスト

私含めて3人の自宅にあるありとあらゆる端末で実機テストです。
幸いにもインターンで総合テストをどのように実施しているのか一部拝見できたので、
それを参考にスプレッドシートに項目をまとめて各自自宅で実機テストをしました。
そしてテストすればする程、細かい修正点が出る出る・・・
工程数少なくて優先順位高いものから潰しました

アプリリリースしての感想

自分が思っていたものが形になり現場の看護職に使ってもらえるかもしれないというのは本当に感慨深いものがあります。
まだ正直信じられません。
1年前の私では1ミリも想像できなかったことです。
ここまで一緒に手伝ってくれた友人に本当に心の底から感謝しています。

新たなサービス開発

この開発したアプリの発展バージョンの構成もあるのですがそれはもう少しお金がなんとかなったら作りたいなと思っています。
今は少しまた別のサービスを開発予定です(昨夜開発のキックオフテレカンしまました)。
と言っても現場で働く看護職の業務改善に特化したアプリです。

今回のアプリに比べるとかなり手が込んでいて正直どこまで実装できるかわかりません。
私含めてジーズアカデミーの同期3人、計4人でLaravelとvueで開発します。
今の我々でできる最高のプロダクトを作りたいと思っています。
もしかしたらそれはエンジニアの方からするとおもちゃかもしれません。
でも最高のおもちゃを作りたいと思います。

インフラやセキュリティ関連が弱弱なのと、
将来的には機械学習を導入したいのでそれを見越したdb設計ができるか・・・
その部分はジーズアカデミーを卒業した先輩やメンターの方にも少しご協力予定です。

最後に

長い文章ここまで読んで頂いてありがとうございました!!
色々中途半端でまだまだ未熟な私ですが、
いつも応援し支えてくれる周囲に感謝の気持ちを忘れず、
1日でも早く現場で疲弊している看護職の業務が楽になるようなサービスを提供できるよう、
今後も日々精進したいと思います。

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

【Laravel6.x】オートインクリメントの主キーを後から文字列型へ変えたい

環境

PHP laravel PostgreSQL
7.4.1 6.18.31 11.8

困ったこと

Laravelはデフォルトでサロゲートキーを採用しており、php artisan make:migrationで生成されるマイグレーションファイルの主キー(id)は初期値がauto incrementとなっています。

ここで、特定のテーブルで後からidを文字列型にしたい場合がありました。

テーブル定義変更のマイグレーションファイルを作成し、change()を使って以下のように記述したところ...

    public function up()
    {
        Schema::table('photos', function (Blueprint $table) {
            //型を変更
            $table->string('id')->primary()->change();
        });
    }

実行結果

$ Illuminate\Database\QueryException  : 
SQLSTATE[42P16]: Invalid table definition: 7 ERROR:  
multiple primary keys for table "photos" are not allowed (SQL: alter table "photos" add primary key ("id"))

どうやら、もともとのidとは別に文字列型のidを定義していると解釈され、「単一の主キーが複数設定されている」というエラーになるようです。

解決策

カラム定義を変更する前に、いったん既存の主キーを除去します。

    public function up()
    {
        Schema::table('photos', function (Blueprint $table) {
            // 主キーを除去
            $table->dropPrimary('id'); // ★追加

            //型を変更
            $table->string('id')->primary()->change();
        });
    }

参考

「インデックス除去」の項
https://readouble.com/laravel/6.x/ja/migrations.html

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

Laravel Socialiteを使用してソーシャルログイン を実装する

Laravel Socialiteを使用してソーシャルログインでの登録・ログイン機能を実装していきます。

Googleの設定

GoogleAPIs
上記にアクセスして、『プロジェクトを作成』を選択する

新しいプロジェクト

プロジェクト名: 適宜
場所: 組織なし
を入力して『作成』を押下

Google Cloud Platform

利用規約をチェックし『同意して続行』を押下

左上ハンバーガーメニュー/『APIとサービス』/『OAUTH同意画面』を選択する

OAuth同意画面

UserType: 外部を(適宜)選択し『作成』を押下

アプリケーション名: 適宜
『保存』を押下

サイドメニュー/『認証情報認証情報を作成』/『OAuthクライアントID』を選択する

OAuthクライアントIDの作成

アプリケーションの種類: 『ウェブアプリケーション』をチェック
名前:適宜
承認済みのリダイレクトURI: http://localhost/login/google/callback
『作成』を押下

OAuthクライアントを作成しましたと表示されるので下記のファイルへ追記する

laravel/.env
GOOGLE_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxx

うまく動作しない場合
APP_URL=http://localhost:10080などAPP_URLを細かく指定すると動作する可能性があります。

Laravel Socialiteをインストール

$ docker-compose exec workspace composer require laravel/socialite

configファイルを編集

laravel/config/services.php
 'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('APP_URL') . '/login/google/callback',
    ],

returnの中に上記を追記する

Googleへのリダイレクト処理

laravel/routes/web.php
Route::prefix('login')->name('login.')->group(function () {
    Route::get('/{provider}', 'Auth\LoginController@redirectToProvider')->name('{provider}');
    Route::get('/{provider}/callback', 'Auth\LoginController@handleProviderCallback')->name('{provider}.callback');
});
Route::prefix('register')->name('register.')->group(function () {
    Route::get('/{provider}', 'Auth\RegisterController@showProviderUserRegistrationForm')->name('{provider}');
    Route::post('/{provider}', 'Auth\RegisterController@registerProviderUser')->name('{provider}');
});

ログインコントローラーにアクションメソッドを追加

下記をコントローラー内に追記する

laravel/app/Http/Controllers/Auth/LoginController.php
use App\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Laravel\Socialite\Facades\Socialite;
// 省略 
public function redirectToProvider(string $provider)
    {
        return Socialite::driver($provider)->redirect();
    }
public function handleProviderCallback(Request $request, string $provider)
    {
        $providerUser = Socialite::driver($provider)->stateless()->user();

        $user = User::where('email', $providerUser->getEmail())->first();

        if ($user) {
            $this->guard()->login($user, true);
            return $this->sendLoginResponse($request);
        }
        return redirect()->route('register.{provider}', [
            'provider' => $provider,
            'email' => $providerUser->getEmail(),
            'token' => $providerUser->token,
        ]); 
    }

ログイン画面のBladeを編集

laravel/resources/views/auth/login.blade.php
<a href="{{ route('login.{provider}', ['provider' => 'google']) }}" class="btn btn-block btn-danger">
  <i class="fab fa-google mr-1"></i>Googleでログイン
</a>

ユーザー登録コントローラーにアクションメソッドを追加

下記をコントローラー内に追記する

laravel/app/Http/Controllers/Auth/RegisterController.php
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
// 省略
 public function showProviderUserRegistrationForm(Request $request, string $provider)
    {
        $token = $request->token;

        $providerUser = Socialite::driver($provider)->userFromToken($token);

        return view('auth.social_register', [
            'provider' => $provider,
            'email' => $providerUser->getEmail(),
            'token' => $token,
        ]);
    }

public function registerProviderUser(Request $request, string $provider)
    {
        $request->validate([
            'name' => ['required', 'string', 'alpha_num', 'min:3', 'max:16', 'unique:users'],
            'token' => ['required', 'string'],
        ]);

        $token = $request->token;

        $providerUser = Socialite::driver($provider)->userFromToken($token);
        $user = User::create([
            'name' => $request->name,
            'email' => $providerUser->getEmail(),
            'password' => null,
        ]);

        $this->guard()->login($user, true);

        return $this->registered($request, $user)
            ?: redirect($this->redirectPath());
    }
laravel/views/auth/register.blade.php
<a href="{{ route('login.{provider}', ['provider' => 'google']) }}" class="btn btn-block btn-danger">
  <i class="fab fa-google mr-1"></i>Googleで登録
</a>

以上でGoogleアカウントで登録・ログインができるようになりました。

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

Laravel + Vue.jsの導入方法

LaravelのBladeにVueコンポーネントを組み込む方法をまとめます。

環境

  • laradock
  • php: 7.3
  • Laravel: 7.2

Vue.jsをインストールする

laravel/package.json
"vue": "^2.6.11", 
"vue-template-compiler": "^2.6.11"
$ docker-compose exec workspace npm install

Vueコンポーネントを作成する

試しにハートボタンを作成します。

laravel/resources/js/components/SampleLike.vue
<template>
  <div>
    <button
      type="button"
      class="btn m-0 p-1 shadow-none"
    >
      <i class="fas fa-heart mr-1"
      />
    </button>
    10
  </div>
</template>

<script>
</script>

app.jsを編集

laravel/resources/js/app.js
import Vue from 'vue'
import Samplelike from './components/SampleLike'

const app = new Vue({
  el: '#app',
  components: {
    SampleLike,
  }
})

laravelの全画面で使用できるようにここで読み込みます。

Laravel Mixを編集

laravel/webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css')
    .version();

上記はresources/js/app.jsをトランスパイルしてpublic/jsのapp.jsというファイルで保存されるよというソースです。
.version()はトランスパイルされる時に毎回idを採番する役割ですが、これは同じidの古いjavascriptをブラウザキャッシュに残さないためです。

.gitignoreを編集

下記はjavascriptが編集される都度トランスパイルされてファイルが作成・更新されていくため、
管理対象から外しておきます。

laravel/.gitignore
/node_modules
/public/css # 追加
/public/hot
/public/js  # 追加
/public/mix-manifest.json # 追加

トランスパイルの実行

$ docker-compose exec workspace npm run watch-poll

DONE Compiled successfullyと表示されればトランスパイル成功です。
上記のコマンドはjavascriptが編集されたら自動でトランスパイルしてくれます。

app.blade.phpを編集

laravel/resources/views/app.blade.php
{{--省略--}}
<body>
  <div id="app"> {{--追加--}}
    @yield('content')
  </div> {{--追加--}}

  <script src="{{ mix('js/app.js') }}"></script> {{--追加--}}
{{--省略--}}
</body>

BladeにVueコンポーネントを組み込む

laravel/resources/views/posts/index.blade.php
<div class="card-body pt-0 pb-2 pl-3">
    <div class="card-text">
      <sample-like>
      </sample-like>
    </div>
  </div>

組み込むときはケバブケースで記述する。

これでハートボタンとダミーの10が表示でき、Vueコンポーネントを組み込めたかと思います。

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

【Laravel】ルートグループを定義して、まとまり感のあるルートを作成する

ルートグループは、ミドルウェアや名前空間のようなルート属性を、一括して複数のルートに適応させる手法です。Route::groupメソッドを使用します。

指定のミドルウェアをルートグループに適応させる

グループとしてまとめられた各ルートにミドルウェアを指定した例がこちら。

routes/web.php
Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // firstとsecondミドルウェアを使用
    });

    Route::get('user/profile', function () {
        // firstとsecondミドルウェアを使用
    });
});

Route::middlewareメソッドを使用してfirstミドルウェアとsecondミドルウェアを適応させることが記述されています。このRoute::middlewaregroupメソッドをチェーンする形で書いていきましょう。グループ内の各ルートにRoute::middlewareメソッドで指定したミドルウェアが適応されます。

このグループ内の各ルートは、firstミドルウェア、secondミドルウェアの順に実行されます。実行される順はRoute::middlewareメソッドに指定した配列の順序となります。

参考文献:https://readouble.com/laravel/7.x/ja/routing.html

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