- 投稿日:2020-02-10T23:43:14+09:00
Laradockを使ってLaravel環境を構築する
*編集中
導入環境
- OS: macOS 10.14
- docker: Docker Desktop 2.2
laradock clone
git clone https://github.com/LaraDock/laradock.git
laradockがダウロードされたことを確認
$ ls
laradock移動
cd laradock
envをコピー
cp env-example .env
コンテナ初期化
docker-compose up -d nginx mysql phpmyadmin redis workspace
- 終わるまで30分くらいかかる。
コンテなにログイン
docker-compose exec --user=laradock workspace bash
laravel 5.8 をインストール
composer create-project laravel/laravel lara_sample --prefer-dist “5.8.*”
コンテなからログアウト
exit
- 投稿日:2020-02-10T22:59:51+09:00
リレーションで特定のユーザーの記事を取得する手順
はじめに
情報共有サイトにおいて、全てのユーザーが投稿した記事を取得することは簡単ですが、特定のユーザーが投稿した記事のみを取得したいとなると、リレーションが必要になり、初学者にとってはちょっと複雑になります。本記事では備忘録も兼ねてその手順をメモします。
テーブルの作成
テーブル名を決める
テーブル名は複数形にします
今回は下記のように定義します。
- 記事を保存するテーブル名→news
- ユーザー情報を保存するテーブル名→users
マイグレーションファイルの作成
下記コマンドを打ちます
$ php artisan make:migration create_テーブル名_tabledatabase/migrationsディレクトリ直下にテーブル名.php というファイルが生成されます。
マイグレーションファイルへの編集
newsテーブルに記事のタイトルと本文のカラムを加える場合、下記のように'title'と'body'を追記します。
public function up() { Schema::create('news', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); // ニュースのタイトルを保存するカラム $table->string('body'); // ニュースの本文を保存するカラム $table->timestamps(); }); }変更を反映させる為には、下記コマンドを実行します。
このコマンドはマイグレーションファイルのup関数を実行します。php artisan migrateモデルの作成
モデルとは簡単に言うとテーブル内のデータを操作でき、保存、編集、削除などが出来るものになります。
テーブルとモデルの紐付け
- Laravelでは1つのテーブルにつき1つのモデルが紐づきます。
- テーブルとモデルは命名規則で紐づけられます。テーブル名の単数形をモデル名にしてモデル名の頭文字を大文字にすることで紐づけられます。 例えば下記のようになります。
テーブル名 モデル名 news News users User モデルの作成
Newsモデルを作成するには下記コマンドを実行します。
php artisan make:model Newsappディレクトリ直下にNews.php というファイルが生成されました。
同様にUserモデルも作成します。ここまでで、ユーザー情報に関するusersテーブル/Userモデルと、記事情報に関するnewsテーブル/Newsモデルを作成しました。これでそれぞれの情報を個々に取得することはできます。しかしある特定のユーザーが投稿した記事を全て取得したいとなるとこのままでは難しいです。これを解決するにはnewsテーブルにusersテーブルのidを加えてあげる必要があります。このnewsテーブルに追加するusersテーブルのidを外部キーと呼びます。
外部キーにも命名規則があります。
usersテーブルのidカラムを外部キーとして設定する場合、リレーション先のカラム名は[リレーション元モデル名_id]となり、'user_id'と設定します。テーブルにカラムを追加する手順
テーブルにカラムを追加する方法としては、テーブル自体を削除して作り直すやり方もあるのですが、これでは保存したデータが消えてしまう問題があります。そこで今回は別の方法でカラムを追加します。詳しいことは下記記事が参考になります。
【Laravel】カラムを追加する方法下記コマンドでカラムを追加するための専用のmigrationファイルを作成します。
php artisan make:migration add_user_id_to_news_table --table=news--table=news:ここにカラムを追加するテーブル名を書きます。
作成されたmigrationファイルを編集します。
public function up() { Schema::table('news', function (Blueprint $table) { $table->integer('user_id'); }); } public function down() { Schema::table('news', function (Blueprint $table) { $table->dropColumn('user_id'); }); }変更を反映させます。
php artisan migrateモデルの紐付け
外部キーによってテーブル間の紐付けができたら次にモデル間の紐付けをします。
テーブル間の関係が '1対他' か '他対1' かによって使用するメソッドが下記のようになります。
- [1対他]のリレーション → hasManyメソッド
- [他対1]のリレーション → belongsToメソッド
今回はusersテーブルとnewsテーブルの関係は[1対他]になり、それぞれのメソッドは下記のように扱います。
- newsテーブルを扱うNewsモデルではbelongsToメソッド
- usersテーブルを扱うUserモデルではhasManyメソッド
それぞれのメソッドはモデル内で下記のように記入します。
Newsモデル
App/News.phppublic function user() { return $this->belongsTo('App\User'); }Userモデル
App/User.phppublic function news() { return $this->hasMany('App\News'); }これで特定のユーザーの記事を取得することができます。
参考
- 投稿日:2020-02-10T02:57:10+09:00
"the requested PHP extension zip is missing from your system"のエラー解決法
当方Macです。
terminalで以下を入力。pecl install zipこれでいけました。
もし
pecl
コマンドがないといわれたら、homebrewでPHPをインストールしてみてください。brew install php@7.4再度、以下を入力
pecl install zip
- 投稿日:2020-02-10T00:08:39+09:00
Rails/Laravel使いに送るドメインモデル~アクティブレコードの功罪~
みなさん、こんにちは!RailsやLaravel使ってますか? ActiveRecord(LaravelではEloquent)ってめっちゃ便利ですね。ただ便利ゆえにActiveRecord以外の存在を知らない人がいるので、メリット・デメリットをまとめてみました。最終的にはドメインモデル入門になっています。
最初にRailsやLaravelから入った人(つまり僕)にありがちなのですが、ActiveRecordがどのようなものか理解せずに実装するため、ActiveRecordなのにロジックがないことがあります。また、ActiveRecordパターン以外を知らないのでActiveRecordのメリット・デメリットを理解してません。そこでActiveRecordがどのようなものかを説明していきたいと思います。
ただ、一年ほどRailsのコードに触れていないので、もし書き方がおかしかったら容赦なく突っ込んでくださし。また個人の見解が多分に含まれているので、皆さんの思うところがあるかもしれません。その時は、ガンガン言ってください。
注意
Railsのよさは結合度の高さによる実装の速さです。RailsはActiveRecordを前提としています。後半に紹介する
POROs
やRepository
はRailsWayから外れたものです。この記事はRailsにRepository層を設けることを勧めているわけではなく、このような考え方もあるよという記事です。もしDDD前提の設計をしたいのであればHanamiというフレームワークがおすすめです。
https://magazine.rubyist.net/articles/0056/0056-hanami.htmlActiveRecordとは
マーチン・ファウラーという方が書いた「Pattern of Enterprise Application Architecture (PofEAA)」という本に、
データベーステーブルまたはビューの行をラップし、データベースアクセスをカプセル化してデータにドメインロジックを追加するオブジェクト
と書かれています。ここからActiveRecordの役割が3つあることがわかります。
- データベースアクセス
- テーブルの行に対応するデータの保持
- ドメインロジックをもつ
データベースアクセスは
rubyuser = new User(params) user.saveのようにモデル自体に
save
やcreate
を持つことです。テーブルの行に対応するデータの保持は
id name 1 ハト太郎 2 ハム助 とテーブルがある場合、
rubyuser = User.find(1) puts(user.id) #ハト太郎のように行単位で属性を保持するインスタンスを生成できます。
ドメインロジックをもつは
例えば、20歳以下であれば料金が半額とする場合はコントローラーに
rubyif user.age <= 20 then # 料金半額 endとするのではなく、モデルにロジックを追加して
rubyclass User < ApplicationRecord 中略 def isPriceHalf self.age <= 20 end endrubyif user.isPriceHalf then # 料金半額 endというふうにユーザーに関するロジック(ドメインロジックといいます)をユーザーモデルにカプセルしてしまうことです。重要なのは自身のデータを用いたインスタンスメソッドを実装することです。
歴史
そもそもアクティブレコードはドメインモデルの一つです。ドメインモデルはかんたんに説明するとオブジェクト指向にのっとり、データとそれに付随するロジックをクラスに閉じ込めたものになります。なぜそうするのが良いかというと、ロジックがデータと一緒にあることで、コードの重複が防げるからです。ここらへんは「現場で役立つシステム設計の原則(増田享)」という本に詳しく載っています。
データに付随するロジックをコントローラ層やサービス層に書くのは自由ですが、一応そういう考え方もあることを知っておいてください。
ActiveRecordのメリット・デメリット
メリット
- テーブルと1対1にモデルが存在するので、テーブル設計が終わったあとすんなりと実装に入れる
- データと永続化メソッドが1つのモデルにあるので、実装スピードが早い
デメリット
- ツールありき。自ら実装するのは割と手間(RailsとLaravelは標準装備なのでこれはデメリットではない)。
- テーブルと1対1にモデルが存在するので、モデルがテーブルに引っ張られる。例えばテーブルを変更するとモデルに影響を与えるので、テーブルとモデルの結合度が高い。
- ツールを利用した場合、モデルの継承(extend)を利用する事が多く1つしかできない継承を消費してしまう(Rubyはmixinという機能があるのであまり問題にならないかもしれない)。
ちなみにPofEAAには次のように書かれています。
アクティブレコードの最大のメリットは、シンプルな構造である。アクティブレコードの構築は容易であり、また理解しやすい。最大の問題は、アクティブレコードが有効であるのが、アクティブレコードオブジェクトがデータベーステーブルと直接対応している(同一構造スキーム)場合だけという点である。
これはテーブルと1対1のモデル設計のメリット・デメリットですね。ほかにデメリットとして
ビジネスロジックが複雑な場合には、オブジェクトの直接的な関係、コレクション、継承などを使用したいとまず考えるだろう。しかし、これらの部品は簡単にはアクティブレコードにマッピングできず、また、断片的に追加すると状況はより複雑になる。以上の理由からデータマッパーの使用を考えるようになる。
と書かれています。オブジェクト指向とリレーショナル・データベースは同一のものではありません。例えばrubyでの配列
rubyarray = ["a", "b", "c']をデータベースに保存するときにどのように保存すればよいでしょうか? リレーショナルデータベースはデータの横持ちが苦手なので、縦持ちにしてテーブルに保存するかもしれません。
また、
Carクラス
とCarクラスを継承するHybridCarクラス
のデータを保存することを考えると、それぞれを保存するのはどうすればよいでしょうか?rubyclass HybridCar < Car def doSomething end endおそらく、一般的には
type
属性を加えて継承を表現するかもしれません。様々な方法があるかもしれませんが、NoSQLとは違ってリレーショナルデータベースはこのような継承を表現するのが苦手です(苦手であってできないことはない)。アクティブレコードはリレーショナルデータベースのテーブルと密結合なので、テーブルが苦手な表現はアクティブレコードも苦手です(くどいができないことはない)。Plain Old Ruby Objects(POROs)について
いままでActiveRecordしか使ったことのない方は、ぜひ他のモデルパターンを知ってほしいです。これから紹介するのは
POROs
とRepository
です。もしデータベースとロジックの分離をしたいのであれば、こちらのパターンはおすすめです。Plain Old Ruby Objectsは特になんのひねりもなくただのRubyの
Class
です。マーティンが単純なJavaのクラスをJavaBeansに対してPlain Old Java Objects (POJOs)と呼んだことにちなんでいます。class Dog def initialize(name) @name = name end def doSomething # do something end endただのRubyのClassなのでActiveRecordなどは継承していません。特定のツールに依存しないので、自分の自由に実装できます。外部API由来のモデルもDB由来のモデルもすべて同じように扱えます。継承を消費しないのでデザインパターンを適用しやすくなります。POROsはオブジェト指向を気持ちよく実装できます。
Repositoryパターンについて
RepositoryパターンはドメインモデルのIOを担当します。今回だと上記のPOROsと外部API通信やデータベースへの永続化を担当します。Repositoryという層をはさむ事によってモデルはデータベースを知る必要がありません。また、データベース以外に外部APIをモデルにマッピングすることもできます。背後にActiveRecordを使ってもQueryBuilderを使っても構いません。
注意: Repositoryパターンは本来インターフェイスを用いて実装することが多いです。今回はクラスとして実装しています。
rubyclass DogRepository def self.findDogById(id) # ActiveRecordもしくはQueryBuilderによって実装 end def self.save(dog) # ActiveRecordもしくはQueryBuilderによって実装 end endrubyclass DogController < ApplicationController def show @dog = DogRepository.findDogById(params.id) end def create dog = new Dog(params) # dog.save()ではない DogRepository.save(dog) end endRepositoryパターンを使うとモデルはActiveRecordではなく、モデル自身に永続化のメソッドがないので、
dog.save()
ではなく、DogRepository.save(dog)
となっていることに注目してください。Repositoryパターンではモデルは自身のロジックに集中してデータベースへの永続化の処理はRepositoryが担当します。モデルは永続化については気にしなくて良いのです。