みなさんNuxt.jsとLaravelはお好きですか?
僕は大好きです。
まだNuxt.jsもLaravelも初心者ですが、その2つを使って開発しているCMSについての紹介と技術周りでハマった点、使ったものなどを紹介させていただきます。
自己紹介
遅れましたが自己紹介を少し。
現在中学3年生でプログラミングは小学4年生からはじめました。
今はLaravelやNuxt.js、ゲーム周りだとUnityやScratchなどを使っています。
最近ポートフォーリオを作ってみたので良ければ見てみてください。
では本題に移ります
CMSとは?
CMSとはContent-Management-Systemの略です。
代表的なものだと
- Wordpress
- OctoberCMS
などが挙げられます。
今回僕が開発しているCMSは以下の機能を持っています。
- 記事投稿
- ファイル管理
- タグ
- SPA
なぜ作ろうと思ったか
主な理由は以下のとおりです
- 自分で開発したCMSなら記事などの投稿のモチベーションが保持できる
- 管理画面やブログがSPAなCMSがほしかった
- 腕試し
実際に開発しているもの
ロゴは適当です。
記事はMarkdownで記述します。
そのMarkdownの機能について少し紹介します。
MarkdownのコードはQiitaみたいにファイル名を指定できるようにしています。
変換後
Markdownも少し拡張していてアラートを出せるようにしています。
変換後技術周りの紹介
今回使ったものは以下のとおりです
- エディタ
- PhpStorm
- WebStorm
- バックエンド
- Laravel
- Laravel Homestead
- フロントエンド
- Nuxt.js
- Vuetify
最近Vuetifyというマテリアルデザインフレームワークを気にっています。
コンポーネントが豊富で機能も優秀すぎる。
おすすめなのでぜひ使ってみてください。
Vuetify
Nuxt.jsは普段僕はVue.jsを使っていたのですが、Nuxt.js知ったときはかなり衝撃的でした。
とにかく楽。コマンド打つだけで静的サイトを生成したりSSRもできたりで便利すぎる。
こちらもおすすめです。
Nuxt.js
そいういえば最近Nuxt.jsのトップページの下の方にすぐにNuxt.jsを試せる[CodeSandbox.io](https://codesandbox.io/)の埋め込みが追加されましたね。 これはこれで嬉しい。ちなみにNuxt.jsでカウンターアプリやVue.jsで「カップ麺タイマー」などを作ったので良ければ見てみてください。
Laravel
最近Ruby on Railsが熱いですがLaravelも再び熱くなってきています。 PHPをやり始めて2年間、そもそも「Webフレームワーク」というものの存在を知らなくて初めてLaravelを知ったときは今までの苦労はなんだったのかと... それぐらい簡単に色々なことができるPHPフレームワークなのでぜひ使ってみてください。ちなみに今回io-cmsを開発するにあたってLaravel HomesteadというVagrantのBoxを使用しました。
やっぱり仮想環境はいいですね。
つまりポイント
今回このio-cmsの開発にあたって詰まったポイントをいくつか紹介したいと思います。
許可
一番悩んだのがどうやってログインしているユーザーだけが管理画面で操作をできるようにするかです。
基本的にNuxt.jsからLaravelのAPIを叩くのでこのときに認証情報をどうやって送ればいいのかぐぐってみるとちょうどいいものが見つかりました。
どうやらJWTという方法があるらしいのでそのJWTをLaravelで使うためのライブラリを探してみました。
https://github.com/tymondesigns/jwt-auth
このライブラリが一番使いやすそうでした。
いろいろ設定ファイルをいじってNuxt.js側でcookieにトークンを格納するロジックを書くだけで実現できてしまいました。
axios地獄
ちょっとふざけてるように感じますが、これもかなり悩まされました。
例えば以下のようなコードがあるとします。
const state = {
methods: {
async new () {
this.article = {
id: -1,
title: '',
body: '',
}
},
async get () {
this.article = await this.$axios.$get('article/' + this.id)
},
save () {
this.$axios.$put('article/' + this.article.id, this.article)
},
delete () {
this.$axios.$delete('article/' + this.id)
}
}
}
今回Nuxt.jsからLaravelへAjaxするにあたってaxiosというライブラリを用いました。
上のコードだとそのaxiosの呼び出しであるthis.$axiosがたくさん見られます。
このコードの問題点は以下のとおりです。
- 一回のリクエストのコードが長い
- 他のプログラムでも同じコードを書いたときに APIの仕様変更をした際に修正する箇所が多い
とりあえずORMみたいなAPIでリクエストできたら見やすくなるかなーなんて思って以下のようにリクエストできるようにしました。
ただしRESTfulAPI前提です。
const state = {
methods: {
new () {
this.article = new Article({
title: '',
body: ''
})
},
async get () {
this.article = await Article.find(this.id)
},
save () {
this.article.save()
},
delete () {
this.article.delete()
}
}
}
ActiveRecordっぽい感じですね。
オブジェクトベースのAPIでLaravelにリクエストできるようにしました。
すこし長いですがこのArticle
オブジェクトは以下のようなコードです。
import Model from './Model'
import MediaFile from './MediaFile'
class Article extends Model
{
static table = 'articles'
columns = ['title', 'body', 'description', 'thumbnail_id', 'is_published', 'published_at']
tags = []
thumbnail = null
buildData () {
let data = super.buildData()
data['tags'] = this.tags.map(tag => tag.id)
data['thumbnail_id'] = this.hasThumbnail() ? this.thumbnail.id : -1
return data
}
constructor (data) {
super()
super.set(data)
this.tags = data.tags
this.thumbnail = new MediaFile(data.thumbnail)
}
url () {
return `/articles/${this.id}`
}
previewUrl () {
return `${this.url()}/preview`
}
hasThumbnail () {
if (this.thumbnail && this.thumbnail.isNew() === false)
{
return true
}
return false
}
releaseThumbnail () {
this.thumbnail = null
}
}
export default Article
予めModel
というクラスを作っており、そのModelクラスを継承してRESTfulAPIのパスを指定すればすぐに使えるようになっています。
こうすればリレーションもオブジェクトベースでアクセスできますし、一石二鳥です。
Object.assignが深い部分までコピーしてくれない
JavaScriptのオブジェクトは基本的に参照が代入されるのでオブジェクトをコピーする際にはObject.assign
関数を持ち入ります。
let a = { value: 10 }
let b = Object.assign({}, a}
a.value = 20
console.log(a) //20
console.log(b) //10
それで何が問題かというとオブジェクトにオブジェクトのプロパティーがあってもObject.assign
はそこまでコピーしてくれない、というものです。
let a = { obj: { value: 10 } }
let b = Object.assign({}, a)
a.obj.value = 20
console.log(a.obj.value) //20
console.log(b.obj.value) //20 上と参照が同じ
そこで自分で関数を用意するのも良かったのですが、lodash
というライブラリにcloneDeep
という関数があったのでそれを利用してみました。
import { cloneDeep } from 'lodash'
let a = { obj: { value: 10 } }
let b = cloneDeep(a)
a.obj.value = 20
console.log(a.obj.value) //20
console.log(b.obj.value) //10
lodash
いいですね。ドキュメント見てても飽きない。
目標
まだ目標が定まっていませんが
- 追加したい機能
- Unsplash連携
- Wordpressで書いた記事の移行システム
- SEO
- テーマ管理
- その他
- OSS
- ある程度機能を作ってソースのリファクタリングをしたらGithubで公開する予定です。
- OSS
最後に
まだNuxt.jsとLaravelの機能を使いこなせていない部分もありますが、このCMSの開発でいろいろなことを学べましたし、これからももっと知識を得ていきたいと思います。
良ければアドバイスなどコメントしていただけると幸いです。
長かったですが最後まで読んでくださってありがとうございました。