August 11, 2020

PHP MVC - Laravel DATABASE

Title: PHP Laravel - 데이터베이스
Author: DongDongE
Tags: Programming
Release: 2020.08.10


[Database: Getting Started - 데이터베이: 시작하기]


데이터베이스란?


라라벨은 Raw SQL를 사용하거나 "쿼리 빌더" 또는 "Eloquent ORM"을 사용하여 다양한 데이터베이스 백엔드에서 데이터베이스와 상호작용을 매우 간편하게 만들 수 있습니다. 현재 라라벨은 4가지 데이터베이스를 지원하고 있습니다.


  • MySQL

  • Postgres

  • SQLite

  • SQL Server



Configuration - 설정하기


애플리케이션의 데이터베이스 설정은 "config/database.php"에 있습니다. 해당 파일에서 모든 데이터베이스 연결을 정의하고 기본적으로 어떤 데이터베이스를 연결할지 지정할 수 있습니다. 지원되는 대부분의 데이터베이스 시스템에 대한 기본 예제가 들어 있습니다.


기본적으로 라라벨의 샘플 환경 구성은 로컬 머신에서 개발을 진행하기 위해 라라벨의 "Homestead"을 사용할 준비가 되어 있습니다. 물론 로컬 데이터베이스 필요에 따라 해당 구성을 자유롭게 변경할 수 있습니다.



SQLite Configuration - SQLite 설정하기


"touch database/database.sqlite"와 같은 명령을 사용하여 새로운 SQLite 데이터 베이스를 만들고 데이터베이스의 절대 경로를 사용하여 새로 만든 데이터베이스를 가리키도록 설정을 쉽게 변경할 수 있습니다.


DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite

.env



SQL Server Configuration - SQL Server 설정하기


라라벨은 supports SQL Server를 지원합니다. 하지만 데이터베이스에 대한 연결 구성은 "config/database.php"에서 추가해야합니다.


'sqlsrv' => [
    'driver' => 'sqlsrv',
    'host' => env('DB_HOST', 'localhost'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8',
    'prefix' => '',
],



읽기 & 쓰기 연결설정


"SELECT"문에 대해 하나의 데이터베이스에 연결하여 사용하고, "INSERT, UPDATE, DELETE" 문에 대해서는 다른 데이터베이스에 연결하여 사용할 수 있습니다. 라라벨은 이부분을 쉽게 만들 수 있도록 처리해주며, Raw 쿼리를 사용하거나, "쿼리 빌더" 또는 "Eloquent ORM"을 사용하든 적절한 연결이 사용됩니다.

읽기 / 쓰기 연결 설정은 아래와 같습니다.


'mysql' => [
    'read' => [
        'host' => '192.168.1.1',
    ],
    'write' => [
        'host' => '196.168.1.2'
    ],
    'driver'    => 'mysql',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
],

config/database.php


mysql 배열에는 "read"와 "write" 라는 2개의 키가 존재합니다. 각 키에는 단일 키 "host"가 포함된 배열값이 존재합니다. "read"와 "write" 연결에 대한 나머지 데이터베이스 옵션은 기본 "mysql" (ex. username, password)배열에서 합쳐져 사용됩니다.

만약 기본 배열(mysql 배열)의 값을 재정의하려는 경우 "read"와 "write" 배열에 항목을 추가하면됩니다. 따라서 위 예제 경우 "192.168.1.1" IP는 "read(읽기 권한만)" 연결을 위한 호스트로 사용되며, "192.168.1.2" IP는 "write(쓰기 권한만)"에 사용됩니다. 메인 "mysql" 배열의 데이터베이스의 연결정보, "prefix", "database", "username" 등 기타 모든 옵션은 두 배열(read, write) 모두에 공유됩니다.



Using Multiple Database Connections - 여러 데이터베이스 연결하기


여러 연결을 사용하는 경우 "DB" 파사드의 "connection" 메소드를 사용하여 각각 연결에 액세스할 수 있습니다. "connection" 메소드에 전달된 "name"은 "config/database.php" 설정파일에 나열된 이름이랑 일치해야 합니다.


$users = DB::connection('foo')->select(...);

연결 인스턴스에서 "getPdo" 메소드를 사용하여 Raw PDO 인스턴스에 접근할 수 있습니다.


$pdo = DB::connection()->getPdo();



Running Raw SQL Queries - Raw SQL 쿼리 실행


데이터베이스 연결을 구성한 후에는 "DB" 파사드를 사용하여 쿼리를 실행할 수 있습니다. "DB" 파사드는 "select, update, insert, delete" 그리고 "statement"의 각 쿼리 유형에 대한 메소드를 제공합니다.


[SELECT 쿼리 실행해보기]

기본 쿼리를 실행하려면 "DB" 파사드에서 "select" 메소드를 사용할 수 있습니다.


<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show a list of all of the application's users.
     *
     * @return Response
     */
    public function index()
    {
        $users = DB::select('select * from users where active = ?', [1]);

        return view('user.index', ['users' => $users]);
    }
}


"select" 메소드에 전달된 첫 번째 인자는 Raw SQL 쿼리이며, 두 번째 인자는 쿼리에 바인딩해야하는 매개변수값입니다. 일반적으로 이들은 "where" 절 제약을 위한 조건값입니다. 매개변수 바인딩은 SQL Injection에 대응하기 위해 제공합니다.

"select" 메소드는 항상 배열로 반환합니다. 배열내의 각 값 결과는 PHP "StdClass" 객체가 되어 결과값에 액세스할 수 있습니다.


foreach ($users as $user) {
    echo $user->name;
}



Using Named Bindings - 이름 바인딩 사용하기

기존 "?"를 사용하여 매개변수 바인딩을 표시하는 대신 이름(Named)를 사용하여 쿼리를 실행할 수 있습니다.

$results = DB::select('select * from users where id = :id', ['id' => 1]);



Running An insert Statement - insert Statement 실행하기

"insert" Statement 실행하려면 "DB" 파사드에서 "insert" 메소드를 사용할 수 있습니다. "select"와 마찬가지로 해당 메소드도 Raw SQL 쿼리를 첫 번째 인자로 전달되며, 두 번째 인자로는 쿼리에 바인딩할 매개변수를 전달합니다.


DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);



Running An Update Statement - update Statement 실행하기

데이터베이스의 기존 레코드(데이터)를 업데이트(수정)하려면 "update" 메소드를 사용하면 됩니다. 결과값으로는 해당 쿼리문의 영향을 받는 행 갯수가 반환됩니다.


$affected = DB::update('update users set votes = 100 where name = ?', ['John']);



Running An Delete Statement - delete Statement 실행하기

데이터베이스의 레코드(데이터)를 삭제하려면 "delete" 메소드를 사용할 수 있습니다. "update" 메소드와 마찬가지로 해당 쿼리문으로 영향을 받는 행 갯수가 반환됩니다.


$deleted = DB::delete('delete from users');



Running An General Statement - General Statement 실행하기

일부 데이터베이스는 값을 반환하지 않는 경우가 있습니다. 이러한 유형은 "DB" 파사드에서 "statement" 메소드를 사용할 수 있습니다.


DB::statement('drop table users');



Listening For Query Events - 쿼리 이벤트 리스닝

애플리케이션에서 실행되는 각 쿼리를 확인하기 위해 "listen" 메소드를 사용할 수 있습니다. 해당 메소드는 "쿼리 로깅" 또는 "디버깅"에 유용합니다. "서비스 프로바이더"에 쿼리 리스너를 등록할 수 있습니다.


<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        DB::listen(function ($query) {
            // $query->sql
            // $query->bindings
            // $query->time
        });
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}



Handing Deadlocks - 교착상태 처리

"transaction" 메소드는 교착상태가 발생할 때 트랜잭션을 재시도해야 하는 횟수를 정의하는 두 번째 인자로 선택적으로 전달 받습니다. 이러한 시도가 모두 종료되면 "exception" 예외가 발생합니다.


DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
}, 5);



Manually Using Transactions - 수동으로 트랜잭션 사용하기

트랜잭션을 수동으로 시작하고 "롤백"과 "커밋"을 완벽하게 제어하기 위해 "DB" 파사드에서 "beginTransaction" 메소드를 사용할 수 있습니다.


DB::beginTransaction();


"rollback" 메소드를 통하여 트랜잭션을 롤백할 수 있습니다.

DB::rollBack();

마지막으로 "commit" 메소드를 통하여 트랜잭션을 커밋할 수 있습니다.

DB::commit();


"DB" 파사드의 트랜잭션 메소드를 사용하여 "쿼리 빌더" 및 "Eloquent ORM"에 대한 트랜잭션도 제어할 수 있습니다.




[Database: Query Builder - 쿼리 빌더]


데이터베이스 쿼리 빌더란?


라라벨의 데이터베이스 쿼리 빌더는 데이터베이스 쿼리를 생성하고 실행하기 위해 편리한 인터페이스를 제공합니다. 애플리케이션에서 대부분의 데이터베이스 작업을 수행하는데 사용할 수 있으며, 지원되는 모든 데이터베이스 시스템에서 작동합니다.

라라벨 쿼리 빌더는 "PDO" 매개 변수 바인딩을 사용하여 SQL Injection으로 부터 애플리케이션을 보호합니다. 바인딩으로 전달되는 문자열을 따로 정리할 필요는 없습니다.




Retrieving Results - 결과 조회하기


테이블 모든 행 조회

쿼리를 시작하기 위해 "DB" 파사드에서 "table" 메소드를 사용할 수 있습니다. "table" 메소드는 주어진 테이블에 대한 쿼리 빌더 인스턴스를 반환하므로, 쿼리에 더 많은 제약 조건을 연결한 다음 마지막으로 "get" 메소드를 사용하여 결과를 가져올 수 있습니다.


<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show a list of all of the application's users.
     *
     * @return Response
     */
    public function index()
    {
        /* Table users 모든행 가져오기 => SELECT * FROM users */
        $users = DB::table('users')->get();

        return view('user.index', ['users' => $users]);
    }
}

"get" 메소드는 각 결과가 PHP "StdClass" 객체의 인스턴스인 결과를 포함하는 "Illuminate\Support\Collection"을 반환합니다. 객체의 속성으로 컬럼에 액세스하여 각 컬럼의 값에 액세스할 수 있습니다.


foreach ($users as $user) {
    /* users 테이블의 "name" 컬럼의 값에 접근 */
    echo $user->name;
}



테이블에서 단일 행(결과) / 컬럼 검색


데이터베이스 테이블에서 단일 행 결과를 가져오려면 "first" 메소드를 사용할 수 있습니다. 해당 메소드는 단일 "StdClass" 개체를 반환합니다.


/* SELECT * FROM users WHERE name = "John" limit 0,1  */
$user = DB::table('users')->where('name', 'John')->first();

echo $user->name;

아래는 위 SQL 쿼리에 실행에 대한 General Log 입니다.


2020-08-11T16:01:28.431755Z         5 Prepare   select * from `users` where `name` = ? limit 1
2020-08-11T16:01:28.431927Z         5 Execute   select * from `users` where `name` = 'dongdonge' limit 1

또한 전체 행이 필요하지 않은 경우 "value" 메소드를 사용하여 레코드에서 단일 값을 출력할 수 있습니다. 해당 메소드는 컬럼 값을 직접 반환합니다.


$email = DB::table('users')->where('name', 'John')->value('email');

아래는 위 SQL 쿼리에 실행에 대한 General Log 입니다.


2020-08-11T16:08:09.885615Z         5 Prepare   select `email` from `users` where `name` = ? limit 1
2020-08-11T16:08:09.885814Z         5 Execute   select `email` from `users` where `name` = 'dongdonge' limit 1
2020-08-11T16:08:09.886155Z         5 Close stmt



컬럼값 리스트 검색


단일 컬럼값을 포함하는 컬렉션을 검색하려면 "pluck" 메소드를 사용할 수 있습니다. 아래 예제에서 "roles" 테이블의 "title" 컬렉션을 검색합니다.

$titles = DB::table('roles')->pluck('title');

foreach ($titles as $title) {
    echo $title;
}

아래는 위 SQL 쿼리에 실행에 대한 General Log 입니다.


2020-08-12T02:54:19.929864Z         8 Prepare   select `title` from `roles`
2020-08-12T02:54:19.930199Z         8 Execute   select `title` from `roles`
2020-08-12T02:54:19.930546Z         8 Close stmt

아래는 PHP artisan(아티즌)을 통하여 해당 결과값을 확인하였습니다.


**>>> DB::table('roles')->pluck('title');
=> Illuminate\Support\Collection {#706
     all: [
       "Big_Title",
       "test123",
     ],
   }**

또한 반환된 컬렉션에 대해 사용자 지정 키 컬럼(열)을 지정할 수 있습니다.

$roles = DB::table('roles')->pluck('title', 'name');

foreach ($roles as $name => $title) {
    echo $title;
}

아래는 위 SQL 쿼리에 대한 General Log 입니다

2020-08-12T04:51:28.263119Z        8 Prepare   select `title`, `name` from `roles`
2020-08-12T04:51:28.263336Z        8 Execute   select `title`, `name` from `roles`
2020-08-12T04:51:28.263454Z          8 Close stmt



Chunking Results - 결과 분할


수천 개의 데이터베이스 레코드로 작업해야하는 경우 "chunk" 메소드를 사용하면 됩니다. 해당 메소드는 한 번에 결과의 작은 "chunk"를 검색하고 각 chunk를 "Closure"에 처리합니다. 해당 방법은 수천 개의 데코드를 처리하는 "Artisan" 명령어를 작성할 때 유용합니다.
예를 들어, "users" 테이블을 한 번에 100개의 레코드 chunk로 작업해 보겠습니다.


DB::table('users')->orderBy('id')->chunk(100, function ($users) {
    foreach ($users as $user) {
        //
    }
});

"Closure"에서 "false"를 반환하여 추가 chunks를 중지할 수 있습니다.

DB::table('users')->orderBy('id')->chunk(100, function ($users) {
    // Process the records...

    return false;
});



Aggregates - 집계 함수


쿼리 빌더는 "count", "max", "min", "avg", "sum"과 같은 다양한 집계 메소드를 제공합니다. 쿼리는 생성한 후 다음 메소드중 하나를 호출하여 사용하시면 됩니다.

$users = DB::table('users')->count();

$price = DB::table('orders')->max('price');


또는 아래와 같이 메소드를 다른 구문과 결합하여 사용할 수 있습니다.

$price = DB::table('orders')
                ->where('finalized', 1)
                ->avg('price');




Selects - 조회


Select 구문 지정

물론, 데이터베이스 테이블에서 모든 컬럼(열)을 항상 선택할 수 있는 것은 아닙니다. "select" 메소드를 사용하여 쿼리에 대한 사용자 지정 "select" 구문을 사용할 수 있습니다.

$users = DB::table('users')->select('name', 'email as user_email')->get();


"distinct" 메소드를 사용하여 쿼리가 고유한 결과를 반환할 수 있도록 할 수 있습니다.

$users = DB::table('users')->distinct()->get();


이미 쿼리 빌더 인스턴스가 있고 기존 "select" 구문에 컬럼(열)을 추가하려는 경우 "addSelect" 메소드를 사용할 수 있습니다.

$query = DB::table('users')->select('name');

$users = $query->addSelect('age')->get();



Raw Expressions - Raw 표현하기


때로는 쿼리에서 Raw 표현식을 사용해야 할 때가 있습니다. 이러한 구문의 쿼리는 문자열로 주입되므로 SQL Injection이 되지 않도록 조심해야 합니다. Raw Expression을 만들려면 "DB::raw" 메소드를 사용할 수 있습니다.

$users = DB::table('users')
                     ->select(DB::raw('count(*) as user_count, status'))
                     ->where('status', '<>', 1)
                     ->groupBy('status')
                     ->get();



Joins - 조인


Inner Join Clause - 내부 조인 구문

쿼리 빌더를 사용하여 "join" 구문을 작성할 수 있습니다. 기본 "inner join(내부 조인)"을 수행하려면 쿼리 빌더 인스턴스에 "join" 메소드를 사용할 수 있습니다. 조인 메소드에 전달된 첫 번째 인수는 조인해야 하는 테이블의 이름이고 나머지 인자는 조인에 대한 컬럼(열) 조건을 지정합니다. 물론 단일 쿼리로 여러 테이블에 조인할 수 있습니다.

$users = DB::table('users')
            ->join('contacts', 'users.id', '=', 'contacts.user_id')
            ->join('orders', 'users.id', '=', 'orders.user_id')
            ->select('users.*', 'contacts.phone', 'orders.price')
            ->get();



Left Join Clause - Left 조인 구문


"inner join(내부조인)" 대신 "left join(왼쪽 조인)"을 수행하려면 "leftJoin" 메소드를 사용해야합니다. "leftJoin" 메소드는 "join" 메소드와 동일한 시그니처를 갖습니다.

$users = DB::table('users')
            ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
            ->get();



Cross Join Clause - Cross 조인 구문


"cross join(교차 조인)"을 수행하려면 교차 결합하려는 테이블의 이름과 함께 "cross join" 메소드를 사용하면 됩니다. "Cross joins"는 첫 번째 테이블과 조인된 테이블 사이 cartesian product을 생성합니다.

$users = DB::table('sizes')
            ->crossJoin('colours')
            ->get();



Advanced Join Clauses - 고급 조인 구문


고급 조인 구문을 지정하여 사용할 수 있습니다. 사용하려면 "join" 메소드에서 두 번째 인수로 "Closure"를 전달하면 됩니다. "Closure"는 "join" 구문에 제약 조건을 지정할 수 있는 "JoinClause" 개체를 받습니다.

DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')->orOn(...);
        })
        ->get();


조인에 "where" 스타일 구문을 사용하려면 join에 "where" 및 "orWhere" 메소드를 사용할 수 있습니다. 두 컬럼을 비교하는 대신 이러한 메소드는 컬럼을 값과 비교합니다.

DB::table('users')
        ->join('contacts', function ($join) {
            $join->on('users.id', '=', 'contacts.user_id')
                 ->where('contacts.user_id', '>', 5);
        })
        ->get();



Unions


또한 쿼리 빌더는 두 쿼리를 합계 "union(결합)"하는 빠른 방법을 제공합니다. 예를 들어, 초기 쿼리를 생성하고 union 메소드를 사용하여 두 번째 쿼리와 "union" 할 수 있습니다.

$first = DB::table('users')
            ->whereNull('first_name');

$users = DB::table('users')
            ->whereNull('last_name')
            ->union($first)
            ->get();


"unionAll" 메소드도 사용할 수 있으며, "union"과 동일한 메소드 시그니처입니다.



Where Clauses - Where 구문


Simple Where Clauses - 간단한 where 구문

쿼리 빌더 인스턴스에서 "where" 메소드를 사용하여 쿼리에 "where" 구문을 추가할 수 있습니다. 가장 기본적인 호출에는 3가지 인자가 필요합니다. 첫 번째 인자는 컬럼명이며, 두 번째 인자는 데이터베이스의 지원되는 모든 연산자가 될 수 있는 연산자이며, 마지막으로 세 번째 인자는 컬럼에 대한 비교 값입니다.

예를 들어, 아래 에제는 "votes" 컬럼의 값이 "100" 인지 확인하는 쿼리입니다.

$users = DB::table('users')->where('votes', '=', 100)->get();


편리하게 사용하기 위해 단순히 컬럼이 주어진 값과 같은지 확인하려는 경우 값을 두 번째 인자로 "where" 메소드에 직접 전달할 수 있습니다.

$users = DB::table('users')->where('votes', 100)->get();


물론 "where" 구문을 사용할 때 다양한 다른 연산자를 사용할 수 있습니다.

$users = DB::table('users')
                ->where('votes', '>=', 100)
                ->get();

$users = DB::table('users')
                ->where('votes', '<>', 100)
                ->get();

$users = DB::table('users')
                ->where('name', 'like', 'T%')
                ->get();


"where" 메소드에 조건을 배열로 전달할 수 있습니다.

$users = DB::table('users')->where([
    ['status', '=', '1'],
    ['subscribed', '<>', '1'],
])->get();



Or Statements - Or 구문


"where" 조건을 함께 연결하고 "or" 구문을 쿼리에 추가할 수 있습니다. "orWhere" 메소드는 "where" 메소드와 동일한 인자를 받습니다.

$users = DB::table('users')
                    ->where('votes', '>', 100)
                    ->orWhere('name', 'John')
                    ->get();



Additional Where Clauses - 추가적인 where 구문


whereBetween

"whereBetween" 메소드는 컬럼의 값이 두 값 사이에 있는지 확인합니다.

$users = DB::table('users')
                    ->whereBetween('votes', [1, 100])->get();



whereNotBetween

"whereNotBetween" 메소드는 컬럼의 값이 두 값 사이 밖에 있는지 확인합니다.

$users = DB::table('users')
                    ->whereNotBetween('votes', [1, 100])
                    ->get();



whereIn / whereNotIn

"whereIn" 메소드는 주어진 컬럼의 값이 배열에 포함되어 있는지 확인합니다.

$users = DB::table('users')
                    ->whereIn('id', [1, 2, 3])
                    ->get();


"whereNotIn" 메소드는 주어진 컬럼의 값이 배열에 포함되어 있지 않는지 확인합니다.

$users = DB::table('users')
                    ->whereNotIn('id', [1, 2, 3])
                    ->get();



whereNull / whereNotNull

"whereNull" 메소드는 주어진 컬럼의 값이 "NULL"인지 확인합니다.

$users = DB::table('users')
                    ->whereNull('updated_at')
                    ->get();


"whereNotNull" 메소드는 컬럼의 값이 "NULL"이 아닌지 확인합니다.

$users = DB::table('users')
                    ->whereNotNull('updated_at')
                    ->get();



whereDate / whereMonth / whereDay / whereYear

"whereDate" 메소드를 사용하여 컬럼의 값을 날짜와 비교할 수 있습니다.

$users = DB::table('users')
                ->whereDate('created_at', '2016-12-31')
                ->get();


"whereMonth" 메소드를 사용하여 특정 연도의 특정 월과 컬럼값을 비교할 수 있습니다.

$users = DB::table('users')
                ->whereMonth('created_at', '12')
                ->get();



"whereDay" 메소드를 사용하여 특정 날짜와 컬럼의 값을 비교할 수 있습니다.

$users = DB::table('users')
                ->whereDay('created_at', '31')
                ->get();



"whereYear" 메소드는 특정 연도와 컬럼의 값을 비교하는데 사용할 수 있습니다.

$users = DB::table('users')
                ->whereYear('created_at', '2016')
                ->get();



whereColumn

"wehreColumn" 메소드를 사용하여 두 컬럼이 같은지 확인할 수 있습니다.

$users = DB::table('users')
                ->whereColumn('first_name', 'last_name')
                ->get();


메소드에 비교 연션자를 추가할 수 있습니다.

$users = DB::table('users')
                ->whereColumn('updated_at', '>', 'created_at')
                ->get();


"whereColumn" 메소드는 여러 조건의 배열을 전달할 수 있습니다. 이러한 조건은 "and" 연산자를 사용하여 연결됩니다.

$users = DB::table('users')
                ->whereColumn([
                    ['first_name', '=', 'last_name'],
                    ['updated_at', '>', 'created_at']
                ])->get();



Parameter Grouping - 파라미터 그룹화


경우에 따라 "where exists" 구문이나 중첩된 매개변수 그룹과 같은 고급 "where" 구문을 만들 수 있습니다. 라라벨 쿼리빌더는 이것도 처리할 수 있으며 시작하려면 괄호 안의 그룹 제약 조건의 예제를 살펴 보겠습니다.

DB::table('users')
            ->where('name', '=', 'John')
            ->orWhere(function ($query) {
                $query->where('votes', '>', 100)
                      ->where('title', '<>', 'Admin');
            })
            ->get();


위 예제를 보시면 "Closure"를 "orWhere" 메소드에 전달하면 쿼리 빌더가 제약 그룹을 시작하도록 지시합니다. "Closure"는 괄호 그룹에 포함되어야 하는 제약 조건을 설정하는데 사용할 수 있는 쿼리 빌더 인스턴스를 받습니다. 위의 예제는 다음 SQL을 생성합니다.

select * from users where name = 'John' or (votes > 100 and title <> 'Admin')



Where Exists Clauses - Where 조항 존재확인


"whereExists" 메소드를 사용하면 "where Exists" SQL 구문을 작성할 수 있습니다. "whereExists" 메소드는 "Closure" 인자를 받습니다. 해당 인수는 "exists" 구문내에 배치되어야 하는 쿼리를 정의할 수 있는 쿼리 빌더 인스턴스를 받습니다.

DB::table('users')
            ->whereExists(function ($query) {
                $query->select(DB::raw(1))
                      ->from('orders')
                      ->whereRaw('orders.user_id = users.id');
            })
            ->get();


다음 쿼리는 SQL을 생성합니다.

select * from users
where exists (
    select 1 from orders where orders.user_id = users.id
)



JSON Where Clauses - JSON Where 조항


라라벨은 또한 JSON 컬럼 유형에 대한 지원을 데이터베이스에서 JSON 컬럼 유형 쿼리를 지원합니다. 현재 여기에는 "MySQL 5.7" 및 "Postgres"가 포함됩니다. "JSON" 컬럼을 쿼리하려면 "->" 연산자를 사용해야 합니다.

$users = DB::table('users')
                ->where('options->language', 'en')
                ->get();

$users = DB::table('users')
                ->where('preferences->dining->meal', 'salad')
                ->get();



Ordering, Grouping, Limit, & Offset


orderBy

"orderBy" 메소드를 사용하면 주어진 컬럼을 기준으로 쿼리 결과를 정렬할 수 있습니다. "orderBy" 메소드의 첫 번째 인자는 정렬 기준이되는 컬럼이어 하고, 두 번째 인자는 "asc" 또는 "desc"로 정렬 방향을 제어합니다.

$users = DB::table('users')
                ->orderBy('name', 'desc')
                ->get();

latest / oldest

"latest" 및 "oldest" 메소드를 사용하면 날짜별로 결과를 쉽게 정렬할 수 있습니다. 기본적으로 결과는 "created_at" 컬럼을 기준으로 정렬됩니다. 또는 정렬하려는 컬럼 이름을 전달할 수 있습니다.

$user = DB::table('users')
                ->latest()
                ->first();

inRandomOrder

"inRandomOrder" 메소드는 쿼리 결과를 무작위로 정렬하는데 사용할 수 있습니다. 예를 들어 해당 메소드를 사용하여 임의의 사용자를 가져올 수 있습니다.

$randomUser = DB::table('users')
                ->inRandomOrder()
                ->first();

groupBy / having / havingRaw

"groupBy" 및 "having" 메소드를 사용하여 쿼리 결과를 그룹화할 수 있습니다. "having" 메소드의 시그니처는 "where" 메소드의 비슷합니다.

$users = DB::table('users')
                ->groupBy('account_id')
                ->having('account_id', '>', 100)
                ->get();


"havingRaw" 메소드는 "having" 구문의 값으로 raw 문자열을 설정할 수 있습니다. 예를 들어, 매출 금액이 "$2,500" 달러 이상인 모든 부서를 찾을 수 있습니다.

$users = DB::table('orders')
                ->select('department', DB::raw('SUM(price) as total_sales'))
                ->groupBy('department')
                ->havingRaw('SUM(price) > 2500')
                ->get();

skip / take

쿼리에서 반환되는 결과 수를 제한하거나 쿼리에서 주어진 결과 수를 건너 뛰려면 "skip" 및 "take" 메소드를 사용할 수 있습니다.

$users = DB::table('users')->skip(10)->take(5)->get();


또는 "limit" 및 "offset" 메소드를 사용할 수 있습니다.

$users = DB::table('users')
                ->offset(10)
                ->limit(5)
                ->get();



Conditional Clauses - 조건 조항


때로는 다른 것이 참일 때만 쿼리에 "when" 쿼리에 적용할 수 있습니다. 예를 들어 주어진 입력 값이 들어오는 요청에 있는 경우에만 "where" 구문을 적용할 수 있습니다. "when" 메소드를 사용하여 이를 수행할 수 있습니다.

$role = $request->input('role');

$users = DB::table('users')
                ->when($role, function ($query) use ($role) {
                    return $query->where('role_id', $role);
                })
                ->get();


"when" 메소드는 첫 번째 매개변수가 "true"일 때 지정된 "Closure(클로저)"만 실행합니다. 첫 번째 인자가 "false"이면 "Closure"가 실행되지 않습니다.

"when" 메소드에 세 번째 인자로 다른 "Closure"를 전달할 수 있습니다. 해당 클로저는 첫 번째 인자가 "false"로 평가되면 실행됩니다. 해당 기능을 사용하는 방법을 설명하기 위해 이 기능을 사용하여 쿼리의 기본 정렬을 구성할 수 있습니다.

$sortBy = null;

$users = DB::table('users')
                ->when($sortBy, function ($query) use ($sortBy) {
                    return $query->orderBy($sortBy);
                }, function ($query) {
                    return $query->orderBy('name');
                })
                ->get();



Inserts


쿼리 빌더는 데이터베이스 테이블에 레코드를 삽입하기 위해 "insert" 메소드를 제공합니다. "insert" 메소드는 컬럼 이름과 값의 배열을 허용합니다.

DB::table('users')->insert(
    ['email' => 'john@example.com', 'votes' => 0]
);


한 번의 호출로 여러 개의 레코드를 테이블에 "insert" 하여 배열의 배열을 전달하여 삽입할 수 있습니다. 각 배열은 테이블에 삽입할 행을 나타냅니다.

DB::table('users')->insert([
    ['email' => 'taylor@example.com', 'votes' => 0],
    ['email' => 'dayle@example.com', 'votes' => 0]
]);



Auto-Incrementing IDs


테이블에 "Auto incrementing(자동 증가)" ID가 있는 경우 "insertGetId" 메소드를 사용하여 레코드를 삽입한 다음 ID를 검색합니다.

$id = DB::table('users')->insertGetId(
    ['email' => 'john@example.com', 'votes' => 0]
);


"PostgreSQL"을 사용할 때 "insertGetId" 메소드를 "auto incrementing" 컬럼의 이름이 "id"가 될 것으로 예상됩니다. 다른 "sequence"에서 ID를 검색하려면 시퀸스 이름을 두 번째 인자로 "insertGetId" 메소드에 전달할 수 있습니다.



Updates


물론 데이터베이스에 레코드를 삽입하는 것 외에도 쿼리 빌더 "update" 메소드를 사용하여 기존 레코드를 업데이트할 수 있습니다. "insert" 메소드와 마찬가지로 "update" 메소드는 업데이트할 컬럼을 포함하는 컬럼 및 깞 쌍의 배열을 받습니다. "where" 절을 사용하여 "update" 쿼리를 제한할 수 있습니다.

DB::table('users')
            ->where('id', 1)
            ->update(['votes' => 1]);



Updating JSON Columns


JSON 컬럼을 업데이트할 때 "->" 구문을 사용하여 JSON 개체의 적절한 키에 액세스할 수 있습니다. 해당 작업은 JSON 컬럼을 지원하는 데이터베이스에서만 사용가능합니다.

DB::table('users')
            ->where('id', 1)
            ->update(['options->enabled' => true]);



Increment & Decrement


또한 쿼리 빌더는 주어진 컬럼의 값을 늘리거나 줄이는 편리한 방법을 제공합니다. 단순히 단축키이며, "update" statement을 수동으로 작성하는 것보다 더 표현적이로 간결한 인터페이스를 제공합니다.

이 두 방법 모두 최소한 하나의 인자 (수정할 컬럼)을 허용합니다. 컬럼이 증가하거나 감소되어야 하는 양을 제어하기 위해 두 번째 인자가 선택적으로 전달될 수 있습니다.

DB::table('users')->increment('votes');

DB::table('users')->increment('votes', 5);

DB::table('users')->decrement('votes');

DB::table('users')->decrement('votes', 5);


작업 중에 업데이트할 추가 컬럼을 지정할 수 있습니다.

DB::table('users')->increment('votes', 1, ['name' => 'John']);



Deletes


쿼리 빌더는 "delete" 메소드를 통하여 테이블에서 레코드를 삭제할 수 있습니다 "delete" 메소드를 호출하기 전에 "where" 구문을 추가하여 "delete" 구문을 제한할 수 있습니다.

DB::table('users')->delete();

DB::table('users')->where('votes', '>', 100)->delete();


모든 행을 제거하고 auto incrementing ID를 0으로 재설정하는 작업을 "truncate method"를 사용하여 처리할 수 있습니다.

DB::table('users')->truncate();



Pessimistic Locking


쿼리 빌더에서는 "select" 구문에 "pessimistic locking"을 수행 하는데 도움이 되는 몇가지 함수도 포함되어 있습니다. "share lock"으로 구문을 실행하려면 쿼리에서 "sharedLock" 메소드를 사용할 수 있습니다. "shared lock"은 트랜잭션이 커밋될 때 까지 선택한 행이 수정되는 것을 방지합니다.

DB::table('users')->where('votes', '>', 100)->sharedLock()->get();


또는 "lockForUpdate" 메소드를 사용할 수 있으며, "for update" 잠금은 행이 수정되거나 shared lock으로 선택되는 것을 방지합니다.

DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();




[Database: Pagination - 페이지네이션]


Pagination이란?


다른 프레임워크에서 "페이지네이션 (페이지 지정)"은 매우 복잡할 수 있습니다. Laravel의 "paginator"는 쿼리 빌더 및 Eloquent ORM과 통합되어 편리하고, 데이터베이스 결과를 페이지네이션으로 편리하게 사용할 수 있습니다.
"paginator"에 의해 생성된 HTML은 Bootstrap, CSS 프레임워크와 호환됩니다.



Basic Usage


Paginating Query Builder Results - 쿼리빌더 결과를 페이지 매기기

항목에 페이지를 매기는 방법에는 여러가지 방법이 있습니다. 가장 간단한 방법은 쿼리 빌더 또는 Eloquent 쿼리에서 "paginate" 메소드를 사용하는 방법입니다.
"paginate" 방법은 사용자가 보고있는 현재 페이지를 기반으로 적절한 limitoffset based 설정을 자동으로 처리합니다. 기본적으로 현재 페이지는 HTTP 요청의 "page" 쿼리 스트링의 인자값으로 감지됩니다. 물론 해당 값은 Laravel에 의해 자동으로 감지되며, "paginator"가 생성한 링크에도 자동으로 삽입됩니다.

아래 예제는 "paginate" 메소드에 전달되는 유일한 인자는 "per page (페이지당)"표시하려는 항목의 수입니다. 이 경우 페이지 당 "15" 항목을 표시하도록 설정하겠습니다.

<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show all of the users for the application.
     *
     * @return Response
     */
    public function index()
    {
        $users = DB::table('users')->paginate(15);

        return view('user.index', ['users' => $users]);
    }
}


현재 "groupBy" 구문을 사용하는 페이지 매김 작업은 Laravel에서 효율적으로 실행할 수 없습니다. 페이지가 매겨진 결과값 "paginate"과 함께 "groupBy" 구문을 사용해야 하는 경우 데이터베이스 쿼리하고 "paginator"를 수동으로 만들어야 합니다.


"Simple Pagination" - 간단한 페이지 지정하기

페이지 매김 보기에 간단한 "Next" 및 "Previous" 링크만 표시해야 하는 경우 "simplaPaginate" 메소드를 사용하여 보다 효과적으로 쿼리를 수행할 수 있습니다.
이는 View를 렌더링할 때 각 페이지 번호에 대한 링크를 표시할 필요가 없는 대규모 데이터 세트에 매우 유용합니다.

$users = DB::table('users')->simplePaginate(15);



Paginating Eloquent Results - Eloquent 결과 페이징하기


Eloquent 쿼리에 페이지를 매길수도 있습니다. 이 예제에서는 페이지 당 "15" 페이지 항목으로 "User" 모델에 페이지를 매깁니다. 해당 구문은 paginating 쿼리 빌더 결과와 비슷합니다.

$users = App\User::paginate(15);


물론 "where" 절과 같이 쿼리에 제약 조건을 설정한 후 "paginate"를 호출할 수 있습니다.

$users = User::where('votes', '>', 100)->paginate(15);


Eloquent 모델에 페이지를 매길 때 "simplePaginate" 메소드를 사용할 수 있습니다.

$users = User::where('votes', '>', 100)->simplePaginate(15);



Manually Creating A Paginator - Paginator 수동 생성하기


때로는 "pagination" 인스턴스를 수동으로 생성하여 항목 배열에 전달할 수 있습니다. 필요에 따라 "Illuminate\Pagination\Paginator" 또는 "Illuminate\Pagination\LengthAwarePaginator" 인스턴스를 만들면됩니다.

"Paginator" 클래스는 결과 집합의 총 항목 수를 알 필요는 없지만, 이 때문에 클래스에는 마지막 페이지의 인덱스를 검색하는 메소드는 없습니다. "LengthAwarePaginator"는 "Paginator"와 거의 동일한 인자를 받습니다. 그러나 결과 set의 총 항목 수에 대한 개수가 필요합니다.

즉, "Paginator"는 쿼리 빌더 및 Eloquent의 "simplePaginate" 메소드에 해당하고 "LengthAwarePaginator"는 "paginate" 메소드에 해당합니다.

paginator 인스턴스를 수동으로 생성할 때 paginator에 전달한 결과 배열을 수동으로 "slice(조각)"해야 합니다. 해당 방법이 어렵다면 PHP 함수의 "array_slice"를 확인해보시면 됩니다.




Displaying Pagination Results


"paginate" 메소드를 호출하면 "Illuminate\Pagination\LengthAwarePaginator"의 인스턴스를 받게 됩니다. "simplePaginate" 메소드를 호출하면 "Illuminate\Pagination\Paginator"의 인스턴스를 받게 됩니다.

이러한 개체는 결과 Set을 지원하는 몇 가지 방법을 제공합니다.
helpers 메소드 외에도 paginator 인스턴스는 iterators이며 배열로 반복될 수 있습니다.

따라서, 결과를 검색한 후 "Blade"를 사용하여 결과를 표시하고 페이지 링크를 사용할 수 있습니다.

<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>

{{ $users->links() }}


"links" 메소드는 결과 Set의 나머지 페이지에 대한 링크를 렌더링합니다. 이러한 각 링크에는 이미 적절한 "page" 쿼리 스트링 변수가 포함되어 있습니다. "links" 메소드로 생성된 HTML은 Bootstrap CSS 프레임 워크와 호환됩니다.




Customizing The Paginator URI - Paginator URI 사용자 지정하기

"setPath" 메소드를 사용하면 링크를 생성할때 paginator가 사용하는 URI를 정의할 수 있습니다. 예를 들어 paginator가 "/custom/url?page=N"과 같은 링크를 생성하도록 하려면 "custom/url"를 "setPath" 메소드에 전달하면됩니다.

Route::get('users', function () {
    $users = App\User::paginate(15);

    $users->setPath('custom/url');

    //
});



Appending To Pagination Links - Pagination 링크 추가

"appends" 메소드를 사용하여 쿼리 스트링에 페이지 매김 링크를 추가할 수 있습니다. 예를 들어, 각 페이지 매김 링크에 "sort=votes"를 추가하려면 "appends"를 다음과 같이 호출하면 됩니다.

{{ $users->appends(['sort' => 'votes'])->links() }}


paginator의 URL에 "hash fragment"을 추가하려면 "fragment" 메소드를 사용할 수 있습니다. 예를 들어, 각 페이지 매김 링크 끝에 "#foo"를 추가하려면 "fragment" 메소드를 다음과 같이 사용하면 됩니다.

{{ $users->fragment('foo')->links() }}




Converting Results To JSON - 결과를 JSON으로 변환하기

Laravel Paginator 결과 클래스는 "Illuminate\Contracts\Support\Jsonable" 인터페이스 계약을 구현하고