20200210のlaravelに関する記事は4件です。

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

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

リレーションで特定のユーザーの記事を取得する手順

はじめに

情報共有サイトにおいて、全てのユーザーが投稿した記事を取得することは簡単ですが、特定のユーザーが投稿した記事のみを取得したいとなると、リレーションが必要になり、初学者にとってはちょっと複雑になります。本記事では備忘録も兼ねてその手順をメモします。

テーブルの作成

テーブル名を決める

テーブル名は複数形にします
今回は下記のように定義します。

  • 記事を保存するテーブル名→news
  • ユーザー情報を保存するテーブル名→users

マイグレーションファイルの作成

下記コマンドを打ちます

$ php artisan make:migration create_テーブル名_table

database/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 News

appディレクトリ直下に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.php
public function user()
    {
        return $this->belongsTo('App\User');
    }

Userモデル

App/User.php
public function news()
    {
        return $this->hasMany('App\News');
    }

これで特定のユーザーの記事を取得することができます。

参考

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

"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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails/Laravel使いに送るドメインモデル~アクティブレコードの功罪~

みなさん、こんにちは!RailsやLaravel使ってますか? ActiveRecord(LaravelではEloquent)ってめっちゃ便利ですね。ただ便利ゆえにActiveRecord以外の存在を知らない人がいるので、メリット・デメリットをまとめてみました。最終的にはドメインモデル入門になっています。

最初にRailsやLaravelから入った人(つまり僕)にありがちなのですが、ActiveRecordがどのようなものか理解せずに実装するため、ActiveRecordなのにロジックがないことがあります。また、ActiveRecordパターン以外を知らないのでActiveRecordのメリット・デメリットを理解してません。そこでActiveRecordがどのようなものかを説明していきたいと思います。

ただ、一年ほどRailsのコードに触れていないので、もし書き方がおかしかったら容赦なく突っ込んでくださし。また個人の見解が多分に含まれているので、皆さんの思うところがあるかもしれません。その時は、ガンガン言ってください。

注意

Railsのよさは結合度の高さによる実装の速さです。RailsはActiveRecordを前提としています。後半に紹介するPOROsRepositoryはRailsWayから外れたものです。この記事はRailsにRepository層を設けることを勧めているわけではなく、このような考え方もあるよという記事です。

もしDDD前提の設計をしたいのであればHanamiというフレームワークがおすすめです。
https://magazine.rubyist.net/articles/0056/0056-hanami.html

ActiveRecordとは

マーチン・ファウラーという方が書いた「Pattern of Enterprise Application Architecture (PofEAA)」という本に、

データベーステーブルまたはビューの行をラップし、データベースアクセスをカプセル化してデータにドメインロジックを追加するオブジェクト

と書かれています。ここからActiveRecordの役割が3つあることがわかります。

  • データベースアクセス
  • テーブルの行に対応するデータの保持
  • ドメインロジックをもつ

データベースアクセスは

ruby
user = new User(params)
user.save

のようにモデル自体にsavecreateを持つことです。

テーブルの行に対応するデータの保持は

id name
1 ハト太郎
2 ハム助

とテーブルがある場合、

ruby
user = User.find(1)
puts(user.id)  #ハト太郎

のように行単位で属性を保持するインスタンスを生成できます。

ドメインロジックをもつは

例えば、20歳以下であれば料金が半額とする場合はコントローラーに

ruby
if user.age <= 20 then
# 料金半額
end

とするのではなく、モデルにロジックを追加して

ruby
class User < ApplicationRecord

  中略

  def isPriceHalf
    self.age <= 20
  end

end 
ruby
if user.isPriceHalf then
  # 料金半額
end

というふうにユーザーに関するロジック(ドメインロジックといいます)をユーザーモデルにカプセルしてしまうことです。重要なのは自身のデータを用いたインスタンスメソッドを実装することです。

歴史

そもそもアクティブレコードはドメインモデルの一つです。ドメインモデルはかんたんに説明するとオブジェクト指向にのっとり、データとそれに付随するロジックをクラスに閉じ込めたものになります。なぜそうするのが良いかというと、ロジックがデータと一緒にあることで、コードの重複が防げるからです。ここらへんは「現場で役立つシステム設計の原則(増田享)」という本に詳しく載っています。

データに付随するロジックをコントローラ層やサービス層に書くのは自由ですが、一応そういう考え方もあることを知っておいてください。

ActiveRecordのメリット・デメリット

メリット

  • テーブルと1対1にモデルが存在するので、テーブル設計が終わったあとすんなりと実装に入れる
  • データと永続化メソッドが1つのモデルにあるので、実装スピードが早い

デメリット

  • ツールありき。自ら実装するのは割と手間(RailsとLaravelは標準装備なのでこれはデメリットではない)。
  • テーブルと1対1にモデルが存在するので、モデルがテーブルに引っ張られる。例えばテーブルを変更するとモデルに影響を与えるので、テーブルとモデルの結合度が高い。
  • ツールを利用した場合、モデルの継承(extend)を利用する事が多く1つしかできない継承を消費してしまう(Rubyはmixinという機能があるのであまり問題にならないかもしれない)。

ちなみにPofEAAには次のように書かれています。

アクティブレコードの最大のメリットは、シンプルな構造である。アクティブレコードの構築は容易であり、また理解しやすい。最大の問題は、アクティブレコードが有効であるのが、アクティブレコードオブジェクトがデータベーステーブルと直接対応している(同一構造スキーム)場合だけという点である。

これはテーブルと1対1のモデル設計のメリット・デメリットですね。ほかにデメリットとして

ビジネスロジックが複雑な場合には、オブジェクトの直接的な関係、コレクション、継承などを使用したいとまず考えるだろう。しかし、これらの部品は簡単にはアクティブレコードにマッピングできず、また、断片的に追加すると状況はより複雑になる。以上の理由からデータマッパーの使用を考えるようになる。

と書かれています。オブジェクト指向とリレーショナル・データベースは同一のものではありません。例えばrubyでの配列

ruby
array = ["a", "b", "c']

をデータベースに保存するときにどのように保存すればよいでしょうか? リレーショナルデータベースはデータの横持ちが苦手なので、縦持ちにしてテーブルに保存するかもしれません。

また、CarクラスとCarクラスを継承するHybridCarクラスのデータを保存することを考えると、それぞれを保存するのはどうすればよいでしょうか?

ruby
class HybridCar < Car

  def doSomething 
  end

end 

おそらく、一般的にはtype属性を加えて継承を表現するかもしれません。様々な方法があるかもしれませんが、NoSQLとは違ってリレーショナルデータベースはこのような継承を表現するのが苦手です(苦手であってできないことはない)。アクティブレコードはリレーショナルデータベースのテーブルと密結合なので、テーブルが苦手な表現はアクティブレコードも苦手です(くどいができないことはない)。

Plain Old Ruby Objects(POROs)について

いままでActiveRecordしか使ったことのない方は、ぜひ他のモデルパターンを知ってほしいです。これから紹介するのはPOROsRepositoryです。もしデータベースとロジックの分離をしたいのであれば、こちらのパターンはおすすめです。

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パターンは本来インターフェイスを用いて実装することが多いです。今回はクラスとして実装しています。

ruby
class DogRepository

  def self.findDogById(id)
    # ActiveRecordもしくはQueryBuilderによって実装
  end

  def self.save(dog)
    # ActiveRecordもしくはQueryBuilderによって実装
  end

end
ruby
class DogController < ApplicationController

  def show
    @dog = DogRepository.findDogById(params.id)
  end

  def create

    dog = new Dog(params)
    # dog.save()ではない
    DogRepository.save(dog)

  end

end

Repositoryパターンを使うとモデルはActiveRecordではなく、モデル自身に永続化のメソッドがないので、dog.save()ではなく、DogRepository.save(dog)となっていることに注目してください。Repositoryパターンではモデルは自身のロジックに集中してデータベースへの永続化の処理はRepositoryが担当します。モデルは永続化については気にしなくて良いのです。

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