Record

목차

개념

-

특징

  • 모델-뷰-컨트롤러(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 요청에 대한 그룹을 지정합니다
            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 로딩

    //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 메소드에 전달됩니다
    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: ''
            }
        },

브로드캐스팅

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 팁

    $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);