"php" 태그로 연결된 49개 게시물개의 게시물이 있습니다.
모든 태그 보기Laravel 5.5로 업그레이드
Laravel 5.4 에서 5.5 로 업그레이드 후기
업그레이드
공홈을 참조해도 된다.
composer.json
에서 아래 패키지의 버전을 바꿔준다.
dependencies
- laravel/framework: 5.5.*
- phpunit/phpunit: ~6.0
dev-dependencies
- filp/whoops: ~2.0
composer clearcache
composer update
이슈
Session, DB 문제
php artisan cache:clear
const UPDATE_AT 문제
const UPDATED_AT = null;
처럼 timestamps 필드 중 하나를 disable 했을 때 5.5 버전에선 오류가 발생한다.
아래 처럼 모델에 setUpdatedAt
함수를 추가해주면 된다.
<?php
public function setUpdatedAt($value) {
return $this;
}
request has 문제
request->has
와 같은 기능으로 동작하려면 request->filled
로 바꿔줘야한다.
<?php
// 5.5에서는 name 값이 비던 안 비던 true
if ($request->has('name')) {
}
// 이게 구버전 has의 기능과 동일하다.
// name 값이 있을 경우만 true
if ($request->filled('name')) {
}
Laravel - Socialite InvalidStateException
가끔 가다가 인증이 안 되는 경우가 있다.
해결법
stateless
Socialite::driver('인증 타입')->stateless()->user();
로 가져오자.
session 명 변경
config/session.php
의 cookie 값을 변경해준다.
session domain 변경
config/session.php
의 domain 값을 null 에서 내 도메인으로 변경한다.
그리고 아래 두 명령어를 실행해주자.
php artisan cache:clear
composer dump-autoload
여담
socialite 설명에선 나오지 않았지만, Socialite 구문을 try/catch
로 감싸주는게 좋았다.
<?php
try {
$user = Socialite::driver('facebook')->stateless()->user();
} catch (\Exception $e) {
return redirect()->route('login');
}
Laravel Query Logging, 쿼리 로그
Laravel DebugBar 를 이용하는게 편하지만 dump 나 json 리턴시에 DebugBar 가 보이지 않으므로 직접 찍어줘야하는 경우가 많다.
소스
<?php
## DB 파사드를 추가한다.
use DB;
...
public function your_func(Request $request) {
// 로그를 enable 시키고
DB::enableQueryLog();
// 쿼리를 여기에 실행한다.
Member::where('조건', '값')->get();
Product::find(1);
// 쿼리 로그를 찍는다.
$queryLogs = DB::getQueryLog();
dump($queryLogs);
}
결과
배열에 query, bindings (preparedStatement 를 위한 것), time 이 상세하게 나온다.
Laravel 5.5 - Debugbar와 BrowserSync의 충돌 해결
Laravel Mix로 browserSync 옵션을 활성화 시에 Debugbar가 생기지 않는 오류가 발생할 경우 (스크립트 단에서 JSON parse 오류가 발생한다) 다음과 같이 설정해주면 된다.
해결
mix.browserSync({
proxy: {
// artisan serve시의 주소
target: "localhost:8000",
reqHeaders: function () {
// host를 직접 지정해준다.
return {
host: "localhost:3000",
};
},
},
});
Laravel 5.5 - Log Permission 문제
웹 서버의 유저로 로그 파일이 생성되어야 하는데, 어느 순간부터 root:root 권한을 달고 daliy log 가 생성되는 경우가 있다.
여러가지 경우의 수가 있는데, 맞는 조건을 찾아서 Permission 오류가 발생하지 않게 처리해보자.
selinux
storage 에는 쓰기권한이 있어야한다.
chcon -R -t httpd_sys_rw_content_t storage
WebServer user
웹 서버의 유저가 다르게 설정 되어있을 때 권한이 바뀔 수 있다. 서버 설정을 열어서 유저가 제대로 설정되어 있는지 확인해보자.
user nginx;
php-fpm user
php-fpm 에서 설정하는 user 와 group 이 다르게 설정 되어 있을 때 권한이 바뀔 수 있다. php-fpm 설정을 열어 유저가 제대로 설정되어 있는지 확인해보자.
user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx
log rotate
로그 파일이 너무 커지는 걸 막기위해 log rotate 설정이 되어있다면 권한이 바뀔 수 있다. logrotate 가 cron 에 물려 있는지 설정을 확인해보자.
$ pwd
/etc/logroate.d
$ vi nginx
$ vi php-fpm
cron 사용시
crontab 의 경우 root 유저로 실행이 되면 cron 에서 Laravel 을 호출할 때 log 가 root 권한으로 생성될 수 있다.
로그 분기
log 파일을 생성하는 프로세스별로 분기해서 해결할 수 있다. Stackoverflow 참조
$app->configureMonologUsing(function(Monolog\Logger $monolog) {
$filename = storage_path('logs/laravel-'.php_sapi_name().'.log');
$handler = new Monolog\Handler\RotatingFileHandler($filename);
$monolog->pushHandler($handler);
});
설정을 추가해 놓으면 logs 폴더 하위에 다음과 같이 로그가 분기되어 생성된다.
$ ls -al .
-rw-r--r-- 1 nginx nginx 718 Aug 18 10:56 laravel-fpm-fcgi-2017-08-18.log
cron 은 root 에서 실행되나 user shell 에서 Laravel 프로세스를 실행하는 경우 root 에 의해 log 가 생성되었다면 다음과 같이 permission 을 변경해서 생성해야한다. Post 참조
$app->configureMonologUsing(function(Monolog\Logger $monolog) {
$filename = storage_path('/logs/laravel-' . php_sapi_name() . '.log');
// 5번째 파라미터로 666 권한을 넘긴다.
$handler = new Monolog\Handler\RotatingFileHandler($filename, 0, \Monolog\Logger::DEBUG, true, 0666);
$monolog->pushHandler($handler);
});
RotatingFileHandler 의 Parameter 는 여기를 참조하자.
666 으로 생성 시엔 굳이 php_sapi_name()
을 사용하지 않아도 된다. (rw 권한이 모두에게 있으니까)
setfacl
다른 해결 방법으로는 ACL 을 수정해 logs 폴더 자체를 해당 user:group 이 편집할 수 있게 처리하면 된다.
$ pwd
/public_html/storage/logs
$ setfacl -d -m u:nginx:rwx .
$ getfacl .
default:user:nginx:rwx
## 삭제
$ setfacl -d -x u:nginx .
여담
Laravel 프로젝트 시작시 bootstrap/app.php
안에 로그를 분기 로직을 넣고 개발하는 게 좋아보인다.
Docker로 LEMP Stack 구축하기
이 포스트 전에 웹서버 세팅을 하나씩 설치해서 띄워보는 걸 권장하고 Docker, SSH Login, LetsEncrypt, sed 명령어의 사용법을 알고 있어야 한다. 구성할 서버 스택은 다음과 같다.
- Docker
- Docker-compose
- Host 에 사용될 Linux (Centos7)
- Alpain Linux
- Nginx ^1.13
- MariaDB ^10.2
- PHP ^7.1
- Laravel =5.4
- LetsEncrypt
- HTTP2
- Redis
Docker 설치
이전 포스트를 참조하자.
Container 쇼핑
Docker Hub에서 마음에 드는 Container 를 사용해도 되지만, 생각처럼 돌아가는 Container 는 다음과 같았다.
- nginx-php-fpm (2M)
- official mariadb (10M)
- bitnami redis (500K)
왜 Laradock을 안 썼죠?
- Laradock 에서 caddy 를 사용하지 않고 nginx 와 certbot 만을 이용해 http2 환경을 구성하는 예제가 없었다.
- 그래도 시도해봤으나 certbot 인증시에 DocumentRoot 를 잡지 못하는 현상을 삽질로 매꿀 시간이 없었다.
- Git repo 를 Clone 받아서 Docker-compose 로 Container 를 구동하기 때문에 추후 ECS 에 적용할 수가 없는 구조였다.
- 직접 구축해보고 싶었다.
세팅
nginx-php-fpm
Laravel 용 및 튜닝을 위해 Docker hub 의 이미지 대신 Git repo 의 이미지를 Clone 해서 세팅을 해보자.
내용 추가 중..
Laravel 5.5 - 다형성 관계
댓글 테이블이 있고 이 댓글은 여러 테이블에서 사용된다고 치자. 그럼 댓글 테이블에 type과 type_id를 가져가야할 것이다. 이 때 사용할 수 있는 관계가 다형성 관계(릴레이션)인데, 공식 문서의 설명이 조금은 부족하다고 느꼈다. 파헤쳐보자.
morphTo
morphTo는 type과 type_id를 가진, 여러 테이블로 연결되어야할 테이블에서 사용하는 릴레이션 메소드이다. 공식 문서에는 데이터를 가져온 뒤 릴레이션을 연결하는 예시만 있고, Eager 로딩 (With 구문을 사용하는 방법) 후 specific한 필드를 사용하게 변경하는 경우에 대한 정보는 없다.
기본 문법
<?php
class YourModel extends Model {
...
// 다형성 관계를 가질 함수를 data로 정의했다
public function data() {
return $this->morphTo();
}
}
이렇게 정의시에 YourModel::with('data')->get()
으로 호출하면 불러와져야되지만, 필드명, 모델명이 정확하지 않으면 쿼리 호출조차 되지 않는다.
(심지어 에러도 발생하지 않는다)
필드명 정의
먼저 morphTo의 소스코드를 를 살펴보자.
<?php
/**
* Define a polymorphic, inverse one-to-one or many relationship.
*
* @param string $name
* @param string $type
* @param string $id
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null)
name, type, id를 파라미터로 받는다. 그럼 파라미터를 넘겨보자.
<?php
class YourModel extends Model {
...
public function data() {
// morphTo의 paremeter로 null, 타입필드명, 타입인덱스 필드명을 넘긴다.
return $this->morphTo(null, 'type', 'type_idx');
}
}
여기서 name엔 도대체 뭘 넣어야 되는거야? 라고 의문이 생길 수가 있다. 함수 내에서 name 변수는
$this->getMorphs(Str::snake($name), $type, $id);
에만 딱 한 번 사용된다.
getMorphs 함수를 따라가보자.
<?php
protected function getMorphs($name, $type, $id) {
// $type과 $id가 명시되면 그 값을 먼저 반환한다.
return [$type ?: $name.'_type', $id ?: $name.'_id'];
}
주석처럼 type과 id가 명시되면 name값은 사용되지 않는 쓰레기 값이 되어버린다.
따라서 null
로 넘겨주면 된다.
타입-모델 바인딩
타입과 인덱스를 명시하면 드디어 오류메세지가 노출된다. 내가 정의한 type명을 가진 Class가 없다 라는 내용인데, 이제 타입과 모델을 연결시켜보자.
이 때 사용할 수 있는 메소드가 공식 문서에서 조금 스크롤을 내리면 있는 Custom Polymorphic Types에 잘 설명되어 있다.
하지만 등록하는 부분에 대한 설명이 **You may register the morphMap in the boot function of your AppServiceProvider or create a separate service provider if you wish.**라고 되어있다. 즉 AppServiceProvider에 넣던지 Service Provider로 생성이다.
한 모델에만 쓸 건데 전체에 등록을 할 필요가 없으니, 사용할 모델에 기능을 넣어보자.
// Relation을 사용해야한다.
use Illuminate\Database\Eloquent\Relations\Relation;
<?php
class YourModel extends Model {
// 이 메소드는 모델이 initialize될 때 실행된다.
protected static function boot() {
parent::boot();
// 여기에 타입 별로 모델을 바인딩한다.
Relation::morphMap([
// type이 product일 경우 id는 product_id를 가리킨다.
'product' => 'App\Models\Product',
// type이 order일 경우 id는 order_id를 가리킨다.
'order' => 'App\Models\Order'
]);
}
public function data() {
// morphTo의 paremeter로 null, 타입필드명, 타입인덱스 필드명을 넘긴다.
return $this->morphTo(null, 'type', 'type_idx');
}
}
완벽해졌다. 이제 오류 없이 실행되는 것을 확인할 수 있다.
morphOne
문서 상에는 설명 되지 않은 morphOne 이란 메소드도 있다. morphMany는 관계가 설정된 값을 배열로 반환하지만 morphOne은 하나의 데이터로 반환한다. (hasOne과 hasMany처럼)
구조는 다음과 같다.
<?php
public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
morphToMany
morphedByMany
여담
다대다 다형성 관계 메소드 (morphToMany, morphedByMany)의 경우는 나중에 사용하게 되면 정리해야겠다. Relation 메소드들은 문서를 대충 훑고 API Docs를 직접 까보는게, 효율적인 것 같다.
Laravel 5.5 - Multi DB Connection
여러 데이터베이스에서 데이터를 가져와야되는 경우가 있다. (마이그레이션 또는 발송 모듈 DB의 분기 등등) 라라벨에선 아주 쉽게 설정이 가능하다.
설정
config/database.php에 새로운 커넥션 정보를 넣어주자.
새 커낵션은 mysql_new
라고 이름지었다.
<?php
return [
...
'connections' => [
// 기본 커넥션
'mysql' => [
...
],
// 새 친구
'mysql_new' => [
'driver' => 'mysql',
'host' => '111.111.111.111',
'port' => '3306',
'database' => 'test',
'username' => 'test',
'password' => 'test1234',
'unix_socket' => '',
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'prefix' => null,
'engine' => null
],
],
];
모델
새 커넥션에 사용할 모델을 만들어주고, 모델에서 연결할 커넥션을 설정해주자.
<?php
...
class OldMember extends Model
{
// 커넥션 변수를 다시 설정해주면 끝
protected $connection = 'mysql_new';
protected $table = 'test';
...
}
사용
기존 모델 사용법과 똑같다. 아주 간단하다.
number_format의 반대 함수
number_format 으로 쉽게 comma 가 들어간 숫자를 만들 수 있는데, 이 반대 방법은 preg_replace 를 통해 comma 를 제거한 뒤에 다시 int 로 형변환을 해야한다.
더 간단하게 변경할 수 있는 방법은 바로 filter_var를 사용하는 것이다.
소스
<?php
$formatted_nubmer = number_format(10000);
echo $formatted_number; // 10,000;
$number = filter_var($formatted_nubmer, FILTER_SANITIZE_NUMBER_INT);
// FILTER_SANITIZE_NUMBER_INT 상수가 너무 길어서 외우기가 힘들다면 519
$number2 = filter_var($formatted_nubmer, 519);
echo $number; // 10000;
echo $number2; // 10000;
필터 옵션
필터 상수에 대해 궁금해졌다면 공홈을 참조하자.