[laravel] 데이터베이스 시딩

시딩이란

라라벨은 시더클래스를 통해서 데이터베이스에 더미 데이터를 넣을수 있는 기능을 제공한다.

1. 시더클래스

아티즌 명령어를 통해 시더를 생성한다. 생성된 클래스는 database/seeds에 생성 된다.

php artisan make:seeder UsersTableSeeder

시더파일을 정의하자

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class UsersTableSeeder extends Seeder
{
  public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10).'@gmail.com',
            'password' => bcrypt('password'),
        ]);
    }
}

이제 시더를 실행하게되면 데이터베이스에 내용이 입력이 될 것이다.

php artisan db:seed --class=UsersTableSeeder

–class 옵션을 붙여주기 귀찮다면 DatabaseSeeder 에 시더를 등록해준다.

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
}

php artisan db:seed 실행해보면 데이터가 잘 들어가는 것을 확인할 수 있다. 마이그레이션에 –seed 옵션을 사용하면 편리하다.

php artisan migrate:refresh --seed

2. Eloquent 모델팩토리 사용하기

시더에 쿼리빌더를 통해 입력하는 방법보다 모델팩토리를 생성해서 시딩하는게 편리하다. 모델팩토리 생성 커맨드를 입력하자.

php artisan make:factory UserFactory

위에서 생성한 UsersTableSeeder, UserFactory는 라라벨에 이미 생성되어 있기때문에 생성할 필요가 없다. 설명을 위해서 설명하는것 뿐이다.

이제 시더클래스에 생성한 팩토리를 사용해보자.

factory(App\User::class, 50)->create();

기존 내용을 삭제하고 시더를 사용해서 새로운 데이터를 넣어보자

php artisan migrate:refresh --seed

3. 관계모델일때 시더정의하기

일대다인 모델을 정의하자.

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

     protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function articles(){
        return $this->hasMany('App\Article');
    }
}
namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = [
        'subject', 'content',
    ];

    public function user(){
        return $this->belongsTo('App\User');
    }
}

팩토리 파일을 생성해준다. 옵션을 붙여 모델을 연결해주면 편리하다.

php artisan make:factory ArticleFactory --model=Article

생성된 파일을 열어 내용을 수정하자.

use App\Article;
use Faker\Generator as Faker;

$factory->define(Article::class, function (Faker $faker) {
    return [
        'subject' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => factory(App\User::class),
    ];
});

이제 UsersTableSeeder 클래스를 수정해주자.

    public function run()
    {
        factory(App\User::class, 50)->create()->each(function($user){
            $user->articles()->save(factory(App\Article::class)->make());
        });
    }

마이그레이션을 해보자.

php artisan migrate:refresh --seed

데이터베이스를 확인해보면 user 테이블과 aticles 테이블 관계에 따라 50개의 레코드가 생성된 것을 확인할 수 있다.


References

[laravel] 관례적인 네이밍 규칙

  • 모델은 단수형
    예)Post
  • 테이블은 복수형
    예)posts
  • 테이블간 관계를 표현할 때
    -테이블 이름_열이름_foreign
    예)posts_user_id_foreign
    -이 이름은 외래키관계를 삭제할 때 사용한다. $table->dropForeign( posts_user_id_foreign );
  • 외래 키 열 이름 및 메서드 이름 관례
    관계가 ‘일(one)’ 이 되는 쪽 모델로 접근하는 메서드는 단수 ‘다(many)’가 되는 쪽은 복수로 사용한다.
app/Post.php
public function author(){
    return  $this->belongsTo(User::class, 'user_id');
}

app/User.php
public function posts(){
    return $this->hasMany(Post::class, 'post_id')
}
  • 피벗 테이블 이름 및 열 이름 관례
    – 다대다로 연결하려는 두 테이블의 이름을 단수로 바꾸고 알파벳 순으로 연결한다. 연결자로는 밑줄(_)을 사용한다.
    – 외래키의 열 이름은 모델이름_id을 사용한다.
idpost_idtag_id
111
212
  • 마이그레이션 생성시
    – create_, make_, add_, drop_, change_ 등으로 시작하고, _table로 끝난다.
    예) create_posts_table
  • 컨트롤러
    – 파스칼 표기법을 사용하여 복수형에 Controller접미사를 붙인다.
    예)PostsController

[php]함수에서 매개변수, 인자의 차이

매개변수는 함수를 정의 할때 정의 되고, 인자는 함수를 호출 할때 실제로 함수에 전달되는 값

function foo($color){
    echo "color: $color";
}

foo('red');

위 함수에서 정의 되는 $color는 매개변수, foo함수를 호출 할때 전달되는 ‘red’는 인자가 된다.

[laravel] 나만보는 라라벨 튜토리얼

라라벨코리아, 라라캐스트를 보고 간단한 프로젝트를 만들면서 정리하는 글

  1. 프로젝트생성
  2. composer 의존성 설치
  3. 프론트앤드 스케폴딩
  4. npm 의존성 설치
  5. laravel-mix 사용하기
  6. model, controller, migration 생성하기
  7. restful한 route 만들기
  8. view 생성하기
  9. 기능구현하기
    • 글쓰기 폼작성
    • 저장하기
    • 글보기
    • 글 수정하기 폼작성
    • 글 업데이트하기
    • 글목록 페이지
    • 삭제하기
  10. 모델을 라우트에 바인딩 시키자
  11. 중복 제거

1.프로젝트 생성

laravel new blog
cd ./blog

2.composer 의존성 설치

composer install

3.프론트앤드 스케폴딩
필요한 부분만 선택하여 설치하면 된다. (본 예제에서는 bootstrap만 스캐폴딩함)

composer require laravel/ui --dev
// Generate basic scaffolding...
php artisan ui bootstrap
php artisan ui vue
php artisan ui react
// Generate login / registration scaffolding...
php artisan ui bootstrap --auth
php artisan ui vue --auth
php artisan ui react --auth

4.npm 의존성 설치
프론트 앤드 스캐폴딩을 하게 되면 package.json에 의존성 목록의 추가된다. 프로젝트에 사용하기 위해서 의존성을 설치해준다.

npm install

5.laravel-mix 사용하기
루트디렉토리의 webpack.mix.js 파일을 열어보면 아래와 같이 기본설정이 되어있다.

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css');

특별히 추가 할게 없다면 npm run dev 명령어를 실행해서 컴파일 하자. 앞에서 부트스트랩을 스캐폴딩했기 때문에 컴파일 하면 부트스트랩도 같이 컴파일 되어 public/js/app.js, public/css/app.css 로 위치하게 된다. (laravel-mix 문서 보기)

추가로 browsersyc를 webpack.mix.js에 추가해서 브라우저 변경시 새로고침없이 자동으로 리로드되게 해주면 개발할때 편리하다.

//mix.browserSync('mydomain.com');
mix.browserSync('localhost:8000');

6.모델, 컨트롤러, 마이그레이션 생성하기
-r 옵션은 리소스컨트롤러로 컨트롤러에 기본적인 메서드가 자동으로 추가된다.

* 모델과 컨트롤러는 단수로, 마이그레이션은 복수로 표현한다.
모델          Article
컨트롤러      ArticleController
마이그레이션  create_articles_table
php artisan make:model Article
php artisan make:controller ArticleController
php artisan make:migration create_aticles_table
// 한번에 생성하기 -c: controller -r:resource controller -m:migration
php artisan make:model Article -c -m
또는
php artisan make:model Article -r -m

생성된 마이그레이션 파일을 열어 테이블 스키마를 지정해준다.

    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->text('title');
            $table->longText('content');
            $table->timestamps();
            $table->boolean('is_published')->nullable()->default(false);
        });
    }
    public function down()
    {
        Schema::dropIfExists('articles');
    }
//up 메서드는 migrate 명령어가 실행될 때 호출되고 down 메서드는 migrate:rollback 될 때 호출된다.

php artisan migrate 명령어를 실행해서 테이블을 생성하면된다. 이때 한번도 마이그레이션하지 않았으면 laravel에서 제공하는 테이블까지 함께 생성된다.

7. restful한 route 만들기

//인덱스 리스트
route::get('/article/', 'ArticleController@index')->name('article.index');
//글쓰기 폼
route::get('/article/create', 'ArticleController@create')->name('article.create');
//새로운글 저장
route::post('/article', 'ArticleController@store')->name('article.store');
//해당글 보기 
route::get('/article/{id}', 'ArticleController@show')->name('article.show');
//해당글 수정 폼
route::get('/article/{id}/edit', 'ArticleController@edit')->name('article.edit');
//해당글 업데이트
route::put('/article/{id}', 'ArticleController@update')->name('article.update');
//해당글 삭제
route::delete('/article/{id}/destroy', 'ArticleController@destroy')->name('article.destroy');

route파사드의 name메서드를 사용하는 편이 나중에 url 관리하기가 편하다.

8.view 생성하기
/resources/views 디렉토리에 이번프로젝트에 뼈대가 될 layout.blade.php 파일을 생성한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="{{ asset('css/app.css')}}">
    <script src="{{ asset('js/app.js') }}"></script>
    <title>Document</title>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container">
            <a class="navbar-brand" href="{{ route('article.index') }}">Navbar</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav">
                <li class="nav-item {{Request::is('article*')? 'active' : ''}}">
                    <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
                </li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container">
        @yield('content')
    </div>
    <div class="footer container mt-3 pt-3 text-center">
        <div class="text-muted">@laravel</div>
    </div>
</body>
</html>

/resources/views/article 디렉토리를 생성하고 앞에서 지정한 라우트에 필요한 뷰파일을 생성해준다.

//index.blade.php
@extends('layout')
@section('content')
리스트페이지
@endsection
//create.blade.php
@extends('layout')
@section('content')
글쓰기 폼
@endsection
//show.blade.php
@extends('layout')
@section('content')
글보기
@endsection
//edit.blade.php
@extends('layout')
@section('content')
글쓰기 수정 폼
@endsection
뷰파일은 /resources/views 내에 어디서든 위치해도 상관없는데 일관성을 위해서 article 디렉토리를 추가했다. 

9.기능구현하기

1)글쓰기 폼 (create.blade.php) 작성

  • ArticleController@create
    public function create()
    {
        return view('article.create');
    }
  • create.blade.php
@extends('layout')
@section('content')
    <form action="{{ route('article.store') }}" method="post">
        @csrf
        <div class="form-group">
          <label for="title">제목</label>
          <input 
          type="text"
          name="title"
          id="title"
          class="form-control @error('title') is-invalid @enderror"
          placeholder=""
          aria-describedby="title"
          value="{{ old('title') }}">
          @error('title')
          <p class="invalid-feedback">제목을 입력하세요</p>
          @enderror
        </div>
        <div class="form-group">
          <label for="content">내용</label>
          <textarea class="form-control @error('content') is-invalid  @enderror" name="content" id="content" rows="3">{{ old('content') }}</textarea>
          @error('content')
          <p class="invalid-feedback">내용을 입력하세요</p>
          @enderror
        </div>
        <button type="submit" class="btn btn-primary">저장</button>
    </form>
@endsection

form안에 @csrf 를 사용해서 csrf 토큰을 꼭 생성해야 한다.
validation처리를 위해서 @error, 글로벌 헬퍼함수 old() 부분을 잘 확인하자.
old()는 넘어온값을 세션에 저장해 두는데 validation에 통과하지 못했을 때 양식에 이전에 입력한 값을 다시 넣어주기 위해 사용한다.

2)저장하기 ArticleController@store

use App\Article; 
public function store()
    {
        request()->validate([
            'title' => 'required',
            'content' => 'required'
        ]);
        $article = new Article();
        $article->title = request('title');
        $article->content= request('content');
        $article->save();
        return redirect()->route('article.index');
    }

3)글보기

  • ArticleController@show 작성
    public function show($id)
    {
        $article = Article::find($id);
        return view('article.show', compact('article'));
    }
  • show.blade.php 작성
@extends('layout')
@section('content')
<h1>{{ $article->title }}</h1>
<small class="small text-muted">{{ $article->updated_at }}</small>
<div class="mt-3 pt-3 border-top">{{ $article->content }}</div>
@endsection

4)글 수정하기폼 ArticleController@edit

  • ArticleController@edit
   public function edit($id)
    {   
       $article = Article::find($id); 
        return view('article.edit', compact('article'));
    }
  • edit.blade.php
@extends('layout')
@section('content')
    <form action="{{ route('article.update', $article->id) }}" method="POST">
        @csrf
        @method('PUT')
        <div class="form-group">
          <label for="title">제목</label>
          <input 
          type="text"
          name="title"
          id="title"
          class="form-control @error('title') is-invalid @enderror"
          placeholder=""
          aria-describedby="title"
          value="{{ $erros->has('title') ? old('title') : $article->title }}">
          @error('title')
          <p class="invalid-feedback">제목을 입력하세요</p>
          @enderror
        </div>
        <div class="form-group">
          <label for="content">내용</label>
          <textarea 
          class="form-control @error('content') is-invalid  @enderror" 
          name="content" 
          id="content" 
          rows="3">{{ $erros->has('content') ? old('content') : $article->content}}</textarea>
          @error('content')
          <p class="invalid-feedback">내용을 입력하세요</p>
          @enderror
        </div>
        <button type="submit" class="btn btn-primary">저장</button>
    </form>
    <form action="{{ route('article.destroy', $article->id) }}" method="POST">
      @csrf
      @method('DELETE')
      <button type="submit" class="btn btn-danger">삭제</button>
    </form>
@endsection

@method 지시자를 이용해서 PUT메서드를 스푸핑하는것을 확인하자.

5)글 업데이트 ArticleController@update

public function update($id)
    {
        request()->validate([
            'title' => 'required',
            'content' => 'required'
        ]);
        $article = Article::find($id);
        $article->title = request('title');
        $article->content= request('content');
        $article->save();
        return redirect()->route('article.show', $article->id);
    }

5)글목록 페이지

  • ArticleController@index
public function index(){
        $articles = Article::orderBy('created_at', 'desc')->paginate(15);
        return view('article.index', compact('articles'));
    }
  • inedx.blade.php
@extends('layout')
@section('content')
@foreach($articles as $article)
<div class="card mb-3">
  <div class="row no-gutters">
    <div class="col-md-3">
      <img src="..." class="card-img" alt="">
    </div>
    <div class="col-md-9">
      <div class="card-body">
        <h5 class="card-title">
          <a href="{{ route('article.show', $article->id) }}">{{ $article->title }}</a>
        </h5>
        <p class="card-text">{{ $article->content }}</p>
        <p class="card-text"><small class="text-muted">{{ $article->updated_at }}</small></p>
      </div>
    </div>
  </div>
</div>
@endforeach
@endsection

6)해당글 삭제하기 ArticleController@destroy

    public function destroy($id)
    {
        $article = Article::find($id);
        $article->delete();
        return redirect()->route('article.index');
    }

10. 모델을 라우트에 바인딩 시키자

라우트의 {id} 부분을 {article}로 변경하고 컨트롤러 메서드에 $id로 전달한 파라미터를 Article $article 타입 힌트로 지정한다.

만약 route의 {article}을 {foo}로 지정했다면 컨트롤러의 타입힌트도 Article $foo로 변경해야 한다.

//web.php
route::get('/article/', 'ArticleController@index')->name('article.index');
route::get('/article/create', 'ArticleController@create')->name('article.create');
route::post('/article', 'ArticleController@store')->name('article.store');
route::get('/article/{article}', 'ArticleController@show')->name('article.show');
route::get('/article/{article}/edit', 'ArticleController@edit')->name('article.edit');
route::put('/article/{article}', 'ArticleController@update')->name('article.update');
route::delete('/article/{article}/destroy', 'ArticleController@destroy')->name('article.destroy');
//ArticleController.php
    public function index(){
        $articles = Article::orderBy('created_at', 'desc')->paginate(15);
        return view('article.index', compact('articles'));
    }
    public function create()
    {
        return view('article.create');
    }
    public function store()
    {
        request()->validate([
            'title' => 'required',
            'content' => 'required'
        ]);
        $article = new Article();
        $article->title = request('title');
        $article->content= request('content');
        $article->save();
        return redirect()->route('article.index');
    }
    public function show(Article $article)
    {
        $article = Article::find(Article $article);
        return view('article.show', compact('article'));
    }
    public function edit(Article $article)
    {   
       $article = Article::find(Article $article); 
        return view('article.edit', compact('article'));
    }
    public function update(Article $article)
    {
       request()->validate([
            'title' => 'required',
            'content' => 'required'
        ]);
        $article = Article::find(Article $article);
        $article->title = request('title');
        $article->content= request('content');
        $article->save();
        return redirect()->route('article.show', $article->id);
    }
    public function destroy(Article $article)
    {
        $article = Article::find(Article $article);
        $article->delete();
        return redirect()->route('article.index');
    }
라우트에 모델을 바인딩하면 라라벨은 자동으로 테이블의 id칼럼을 조회하게 된다. id가 아닌 다른 칼럼을 조회하게 하려면 model에 getRouteKeyName메서드를 오버라이드 해준다.
class Article extends Model
{
  function getRouteKeyName(){
    return 'title';
  }
}

11. 중복제거

validate와 게시글 저장, 업데이트 하는부분이 store와 update에서 중복이 발생했다. validation 부분을 메서드로 작성하여 중복을 제거하자.

public function index(){
        $articles = Article::orderBy('created_at', 'desc')->paginate(15);
        return view('article.index', compact('articles'));
    }
    public function create()
    {
        return view('article.create');
    }
    public function store()
    {
        Article::create($this->validateArticle());
        return redirect()->route('article.index');
    }
    public function show(Article $article)
    {
        return view('article.show', compact('article'));
    }
    public function edit(Article $article)
    {   
        return view('article.edit', compact('article'));
    }
    public function update(Article $article)
    {
        $article->update($this->validateArticle());
        return redirect()->route('article.show', $article->id);
    }
    public function destroy(Article $article)
    {
        $article->delete();
        return redirect()->route('article.index');
    }
    protected function validateArticle(){
        return request()->validate([
            'title' => 'required',
            'content' => 'required'
        ]);
    }

중복을 제거 했지만 글을 작성하거나 업데이트 하면 아래와 같이 에러가 발생할 것이다.

Add [title] to fillable property to allow mass assignment on [App\Article]. 

기본적으로 모든 Eloquent 모델은 대량 할당-Mass Assignment 으로부터 보호되기 때문에, 모델의 ‘fillable’나 ‘guarded’속성을 지정해야 주어야 한다.
fillable은 화이트리스트방식이고, guarded는 블랙리스트 방식이라고 생각하면 된다.

class Article extends Model
{
  protected $guarded = [];
  또는
  protected $fillable = ['title', 'content'];
}

[laravel/storage] 업로드된 파일 저장하기

파일스토리지 설정하기

  • 파일 시스템 설정파일위치 config/filesystem.php
  • 설정파일에서 이미지 저장 path, url등을 수정할 수 있다.

Public 디스크

  • public 디스크는 local드라이버를 사용하고 storage/app/public에 저장함.
  • 웹에서 파일을 서빙하려면 public/storage를 storage/app/public으로 심볼릭 링크를 생성해야한다. 아티즌명령으로 심볼릭링크를 생성할 수 있다.
php artisan storage:link

명령을 실행하고 public/storage를 확인하면 심볼링 링크가 생성된걸 확인 할 수 있다. 이제 파일을 업로드하고 처리하는 코드를 작성해보자.

#write.blade.php

<form enctype="multipart/form-data">
    <input type="file" name="file">
</form>
#controller

$request->file('file')->store('images', 'public');

store메소드의 첫번째 인자는 파일이 저장될 폴더명이다. 예제에서는 ‘images’로 지정했기 때문에 /storage/app/public에 images 폴더를 생성한 뒤 파일을 저장한다.
파일명은 자동으로 랜덤한 값이 들어가고 경로와함께 반환된다. 예) images/임의이름.jpg
두번째 인자는 저장에 사용할 디스크를 지정한다.

#view.blade.php // 파일 서빙

{{asset('storage/file.jpg')}}

asset(‘storage/file.jpg’)을 통해서 반환되는 이미지 경로는 mysite.com/storage/images/file.jpg 가 된다.

실제로사용하기

  • 글을저장하는 post테이블, 파일을 저장하는 file테이블이 있다.
  • post모델에 1:다 관계(hasMany)를 지정한다.
  • file모델에는 1:1 관계( belongsTo)를 지정한다.

뷰에서 값을 확인하면 객체를 포함한 배열을 볼수 있다. 아래와 같이 배열의 길이를 확인하고 처리하면된다.

@if(count($post->file))
    {{$post->file[0]->ori_fname}}
@endif

참고

파일스토리지
업로드된 파일 저장하기

[php]간단한 달력만들기

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>달력</title>
</head>
<body>
    <?php
    if(empty($_GET['date'])){ // 입력값이 없으면 오늘을 기준으로 한다
        $thisDay = date("Y-m-d");    
    } else {
        if(isset($_GET['btn'])){
            if($_GET['btn'] == 'prev'){ // 이전달 구하기
                $thisDay = date("Y-m-d", strtotime($_GET['date']." -1 month"));
            } elseif($_GET['btn'] == 'next'){ // 다음달 구하기
                $thisDay = date("Y-m-d", strtotime($_GET['date']." +1 month"));
            }
        } else {
            $thisDay = $_GET['date']; //입력한 날짜 가져오기
        }
    }
    
    $thisDayArry = explode('-', $thisDay);
    $thisY = $thisDayArry[0];
    $thisM = $thisDayArry[1];
    $thisD = $thisDayArry[2];

    $maxDay = date("t", strtotime($thisDay)); // 총일수
    $startWeek = date("w", strtotime($thisY."-".$thisM."-01")); // 1일은 무슨 요일인가
    $maxWeek = ceil(($maxDay+$startWeek)/7); // 총주수
    $endWeek = date("w", strtotime($thisY."-".$thisM."-".$maxDay));// 마지막일은 무슨 요일인가

    ?>
    <form action="./cal.php" method="GET">
        <a href="./cal.php?date=<?php echo $thisDay?>&btn=prev">prev</a>
        <input type="text" name="date" value="<?php echo $thisDay?>">
        <a href="./cal.php?date=<?php echo $thisDay?>&btn=next">next</a>
    </form>

    <table>
        <thead>
            <tr>
                <th>일</th>
                <th>월</th>
                <th>화</th>
                <th>수</th>
                <th>목</th>
                <th>금</th>
                <th>토</th>
            </tr>
        </thead>
        <tbody>
            <?php $day=1; ?>
            <?php for($i=1; $i<=$maxWeek; $i++){?>
                <tr>
                <?php for($j=0; $j<7; $j++){ ?>
                    <td>
                        <?php 
                        if(($i==1 && $j < $startWeek) || ($i==$maxWeek && $j> $endWeek) ){ // 첫째 주이고 j가 1일의 요일보다 작은 경우 패스 || 마지막 주 이고 j가 마지막일의 요일보다 크면 패스
                            echo '';
                        } else {
                            if($day == $thisD){ echo '<b>'; }
                            echo $day;
                            if($day == $thisD){ echo '</b>'; }
                            $day++;
                        }
                        ?>
                    </td>
                <?php } ?>
                </tr>
            <?php } ?>
        </tbody>
    </table>
</body>
</html>

참고사이트:
https://phpheaven.tistory.com/104

[그누보드5] 스마트에디터 filetype not allowed 에러

그누보드 게시판에 설치된 스마트에디터를 사용해서 이미지를 업로드하는중 ‘filetype not allowed’ 또는 ‘file_upload failed’ 에러가 나면서 이미지 업로드가 실패하는 상황이 생겼다.


주로 아래와 같은 이유로 문제가 발생하는 듯 하다.

  1. 파일타입이 잘못된 경우
  2. 업로드 용량제한이 있는 경우
  3. 메모리 제한이 있는 경우
# .htaccess

php_value upload_max_filesize 30M
php_value post_max_size 30M
php_value max_file_uploads 30
php_value memory_limit 512M

.htaccess 의 설정값을 추가해 해결함

[php] input이 1000개 이상 안넘어갈때

post로 넘긴 값을 확인해보는데 1000개까지만 출력이 되고 그 이상은 삭제되는 현상을 발견했다.

해결방법은 .htaccess 파일에 최대 개수 제한을 늘려주면된다.

#.htaccess

php_value max_input_vars 3000

3000개 까지 input으로 값을 넘겨줄수 있게 되었다.


참고한사이트

https://stackoverflow.com/questions/6135427/increasing-the-maximum-post-size

[php] db데이터 불러와 엑셀파일 만들기

db에 있는 데이터를 엑셀로 뽑아야하는 경우가 있어 구글링하면서 나름대로 방법을 찾아봤습니다.

style 속성을 통해 셀의 배경색, 높이, 넓이 등 스타일을 지정해 줄수도 있습니다.  이미지 위치조정을 위해서 margin이나 left 사용해 봤는데 적용되지 않았습니다.

<?php
include 'wp-config.php';
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$query = $mysqli->query("SELECT * FROM `wp_posts`");

header("Content-Type: application/xls");
header("Content-Disposition: attachment; filename=file.xls");
header("Pragma: no-cache");
header("Expires: 0");

?>
<table>
    <thead>
        <tr>
            <th>이미지</th>
            <th>제목</th>
            <th>날짜</th>
        </tr>
    </thead>
    <tbody>
        <?php 
        for ($i=0; $row = $query->fetch_array(MYSQLI_ASSOC); $i++) { 
            ?>
            <tr>
                <td style="width:100px; height: 100px;"><img src="http://placehold.it/100x100" alt=""></td>
                <td><?php echo $row['post_title'];?></td>
                <td><?php echo $row['post_date'];?></td>
            </tr>
            <?php
        }
        ?>
    </tbody>
</table>

한글이 깨지는 경우 header값을 수정하고  utf-8 BOM 객체를 생성합니다.

<?php
header('Content-Encoding: UTF-8');
header("Content-Type: application/xls; charset=UTF-8");    
header("Content-Disposition: attachment; filename=file.xls");  
header("Pragma: no-cache"); 
header("Expires: 0");
echo "\xEF\xBB\xBF"; // UTF-8 BOM
?>