ラベル Laravel の投稿を表示しています。 すべての投稿を表示
ラベル Laravel の投稿を表示しています。 すべての投稿を表示

2021年6月15日火曜日

Vueについて

システム構築にはクライアント側とサーバ側に分割し、サーバ側はAPI仕様が良い
このほうがつくりが良くて、デザインはどのようにでもできるメリットがある。

サーバ言語とクライアント言語

APIなので、サーバ側の言語はPython、Java、PHP、C#などどれでもよいのであろうが、現在はPHPを利用することが多い。
臨機応変でもあるし、フレームワークがしっかりしているのと、言語でかかるコストが安いのも魅力です

それに現在ではセキュリティ面でも強化が可能です。

クライアント側は、TypeScriptが主流になり、また、ReactとVueとの両方を比べると、Vueのほうが比較的利用しやすい
というより学習コストが比較的、かからない


デザインについて

従来からadminLTEを利用したBootStrapを利用してきたが、VueからVuefityを利用してメリットが大きく、Vuefityに変更して構築している
学習コストが比較的安い

※学習コストとは、覚えるまでの時間が他のものに比較している
 覚えやすいことは利用しやすいことにもなり、メリットが際立つ


エンジニアとして考えること

エンジニアは勉学の時間は取れないことが多い。
そのため比較的学習コストがかからないものを覚えると、次の応用ができる
1個のことを覚えるのも大変だろうが、複数のことを同時にやらないと、今後、新型コロナウィルスの影響が小さくなると、技術が大きく変わるのでエンジニアとしての武器を今のうちに増やしましょう

2021年6月5日土曜日

Laravel11:いくつもの技術の集約

 Laravel+Vueで構築していると

  • PHP
  • typescript
  • Javascript
  • そのた

もろもろの技術が重なっているので複雑になる。
慣れれば、いろいろなことができる

  • SNS認証
  • 暗号化と復号化
  • ファイルのアップロードダウンロード
  • 二段階認証

ただそれを覚えて応用するまでは時間が必要だ。


今後、これらを備忘記録していく予定です。

こうご期待


2021年5月22日土曜日

Laravel10:Vueの親子コンポーネント

親子コンポーネントについて

見本ソースを参照している

親子間のデータ連携

子供のコンポーネント

<template>
  <p>{{ foo }}</p> <!-- hoge -->
</template>

<script>
export default {
  props: [
    'foo'
  ]
}
</script>

親コンポーネント

<template>
  <child-component v-bind:foo="var" />
</template>

<script>
export default {
  data() {
    return {
      var: "hoge"
    }
  }
}
</script>

props 経由で受け取った foo の中身はリテラルではななく、親で定義されたオブジェクトのため、foo の値として hoge が出力される。

v-bindはリアクティブのために親子間で連携していることを意味します
下記も同様です。

<child-component :foo="var" />


子コンポーネントから親のイベントを呼出

親のコンポーネント

<template>
  <div>
    ボタンA<child-component v-on:call-parent="parentFunc" foo="1" bar="A" />
    ボタンB<child-component v-on:call-parent="parentFunc" foo="2" bar="B" />
  </div>
</template>

<script>
export default {
  methods: {
    parentFunc(foo, bar) {
      console.log(foo)
      console.log(bar)
    }
  }
}
</script>

子供のコンポーネント

<template>
  <button v-on:click="childFunc(foo, bar)" type="button">Button</button>
</template>

<script>
export default {
  props: [
    'foo',
    'bar'
  ],
  methods: {
    childFunc(foo, bar) {
      this.$emit('call-parent', foo, bar)
    },
  },
}
</script>

子コンポーネントから親のメソッドを実行するためには、子から $emit でイベントを発火させるのがポイントです。

template から直接イベントを発生させることができないため、クリックイベントを一旦 子コンポーネントのメソッド ( ここでは childFunc ) で受け取り、そのメソッド内でイベントを処理します。

親子間でデータ量が増えた場合はVUEXを使うのが最善。

今、思案しているところでした・・・

2021年5月7日金曜日

Laravel:migrationで複数テーブルの共通カラム定義を1か所に共通化する

migrationで作成日などの情報がいろいろなテーブルに定義する。

この場合、いちいち書くのは面倒であるし、間違いやすい。

共通に書く方法

macroとは、Laravelフレームワーク自体が提供しているクラスに好きなメソッドを追加することができる機能です。

例えば、

Requestクラスにユーザーエージェントを判定するメソッドを追加してみたり、

Carbonクラスに特定のフォーマットに変換するメソッドを追加してみたり、

Laravelのクラスに自由にメソッドを追加できます。


migrationで利用されているBlueprintクラスにこのmacroで機能追加することによって

共通カラムのコード共通化を行います。


Blueprintにmacro追加

Blueprintのmacroを定義するためにサービスプロバイダを新しく作ります。

makeコマンド実行。

php artisan make:provider BlueprintServiceProvider


app/Providers/BlueprintServiceProvider.php

    public function boot()
    {   // 共通カラム
        Blueprint::macro('systemColumns'function () {
            $this->boolean('isactive')
                ->comment('アクティブ区分')->default(true);            
            $this->timestamp('created_at')->comment('作成日')->nullable();
            $this->unsignedBigInteger('created_by')
                ->comment('作成ユーザ')->nullable();
            $this->timestamp('updated_at')->comment('更新日')->nullable();
            $this->unsignedBigInteger('updated_by')
                ->comment('更新ユーザ')->nullable();
            $this->timestamp('deleted_at')->comment('削除日')->nullable();
            $this->unsignedBigInteger('deleted_by')
                ->comment('削除ユーザ')->nullable();
        });
    }

config/app.php

    'providers' => [

       中略

        App\Providers\BlueprintServiceProvider::class,

    ],

migrationでmacroを利用

    public function up()
    {
        Schema::create('clients'function (Blueprint $table) {
            // base
            $table->uuid('id')->comment('id')->primary();
            $table->string('value'100)->comment('略称');
            $table->string('name'150)->comment('名前');
            $table->string('description'255)->comment('説明')->nullable();

            // 管理者:1社なら使わない


            // その他情報はinfoへ


            // 共通カラム定義
            $table->systemColumns(); 

            // 外部キー
            $table->foreign('created_by')->references('id')->on('users');
            $table->foreign('updated_by')->references('id')->on('users');
            $table->foreign('deleted_by')->references('id')->on('users');
        });
    }


2021年4月18日日曜日

PHP:Laravel-08

 PHP:Laravel-07の参考URLをもとにしていろいろと変更していきます。

1.CSSで、Bootstrapを利用します

npm install bootstrap-vue

※ Composerは、PHPの依存関係を管理するツールです。 Packagistを使用して依存関係に関する情報を取得し、適切にインストールします。
NPMはNodeエコシステムの一部であり、主にNode.jsアプリケーションの依存関係を管理するために構築されました。

2.設計関係の確認

下記の設計関係のドキュメントが必要です。理由はプロダクトの際に必要になる情報です。

常に製造の品質を上げるときは設計が重要です。設計することによってテスト内容が把握でき、結果の保証します。

参考にしているURLには設計関係のものがありますので参考にして、今後、追加と修正をしていきます。

  • 画面設計
  • 画面遷移図
  • データベース設計
  • URL設計

3.bootstrap-vueの利用

■bootstrap-vueを利用しているので、これを利用できるようにします

resources/app.js に下記を追加

・・・

import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// BootstrapVueのプラグインの利用
Vue.use(BootstrapVue)
Vue.config.productionTip = false

・・・

4.ログイン関係の修正

クライアント側での動きの制御がVue、Vuex

サーバ側での動きはLaravelのAPI

この動きを設計書に基づき、プロダクトを見てみましょう

■resources/app.js

import './bootstrap'
import Vue from 'vue'
import router from './router'
import store from './store'
import App from './app.vue'

import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// BootstrapVueのプラグインの利用
Vue.use(BootstrapVue)
Vue.config.productionTip = false

const createApp = async () => {
  await store.dispatch('auth/currentUser')

  new Vue({
    el: '#app',
    router,
    store,
    components: { App },
    template: '<App />'
  })
}

createApp()

App.vue

初期画面構成となります

router.js

VueRouterプラグインを使用したルーティングを行います

コンポーネントと紐づけをしています 

 <RouterView />が利用可能になる

 const routes = [

 {
    path: '/',
    component: PhotoList,
    props: route => {
      const page = route.query.page
      return { page: /^[1-9][0-9]*$/.test(page) ? page * 1 : 1 }
    }
  },
  {
    path: '/photos/:id',
    component: PhotoDetail,
    props: true
  },
  {
    path: '/login',
    component: Login,
    beforeEnter(to, from, next) {
      if (store.getters['auth/check']) {
        next('/')
      } else {
        next()
      }
    }
  },
  {
    path: '/500',
    component: SystemError
  },
  {
    path: '*',
    component: NotFound
  }
]

■resources/store/index.js

resources/app.js が参照しているresources/store配下のjsを見てみましょう

import Vue from 'vue'
import Vuex from 'vuex'

import auth from './auth'
import error from './error'
import message from './message'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    auth,   // 認証関係
    error,  // エラー関係
    message // メッセージ関係
  }
})

export default store

storeで利用できるものは

  •  auth,   // 認証関係
  •  error,  // エラー関係
  •  message // メッセージ関係

となります

■resources/store/auth.js

ソースに詳細を記入しています

import { OK, CREATED, UNPROCESSABLE_ENTITY } from '../util'

const state = {
  user: null,
  apiStatus: null,
  loginErrorMessages: null,
  registerErrorMessages: null
}

/**
 * ゲッターの定義 
 * storeを参照できる
 */
const getters = {
  check: state => !!state.user,
  username: state => state.user ? state.user.name : '',
}

/**
 * ミューテーションの登録
 * Vuex のストアの状態を変更できる唯一の方法は、
 * ミューテーションをコミットすること
 */

const mutations = {
  setUser(state, user) {
    state.user = user
  },
  setApiStatus(state, status) {
    state.apiStatus = status
  },
  setLoginErrorMessages(state, messages) {
    state.loginErrorMessages = messages
  },
  setRegisterErrorMessages(state, messages) {
    state.registerErrorMessages = messages
  }
}

/**  
 * アクションの登録
 */
const actions = {
  // 会員登録系
  async register(context, data) {
    // context.commit('setApiStatus', null):引数はtypeと値
    context.commit('setApiStatus'null)
    // サーバに対してAPI要求
    const response = await axios.post('/api/register', data)

    // サーバのレスポンス情報で処理を分けている
    if (response.status === CREATED) {
      context.commit('setApiStatus'true)
      context.commit('setUser', response.data)
      return false
    }

    context.commit('setApiStatus'false)
    if (response.status === UNPROCESSABLE_ENTITY) {
      context.commit('setRegisterErrorMessages', response.data.errors)
    } else {
      context.commit('error/setCode', response.status, { root: true })
    }
  },

  // ログイン系
  async login(context, data) {
    context.commit('setApiStatus'null)
    const response = await axios.post('/api/login', data)

    if (response.status === OK) {
      context.commit('setApiStatus'true)
      context.commit('setUser', response.data)
      return false
    }

    context.commit('setApiStatus'false)
    if (response.status === UNPROCESSABLE_ENTITY) {
      context.commit('setLoginErrorMessages', response.data.errors)
    } else {
      context.commit('error/setCode', response.status, { root: true })
    }
  },

  // ログアウト
  async logout(context) {
    context.commit('setApiStatus'null)
    const response = await axios.post('/api/logout')

    if (response.status === OK) {
      context.commit('setApiStatus'true)
      context.commit('setUser'null)
      return false
    }

    context.commit('setApiStatus'false)
    context.commit('error/setCode', response.status, { root: true })
  },

  // ログインユーザーチェック
  async currentUser(context) {
    context.commit('setApiStatus'null)
    const response = await axios.get('/api/user')
    const user = response.data || null

    if (response.status === OK) {
      context.commit('setApiStatus'true)
      context.commit('setUser', user)
      return false
    }

    context.commit('setApiStatus'false)
    context.commit('error/setCode', response.status, { root: true })
  }
}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

URLから、dispathされ、actionに渡されて、actionが起動、そして、APIを起動します

ログインを見てみましょう

  // ログイン系
  async login(context, data) {
    context.commit('setApiStatus'null)
    const response = await axios.post('/api/login', data)

    if (response.status === OK) {
      context.commit('setApiStatus'true)
      context.commit('setUser', response.data)
      return false
    }

    context.commit('setApiStatus'false)
    if (response.status === UNPROCESSABLE_ENTITY) {
      context.commit('setLoginErrorMessages', response.data.errors)
    } else {
      context.commit('error/setCode', response.status, { root: true })
    }
  },

context.commit('setApiStatus', null) ミューテーションを呼び出します

 例では setApiStatus に null をセットします

const response = await axios.post('/api/login', data)

 例ではloginコントローラが呼び出される

画面については次回になります


2021年4月15日木曜日

PHP:Laravel-07

Vueを利用している参照としているのは、

参考にしているもの

1.インストール

composer create-project laravel/laravel プロジェクト名

cd プロジェクト名

php artisan serve

これでローカルでWeb画面が開けばインストール完了

2.Laravel8 (laravel/ui)でのLogin機能の実装用

LaravelはJavaScriptやCSSプリプロセッサの使用を規定してはいませんが、開発時点の元としてほとんどのアプリケーションで役立つだろうBootstrapやReact、Vueを提供しています。これらのフロントエンドパッケージをインストールするため、LaravelはNPMを使用しています。

Laravelが提供するBootstrapとVueのスカフォールドは、Composerを使いインストールする

>composer require laravel/ui

 laravel/uiパッケージをインストールできたら、ui Artisanコマンドを使いフロントエンドのスカフォールドをインストールします。

// 基本的なスカフォールドを生成

>php artisan ui vue

// ログイン/ユーザー登録スカフォールドを生成

>php artisan ui vue --auth

3.Vue Router

>npm install --save-dev vue-router


下記は必要事項のみを記載している。
自ら勉学し、苦労することによって知恵が身につきます
参考のURLでは動かない時があるので、そこは自ら学習して起動するようにしましょう

4.マイグレーション

■テーブルの作成

>php artisan make:migration create_photos_table --create=photos

>php artisan make:migration create_likes_table --create=likes

>php artisan make:migration create_comments_table --create=comments


■マイグレーション

>php artisan migrate


5.モデルの作成

>php artisan make:model Photo

>php artisan make:model Comment


6.リクエストの作成

>php artisan make:request StoreComment

>php artisan make:request StorePhoto  


7.シンボリックリンクの作成

アップロードしただけでは、ブラウザからアップロードしたファイルを閲覧することはできません。ブラウザからサーバ上のファイルにアクセスするためには、Laravelインストールディレクトリの下にあるpublicディレクトリの下に保存する必要があります。しかし、アップロードしたファイルは/storage/appの下にあるためアクセスすることができません。/publicディレクトリと/storage/appディレクトリとの間でリンクを持たせる必要があります。

Laravelではリンクを貼る設定を行う機能も備えています。

php artisan storage:linkを実行します。

>php artisan storage:link

基本的な作成はこれまでと変わらず。

今後は必要なことや、技術的なこと記載させていただきます。


2021年4月13日火曜日

PHP:Laravel-06

今回は参照としているのは、
参考にしているもの

学習のポイントはエラーの出し方やログイン情報です。

DB内のテーブル情報を作り直す便利なコマンドは

>php artisan migrate:fresh

項目などを追加したり、削除するにも便利です

1.インストール

composer create-project laravel/laravel プロジェクト名

cd プロジェクト名

php artisan serve

これでローカルでWeb画面が開けばインストール完了

2.Laravel8 (laravel/ui)でのLogin機能の実装用

>php artisan ui bootstrap --auth

3.フロントエンドパッケージインストール

laravel/uiのvueベースをインストールした際に、必要なフロントエンドパッケージがpackage.jsonに追記されました。

bootstrap、jqueryなどが追記されています。

これらのパッケージをインストールします。

>npm install

4.フロントエンドビルド実行

必要なパッケージは全てインストール完了したので、最後にフロントエンドソースコードをビルドしてみます。

これは毎回必要です

>npm run dev

このコマンドを実行することで、Laravel Mixのビルド処理が実行され、コンパイルされたjs、cssが/public/js public/cssに出力されます。

日本語の処理は参考としているURLをご覧ください。

5.ログイン状態の取得

Auth クラスの check メソッドでログインしているかどうかを確認することができます 。
アクセスしたユーザーがログインしていれば Auth::check() は true を返し、ログインしていなければ false を返します。

テンプレートではこの Auth::check() を利用してログインしていた場合の要素とログインしていない場合の要素を出し分けています。

@if(Auth::check())

  <!-- ログインしていた場合 -->

@else

  <!-- ログインしていない場合 -->

@endif

Auth::check() とは逆の働きをする guest メソッドも存在します。つまり Auth::guest() はユーザーがログインしていない場合に true を返します。


ログインユーザーの取得

Auth::user() でログイン中のユーザーを取得できます。返却値はログインユーザーの情報が入った User モデルのインスタンスです。


Auth::check() や Auth::user() はコントローラーなどでも使用することができます。実際に後述の処理でも使用します。便利なメソッドですので覚えておきましょう。


ログアウト

次にログアウト処理を実装します。

@if(Auth::check())

  <script>

    document.getElementById('logout').addEventListener('click', function(event) {

      event.preventDefault();

      document.getElementById('logout-form').submit();

    });

  </script>

@endif

6.ページに認証を求める

アプリケーションの利用にログイン認証を求める機能を実装します。
ログインしないとフォルダやタスクの作成・閲覧ページにアクセスできないようにします。

ページに認証を求める処理はミドルウェアを用いて実現します。ミドルウェアとは、ルートごとの処理に移る前に実行されるプログラムでした。認証状態の確認はさまざまなルートに共通して実行したい処理なのでミドルウェアで実現するのに適しています。

認証を求めるミドルウェアはデフォルトで用意されていますので、routes/web.php でルートにミドルウェアを適用します。

Route::group(['middleware' => 'auth'], function() {
    # ホームページ機能
    Route::get('/'
        'App\Http\Controllers\HomeController@index')->name('home');

    # フォルダ作成機能
    Route::get('/folders/create'
        'App\Http\Controllers\FolderController@showCreateForm')
            ->name('folders.create');
    Route::post('/folders/create'
        'App\Http\Controllers\FolderController@create');

    Route::group(['middleware' => 'can:view,folder'], function() {
        # タスクリスト機能
        Route::get('/folders/{folder}/tasks'
            'App\Http\Controllers\TaskController@index')->name('tasks.index');      

ミドルウェアは 'auth' という名前で指定されていますが、app/Http/Kernel.php というファイルに実際のクラスと名前の定義があります。

protected $routeMiddleware = [

    'auth' => \App\Http\Middleware\Authenticate::class,

    // ...

];

このミドルウェアはアクセスしたユーザーの認証状態をチェックして、ログインしていたらそのままコントローラーメソッド(または次のミドルウェア)に処理を渡します。ログインしていなければログイン画面にリダイレクトさせます。

7.ログイン前のみ閲覧できるページ

認証「されていない」ことを確かめるミドルウェアを編集します。

ログインページや会員登録ページはログインしていないときにだけアクセスできるべきですね。すでにログインしているユーザーがそのようなページにアクセスすることを防ぐミドルウェアは用意されていますが、一部編集する必要があります。

app/Http/Middleware/RedirectIfAuthenticated.php 

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/');
        }

        return $next($request);
    }

RedirectIfAuthenticated ミドルウェアは会員登録コントローラーやログインコントローラーのコンストラクタで適用されています。

RegisterController.php

public function __construct()

{

    $this->middleware('guest');

}

LoginController.php

public function __construct()

{

    $this->middleware('guest')->except('logout');

}

'guest' という名前は Kernel.php で定義されています。

Kernel.php

protected $routeMiddleware = [

    // ...

    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,

    // ...

];

参考にすべき情報があると、知っているだけで知識と知恵が備わります。
今回学習すべき点はルーティングやログインに関してです。
エラーの出し方がわかると、次にVUEと合体させて学習し、実務に活かしましょう。


2021年4月12日月曜日

PHP:Laravel-05

Vueと同時にしていると、Laravel自体の特徴がうまく使えないので、
改めてプロジェクトを作成することにする

まずは設計

1.機能設計

ToDo アプリが持つ機能の箇条書きは以下の通りです。

  • ユーザーはタスク(ToDo)を作成することができる。
  • タスクはタイトル、期限日、状態を持つ。
  • タスクの状態とは、「未着手」「着手中」「完了」の3種類である。
  • ユーザーはタスクを一覧表示することができる。
  • ユーザーはタスクのタイトル、期限日、状態を編集することができる。
  • タスクはフォルダに分類して管理する。
  • そのため、ユーザーはフォルダを作成することができる必要がある。
  • また、ユーザーはフォルダの一覧を表示できる必要がある。
  • ユーザーごとにアカウントを持ち、ログインしたユーザーは自分のフォルダおよびタスクだけを閲覧または編集することができる。
  • ユーザーはパスワードを忘れた場合には再登録することができる。

2.画面設計

画面は省略

2.1.タスク一覧ページ

出入力

項目出入力
フォルダ タイトル出力
タスク タイトル出力
タスク 状態出力
タスク 期限日出力

アクション

項目種類働き
フォルダ作成リンクフォルダ作成ページに遷移する。
タスク作成リンクタスク作成ページに遷移する。
タスク編集リンクタスク編集ページに遷移する。

2.2.フォルダ作成ページ

出入力

項目出入力
フォルダ タイトル入力

アクション

項目種類働き
送信ボタンフォルダを新規作成し、タスク一覧ページに遷移する。

2.3.タスク作成ページ

出入力

項目出入力
タスク タイトル入力
タスク 状態入力
タスク 期限日入力

アクション

項目種類働き
送信ボタンタスクを新規作成し、タスク一覧ページに遷移する。

2.4.タスク編集ページ

出入力

タスク編集ページでは登録済みの情報を画面に出力しておく必要もあります。

項目出入力
タスク タイトル出力
タスク 状態出力
タスク 期限日出力
タスク タイトル入力
タスク 状態入力
タスク 期限日入力

アクション

項目種類働き
送信ボタンタスクを編集し、タスク一覧ページに遷移する。

2.5.ヘッダー

画面をまたいで共通で定義されるパーツもあるでしょう。

今回はヘッダーナビゲーションがすべての画面に共有して配置されます。

出入力

項目出入力
アプリ名出力
ユーザー名出力

アクション

項目種類働き
ログアウトリンクログインしている時のみ表示する。
ログアウト処理を行い、ログイン画面に遷移する。
会員登録リンクログインしていない時のみ表示する。
会員登録ページに遷移する。
ログインリンクログインしていない時のみ表示する。
ログインページに遷移する。

3.URL設計

URLメソッド処理
/folders/{フォルダID}/tasksGETタスク一覧ページを表示する。
/folders/createGETフォルダ作成ページを表示する。
/folders/createPOSTフォルダ作成処理を実行する。
/folders/{フォルダID}/tasks/createGETタスク作成ページを表示する。
/folders/{フォルダID}/tasks/createPOSTタスク作成処理を実行する。
/folders/{フォルダID}/tasks/{タスクID}/editGETタスク編集ページを表示する。
/folders/{フォルダID}/tasks/{タスクID}/editPOSTタスク編集処理を実行する。

4.テーブル定義

フォルダテーブル

カラム論理名カラム物理名型の意味
IDidSERIAL連番(自動採番)
タイトルtitleVARCHAR(20)20文字までの文字列
作成日created_atTIMESTAMP日付と時刻
更新日updated_atTIMESTAMP日付と時刻

タスクテーブル

カラム論理名カラム物理名型の意味
IDidSERIAL連番(自動採番)
フォルダIDfolder_idINTEGER数値
タイトルtitleVARCHAR(100)100文字までの文字列
状態statusINTEGER数値
期限日due_dateDATE日付
作成日created_atTIMESTAMP日付と時刻
更新日updated_atTIMESTAMP日付と時刻