Record
목차
- 개념
- 특징
- 설치
- 시작
- 주요 디렉토리
- 주요 기능
- artisan
- Assets
- PEAR, PECL
- Blade
- 기타
- 라라벨 프로젝트 버전 확인
- Honeypot
- route 수정 후 적용 안될 때
- .env 등 config 파일 수정 시
- Log
- [Sql Log][#sql-log]
- laravel-debugbar
- vue 연동
- Facade
- ORM 함수 설명
- 테스트
- Eloquent 팁
- Queues
- Command
- Attribute Accessors & Mutators
- 로컬 api 개발
- tip
- tinker
개념
-
특징
- 모델-뷰-컨트롤러(MODEL-VIEW-CONTROLLER) 아키텍쳐 패턴 지원
- 객체지향(OBJECT-ORIENTED) 라이브러리
- 명령 줄 인터페이스(COMMAND-LINE INTERFACE)
- 라라벨의 템플릿 엔진
- 효율적인 ORM과 데이터베이스 관리
- 효과적인 단위 테스트를 위한 적절한 지원.
- 라라벨은 100% 오픈소스이다.
설치
- npm
- yarn
- /bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)”
- brew update
- composer
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '906a84df04cea2aa72f40b5f787e49f22d4c2f19492ac310e8cba5b96ac8b64115ac402c8cd292b8a03482574915d1a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
- sudo mv composer.phar /usr/local/bin/composer
- composer global require laravel/installer
시작
-
라라벨 설치
- laravel new blog
- composer create-project –prefer-dist laravel/laravel blog2
-
PHP에 내장된 개발 서버를 사용하여 애플리케이션을 구동
- php artisan serve
주요 디렉토리
- routes
- web.php : web.php 파일은 RouteServiceProvider 의 web 미들웨어 그룹안에 포함되는 라우트들을 포함하고 있으며, 세션 상태, CSRF 보호, 쿠키 암호화 기능을 제공합니다. 만약 여러분의 애플리케이션이 상태를 저장하지 않는 RESTful API를 제공하지 않는다면, 대부분의 라우트는 web.php 파일안에 정의될것입니다.
// view return
Route::get('/', function () {
return view('welcome');
});
==>
resources/views/welcome.blade.php
// controller return
Route::get('/home', 'HomeController@index')->name('home');
==>
app/Http/Controllers/HomeController.php
function index()
- app : app 디렉토리에는 애플리케이션의 핵심 코드가 들어 있습니다. 우리는 이 디렉토리를 더 자세히 살펴볼 것입니다. 애플리케이션의 거의 모든 클래스가 이 디렉토리에 있습니다.
- Http : Http 디렉토리는 컨트롤러, 미들웨어 그리고 form request 를 가지고 있습니다. 애플리케이션으로 들어오는 request를 처리하는 대부분의 로직은 이 디렉토리에 위치하고 있습니다.
- Contollers : 컨트롤러는 클래스를 구성하여 HTTP 요청에 대한 그룹을 지정합니다
- Http : Http 디렉토리는 컨트롤러, 미들웨어 그리고 form request 를 가지고 있습니다. 애플리케이션으로 들어오는 request를 처리하는 대부분의 로직은 이 디렉토리에 위치하고 있습니다.
php artisan make:controller BlogController
//Laravel 리소스 라우팅은 일반적인 "CRUD" 경로를 한 줄의 코드로 컨트롤러에 할당합니다.
php artisan make:controller CategoryController --resource
//route 정보 확인
php artisan route:list
- Middleware : 미들웨어는 애플리케이션으로 들어온 HTTP 요청을 간편하게 필터링할 수 있는 방법을 제공합니다
-
database : database 디렉토리는 데이터베이스 마이그레이션 파일, 모델 팩토리, 시딩 파일들을 포함하고 있습니다. 원한다면, 이 디렉토리를 SQLite 데이터베이스가 저장되는 곳으로 사용할수도 있습니다.
-
resources : 디렉토리는 뷰 파일과 LESS, SASS, 자바스크립트와 같이 컴파일 되기 전의 asset파일들을 가지고 있습니다. 이 디렉토리는 또한 다국어 파일도 들어 있습니다.
- views
<a href="">go to home show</a>
<==
Route::get('/home/show', 'HomeController@show')->name('home.show');
- layouts
//layouts 적용
@extends('layouts.app')
==>
resources/views/layouts/app.blade.php
//layouts 의 위치 적용
@section('content')
==>
@yield('content')
- .env
주요 기능
eloquent ORM
- Model
php artisan make:model Blog -mc
//m - migration 생성
//c - controller 생성
// 생성 파일
app/Http/Controllers/BlogController.php
app/Blog.php
database/migrations/2022_03_10_053827_create_blogs_table.php
// migration
php artisan migrate
- model
class Blog extends Model
{
//대량 할당(Mass Assignment)할 모델의 속성을 정의해야 하며 이는 모델에 $fillable 속성을 사용해서 할 수 있습니다.
// protected $fillable = [
// 'user_id','title', 'description'
// ];
//모든 속성들이 대량 할당이 가능하게 하고자 한다면, $guarded 프로퍼티를 빈 배열로 정의하면 됩니다.
protected $guarded = [];
}
- controller
public function store(Request $request)
{
// return $request->all();
// dd($request->all());
//
// case 1
// $blog = new Blog();
// $blog->user_id = $request->user()->id;
// $blog->title = $request->title;
// $blog->description = $request->description;
// $blog->save();
//
// case 2
Blog::create([
'user_id' => $request->user()->id,
'title' => $request->title,
'description' => $request->description,
]);
return redirect()->back();
}
- One To One
//CASE 1
//User.php
public function userDetail()
{
return $this->hasOne('App\UserDetail');
}
//
public function show($id)
{
$user = User::find($id)->userDetail;
dd($user);
}
//CASE 2
//UserDetail.php
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'foreign_key', 'other_key');
}
//
public function user()
{
return $this->belongsTo('App\User');
}
//public function show($id)
//{
// $user_detail = UserDetail::find($id);
// dd($user_detail);
//}
- One To Many
//BlogController.php
public function show($id)
{
// $blog = Blog::where('id', $id)->firstOrFail();
$blog = Blog::with(['comments'])->where('id', $id)->firstOrFail();
//compact 메서드를 이용해 blade 에 데이터를 전달.
return view('blogs.show', compact('blog'));
}
//show.blade.php
<div>
@foreach($blog->comments as $comment)
<p></p>
@endforeach
</div>
<form action="" method="POST">
@csrf
<input type="hidden" name="blog_id" value=><br>
<input type="text" name="body" placeholder="write..."><br>
<button>save</button>
</form>
//CommentController.php
public function store(Request $request)
{
Comment::create([
'user_id' => $request->user()->id,
'blog_id' => $request->blog_id,
'body' => $request->body,
]);
return redirect()->back();
}
//Blog.php
public function Comments()
{
return $this->hasMany('App\Comment');
}
-
One To Many (Inverse)
-
Many To Many
//migrate php file
//Foreign Key Constraints
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
//web.php
Route::get('/postcreate', 'PostController@create')->name('posts.create');
Route::post('/posts', 'PostController@store')->name('posts.store');
//PostController.php
public function create()
{
$posts = Post::with(['tags'])->get();
return view('posts.create', compact('posts'));
}
public function store(Request $request)
{
// dd($request->only(['title']));
// dd($request->all());
// $temp = $request->user()->posts();
// dd($temp);
$post = $request->user()->posts()->create($request->only(['title']));
// dd($post);
$tags = explode(',', $request->tags);
$tags = array_map('trim', $tags);
$tags = array_filter($tags, 'strlen');
foreach($tags as $tag) {
$tag = Tag::updateOrCreate([
'name' => $tag
]);
$post->tags()->attach($tag->id);
}
return redirect()->back();
}
//create.blade.php
@foreach($posts as $post)
<div class="mb-3">
<div>
Title: <br>
Tags: @foreach($post->tags as $tag) @endforeach
</div>
</div>
@endforeach
<div class="mb-5">
<form action="" method="POST">
@csrf
Title: <input type="text" name="title" >
<br>
<br>
Tags: <input type="text" name="tags"><br>
<br>
<br>
<button>submit</button>
</form>
</div>
//Post.php
public function tags()
{
return $this->belongsToMany('App\Tag');
}
Eager 로딩
- Eager Loading
-
https://laravel.kr/docs/7.x/eloquent-relationships#Eager%20%EB%A1%9C%EB%94%A9
- N+1 쿼리
//Comment.php
public function blog()
{
return $this->belongsTo('App\Blog');
}
//CommentController.php
public function index()
{
// $comments = Comment::all();
$comments = Comment::with('blog')->get();
return view('comments.index', compact('comments'));
}
//index.blade.php
@foreach ($comments as $comment)
<p>Comment: </p>
<p>Blog Title: </p>
@endforeach
with
$users = User::with('posts')->get();
without
$users = User::without('posts')->get();
csrf
- CSRF 공격(Cross Site Request Forgery)은 웹 어플리케이션 취약점 중 하나로 인터넷 사용자(희생자)가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 만드는 공격입니다.
- form
<form method="POST" action="/profile">
@csrf
...
</form>
- javascript
resources/js/bootstrap.js
//token
let token:any = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
(<any>window).axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
eloquent orm query log
config/logging.php
//
'channels' => [
'sql' => [
'driver' => 'daily',
'path' => storage_path('logs/sql.log'),
'level' => 'debug',
'days' => 14,
],
//
app/Provides/AppServiceProvider.php
//
public function register()
{
if ($this->app->environment() !== 'production') {
\Event::listen('Illuminate\Database\Events\QueryExecuted', function ($query) {
\Log::channel('sql')->info([
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
]);
});
}
}
view 값 전달
- compact
return view('products.index', compact('title', 'description'));
- with
return view('products.index') ->with(['title'=>$title,'description'=>$description]);
- direct
return view('products.index', ['data' => $data]);
json response
return response()->json(['name'=>'Name', 'state'=>'CA'], 200);
collection
Route::get('/collect', function () {
$collection = collect(['taylor', 'abigail', null])->map(function ($name) {
return strtoupper($name);
})->reject(function ($name) {
return empty($name);
});
dd($collection);
});
middleware
php artisan make:middleware CheckAge
//
//app/Http/Middleware/CheckAge.php
//before
public function handle($request, Closure $next)
{
if ($request->age <= 20) {
return redirect('too-young');
}
return $next($request);
}
//after
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->age <= 20) {
return redirect('too-young');
}
return $response;
}
//
//app/Http/Kernel.php
protected $routeMiddleware = [
'checkage' => \App\Http\Middleware\CheckAge::class,
];
//
//web.php
Route::get('/enter', function () {
dd('testing');
})->middleware('checkage');
pagination
//index.blade.php
@foreach ($users as $user)
<p>name: </p>
@endforeach
//web.php
Route::get('/pagination', 'PaginationController@index')->name('pagination');
//PaginationController.php
public function index()
{
$users = User::paginate(10);
return view('pagination.index', compact('users'));
}
트랜잭션
- exception 이 발생하게 되면 트랜잭션은 자동으로 롤백됩니다
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
- 데드락 처리하기
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
}, 5);
- 수동으로 트랙잭션 사용하기
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
권한
- https://laravel.kr/docs/5.8/authorization
- Gate는 사용자가 주어진 액션에 대해서 수행할 수 있는 권한이 있는지 없는지 확인하는 클로저
- Policy는 특정 모델 또는 리소스에 대한 권한을 확인하는 방법을 클래스로 만든 것입니다. 예를 들어, 블로그 애플리케이션의 경우, Post 모델과 해당 포스트를 생성하거나 수정할 수 있는 액션을 허용하는 PostPolicy가 있을 수 있습니다.
- 미들웨어를 통해서
- can 미들웨어에 두개의 인자를 전달합니다. 첫번째는 권한을 확인하고자 하는 액션의 이름이고, 두번째는 policy 메소드에 전달하고자 하는 라우트 파라미터 입니다.
- 이 경우, 묵시적(implicit) 모델 바인딩을 사용하고 있기 때문에, 하나의 Post 모델이 policy 메소드에 전달됩니다
- can 미들웨어에 두개의 인자를 전달합니다. 첫번째는 권한을 확인하고자 하는 액션의 이름이고, 두번째는 policy 메소드에 전달하고자 하는 라우트 파라미터 입니다.
use App\Post;
Route::put('/post/{post}', function (Post $post) {
// The current user may update the post...
})->middleware('can:update,post');
- create와 같은 몇몇 액션은 모델 인스턴스를 필요로 하지 않습니다. 이러한 경우 미들웨어에 클래스 이름을 전달하면 됩니다
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
- 컨트롤러 헬퍼를 통해서
- authorize 메소드를 제공합니다. can 메소드와 같이, 이 메소드는 권한을 확인 하고자 하는 액션의 이름과, 관련된 모델을 인자로 받습니다.
class PostController extends Controller
{
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// The current user can update the blog post...
}
}
- create와 같은 몇몇 액션은 모델 인스턴스를 필요로 하지 않습니다
public function create(Request $request)
{
$this->authorize('create', Post::class);
// The current user can create blog posts...
}
Policy 생성하기
-
Policies 파일 생성하기
- php artisan make:policy ForwarderPolicy –model=Forwarder
-
Policy 등록하기
- AuthServiceProvider
protected $policies = [
\App\Model\Order\Order::class => \App\Policies\OrderPolicy::class,
];
- 주의 사항
- Request 의 파라미터 대소문자와 Policy 파라미터 대소문자가 일치해야 함.
artisan
- artisan 명령어
-
artisan 은 라라벨에 내장된 명령행 방식의 통합 유틸리티로 루비온레일의 rails 콘솔처럼 라라벨로 프로젝트를 진행하기 위해서는 꼭 알아야 할 필수 요소로 다음과 같은 작업을 할 수 있습니다.
- 라라벨 애플리케이션의 구성(라우팅, 캐시, 환경값)을 검사하고 설정 변경
- 애플리케이션을 정비/운영 모드로 전환
- 라라벨 개발에 필요한 여러 가지 타입(컨트롤러, 리스너, 모델, 미들웨어등) 생성
- 라라벨 성능에 영향을 주는 여러 가지 설정 변경
- 대화형으로 라라벨 애플리케이션 프로토타이핑 및 디버깅
artisan migrate
- 테이블 변경사항 공유
php artisan migrate
- up 메소드는 데이터베이스에 테이블, 컬럼, 인덱스를 추가하는데 사용되고
- down 메소드는 up 메소드의 동작을 취소합니다.
- 마이그레이션 파일 생성
php artisan make:migration create_users_table
- 마이그레이션 되돌리기
php artisan migrate:rollback
php artisan migrate:rollback --step=5 //최근 5개의 마이그레이션만 되돌립니다:
php artisan migrate:reset //모든 마이그레이션 되돌리기
-
마이그레이션 명령어
Schema::create('users', function (Blueprint $table) { $table->increments('id'); });
- 테이블 / 컬럼이 존재하는지 확인하기
if (Schema::hasTable('users')) { // } if (Schema::hasColumn('users', 'email')) { // }
- 데이터베이스 커넥션-connection & 테이블 옵션
Schema::connection('foo')->create('users', function (Blueprint $table) { $table->increments('id'); });
- 테이블 이름 변경 / 제거하기
Schema::rename($from, $to); Schema::drop('users'); Schema::dropIfExists('users');
- 컬럼 생성하기
Schema::table('users', function (Blueprint $table) { $table->string('email'); });
- 컬럼 Modifiers
Schema::table('users', function (Blueprint $table) { $table->string('email')->nullable(); });
-
사전 준비사항
-
컬럼을 수정하기 전에, 꼭 composer.json 파일에 doctrine/dbal 의존성을 추가하십시오. Doctrine DBAL 라이브러리는 컬럼의 현재 상태를 확인하고 필요한 SQL 쿼리를 생성하여 컬럼에 지정된 변경사항을 수행하기 위해 사용됩니다:
composer require doctrine/dbal
- 컬럼의 속성 변경하기
Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->change(); }); 컬럼을 nullable로 수정할 수도 있습니다: Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->nullable()->change(); });
- 컬럼의 이름 변경하기
Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); });
- 컬럼 삭제하기
Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); }); dropColumn 메소드에 컬럼 이름들의 배열을 전달하면 테이블에서 여러 개의 컬럼을 없앨 수 있습니다: Schema::table('users', function (Blueprint $table) { $table->dropColumn(['votes', 'avatar', 'location']); });
- 인덱스 생성하기
$table->string('email')->unique(); 위와 같이 하는 대신에, 컬럼을 정의한 뒤에 인덱스를 생성할 수도 있습니다. 예를 들어: $table->unique('email'); 인덱스 메소드에 컬럼의 배열을 전달하여 복합 인덱스를 생성할 수도 있습니다: $table->index(['account_id', 'created_at']); 라라벨은 자동으로 알맞은 인덱스 이름을 생성할 것입니다만, 메소드의 두번째 인자로 인덱스 이름을 지정할 수도 있습니다: $table->unique('email', 'unique_email');
- 인덱스 이름 변경하기
$table->renameIndex('from', 'to')
- 인덱스 삭제하기
Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // Drops index 'geo_state_index' });
- 외래키 제한
라라벨은 데이터베이스에서 또한 참조 무결성을 강제하는 외래 키 제한을 생성하는 것을 제공합니다. 예를 들어 users 테이블의 id 컬럼을 참조하는 posts 테이블에 user_id 컬럼을 정의해보겠습니다: Schema::table('posts', function (Blueprint $table) { $table->unsignedInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); }); 제한(constraint)의 "on delete"와 "on update" 속성에 원하는 동작을 지정할 수도 있습니다: $table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade'); 외래 키를 지우기 위해서는 dropForeign 메소드를 사용할 수 있습니다. 외래 키 제한은 인덱스와 같은 명명 규칙을 사용합니다. 따라서 테이블 이름과 제한(constraint)의 컬럼들을 합치고 뒤에 "_foreign"을 붙일 것입니다: $table->dropForeign('posts_user_id_foreign'); 또는 배열 값을 전달하면 삭제시 자동으로 컨벤션 규칙에 따르는 이름이 사용됩니다: $table->dropForeign(['user_id']); 마이그레이션에서 다음의 메소드들을 사용하여 외래키 제약을 활성/비활성 할 수 있습니다: Schema::enableForeignKeyConstraints(); Schema::disableForeignKeyConstraints();
-
phpunit testing 환경 구성
php artisan migrate:status --env=testing php artisan migrate --env=testing
artisan db:seed
- 라라벨에서는 시드(seed) 클래스를 사용해서 테스트 데이터를 데이터베이스에 설정하는 간단한 메소드를 포함하고 있습니다.
php artisan cache:clear
- 애플리케이션 캐시를 비우는 artisan 명령어
- ll storage/framework/cache
Assets 변경 감시하기
- npm run watch 명령어는 터미널에서 계속 실행되면서 모든 관련된 파일의 변경사항을 감시합니다. Webpack은 변경사항이 감지되면 자동으로 이를 다시 컴파일 합니다:
npm run watch
- 구동환경에 따라서 파일의 변경사항을 발생해도 Webpack이 업데이트 되지 않게 할 수도 있습니다. 사용하는 시스템이 이 경우라면, watch-poll 명령어 사용을 고려하십시오:
npm run watch-poll
PEAR, PECL
PHP Extension and Application Repository; PEAR
PHP 익스텐션 및 애플리케이션 저장소; 피어
/usr/bin/pear
PHP Extension Community Library; PECL
PHP 확장모듈 커뮤니티 라이브러리; 피클
/usr/bin/pecl
- PEAR
- PHP 확장모듈 프레임워크 및 배포 시스템
- 되도록이면 이것 대신 composer 사용을 권장함[1]
[root@zetawiki ~]# cat /usr/bin/pear
#!/bin/sh
exec /usr/bin/php -C -d include_path=/usr/share/pear \
-d output_buffering=1 /usr/share/pear/pearcmd.php "$@"
- PECL - PHP 확장모듈 저장소 - 자매 저장소 PEAR와 공유됨 Peclsmall.gif 2.1 기본값 pecl은 알고 보면 리눅스쉘에서 peclcmd.php를 돌리는 스크립트이다.
[root@zetawiki ~]# cat /usr/bin/pecl
#!/bin/sh
exec /usr/bin/php -C -n -q -d include_path=/usr/share/pear \
-d output_buffering=1 /usr/share/pear/peclcmd.php "$@"
- 설치, 확인
pear version
pecl version
apt-get install php-pear
Blade
조건문
- @if, @elseif, @else, @endif
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
- @unless, @isset, @empty
@unless (Auth::check())
You are not signed in.
@endunless
@isset($records)
// $records is defined and is not null...
@endisset
@empty($records)
// $records is "empty"...
@endempty
- @auth, @guest
인증 관련 지시어
@auth 그리고 @guest 지시어는 현재 접속자가 인증된 사용자인지 아니면 guest 인지 판별하는데 사용가능한 편의 기능입니다.
@auth
// The user is authenticated...
@endauth
@guest
// The user is not authenticated...
@endguest
필요한 경우, @auth 와 @guest 지시어에 체크하고자 하는 인증 guard를 지정할 수 있습니다.
@auth('admin')
// The user is authenticated...
@endauth
@guest('admin')
// The user is not authenticated...
@endguest
- @hasSection
섹션 지시어
@hasSection 지시어를 사용하여 섹션이 내용을 가지고 있는지 확인할 수 있습니다.
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
스위치
- @switch, @case, @break, @default, @endswitch
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
반복문
- @for, @foreach, @forelse, @while
@for ($i = 0; $i < 10; $i++)
The current value is
@endfor
@foreach ($users as $user)
<p>This is user </p>
@endforeach
@forelse ($users as $user)
<li></li>
@empty
<p>No users</p>
@endforelse
@while (true)
<p>I'm looping forever.</p>
@endwhile
- @break, @continue
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li></li>
@if ($user->number == 5)
@break
@endif
@endforeach
@foreach ($users as $user)
@continue($user->type == 1)
<li></li>
@break($user->number == 5)
@endforeach
- $loop
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
<p>This is user </p>
@endforeach
반복문이 중첩된 경우라면, 상위 반복문의 $loop 변수에 parent 속성을 통해서 액세스 할 수 있습니다.
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
This is first iteration of the parent loop.
@endif
@endforeach
@endforeach
주석
- 블레이드는 또한 뷰에 주석을 정의할 수 있습니다. 하지만 HTML 주석과는 다르게 블레이드 주석은 애플리케이션이 반환하는 HTML에 포함되어 있지 않습니다.
### PHP
- 블레이드는 또한 뷰에 주석을 정의할 수 있습니다. 하지만 HTML 주석과는 다르게 블레이드 주석은 애플리케이션이 반환하는 HTML에 포함되어 있지 않습니다.
```JavaScript```
## 기타
### 라라벨 프로젝트 버전 확인
```JavaScript
root@zetawiki:/var/www/laravel# cat composer.json | grep '"laravel/framework"'
"laravel/framework": "5.5.*",
Honeypot
route 수정 후 적용 안될 때
php artisan route:cache
.env 등 config 파일 수정 시
php artisan config:clear
php artisan cache:clear
clear 종류
1. Configuration Cache
$ php artisan config:clear
$ php artisan config:cache
2. Route Caching
$ php artisan route:clear
$ php artisan route:cache
3. Views Caching
$ php artisan view:clear
$ php artisan view:cache
4. Events Cache
$ php artisan event:clear
$ php artisan event:cache
5. Application Cache
$ php artisan cache:clear
6. Clearing All Cache
$ php artisan optimize:clear
Log
use Illuminate\Support\Facades\Log;
Log::debug('An informational message.');
SQL Log
//app/Providers/AppServiceProvider.php
public function register()
{
//daekyu.park debug..
if ($this->app->environment() !== 'production') {
\Event::listen('Illuminate\Database\Events\QueryExecuted', function ($query) {
\Log::channel('sql')->info([
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
]);
});
}
}
//config/logging.php
//daekyu.park debug..
'sql' => [
'driver' => 'daily',
'path' => storage_path('logs/sql.log'),
'level' => 'debug',
'days' => 14,
],
laravel-debugbar
composer require barryvdh/laravel-debugbar --dev
APP_DEBUG is true
vue 연동
//laravel old
php artisan preset none
php artisan preset vue
//laravel 7.x
composer require laravel/ui --dev
php artisan ui vue
"devDependencies": {
"vue": "^2.5.17",
"vue-template-compiler": "^2.6.10"
}
https://github.com/laravel-frontend-presets/tailwindcss
https://packagist.org/packages/laravel-frontend-presets/tailwindcss#4.2.0
composer require laravel-frontend-presets/tailwindcss:4.2 --dev
php artisan ui tailwindcss --auth
npm install
php artisan serve
npm run dev // resources -> public 으로 최적화
php artisan migrate
npm run watch //
//chobo-chat/resources/js/app.js
const app = new Vue({
el: '#app',
});
//debug bar
composer require barryvdh/laravel-debugbar --dev
php artisan make:model Message -mc
php artisan migrate
- api route
//chobo-chat/routes/api.php
// domain/api/messages
Route::prefix('messages')->middleware('auth:api')->group(function(){
Route::post('/', 'MessageController@store');
});
- current-user == currentUser
//home.blade.php
<the-chat :current-user="\{\{ auth()->id() \}\}"></the-chat>
//**
props: {
currentUser: {
type: Number,
required: true
}
},
- child ‘updatedChatWith’ == parent @updatedChatWith
//ChatUserList.vue
methods: {
updateChatWith(userId) {
this.$emit('updatedChatWith', userId);
}
}
//Chat.vue
<ChatUserList
:current-user="currentUser"
@updatedChatWith="updateChatWith"
/>
methods: {
updateChatWith(value) {
this.chatWith = value;
}
}
- v-model == text 양방향처리
//Chat.vue
<input
class="border-2 border-solid rounded border-gray-600 w-full p-3"
type="text"
v-model="text"
@keyup.enter="submit"
>
data() {
return {
chatWith: null,
text: ''
}
},
브로드캐스팅
-
Pusher Channels 설치
composer require pusher/pusher-php-server "~4.0"
- 브로드캐스트 받기 laravel-echo
npm install --save laravel-echo pusher-js
- bootstrap.js
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: true
});
- .env
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=
BROADCAST_DRIVER=pusher
- BroadcastServiceProvider
/config/app.php
App\Providers\BroadcastServiceProvider::class,
- 이벤트 설치
php artisan make:event MessageSent
//implements 추가
class MessageSent implements ShouldBroadcast
서비스 프로바이더
-
라라벨에 포함되어 있는 config/app.php 파일을 열어 본다면 providers 배열을 볼 수 있을 것입니다. 배열 안에 있는 모든 서비스 프로바이더 클래스가 애플리케이션에 로드됩니다. 대부분의 프로바이더는 “지연된” 프로바이더입니다. 이 말은 모든 요청에 대해서 반드시 로드되지 않고 실제로 필요할 때에 로드 된다는 것을 의미합니다.
-
프로바이더를 생성
php artisan make:provider RiakServiceProvider
-
Register 메소드
- 서비스 컨테이너에 무언가를 바인딩
-
Boot 메소드
- 이 메소드는 모든 다른 서비스 프로바이더들이 등록된 이후에 호출됩니다. 즉, 프레임 워크에 의해 등록된 다른 모든 서비스들에 액세스 할 수 있다는 것을 의미합니다.
-
프로바이더 등록하기
'providers' => [
// Other Service Providers
App\Providers\ComposerServiceProvider::class,
],
- 지연(deferred) 프로바이더
- 프로바이더를 지연(defer) 로딩 하려면, \Illuminate\Contracts\Support\DeferrableProvider 인터페이스를 구현하고 provides 메소드를 정의하면 됩니다. provides 메소드는 프로바이더에 의해서 바인딩이 등록된 서비스 컨테이너를 리턴해야 합니다.
Facade
- 라라벨 Facade 는 서비스 컨테이너에 등록되어 있는 서비스를 정적 메소드 형태로 사용해 줄 수 있게 한다.
- 라라벨 Facade 는 getFacadeAccessor() 를 구현하면 되며 config/app.php 에 라라벨 Facade 들을 확인해 볼수 있다.
- 라라벨 Facade 는 디자인 패턴 Facade Pattern 과는 개념이 다르다.
커스텀 헬퍼 클래스 추가
composer.json 에 autoload 추가
composer dump-autoload 실행
ORM 함수 설명
- created
- updated
- saving
- saved
-
deleted
- findOrFail
- 때때로 모델을 찾을 수 없는 경우 예외를 throw하고 싶을 수 있습니다. 이것은 라우트나 컨트롤러에서 특히 유용합니다. findOrFail및 메서드는 쿼리의 첫 firstOrFail번째 결과를 검색합니다. 그러나 결과가 없으면 Illuminate\Database\Eloquent\ModelNotFoundExceptionthrow됩니다.
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();
- ModelNotFoundException잡히지 않으면 404 HTTP 응답이 자동으로 클라이언트로 다시 전송됩니다 .
use App\Models\Flight;
Route::get('/api/flights/{id}', function ($id) {
return Flight::findOrFail($id);
});
- refresh
- 데이터베이스에서 가져온 Eloquent 모델의 인스턴스가 이미 있는 경우 fresh및 refresh메서드를 사용하여 모델을 “새로 고침”할 수 있습니다. fresh메서드는 데이터베이스에서 모델을 다시 검색합니다 . 기존 모델 인스턴스는 영향을 받지 않습니다.
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();
- 이 refresh방법은 데이터베이스의 새로운 데이터를 사용하여 기존 모델을 재수화합니다. 또한 로드된 모든 관계도 새로 고쳐집니다.
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
- isDirty, isClean
- isDirty메서드는 모델이 검색된 이후에 모델의 속성이 변경되었는지 확인합니다
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isDirty(['first_name', 'title']); // true
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->isClean(['first_name', 'title']); // false
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
기타 함수 설명
-
dispatch
- Job 클래스를 작성한 뒤에 클래스의 dispatch 메소드를 사용하여 이를 처리할 수 있습니다. dispatch 메소드에 전달할 인자는 job의 생성자입니다.
-
papertrail
- 로그 관련
-
filter_var
- 지정된 필터로 변수를 필터링합니다
<?php
var_dump(filter_var('bob@example.com', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('http://example.com', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
?>
위의 예는 다음과 같이 출력됩니다.
string(15) "bob@example.com"
bool(false)
테스트
phpunit
-
PHPunit in a Laravel project
php artisan config:clear php artisan config:cache --env=testing vendor/bin/phpunit
-
phpunit testing 환경 구성
php artisan migrate:status --env=testing php artisan migrate --env=testing
-
phpunit testing 생성
// Create a test in the Feature directory... php artisan make:test UserTest php artisan make:test Api/V1/Forwarder/ForwarderUserControllerTest.php // Create a test in the Unit directory... php artisan make:test UserTest --unit
-
팩토리 생성
Eloquent 팁
-
Model의 속성: timestamps, appends 등. https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Model.php
-
WhereX
$users = User::where('approved', 1)->get();
==>
$users = User::whereApproved(1)->get();
- “where”과 필드의 이름을 접미사로 더하면 마술같이 동작합니다. 또한 엘로퀀트에는 날짜/시간에 관련된 몇가지 메소드 들이 존재합니다.
User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));
- BelongsTo default
author()는 만약 post에 Author가 없다면 빈 Author 모델을 반환합니다.
return $this->belongsTo('App\Author')->withDefault([
'name' => 'Guest Author'
]);
- Raw query 메소드
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->orderByRaw('(updated_at - created_at) desc')
- 복제: 새로운 복재 행을 만듭니다.
$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();
- 큰 테이블을 위한 Chunk() 메소드
$users = User::all();
foreach ($users as $user) {
// ...
==>
User::chunk(100, function ($users) {
foreach ($users as $user) {
// ...
}
});
Queues
Queue-큐 worker 실행하기
-
php artisan queue:work
-
하나의 단일 Job 처리하기
- php artisan queue:work –once
-
대기중인 모든 작업 처리 및 종료
- php artisan queue:work –stop-when-empty
Command
- 명령어 생성
- php artisan make:command SendEmails
- 명령어 실행
- php artisan SendEmails:do
- php artisan SendEmails:do 1 2 // 파라미터
Attribute Accessors & Mutators
Defining An Accessor
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
$firstName = $user->first_name;
Defining A Mutator
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
$user->first_name = 'Sally';
로컬 api 개발
./vendor/bin/openapi -o public/documents/open-api.json --format json ./app/Http ./app/Model
app/Lib/OpenApi/filter public/documents/open-api.json public/documents/open-api-filtered.json
redoc-cli bundle public/documents/open-api-filtered.json
tip
where orm
$postsByDate = Post:: whereDate('published_at', '>', '2023-06-15')→get();
// Get all posts published after the specific date (greater than June 15, 2023)
$postsByMonth = Post:: where Month ('published_at', '<', '6')→get();
// Get all posts published in or before the specific month (less than or equal to June)
$postsByDay Post:: whereDay ('published_at', '', '1')→get();
// Get all posts published on any day except the 1st day
$postsByYear = Post:: where Year('published_at', '', '2023')→get();
// Get all posts published in or after the specific year (greater than or equal to 2023)
$postsByTime = Post:: whereTime('published_at', '<', '10:00:00')->get();
// Get all posts published before the specific time (less than 10:00:00)
Convert arrays to query strings
use Illuminate\Support\Arr;
// Retrieve data from the request and construct the array directly
$filters = [
'location'=>request('location'), // New York
'date_range' => [
'start' =>request('start_date'), // 17 2023-07-01
'end'=>request('end_date'), // 2023-07-10
],
'passengers' =>request('passengers'), // 4
'car_options' => [
'features' => request('features', []), // ['GPS', 'Bluetooth']
'category' => [
'type' => request('car_type') //SUV
'seats' => request('car_seats'), // 7
],
];
];
// Generate the URL with query parameters
$queryString = Arr::query($filters);
$url = 'https://carbookingapi.com/cars?' . $queryString;
echo $url;
https://carbookingapi.com/cars?
location=New+York
&date_range [start]=2023-07-01
&date_range[end]=2023-07-10
&passengers=4
&car_options[features][0]=GPS
&car_options[features][1]=Bluetooth
&car_options[category][type]=SUV
&car_options[category][seats]=7
casting model
use App\Models\Product;
use App\Models\Order;
use Carbon Carbon;
// Real-Time Example for mergeCasts
class Product extends Model
{
protected $casts = [
'price' => 'float',
];
}
$product = new Product();
$product->mergeCasts([
'is_on_sale' => 'boolean',
]);
// In this example, the 'is_on_sale' attribute will be cast to a boolean on
// this specific $product instance,
// in addition to the existing cast of 'price' attribute to float.
// Real-Time Example for withCasts
$orders = Order::selectRaw('id, (quantity * unit_price) as total_amount, order_date')
->withCasts ([
'total_amount' => 'decimal: 2',
'order_date' => 'datetime',
])
->get();
foreach ($orders as $order) {
echo 'Order ID: ' . $order-id .
' | Total Amount: $' . $order-total_amount .
' | Order Date:' . $order->order_date->format('d F Y') . "\n";
}
// In this example, we use the selectRaw method to calculate the total amount as a raw column // by multiplying the quantity and unit_price columns. We also select the order date column.
// The withCasts method is then used to apply casts to the resulting model instances.
// The total amount attribute is cast to a decimal with 2 decimal places, and
// the order_date attribute is cast to a datetime value.
// Differences:
// The mergeCasts method is used on a specific instance of the model and
// adds temporary casts to the existing casts of that model.
// These casts will only be applied to that particular instance.
// The withCasts method is used on the model class itself and
// applies casts to the attributes returned by a query.
// These casts will be applied to the resulting model instances returned by the query.
casting model
// Dot Notation
$user = User::with('posts.comments', 'profile', 'orders.items')->find(1);
// Accessing relationships
$userPosts = $user->posts;
// Get the user's posts
$userCommentsOnPosts = $user->posts->flatMap->comments;
// Get all comments on the user's posts
$userProfile = $user->profile;
// Get the user's profile
$userOrders = $user->orders;
// Get the user's orders
$userOrderItems = $user->orders->flatMap->items;
//Get all items in the user's orders
// Array Syntax
$user = User::with([
'posts' => ['comments'].
'profile',
'orders' => ['items']
])-> find (1);
// Accessing relationships
$userPosts = $user->posts;
// Get the user's posts
$userCommentsOnPosts = $user->posts->flatMap->comments;
// Get all comments on the user's posts
$userProfile = $user->profile;
// Get the user's profile
$userOrders = $user->orders;
// Get the user's orders
$userOrderItems = $user->orders->flatMap->items;
// Get all items in the user's orders
code 가독성
<?php
// Tip: Numeric literal separators
// using underscores (_) can be used
// in PHP 7.4 and later versions for
// improved readability.
$number1 = 1_000_000_000;
// Represents 1 billion
$number2 = 123_456_789.99;
// Represents 123,456,789.99
echo $number1 . "\n"; // Output: 1000000000
echo $number2 . "\n"; // Output: 123456789.99 ?>
tinker
php artisan tinker
$cacheKey = ‘01043108945_1’;
\Cache::forget($cacheKey);