LaravelとNUXTを使ったプロジェクトの準備

Laravel側

プロジェクトの作成

laravel new ${プロジェクト名}

laravel new synapse_laravel

バージョンを指定する場合

$ composer create-project ”laravel/laravel=6.*” synapse_laravel

サーバーを起動

cd synapse_laravel
php artisan serve

API作成

routes/api.phpファイルを編集

Route::get('/', function() {
    return 'helloworld';
});

http://localhost:8000/apiに接続するとhelloworldと表示

NUXT側

プロジェクトの作成

npm create-nuxt-app ${プロジェクト名}

npx create-nuxt-app synapse_nuxt

@nuxtjs/axiosの導入

cd synapse_nuxt
npm install --save @nuxtjs/axios

nuxt.config.jsファイルを編集してmodules"@nuxtjs/axios"を追記する

modules: [
  "@nuxtjs/axios"
],

サーバーを起動

yarn run dev

http://localhost:3000/に接続するとロゴの下にsynapse_nuxtと表示

API使用

page/index.vueファイルを編集

<template>
  <div class="container">
    <div>
      <Logo />
      <h1 class="title">
        {{ data }} //synapse_nuxtを編集
      </h1>
      // 省略
    </div>
  </div>
</template>

<script>
export default {
  async asyncData(app) { //ページコンポーネントがローディングされる前に呼びされる
    const data = await app.$axios.$get('http://localhost:8000/api')
    return {
      data
    }
  }
}
</script>

http://localhost:3000/に接続するとロゴの下にhelloworldと表示されていたら成功!!

リーダブルコード第3章「誤解されない名前」の輪読会をした備忘録と感想

毎週月曜日に「リーダブルコード」の輪読会を行っています。 今回は3章を終えた感想や備備忘録を書きます。 3章は、名前が「他の意味と間違えられることはないだろうか?」というテーマです。

誤解されない名前

英語がネイティブでない自分にとって、英語のニュアンスを掴むことは難しいです。 たとえばfilterという単語。 辞書をひくと

〈水・ガスなど〉をろ過する, こしてきれいにする; …をろ過して取り除く; 〈光・音など〉の一部を除去[カット]する, 遮断する; 〈不要・有害なもの〉を除去する; 〈人・物〉を取捨選択する

とありますが、この説明を見ると「除外する」という意味がありそうです。 しかし、phparray_filterTRUEのものを配列で返します。 つまり、「選択をする」という意味をもっています。

このように英語の単語は曖昧なニュアンスを含んでいるので、「選択をする」という意味で名前をつけるならばselect()とすることで、誤解のない名前になります。

lengthという単語も、どういう使われ方をしているかで名前を付けます。 最大の長さという意味で使うのであればmax_lengthとします。 さらにlengthという単語自体も曖昧で、「バイト数」なのか「文字数」なのか、はたまた「単語数」なのか? 文字数という意味で使うのであればmax_charとするのが良さそうです。

正直、ここまでは考えていなかったです・・。 いろんな解釈ができる名前は避け、明確な名前をつけましょう。

限界値と範囲と包含/排他的範囲

「以上」か「より大きい」か?「以下」か「未満」か? この違いで起こるエラーを「off_by_oneエラー」というようです。

off_by_oneエラーを避けるには「>=」と「>」に注意するというのもありますが、誤解されない名前をつけることでも避けることができます。 商品カートの上限値であればCART_TOO_BIG_LIMITという名前ではなく、限界値を含むMAX_ITEMS_IN_CARTとつけるべきです。

範囲を指定するときはfirstとlastを使うというのは馴染みが深いです。

print integer_range(start=2, stop=4)

「2, 3」ではなく「2, 3, 4」を表示したいのであれば、(start, stop)ではなく(first, last)を使うというものです。

包含/排他的範囲にはbeginとendを使う。

PrintEventsInRange(“OTC 16 12:00am”, “OTC 17 12:00am”)
PrintEventsInRange(“OTC 16 12:00am”, “OTC 16 11:59.9999pm”)

うーん。。こちらは馴染みがないですが、意識しておかないとついstartとかlastとかを使ってしまいそうですね。

ブール値の名前

ブール値の頭にisやhasをつけるのもすでにできていることで、とくに議論はなかったです。 ブール値では否定形は避けるというのも納得です。というか書いててわかりにくいですよね。

× bool disable_ssl = false;
○ bool use_ssl = true;

この項目は共感しやすかったです。

ユーザの期待に合わせる

getsizeは「軽量アクセサ」が期待されるというのは、今までまったく意識してこなかったです。 ふつうにDBから取得するときはgetくらいの意識しかなかったですね。 全件取得のときとかは気をつけないとですね。

まだまだ、馴染みがないことも多く、経験の無さを実感します。 とくにユーザが期待することとかは、周りの同僚も意識していなく、結局はチーム内で共通の認識があれば良いねという結論に達しました。 ただ、このような認識の違いがあるということはしっかり覚えておかないと、別のプロジェクトに行ったとき苦労しそうだなと思いました。

【JavaScript】canvasで画像上に座標を指定してブロック枠を描画する方法

  • Vue.jsを使用
  • 画像を描画
  • 画像上に座標を指定してブロック枠を描画

コード

<template>
  <div>
    <!-- canvas要素を配置、サイズ、refを指定 -->
    <canvas ref="canvas" :width="canvasWidth" :height="canvasHeight" />
  </div>
</template>
<script>
export default {
  data() {
    return {
      canvasWidth: 400,
      canvasHeight: 800
    }
  },
  computed: {
    // 画像のURLを取得しています
    imageUrl() {
      return this.$store.getters["images/getImageUrl"]
    },
    // ブロックの各頂点の座標を取得しています
    coordinate() {
      retun this.$store.getters["coordinates"/getCoordinate"]
    }
  },
  async mounted() {
    // DOMが生成されている必要があるのでmounted内で呼び出し
    await this.getImageUrl()
    this.draw()
  },
  methods: {
    async getImageUrl() {
      await this.$store.dispatch(
        "images/getImageUrl",
      )
    },
    draw() {
      // * $refsでcanvas要素を取得
      const canvas = this.$refs.canvas
      // 平面に描画するので2d
      const ctx = canvas.getContext("2d")
      // img要素を作成
      const img = document.createElement("img")
      // src属性にURLを指定
      img.src = this.imageUrl

      // 画像の読み込みが終わったあとに描画したいのでloadイベントのあとで処理
      img.addEventListener("load", () => {
        // 元の画像の横幅を取得
        const originalWidth = img.naturalWidth
        // 縮尺を計算
        const reducedScale = this.canvasWidth / originalWidth
        // 座標空間の変形
        // 変形を適用する場合には、先に変形を指定してから図形を描画するという順序になります。
        // 変形を後から指定しても、先に描画した図形には変形が適用されないので注意してください。
        ctx.scale(reducedScale, reducedScale)

        // 画像の描画、img要素と描画したい位置を指定
        ctx.drawImage(img, 0, 0)

        // 線の太さを指定
        ctx.lineWidth = 5
        // 線の色を指定
        ctx.strokeStyle = "#0f0"

        // 各頂点を取得
        cost xMax = this.Coordinate.x_max
        cost xMin = this.Coordinate.x_min
        cost yMax = this.Coordinate.y_max
        cost yMin = this.Coordinate.y_min
        // 新しいパスを開始
        ctx.beginPath()
        // パスの始点を移動
        ctx.moveTo(xMin, yMin)
        // どこまで線を引くか指定、右に移動
        ctx.lineTo(xMax, yMin)
        // 下に移動
        ctx.lineTo(xMax, yMax)
        // 左に移動
        ctx.lineTo(xMin, yMax)
        // パスを閉じる
        ctx.closePath()
        // 線を描画
        ctx.stroke()
      })
    }
  }
}
</script>

【Laravel】インストール時のエラー、zip展開できない

$ composer global require laravel/installer

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

  Problem 1
    - laravel/installer v3.0.1 requires ext-zip * -> the requested PHP extension zip is missing from your system.
    - laravel/installer v3.0.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.
    - Installation request for laravel/installer ^3.0 -> satisfiable by laravel/installer[v3.0.0, v3.0.1].


Installation failed, deleting ./composer.json.

laravel/installerPHPのzipの拡張機能を必要としているよ。

でもあなたのsystemにはそれが無いじゃない。

と言っているようです。

どうやらMacにプリインストールされているPHPではzipを展開するext-zipが入っていないらしい。

$ php --ri zip
Extension 'zip' not present.

たしかに無い。

HomebrewでPHPを再インストールします。

$ brew install php@7.3

パスを通します。

$ brew link php@7.3

ターミナルを再起動させて再度Laravelのインストールを行います。

$ composer global require laravel/installer

無事インストールされました。

`chmod`は`chage mode`という意味

$ chmod a+x /usr/local/bin/composer

chmodとはLinaxコマンドの一つ。

change modeという意味。パーミッションを設定する。

aすべての権限

x実行権限

a+xすべてのユーザに実行権限を与える

これで、いつでもcomposerを呼び出すことができる。

画像アップロード、プレビュー、取り消し

<%= link_to blogs_path, remote: true, onClick: "deleteNewMarker();" do %>
  <%= fa_icon 'chevron-left' %>
<% end %>

<%= form_with(model: blog) do |form| %>
  <div class="field">
    <%= form.text_area :content, class: 'form-control', id: 'text', placeholder: t(%q[What's happening?]) %>
  </div>

  <div class="field test">
    <%= form.label :image %>
    <%= form.file_field :image, id: 'image_form', class:'form-control' %>
  </div>

  <div class="blogs_image" id="blog_form_image">
    <%= image_tag blog.image.url, id: 'image_preview'%>
  </div>

  <div id="image_clear" class="btn btn-link" style="display: none;">取り消し</div>

  <%= form.hidden_field :lat %>
  <%= form.hidden_field :lng %>

  <div class="actions">
    <%= form.submit class: 'btn btn-primary pull-right', id:"submit" %>
  </div>
<% end %>

<script>
  $(function() {
    if ( $('#text').val().length == 0 ) {
      $('#submit').attr('disabled', 'disabled');
    }
    $('#text').bind('keydown keyup keypress change', function() {
      if ( $(this).val().length > 0 ) {
        $('#submit').removeAttr('disabled');
      } else {
        $('#submit').attr('disabled', 'disabled');
      }
    });
  });

  $(function() {
    function readURL(input) {
        if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
    $('#image_preview').attr('src', e.target.result);
        }
        reader.readAsDataURL(input.files[0]);
        }
    }
    $("#image_form").change(function(){
        readURL(this);
    });
  });

  $(function() {
    $('input[type=file]').change(function() {
      $('#image_clear').show();
    });

    $('#image_clear').click(function() {
      $('input[type=file]').val('');
      $(this).hide();
      $('#blog_form_image').html("<%= j(image_tag blog.image.url, id: 'image_preview')%>");
    });
  });
</script>

【Rails】flashを使う

<% if notice %>
  <div class="alert alert-info role="alert">
    <%= notice %><button type="button" class="close" data-dismiss="alert">&times;</button>
  </div>
<% end %>

<% if alert %>
  <div class="alert alert-danger" role="alert">
    <%= alert %><button type="button" class="close" data-dismiss="alert">&times;</button>
  </div>
<% end %>
$(function(){
  setTimeout("$('.alert').fadeOut('slow')", 3000);
});
.alert {
  text-align: center;
  position: fixed;
  width: 100%;
  top: 0;
}