반응형

회원가입, 상품등록을 할 때 빠질수 없는게 유효성검사!

 

JAVA에서는 Controller랑 DTO부분에서 유효성검사를 처리하였었는데

Laravel에서는 Controller에서 Validate를 사용한다

단순 공백, 정규식검사 뿐만 아니라 테이블에 값의 여부도 판단해주는 생각보다 편한 기능일지도🤔

 

우선 view blade와 JS의 ajax는 아래와 같이 간단하게 구성

라벨값은 localization 처리했으니 알아서 생각해주시길

<div class="container mt-5 w-75">
    <form data-action="/insertClient" method="POST" enctype="multipart/form-data" id="clientForm">
        @csrf
        <div class="input-group row mb-3">
            <label class="col-2 text-center" for="clientName">
                {{__('custom.label.client-name')}}
            </label>
            <input class="input-group-text col-8" type="text" id="clientName" name="clientName">
        </div>
        <div class="input-group row mb-3">
            <label class="col-2 text-center" for="clientPwd">
                {{__('custom.label.password')}}
            </label>
            <input class="input-group-text col-8" type="password" id="clientPwd" name="clientPwd">
        <div class="col-1" id="pwdView">
            <i class="bi bi-eye" id="pwdViewIcon" style="cursor:pointer;"></i>
        </div>
        </div>
        <div class="input-group row mb-3">
            <label class="col-2 text-center" for="clientEmail">
                {{__('custom.label.email')}}
            </label>
            <input class="input-group-text col-8" type="text" id="clientEmail" name="clientEmail">
        </div>
        <button class="btn btn-secondary btn-lg ml-2" type="button" style="float:right;" onclick="location.href = '/showClient'">
            {{__('custom.button.back')}}   
        </button>
        <button class="btn btn-primary btn-lg" type="submit" id="submit" style="float:right;">
            {{__('custom.button.enroll')}}
        </button>
    </form>
</div>

 

$(document).ready(function(){

    var form = '#clientForm';

    $('form').on('submit', function(event){
        event.preventDefault();

        let url = $(this).attr('data-action');
        let data = new FormData(this);
        console.log(data);

        $.ajax({
            url: url,
            method: 'post',
            dataType: 'json',
            processData: false,
            contentType: false,
            data: new FormData(this),
            success: function(result){
                alert(result.message);
                location.href = '/showClient';
            },
            error: function(request, status, error){
                styleInit();
                let resText = JSON.parse(request.responseText);
                let errors = resText.errors;

                for(let key in errors) {
                    console.log(key+"error >>> "+errors[key]);
                    $('label[for='+key+']').attr('style','color:red');
                    
                } 
            }
        })
    });

    $('#pwdView').click(function(){
        let icon = $('#pwdViewIcon');
        if(icon.hasClass('bi-eye')){
            $('#pwdViewIcon').removeClass('bi-eye');
            $('#pwdViewIcon').addClass('bi-eye-slash');
            $('#clientPwd').attr('type', 'text');
        } else{
            $('#pwdViewIcon').removeClass('bi-eye-slash');
            $('#pwdViewIcon').addClass('bi-eye');
            $('#clientPwd').attr('type', 'password');
        }
    });
});

function styleInit(){
    $('label').removeAttr('style');
}

 

 

Controller로 가보자

name, pwd, email은 모두 필수값(required) email은 email 양식에 맞도록 email을 추가하였다

여기서 email값은 DB 테이블에서 unique로 설계 되어있어서 확인이 필요했는데

처음엔 DB SELECT문으로 조회하는 코드를 사용했었다

        $emailChk = DB::select(DB::raw(
            "SELECT COUNT(client_email) AS count 
            FROM client
            WHERE client_email='".$params['clientEmail']."'"
        ));
        $emailCtn = $emailChk[0]->count;
        if($emailCtn>0){
            return json_encode(array('code'=>600, 'message'=>'登録されているクライアントです。'));
        }

여기서 validate unique를 사용한다면 한줄로 중복체크가 가능하다..!

unique:{table},{column}

만약 반대로 테이블에 값이 없으면 에러를 발생시키고 싶다면?

unique와 동일한 방법으로 exists를 사용하면 된다

exists:{table},{column}

이것도 굉장히 유용할거 같다

 

그렇게 짠 Controller 코드가 이거

    public function insert(Request $request) {

        $request -> validate([
            'clientName' => 'required',
            'clientPwd' => 'required',
            'clientEmail' => 'required|email|unique:client, client_email'
        ]);
    }

중복 테스트를 위해 email에 기존 테이블에 있는 이메일을 입력 후 submit 버튼을 눌렀을때 아래와 같은 에러가 발생하였다

[previous exception] [object] (PDOException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column ' client_email' in 'where clause' at C:\\project\\mallCRM\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Connection.php:368) [stacktrace]

WHERE절이 뭐가 문제일까 엄청 해멨는데

설마설마해서 unique:client, client_email 여기 쉼표 다음 스페이스바를 없애니까 오류가 사라졌다......

생각해보니까 홑땀('') 안에 있는 문장이니까 스페이스바를 조심해야하는것 같다

 

아무튼! 

user123@naver.com이라는 이메일이 테이블에 등록되어있는 상태에서 아래와 같이 입력 후 등록 버튼을 누른다면?

이렇게 데브툴에서 이쁜 에러를 확인 할수있다😎

 

에러가 발생하면 그다음 코드는 실행 안되니까 에러처리만 뷰쪽에 잘 보여주면 된다

 

 

참고자료

https://reffect.co.jp/laravel/laravel_validation_understanding/

 

반응형
반응형

깃허브에 Laravel-JS-Localization라는 라이브러리가 있는데

적용이 잘 안되어서 다른방법을 찾다가 web.php에서 설정하는 방법을 찾았다!

방법은 매우 간단!!

 

 

lang파일 설정

기본적으로 resources-lang-en이 존재하는데, 내가 사용할 언어폴더를 만든 후

en에 있던 기본php 파일들을 복사하였다

(쓸지 않쓸지는 의문.. 우선 복사한 이유는 세팅한 언어가 영어로 나올지 해당 언어로 나올지 테스트용!)

그리고 실제로 사용할 언어변수들은 custom.php에 모아두고 싶었기 때문에 생성하였는데 이건 취향껏!

 

config - app.php의 locale 설정

기본은 영어(en)으로 세팅 되어있고, 나같은 경우 locale만 변경하였다

//기본으로 세팅할 언어
'locale' => 'ja',

//세팅한 언어를 불러오는데 실패하였을때 출력될 언어
'fallback_locale' => 'en',

//라이브러리 언어설정
'faker_locale' => 'en_US',

 

public-routes-web.php

lang 정보를 캐시에 저장해주는 부분이다

Cache::rememberForever를 사용하면 자동갱신하여 캐시에 저장한다고 적혀있었는데 테스트를 해보니 갱신이 안되는것 같았다

코멘트를 보니까 다들 Cache::forget('lang.js') 구문을 추가하길래 또 검색검색..

rememberForever은 캐시가 수동으로 지워질 때까지 갱신되지 않는 함수로 캐시를 지워주는 Cache::forget으로 먼저 지워줌으로서 자동갱신 역할을 할수 있게 추가하니까 아주 잘 된다!

로그를 찍어보면 $strings에 키와 값으로 된 배열로 저장된것을 확인 할 수 있다.

21번째줄의 echo 안에 있는 구문으로 js에서 호출하면 된다!

// Localization
Route::get('/js/lang.js', function () {
    Cache::forget('lang.js');
    $strings = Cache::rememberForever('lang.js', function () {
        $lang = config('app.locale');

        Log::info('Language set >>> '.$lang);

        $files = glob(resource_path('lang/' . $lang . '/*.php'));
        $strings = [];

        foreach ($files as $file) {
            $name = basename($file, '.php');
            $strings[$name] = require $file;
        }

        return $strings;
    });

    header('Content-Type: text/javascript');
    echo('window.lang = ' . json_encode($strings) . ';');
    exit();
})->name('assets.lang');

 

app.blade.php

위에서 만든 루트 경로를 호출하는 구문을 추가한다.

app.blade.php에서 라이브러리 구문을 작성하여서 해당 구문도 여기에 추가하였다.

 <head>
    <!-- localization -->
    <script src="/js/lang.js"></script>
 </head>

 

호출 예시

custom.php에서 작성한 label 안의 password 문구를 가져온다고 하자

<?php
    return [
        'menu' => [

        ],
        'label' => [
            'password' => 'パスワード',
            'email' => 'e-mail',
        ],
        'button' => [
            'enroll' => '登録',
            'back' => '戻る'
        ],
        'message' => [

        ],
    ]
?>
lang.custom.label['password']

결과

 

잡담

만약 ja폴더안의 custom.php에 언어를 설정하였는데 en폴더에 custom.php파일이 없는 상황에서 locale을 ja->en으로 변경하면 fallback_locale이 될까?

결과는 JS 구문오류가 발생한다.

이점도 고려해서 구조를 짜는게 좋을것 같다.

다국어를 사용하지 않을 경우는 아예 locale을 전부 ja로 설정한다던가..

 

localization 말 그대로 지역화이지만 전직장에서는 지역화가 굳이 필요하지 않은 어플리케이션이였지만 이런식으로 화면에 보여지는 문구를 설정하였다.

'확인', '취소', '등록되었습니다.' 등등 자주 사용되는 언어가 만약 클라이언트의 요청에 의해서 변경해야하는 경우 사용된 페이지를 하나하나 들어가서 고치는것도 고역일 것이다.

그런 의미에서 지역화가 필요하지 않은 어플리케이션이라고 해도 이곳에서 언어세팅을 해두면 여러모로 편할것 같다고 생각한다.

 

 

참고자료

https://medium.com/@serhii.matrunchyk/using-laravel-localization-with-javascript-and-vuejs-23064d0c210e

 

Using Laravel localization with JavaScript and VueJS

We do know that Laravel has a wonderful localization support out of box which by default is located in resources/lang folder. We can use it…

medium.com

https://blog.capilano-fw.com/?p=1344 

 

永久保存版!Laravel・Cacheの使い方大全

さてさて、ウェブサイト開発でページ表示の速度を向上させるためによく「キャッシュ」を利用することがあります。 キャッシュとは、時間がかかる計算結果を一時的に保管しておいて、一

blog.capilano-fw.com

https://laravel.com/docs/10.x/cache

 

Laravel - The PHP Framework For Web Artisans

Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small things.

laravel.com

 

반응형
반응형

주문번호, 주문일자, 주문자이메일이 있는 orders 테이블에서 이후 주문내역이 있으면

몇일만에 구입한 것인지 일수 계산이 하고 싶었다

 

처음엔 일수를 컬럼으로 저장해야하나? 싶었는데 MySQL LEAD 함수를 사용하니 민망할정도로 간단하게 구할수 있었다

 

ord_no distinct 처리를 한것은 orders 테이블 PK가 다른 키여서 중복값이 있기 때문

1회만 구입한 경우는 계산할 필요가 없기 때문에 LEAD 함수로 불러온 값이 null이 아닌경우만 출력했다

SELECT ord_no, ord_date, ord_next, DATEDIFF(ord_next, ord_date) AS date_diff, ord_email
FROM (
	SELECT ord_no, ord_date, LEAD(ord_date, 1) OVER(partition by ord_email order by ord_date) ord_next, ord_email
	FROM (
		SELECT distinct ord_no, ord_date, ord_email
	FROM orders) A) B
WHERE ord_next is not null;

 

새로운 함수 알아갈 때마다 신기하고 재밌음!

 

참고자료

https://it-mi.tistory.com/56

 

MySQL | LEAD, LAG 윈도우 함수

1. MySQL LEAD Function LEAD()함수는 현재 행에서 여러 행을 보고 해당 행의 데이터에 액세스 할 수 있는 윈도우 함수입니다. LAG()함수와 비슷하며, LEAD()기능은 현재 행과 동일한 결과 집합 내의 후속 행

it-mi.tistory.com

 

반응형
반응형
##지난말 말일
LAST_DAY(NOW() - INTERVAL 1 MONTH),
##지난날 1일
date_format(NOW()-INTERVAL 1 MONTH, "%Y-%m-01")

 

참고자료

https://mo-world.tistory.com/entry/mysql-%ED%95%9C%EB%8B%AC%EC%A0%84-%ED%95%98%EB%A3%A8%EC%A0%84-%ED%95%9C%EC%8B%9C%EA%B0%84%EC%A0%84

 

mysql 한달전 하루전 한시간전

mysql 한달전 where reg_date >= date_add(now(), interval -1 month) 하루전 where reg_date >= date_add(now(), interval -1 day) 한시간전 where reg_date >= date_add(now(), interval -1 hour) +이면 후가 되겠죠! 기타 날짜 조건 select date_fo

mo-world.tistory.com

 

반응형
반응형

컴포저 명령어 실행(프로젝트 path에서 실행!!)

composer require barryvdh/laravel-debugbar --dev

.env 디버그값 true 설정

APP_DEBUG=true

 

적용된 화면

 

라라벨 처음 사용해보는데 로그 찍은거 어디서 보나 했더니 여기서 보면 쉽게 볼수있었다

지금까지 storage/logs에서 봄...^^.....

사용법은 직접 쓰면서 참고자료 블로그 참고해야겠다

 

 

참고자료

https://anko3899.tistory.com/420

 

디버그바 설치

1. 컴포저 설치composer require barryvdh/laravel-debugbar --dev 2. .env 3. 디버그바 설치화면

anko3899.tistory.com

https://jjomnoon-diary.tistory.com/19

 

[Laravel] 라라벨 디버그 바, 디버그 페이지 정리

모든 개발자는 디버깅이 필수고, 어떻게 디버깅하냐에 따라 디버그 실력을 알 수 있다. 개인적으로는 디버깅실력도 개발이라고 생각한다 ㅎ.ㅎ 하지만! php는 디버깅이 너~~~~~~~~무 불편하다. 오

jjomnoon-diary.tistory.com

 

반응형
반응형

 

 

마이그레이션 생성

php artisan make:migration create_sales_view

 

create, drop문 작성

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

class CreateSalesView extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::statement("DROP VIEW IF EXISTS sales_summary");
        DB::statement("CREATE VIEW sales_summary AS
                        SELECT 
                            ord_email AS ord_email,
                            (CASE
                                WHEN (COUNT(ord_no) = 1) THEN 'F1'
                                WHEN (COUNT(ord_no) = 2) THEN 'F2'
                                ELSE 'F3'
                            END) AS F,
                            CONCAT(YEAR(ord_date), '-', MONTH(ord_date)) AS sales_month,
                            SUM(ord_total_price) AS total_sales_price
                        FROM orders
                        GROUP BY ord_email, ord_date");
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::statement("DROP VIEW IF EXISTS sales_summary");
    }
}

 

참고자료

https://cakephper.hatenadiary.org/entry/20150731/1438347861

 

LaravelのマイグレーションでView Tableを作成する - cakephperの日記(CakePHP, Laravel, PHP)

Laravel5で試していますがLaravel4でも問題なく動くと思います。複雑なテーブル構成の場合、LaravelのEloquentなどで頑張ってリレーションの情報を定義して取得するのも良いのですが、書き間違え

cakephper.hatenadiary.org

 

반응형
반응형

* 데이터 베이스는 연결과 Model, Migration 설정은 마친상태

 

Laravel Excel Install(laravel8 이전까지)

composer require maatwebsite/excel

 

ajax로 url을 호출하기 위해 form에 넣지 않았다

<div class="container">
    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <input type="file" class="custom-file-input" id="upFile" name="upFile" required>
            <button type="button" class="btn btn-secondary btn-lg" id="upCsv">UPLOAD</button>
        </div>
    </div>
</div>

 

버튼 클릭시 ajax 호출

로딩 모달은 아래 사이트를 이용했다

https://www.jqueryscript.net/loading/Fullscreen-Loading-Modal-Indicator-Plugin-For-jQuery-loadingModal.html

 

Fullscreen Loading Modal / Indicator Plugin For jQuery - loadingModal

loadingModal is a simple yet customizable jQuery loading indicator plugin which displays a fullscreen loading modal / overlay with more than 10 CSS3 powered loading spinners.

www.jqueryscript.net

로딩모달 라이브러리 추가

<link rel="stylesheet" href="library/jquery.loadingModal.css">
<script src="library/jquery.loadingModal.js"></script>
$(document).ready(function(){
    $('#upCsv').click(function(e){
    	//php에 가져갈 값 세팅
        let fileInput = ($("#upFile")[0]).files[0];
        let mallTypeInput = $('input[name=mallType]:checked').val();
        let formData = new FormData();
        formData.append('upFile', fileInput);
        formData.append('mallType', mallTypeInput);

        $.ajax({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            },
            url: '/orderList/upload',
            type: 'POST',
            dataType: 'html',
            enctype: 'multipart/form-data',
            processData: false,
            contentType: false,
            data: formData,
            beforeSend: function(){
                $('body').loadingModal({
                    text: '데이터를 불러오는 중'
                  });
            },
            success: function(result) {
                $('body').loadingModal('hide');
                alert(result);
                location.href = '/orderList';
            },
            error: function(e) {
                $('body').loadingModal('hide');
                alert('ERROR CHECK');
                location.href = '/orderList';
            }
        });
    });
});

 

순서로 보자면

1. 버튼 클릭시 OrderController의 parseImport 함수 호출

2. parseImport에서 엑셀을 1000개 단위로 나눈 후 return redirect("import") 반환

3. import 함수 호출 후 나눈 엑셀을 DB에 저장하고 파일 삭제

 

web.php 

Route::post('/orderList/upload', [OrderController::class, 'parseImport']);
Route::get('import',  [OrderController::class, 'import']);

Controller

<?php

namespace App\Http\Controllers;

use App\Imports\OrdersImport;
use App\Jobs\ProcessImportJob;
use App\Models\Order;
use Database\Seeders\OrderSeeder;
use Exception;
use GuzzleHttp\Psr7\Message;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use SplFileObject;

class OrderController extends Controller
{
    public function parseImport(Request $req)
    {        
        $path = $req->file('upFile')->getRealPath();
        $file = file($path);
        $data = array_slice($file, 1);

        $parts = array_chunk($data, 1000);
        $i = 1;

        foreach($parts as $line) {
        	//임시로 저장할 파일의 파일명 중복 방지 
            $filename = base_path('database/csv/'.date('y-m-d-H-i-s').$i.'.csv');
            file_put_contents($filename, $line);
            $i++;
        }
        return redirect("import");
    }

    public function import()
    {    	
        $path = base_path('database/csv');   
        $fileNameArr = glob($path.'/*.csv');
        
        foreach ($fileNameArr as $file) {
            $csv_content = new SplFileObject($file);
            
            $csv_content->setFlags(
                \SplFileObject::READ_CSV |
                \SplFileObject::READ_AHEAD |
                \SplFileObject::SKIP_EMPTY |
                \SplFileObject::DROP_NEW_LINE
            );
            $lists = [];

            foreach ($csv_content as $i => $line) {
                if ($i >= 0) {
                    $lists[] = [
                        'ord_no'            => $line[0],                    //注文番号
                        'mall_id'           => '1',                         //==>後で削除
                        'ord_date'          => $line[1],                    //注文日
                        'pay_price'         => $line[2] ? $line[2] : 0,     //請求金額
                        'ord_total_price'   => $line[3],                    //合計金額
                        'point_price'       => $line[4] ? $line[4] : 0,     //ポイント利用額
                        'coupon_price'      => $line[5] ? $line[5] : 0,     //クーポン利用総額
                        'ord_email'         => $line[6],                    //注文者メールアドレス
                        'product_id'        => $line[7],                    //商品明細ID
                    ];
                }
            }

            DB::transaction(function () use ($lists) {
                $pack = [];
                $inserts = 0;
                foreach($lists as $list){
                    $pack[] = $list;
                    if(count($pack) >= 1000){
                        $inserts += count($pack);
                        Order::upsert(
                            //데이터
                            $pack,
                            //update조건
                            ['ord_no', 'product_id'],
                            //update 또는 insert 할 columns 
                            ['mall_id', 'ord_date', 'pay_price', 'ord_total_price', 'point_price', 'coupon_price', 'ord_email']);
                        $pack = [];
                    }
                }
                $inserts += count($pack);
    
                Order::upsert(
                    //데이터
                    $pack,
                    //update조건
                    ['ord_no', 'product_id'],
                    //update 또는 insert 할 columns
                    ['mall_id', 'ord_date', 'pay_price', 'ord_total_price', 'point_price', 'coupon_price', 'ord_email']);
                $pack = [];
            });

            //DB upsert 후 해당 파일 삭제
            unlink($file);
        }

        return '임포트 성공!';
    }
}

 

 

참고자료

https://reffect.co.jp/laravel/laravel_excel_master/

 

Laravel Excelをマスターしよう | アールエフェクト

Laravel環境では、Laravel ExcelをインストールすることでEXCELファイルかへのテーブルデータのエクスポート、作成したEXCELファイルからLaravelテーブルへインポートを行うことができます。日本語

reffect.co.jp

https://mtoo.tistory.com/69

 

PHP + AJAX를 사용하여 파일 업로드 하기

PHP + AJAX를 사용하여 파일 업로드 하기 enctype: 'multipart/form-data', // 필수 processData: false, // 필수 contentType: false, // 필수 data: formData, // 필수 ajax_file_upload_test.html : 파일 업로드 ajax 업로드 ajax_file_upload

mtoo.tistory.com

이것 저것 짬뽕한거라 참조자료 누락되었으면 미안합니다요

반응형

+ Recent posts